├── bin ├── cxt2pcap.pl ├── cxtracker2db.pl └── m_carve.php ├── doc ├── COPYING ├── INSTALL ├── README ├── ROADMAP ├── TODO └── sguil-notes.txt ├── etc └── init.d │ ├── cxtracker-redhat │ ├── cxtracker-ubuntu │ └── default │ └── cxtracker └── src ├── Makefile ├── cxtracker.c ├── cxtracker.h ├── format.c ├── format.h ├── ip.c ├── ip.h └── smallcxt.c /bin/cxt2pcap.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl -w 2 | # ---------------------------------------------------------------------- 3 | # cxt2pcap.pl - Carve a session from a cxtracker indexed pcap 4 | # 5 | # Copyright (C) 2010-2013, Edward Fjellskål 6 | # Ian Firns 7 | # 8 | # This program is free software; you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation; either version 2 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program; if not, write to the Free Software 20 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 21 | # ---------------------------------------------------------------------- 22 | 23 | use strict; 24 | use warnings; 25 | use Getopt::Long qw/:config auto_version auto_help/; 26 | 27 | =head1 NAME 28 | 29 | cxt2pcap.pl - Carve a session from a cxtracker indexed pcap 30 | 31 | =head1 VERSION 32 | 33 | 0.0.1 34 | 35 | =head1 SYNOPSIS 36 | 37 | $ cxt2pcap.pl [options] 38 | 39 | OPTIONS: 40 | 41 | -r : pcap file too read from 42 | -w : pcap file to write too 43 | --proto : protocol 44 | --src-ip : Source IP 45 | --dst-ip : Destination IP 46 | --src-port : Source Port 47 | --dst-port : Destination Port 48 | -s : Byteoffset on where to start carving 49 | -e : Byteoffset on where to end carving 50 | -v|--verbose : Verbose output 51 | -d|--debug : Enables debug output 52 | 53 | EXAMPLES: 54 | 55 | cxt2pcap.pl -r /tmp/big.pcap -s 1238374 -e 1833344 --src-ip 10.0.0.1 --src-port 1031 --dst-ip 10.0.0.5 --dst-port 80 --proto 6 -w /tmp/mysession.pcap 56 | 57 | =cut 58 | 59 | my $DEBUG = 0; 60 | my $VERBOSE = 0; 61 | 62 | my ($R_PCAP, $W_PCAP, $BPF, $BS, $BE, $FH_PCAP) = qq(); 63 | my ($SRC_IP, $DST_IP, $SRC_PORT, $DST_PORT, $PROTO, $IP_VERS) = qq(any); 64 | 65 | use constant SEEK_SET => 0; 66 | use constant SEEK_CUR => 1; 67 | use constant SEEK_END => 2; 68 | 69 | # commandline overrides config & defaults 70 | Getopt::Long::GetOptions( 71 | 'r=s' => \$R_PCAP, 72 | 'w=s' => \$W_PCAP, 73 | 'proto=s' => \$PROTO, 74 | 'ipversion=s' => \$IP_VERS, 75 | 'src-ip=s' => \$SRC_IP, 76 | 'dst-ip=s' => \$DST_IP, 77 | 'src-port=s' => \$SRC_PORT, 78 | 'dst-port=s' => \$DST_PORT, 79 | 's=s' => \$BS, 80 | 'e=s' => \$BE, 81 | 'v|verbose' => \$VERBOSE, 82 | 'd|debug' => \$DEBUG, 83 | ); 84 | 85 | print "[*] cxt2pcap starting...\n"; 86 | 87 | if ( -e $R_PCAP ) { 88 | print "[*] Opening $R_PCAP\n" if ($VERBOSE||$DEBUG); 89 | open(RFILE,$R_PCAP) || die ("[E] Failed to open: $R_PCAP !"); 90 | binmode RFILE; 91 | seek(RFILE,0,SEEK_SET); 92 | read(RFILE,$FH_PCAP,24); 93 | } 94 | 95 | #if( ! -f $W_PCAP || ! -s $W_PCAP ){ 96 | print "[*] Opening > $W_PCAP\n" if ($VERBOSE||$DEBUG); 97 | open(WFILE,">$W_PCAP") || die ("[E] Unable to open file $W_PCAP"); 98 | binmode WFILE; 99 | #}else{ 100 | #print "Opening >> $W_PCAP\n" if ($VERBOSE||$DEBUG); 101 | #open(WFILE,">>$W_PCAP") || die("[E] Unable to open file $W_PCAP"); 102 | #binmode WFILE; 103 | #die("[E] Unable to open file $W_PCAP"); 104 | #} 105 | 106 | # SET 107 | seek(RFILE,$BS,SEEK_SET); 108 | sysseek(WFILE,0,SEEK_END); 109 | 110 | my $BUFFER = qq(); 111 | my $PLENGTH = $BE - $BS; 112 | 113 | #READ+FILTER 114 | my $pktHdrFormat = checkFileHdr($FH_PCAP); 115 | while (!eof(RFILE)) { 116 | my $PKTBUFFER = qq(); 117 | my $pktHdr; 118 | 119 | read(RFILE, $pktHdr, 16); 120 | print "[D] Read 16 bytes...\n" if $DEBUG; 121 | my ($pktSecond,$pktMicroSecond,$capturedPktLen,$actualPktLen) = unpack($pktHdrFormat, $pktHdr); 122 | #print "$pktSecond,$pktMicroSecond,$capturedPktLen,$actualPktLen\n"; 123 | if (my $rl = read(RFILE, $PKTBUFFER, $capturedPktLen) != $capturedPktLen ) { 124 | print "[W] Failed to read pkt data: $capturedPktLen/$rl:$!\n"; 125 | last; 126 | } 127 | 128 | # Check eth_header 129 | # ** VLAN ? 130 | # If IP - Check proto 131 | my $tproto = unpack("C", substr($PKTBUFFER, 23,1)); 132 | print "[*] Processing a packet with protocol nr: $tproto\n" if $DEBUG; 133 | # use switch/case instead! 134 | if ($IP_VERS == 10) { 135 | $BUFFER .= "$pktHdr$PKTBUFFER" if processIPvSixPkt($PKTBUFFER); 136 | } 137 | if ($tproto == 6 && $PROTO == 6 ) { 138 | $BUFFER .= "$pktHdr$PKTBUFFER" if processTCPPkt($PKTBUFFER); 139 | } elsif ($tproto == 17 && $PROTO == 17 ) { 140 | $BUFFER .= "$pktHdr$PKTBUFFER" if processUDPPkt($PKTBUFFER); 141 | } elsif ($tproto == 1 && $PROTO == 1 ) { 142 | $BUFFER .= "$pktHdr$PKTBUFFER" if processICMPPkt($PKTBUFFER); 143 | } else { 144 | $BUFFER .= "$pktHdr$PKTBUFFER" if processANYPkt($PKTBUFFER); 145 | } 146 | if (tell RFILE > $BE) { 147 | print "[*] Last byte position in READ reached ($BE)\n" if ($VERBOSE||$DEBUG); 148 | last; 149 | } 150 | } 151 | 152 | print "[*] " . tell RFILE if ($VERBOSE||$DEBUG); 153 | print " > $BE\n" if ($VERBOSE||$DEBUG); 154 | 155 | # WRITE 156 | my $BUFFLENGTH=length($BUFFER); 157 | print "[D] Writing pcap-file-header to $W_PCAP\n" if $DEBUG; 158 | syswrite(WFILE,"$FH_PCAP",24) || die ("[E] Failed to write pcap-file-header to $W_PCAP"); 159 | print "[D] Writing session to $W_PCAP ($BUFFLENGTH Bytes)\n" if $DEBUG; 160 | if ($BUFFLENGTH ne 0) { 161 | syswrite(WFILE,$BUFFER,$BUFFLENGTH) || die ("[E] Failed to write session to $W_PCAP"); 162 | } else { 163 | print "[*] No session data found!\n"; 164 | } 165 | 166 | # END 167 | close(RFILE); 168 | close(WFILE); 169 | 170 | sub checkFileHdr { 171 | my $fHdr = shift; 172 | my $signature = unpack("N", substr($fHdr,0,4)); 173 | if ($signature == 0xa1b2c3d4) { 174 | return "NNNN"; 175 | } elsif ($signature == 0xd4c3b2a1) { 176 | return "VVVV"; 177 | } else { 178 | die "[E] Unknown PCAP Header Format!"; 179 | } 180 | } 181 | 182 | sub processTCPPkt { 183 | my $pktBuf = shift; 184 | my $srcip = substr($pktBuf, 26,4); 185 | my $dstip = substr($pktBuf, 30,4); 186 | my $srcport = substr($pktBuf, 34,2); 187 | my $dstport = substr($pktBuf, 36,2); 188 | my $binstr = "$srcip$srcport$dstip$dstport"; 189 | printSession ($binstr) if ($DEBUG || $VERBOSE); 190 | my @B = unpack("C*", $binstr); 191 | $srcip = "$B[0].$B[1].$B[2].$B[3]"; 192 | $dstip = "$B[6].$B[7].$B[8].$B[9]"; 193 | $srcport = $B[4]*256+$B[5]; 194 | $dstport = $B[10]*256+$B[11]; 195 | if (( $srcip eq $SRC_IP && $dstip eq $DST_IP ) || ( $srcip eq $DST_IP && $dstip eq $SRC_IP )) { 196 | if (( $srcport eq $SRC_PORT && $dstport eq $DST_PORT ) || ( $srcport eq $DST_PORT && $dstport eq $SRC_PORT )) { 197 | print "[D] Got matching TCP packet\n" if $VERBOSE; 198 | return 1; 199 | } 200 | } 201 | return 0; 202 | } 203 | 204 | sub processUDPPkt { 205 | # Work in progress 206 | my $pktBuf = shift; 207 | my $srcip = substr($pktBuf, 26,4); 208 | my $dstip = substr($pktBuf, 30,4); 209 | my $srcport = substr($pktBuf, 34,2); 210 | my $dstport = substr($pktBuf, 36,2); 211 | my $binstr = "$srcip$srcport$dstip$dstport"; 212 | printSession ($binstr) if ($DEBUG || $VERBOSE); 213 | my @B = unpack("C*", $binstr); 214 | $srcip = "$B[0].$B[1].$B[2].$B[3]"; 215 | $dstip = "$B[6].$B[7].$B[8].$B[9]"; 216 | $srcport = $B[4]*256+$B[5]; 217 | $dstport = $B[10]*256+$B[11]; 218 | if (( $srcip eq $SRC_IP && $dstip eq $DST_IP ) || ( $srcip eq $DST_IP && $dstip eq $SRC_IP )) { 219 | if (( $srcport eq $SRC_PORT && $dstport eq $DST_PORT ) || ( $srcport eq $DST_PORT && $dstport eq $SRC_PORT )) { 220 | print "[D] Got matching UDP packet\n" if $VERBOSE; 221 | return 1; 222 | } 223 | } 224 | return 0; 225 | } 226 | 227 | sub processICMPPkt { 228 | # Work in progress 229 | my $pktBuf = shift; 230 | my $srcip = substr($pktBuf, 26,4); 231 | my $dstip = substr($pktBuf, 30,4); 232 | #my $srcport = 0; # change to icmp id/type? 233 | #my $dstport = 0; # change to icmp id/type? 234 | my $binstr = "$srcip$dstip"; 235 | printSession ($binstr) if ($DEBUG || $VERBOSE); 236 | my @B = unpack("C*", $binstr); 237 | $srcip = "$B[0].$B[1].$B[2].$B[3]"; 238 | $dstip = "$B[4].$B[5].$B[6].$B[7]"; 239 | if (( $srcip eq $SRC_IP && $dstip eq $DST_IP ) || ( $srcip eq $DST_IP && $dstip eq $SRC_IP )) { 240 | return 1; 241 | } 242 | return 0; 243 | } 244 | 245 | sub processIPvSixPkt { 246 | my $pktBuf = shift; 247 | my $srcip = substr($pktBuf, 22,16); 248 | my $dstip = substr($pktBuf, 38,16); 249 | my $binstr = "$srcip$dstip"; 250 | printSession ($binstr) if ($DEBUG || $VERBOSE); 251 | my @B = unpack("C*", $binstr); 252 | foreach (@B) 253 | { 254 | $_ = sprintf("%02x", $_); 255 | } 256 | $srcip = "$B[0]$B[1]:$B[2]$B[3]:$B[4]$B[5]:$B[6]$B[7]:$B[8]$B[9]:$B[10]$B[11]:$B[12]$B[13]:$B[14]$B[15]"; 257 | $dstip = "$B[16]$B[17]:$B[18]$B[19]:$B[20]$B[21]:$B[22]$B[23]:$B[24]$B[25]:$B[26]$B[27]:$B[28]$B[29]:$B[30]$B[31]"; 258 | if (( $srcip eq $SRC_IP && $dstip eq $DST_IP ) || ( $srcip eq $DST_IP && $dstip eq $SRC_IP )) { 259 | return 1; 260 | } 261 | return 0; 262 | } 263 | 264 | sub processANYPkt { 265 | my $pktBuf = shift; 266 | my $srcip = substr($pktBuf, 26,4); 267 | my $dstip = substr($pktBuf, 30,4); 268 | my $binstr = "$srcip$dstip"; 269 | printSession ($binstr) if ($DEBUG || $VERBOSE); 270 | my @B = unpack("C*", $binstr); 271 | $srcip = "$B[0].$B[1].$B[2].$B[3]"; 272 | $dstip = "$B[4].$B[5].$B[6].$B[7]"; 273 | if (( $srcip eq $SRC_IP && $dstip eq $DST_IP ) || ( $srcip eq $DST_IP && $dstip eq $SRC_IP )) { 274 | return 1; 275 | } 276 | return 0; 277 | } 278 | 279 | sub printSession { 280 | my $session = shift; 281 | my @B = unpack("C*", $session); 282 | printf "[Session] %d.%d.%d.%d:%d --> %d.%d.%d.%d:%d\n", 283 | $B[0], $B[1],$B[2],$B[3], $B[4]*256+$B[5], $B[6],$B[7],$B[8],$B[9], $B[10]*256+$B[11]; 284 | } 285 | -------------------------------------------------------------------------------- /bin/cxtracker2db.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl -w 2 | # 3 | # Copyright (C) 2010 - 2013, Edward Fjellskål 4 | # Copyright (C) 2011 - 2013, Ian Firns 5 | # 6 | # This file is a part of cxtracker: https://github.com/gamelinux/cxtracker 7 | # 8 | use strict; 9 | use warnings; 10 | use POSIX qw(setsid); 11 | use DateTime; 12 | use Getopt::Long qw/:config auto_version auto_help/; 13 | use Sys::Hostname; 14 | use DBI; 15 | 16 | =head1 NAME 17 | 18 | cxtracker2db.pl - Load session metadata from cxtracker into a db 19 | 20 | =head1 VERSION 21 | 22 | 0.2 23 | 24 | =head1 SYNOPSIS 25 | 26 | $ cxtracker2db.pl [options] 27 | 28 | OPTIONS: 29 | 30 | --dir : set the dir to monitor for session files 31 | --daemon : enables daemon mode 32 | --hostname : specify the hostname 33 | --debug : enable debug messages (default: 0 (disabled)) 34 | --help : this help message 35 | --version : show cxtracker2db.pl version 36 | --format : "indexed" or "standard" database format 37 | 38 | =cut 39 | 40 | our $VERSION = 0.2; 41 | our $DEBUG = 0; 42 | our $DAEMON = 0; 43 | our $NOT_ESTABLISHED = 1; 44 | our $FORMAT = "any"; 45 | our $INPUTFORMAT = 0; 46 | our $TIMEOUT = 5; 47 | our $HOSTNAME = hostname; 48 | my $SDIR = ""; 49 | my $FDIR = ""; 50 | my $LOGFILE = q(/var/log/cxtracker2db.log); 51 | my $PIDFILE = q(/var/run/cxtracker2db.pid); 52 | our $DB_NAME = "cxtracker"; 53 | our $DB_HOST = "127.0.0.1"; 54 | our $DB_PORT = "3306"; 55 | our $DB_USERNAME = "cxtracker"; 56 | our $DB_PASSWORD = "cxtracker"; 57 | our $DBI = "DBI:mysql:$DB_NAME:$DB_HOST:$DB_PORT"; 58 | our $AUTOCOMMIT = 0; 59 | my $SANCP_DB = {}; 60 | 61 | GetOptions( 62 | 'dir=s' => \$SDIR, 63 | 'hostname=s' => \$HOSTNAME, 64 | 'debug=s' => \$DEBUG, 65 | 'daemon' => \$DAEMON, 66 | 'format=s' => \$FORMAT 67 | ); 68 | 69 | if ($SDIR eq "") { 70 | $SDIR = "/nsm_data/$HOSTNAME/session/"; 71 | $FDIR = "$SDIR/failed/"; 72 | } else { 73 | $FDIR = "$SDIR/failed/"; 74 | } 75 | 76 | check_dir($SDIR); 77 | check_dir($FDIR); 78 | 79 | # Signal handlers 80 | use vars qw(%sources); 81 | $SIG{"HUP"} = \&recreate_merge_table; 82 | $SIG{"INT"} = sub { game_over() }; 83 | $SIG{"TERM"} = sub { game_over() }; 84 | $SIG{"QUIT"} = sub { game_over() }; 85 | $SIG{"KILL"} = sub { game_over() }; 86 | #$SIG{"ALRM"} = sub { dir_watch(); alarm $TIMEOUT; }; 87 | 88 | warn "[*] Starting cxtracker2db.pl...\n"; 89 | 90 | # Prepare to meet the world of Daemons 91 | if ( $DAEMON ) { 92 | print "[*] Daemonizing...\n"; 93 | chdir ("/") or die "chdir /: $!\n"; 94 | open (STDIN, "/dev/null") or die "open /dev/null: $!\n"; 95 | open (STDOUT, "> $LOGFILE") or die "open > $LOGFILE: $!\n"; 96 | defined (my $dpid = fork) or die "fork: $!\n"; 97 | if ($dpid) { 98 | # Write PID file 99 | open (PID, "> $PIDFILE") or die "open($PIDFILE): $!\n"; 100 | print PID $dpid, "\n"; 101 | close (PID); 102 | exit 0; 103 | } 104 | setsid (); 105 | open (STDERR, ">&STDOUT"); 106 | } 107 | 108 | warn "[*] Connecting to database...\n"; 109 | my $dbh = DBI->connect($DBI,$DB_USERNAME,$DB_PASSWORD, {RaiseError => 1, mysql_auto_reconnect=>1}) or die "$DBI::errstr"; 110 | # Make todays table, and initialize the session merged table 111 | setup_db(); 112 | 113 | # Start dir_watch() which looks for new session files and put them into db 114 | warn "[*] Looking for session data in: $SDIR \n" if $DEBUG; 115 | dir_watch(); 116 | exit; 117 | 118 | =head1 FUNCTIONS 119 | 120 | =head2 dir_watch 121 | 122 | This sub looks for new session data in a dir. 123 | Takes $dir to watch as input. 124 | 125 | =cut 126 | 127 | sub dir_watch { 128 | #infinite loop 129 | while (1) { 130 | my @FILES; 131 | # Open the directory 132 | if( opendir( DIR, $SDIR ) ) { 133 | # Find session files in dir (stats.eth0.1229062136) 134 | while( my $FILE = readdir( DIR ) ) { 135 | next if( ( "." eq $FILE ) || ( ".." eq $FILE ) ); 136 | next unless ($FILE =~ /^stats\..*\.\d{10}$/); 137 | push( @FILES, $FILE ) if( -f "$SDIR$FILE" ); 138 | } 139 | closedir( DIR ); 140 | } 141 | foreach my $FILE ( @FILES ) { 142 | my $result = get_session ("$SDIR$FILE"); 143 | if ($result == 1) { 144 | rename ("$SDIR$FILE", "$FDIR$FILE") or warn "[*] Couldn't move $SDIR$FILE to $FDIR$FILE: $!\n"; 145 | } 146 | unlink("$SDIR$FILE") if $result == 0; 147 | } 148 | # Dont pool files to often, or to seldom... 149 | sleep $TIMEOUT; 150 | } 151 | } 152 | 153 | =head2 get_session 154 | 155 | This sub extracts the session data from a session data file. 156 | Takes $file as input parameter. 157 | 158 | =cut 159 | 160 | sub get_session { 161 | my $SFILE = shift; 162 | my $result = 0; 163 | my %signatures; 164 | if (open (FILE, $SFILE)) { 165 | print "[*] Found session file: ".$SFILE."\n" if $DEBUG; 166 | # Verify the data in the session files 167 | LINE: 168 | while (my $line = readline FILE) { 169 | chomp $line; 170 | $line =~ /^\d{19}/; 171 | unless($line) { 172 | warn "[*] Error: Not valid session start format in: '$SFILE'"; 173 | next LINE; 174 | } 175 | my @elements = split/\|/,$line; 176 | if (@elements == 15) { 177 | if ($FORMAT eq "indexed") { 178 | warn "[*] Error: Not valid Nr. of session args for format: '$FORMAT' in: '$SFILE'"; 179 | next LINE; 180 | } elsif ($INPUTFORMAT == 0) { 181 | $INPUTFORMAT = 1; 182 | if ($FORMAT eq "any") { 183 | #the user gave us no guidance on format 184 | #we are now getting guidance from the 185 | #input file for the first time 186 | #construct the db accordingly 187 | #as it had previously been waiting on this. 188 | $FORMAT = "standard"; 189 | setup_db(); 190 | } 191 | } 192 | } elsif (@elements == 20) { 193 | if ($FORMAT eq "standard") { 194 | if($INPUTFORMAT == 0) { 195 | $INPUTFORMAT = 2; 196 | } 197 | #warn "[*] Reading indexed input as standard in: '$SFILE'"; 198 | } elsif ($INPUTFORMAT == 0) { 199 | $INPUTFORMAT = 2; 200 | if ($FORMAT eq "any") { 201 | #the user gave us no guidance on format 202 | #we are now getting guidance from the 203 | #input file for the first time 204 | #construct the db accordingly 205 | #as it had previously been waiting on this. 206 | my $tablename = get_table_name(); 207 | my $tableformat = check_table_format($tablename); 208 | #check if we need to roll indexed or standard 209 | if($tableformat == 1) { 210 | $FORMAT = "standard"; 211 | } else { 212 | $FORMAT = "indexed"; 213 | } 214 | #$FORMAT = "indexed"; 215 | setup_db(); 216 | } 217 | } 218 | } else { 219 | warn "[*] Error: Not valid Nr. of session args format in: '$SFILE'"; 220 | next LINE; 221 | } 222 | # Things should be OK now to send to the DB 223 | $result = put_session2db($line); 224 | } 225 | close FILE; 226 | } 227 | return $result; 228 | } 229 | 230 | =head2 ip_is_ipv6 231 | 232 | Check if an IP address is version 6 233 | returns 1 if true, 0 if false 234 | 235 | =cut 236 | 237 | sub ip_is_ipv6 { 238 | my $ip = shift; 239 | 240 | # Count octets 241 | my $n = ($ip =~ tr/:/:/); 242 | return (0) unless ($n > 0 and $n < 8); 243 | 244 | # $k is a counter 245 | my $k; 246 | 247 | foreach (split /:/, $ip) { 248 | $k++; 249 | 250 | # Empty octet ? 251 | next if ($_ eq ''); 252 | 253 | # Normal v6 octet ? 254 | next if (/^[a-f\d]{1,4}$/i); 255 | 256 | # Last octet - is it IPv4 ? 257 | if ($k == $n + 1) { 258 | next if (ip_is_ipv4($_)); 259 | } 260 | 261 | print "[*] Invalid IP address $ip"; 262 | return 0; 263 | } 264 | 265 | # Does the IP address start with : ? 266 | if ($ip =~ m/^:[^:]/) { 267 | print "[*] Invalid address $ip (starts with :)"; 268 | return 0; 269 | } 270 | 271 | # Does the IP address finish with : ? 272 | if ($ip =~ m/[^:]:$/) { 273 | print "[*] Invalid address $ip (ends with :)"; 274 | return 0; 275 | } 276 | 277 | # Does the IP address have more than one '::' pattern ? 278 | if ($ip =~ s/:(?=:)//g > 1) { 279 | print "[*] Invalid address $ip (More than one :: pattern)"; 280 | return 0; 281 | } 282 | 283 | return 1; 284 | } 285 | 286 | =head2 expand_ipv6 287 | 288 | Expands a IPv6 address from short notation 289 | 290 | =cut 291 | 292 | sub expand_ipv6 { 293 | 294 | my $ip = shift; 295 | 296 | # Keep track of :: 297 | $ip =~ s/::/:!:/; 298 | 299 | # IP as an array 300 | my @ip = split /:/, $ip; 301 | 302 | # Number of octets 303 | my $num = scalar(@ip); 304 | 305 | # Now deal with '::' ('000!') 306 | foreach (0 .. (scalar(@ip) - 1)) { 307 | 308 | # Find the pattern 309 | next unless ($ip[$_] eq '!'); 310 | 311 | # @empty is the IP address 0 312 | my @empty = map { $_ = '0' x 4 } (0 .. 7); 313 | 314 | # Replace :: with $num '0000' octets 315 | $ip[$_] = join ':', @empty[ 0 .. 8 - $num ]; 316 | last; 317 | } 318 | 319 | # Now deal with octets where there are less then 4 enteries 320 | my @ip_long = split /:/, (lc(join ':', @ip)); 321 | foreach (0 .. (scalar(@ip_long) -1 )) { 322 | 323 | # Next if we have our 4 enteries 324 | next if ( $ip_long[$_] =~ /^[a-f\d]{4}$/ ); 325 | 326 | # Push '0' until we match 327 | while (!($ip_long[$_] =~ /[a-f\d]{4,}/)) { 328 | $ip_long[$_] =~ s/^/0/; 329 | } 330 | } 331 | 332 | return (lc(join ':', @ip_long)); 333 | } 334 | 335 | =head2 sanitize_table_name 336 | 337 | Takes a string and makes it MySQL table friendly... 338 | 339 | =cut 340 | 341 | sub sanitize_table_name { 342 | my $string = shift; 343 | $string =~ s/[^A-Za-z_]/_/g; 344 | return $string; 345 | } 346 | 347 | =head2 put_session2db 348 | 349 | takes a session line as input and stores it in DB 350 | 351 | =cut 352 | 353 | sub put_session2db { 354 | my $SESSION = shift; 355 | my $tablename = get_table_name(); 356 | my $ip_version = 2; # AF_INET 357 | 358 | # Check if table exists, if not create and make new session merge table 359 | if ( ! checkif_table_exist($tablename) ) { 360 | new_session_table($tablename); 361 | recreate_merge_table(); 362 | } 363 | 364 | my( $cx_id, $s_t, $e_t, $tot_time, $ip_type, $src_dip, $src_port, 365 | $dst_dip, $dst_port, $src_packets, $src_byte, $dst_packets, $dst_byte, 366 | $src_flags, $dst_flags, $ip_versionb, $s_file, $s_byte, 367 | $e_file, $e_byte) = split /\|/, $SESSION, 20; 368 | 369 | if ( $NOT_ESTABLISHED && $ip_type == 6 ) { 370 | if ( $tot_time <= 1 ) { # Do we need this ? 371 | if ( $src_packets == 0 || $dst_packets == 0 ) { # What if a reset is sent? 372 | # $src_flags & $dst_flags ? 373 | warn "Skipping record: Only established TCP sessions are recorded\n" if $DEBUG; 374 | return 0; 375 | } 376 | } 377 | } 378 | 379 | if ( ip_is_ipv6($src_dip) || ip_is_ipv6($dst_dip) ) { 380 | $src_dip = expand_ipv6($src_dip); 381 | $dst_dip = expand_ipv6($dst_dip); 382 | $src_dip = "INET_ATON6(\'$src_dip\')"; 383 | $dst_dip = "INET_ATON6(\'$dst_dip\')"; 384 | $ip_version = 10; # AF_INET6 385 | } else { 386 | if ($INPUTFORMAT == 2) { 387 | $src_dip = "INET_ATON(\'$src_dip\')"; 388 | $dst_dip = "INET_ATON(\'$dst_dip\')"; 389 | } 390 | } 391 | 392 | my ($sql, $sth); 393 | 394 | if ($FORMAT eq "standard") { 395 | eval{ 396 | 397 | $sql = qq[ 398 | INSERT INTO $tablename ( 399 | sid,sessionid,start_time,end_time,duration,ip_proto, 400 | src_ip,src_port,dst_ip,dst_port,src_pkts,src_bytes, 401 | dst_pkts,dst_bytes,src_flags,dst_flags,ip_version 402 | ) VALUES ( 403 | '$HOSTNAME','$cx_id','$s_t','$e_t','$tot_time', 404 | '$ip_type',$src_dip,'$src_port',$dst_dip,'$dst_port', 405 | '$src_packets','$src_byte','$dst_packets','$dst_byte', 406 | '$src_flags','$dst_flags','$ip_version' 407 | )]; 408 | 409 | $sth = $dbh->prepare($sql); 410 | $sth->execute; 411 | $sth->finish; 412 | }; 413 | } 414 | 415 | if ($FORMAT eq "indexed") { 416 | eval{ 417 | 418 | $sql = qq[ 419 | INSERT INTO $tablename ( 420 | sid,sessionid,start_time,end_time,duration,ip_proto, 421 | src_ip,src_port,dst_ip,dst_port,src_pkts,src_bytes, 422 | dst_pkts,dst_bytes,src_flags,dst_flags,ip_version, 423 | s_file,s_byte,e_file,e_byte 424 | ) VALUES ( 425 | '$HOSTNAME','$cx_id','$s_t','$e_t','$tot_time', 426 | '$ip_type',$src_dip,'$src_port',$dst_dip,'$dst_port', 427 | '$src_packets','$src_byte','$dst_packets','$dst_byte', 428 | '$src_flags','$dst_flags','$ip_version','$s_file', 429 | '$s_byte','$e_file','$e_byte' 430 | )]; 431 | 432 | $sth = $dbh->prepare($sql); 433 | $sth->execute; 434 | $sth->finish; 435 | }; 436 | } 437 | 438 | if ($@) { 439 | # Failed 440 | return 1; 441 | } 442 | return 0; 443 | } 444 | 445 | =head2 setup_db 446 | 447 | Create todays table if it dont exist (session_hostname_date). 448 | Make a new merge of all session_% tables. 449 | 450 | =cut 451 | 452 | sub setup_db { 453 | my $tablename = get_table_name(); 454 | my $tableformat = check_table_format($tablename); 455 | print "[*] tableformat: $tableformat\n" if $DEBUG; 456 | my $sessiontables = find_session_tables(); # 457 | print "[*] session tables: $sessiontables\n" if $DEBUG; 458 | if ($sessiontables) { 459 | if(($tableformat == 2) && ($FORMAT eq "standard")) { 460 | die("[E] ERROR: Database Format mismatch!\n"); 461 | } 462 | if(($tableformat == 1) && ($FORMAT eq "indexed")) { 463 | die("[E] ERROR: Database format mismatch!\n"); 464 | } 465 | if(($tableformat == 0) && ($FORMAT eq "any")) { 466 | if ($INPUTFORMAT == 0) { 467 | #there's neither input nor argument 468 | #to specify what kind. 469 | #and therefor no harm in waiting. 470 | return; 471 | } 472 | if ($INPUTFORMAT == 1) { 473 | $FORMAT = "standard"; 474 | } 475 | if ($INPUTFORMAT == 2) { 476 | $FORMAT = "indexed"; 477 | } 478 | } 479 | } 480 | 481 | new_session_table($tablename); 482 | delete_merged_session_table(); 483 | $sessiontables = find_session_tables(); 484 | print "[*] session tables2: $sessiontables\n" if $DEBUG; 485 | merge_session_tables($sessiontables); 486 | return; 487 | } 488 | 489 | =head2 check_table_format 490 | 491 | Checks to see if table is indexed. Returns 1 if table is standard, 492 | 2 if table is indexed, 0 if table does not exist. 493 | 494 | =cut 495 | 496 | sub check_table_format { 497 | my ($tablename) = shift; 498 | my $tableformat = 0; 499 | 500 | if(checkif_table_exist($tablename)) { 501 | my ($sql, $sth); 502 | # $sql = "SHOW COLUMNS FROM $tablename LIKE 's_file'"; 503 | $sql = "SELECT * FROM $tablename WHERE 1=0"; 504 | $sth = $dbh->prepare($sql); 505 | $sth->execute; 506 | # my $tester = $sth->{'NAME'}; 507 | my $cols = @{$sth->{NAME}}; # or NAME_lc if needed 508 | print "[*] cols = $cols\n" if $DEBUG; 509 | # print "\n tester = $tester \n"; 510 | if($cols == 21) { 511 | $tableformat = 2; 512 | } 513 | else { 514 | $tableformat = 1; 515 | } 516 | } 517 | return $tableformat; 518 | } 519 | 520 | =head2 new_session_table 521 | 522 | Creates a new session_$hostname_$date table 523 | Takes $hostname and $date as input. 524 | 525 | =cut 526 | 527 | sub new_session_table { 528 | my ($tablename) = shift; 529 | my ($sql, $sth); 530 | 531 | return 0 if ($FORMAT eq "any"); 532 | 533 | if ($FORMAT eq "standard") { 534 | warn "[*] Creating standard session table\n" if $DEBUG; 535 | $sql = " \ 536 | CREATE TABLE IF NOT EXISTS $tablename \ 537 | ( \ 538 | sid INT(10) UNSIGNED NOT NULL, \ 539 | sessionid BIGINT(20) UNSIGNED NOT NULL, \ 540 | start_time DATETIME NOT NULL, \ 541 | end_time DATETIME NOT NULL, \ 542 | duration INT(10) UNSIGNED NOT NULL, \ 543 | ip_proto TINYINT UNSIGNED NOT NULL, \ 544 | src_ip DECIMAL(39,0) UNSIGNED, \ 545 | src_port SMALLINT UNSIGNED, \ 546 | dst_ip DECIMAL(39,0) UNSIGNED, \ 547 | dst_port SMALLINT UNSIGNED, \ 548 | src_pkts INT UNSIGNED NOT NULL, \ 549 | src_bytes INT UNSIGNED NOT NULL, \ 550 | dst_pkts INT UNSIGNED NOT NULL, \ 551 | dst_bytes INT UNSIGNED NOT NULL, \ 552 | src_flags TINYINT UNSIGNED NOT NULL, \ 553 | dst_flags TINYINT UNSIGNED NOT NULL, \ 554 | ip_version TINYINT UNSIGNED NOT NULL, \ 555 | PRIMARY KEY (sid,sessionid), \ 556 | INDEX src_ip (src_ip), \ 557 | INDEX dst_ip (dst_ip), \ 558 | INDEX dst_port (dst_port), \ 559 | INDEX src_port (src_port), \ 560 | INDEX start_time (start_time) \ 561 | ) ENGINE=MyISAM \ 562 | "; 563 | } 564 | 565 | if ($FORMAT eq "indexed") { 566 | warn "[*] Creating indexed session table\n" if $DEBUG; 567 | $sql = " \ 568 | CREATE TABLE IF NOT EXISTS $tablename \ 569 | ( \ 570 | sid INT(10) UNSIGNED NOT NULL, \ 571 | sessionid BIGINT(20) UNSIGNED NOT NULL, \ 572 | start_time DATETIME NOT NULL, \ 573 | end_time DATETIME NOT NULL, \ 574 | duration INT(10) UNSIGNED NOT NULL, \ 575 | ip_proto TINYINT UNSIGNED NOT NULL, \ 576 | src_ip DECIMAL(39,0) UNSIGNED, \ 577 | src_port SMALLINT UNSIGNED, \ 578 | dst_ip DECIMAL(39,0) UNSIGNED, \ 579 | dst_port SMALLINT UNSIGNED, \ 580 | src_pkts INT UNSIGNED NOT NULL, \ 581 | src_bytes INT UNSIGNED NOT NULL, \ 582 | dst_pkts INT UNSIGNED NOT NULL, \ 583 | dst_bytes INT UNSIGNED NOT NULL, \ 584 | src_flags TINYINT UNSIGNED NOT NULL, \ 585 | dst_flags TINYINT UNSIGNED NOT NULL, \ 586 | ip_version TINYINT UNSIGNED NOT NULL, \ 587 | s_file VARCHAR(15) NOT NULL, \ 588 | s_byte BIGINT UNSIGNED NOT NULL, \ 589 | e_file VARCHAR(15) NOT NULL, \ 590 | e_byte BIGINT UNSIGNED NOT NULL, \ 591 | PRIMARY KEY (sid,sessionid), \ 592 | INDEX src_ip (src_ip), \ 593 | INDEX dst_ip (dst_ip), \ 594 | INDEX dst_port (dst_port), \ 595 | INDEX src_port (src_port), \ 596 | INDEX start_time (start_time) \ 597 | ) ENGINE=MyISAM \ 598 | "; 599 | } 600 | 601 | eval{ 602 | $sth = $dbh->prepare($sql); 603 | $sth->execute; 604 | $sth->finish; 605 | }; 606 | 607 | if ($@) { 608 | # Failed 609 | return 1; 610 | } 611 | return 0; 612 | } 613 | 614 | =head2 find_session_tables 615 | 616 | Find all session_% tables 617 | 618 | =cut 619 | 620 | sub find_session_tables { 621 | my ($sql, $sth); 622 | my $tables = q(); 623 | $sql = q(SHOW TABLES LIKE 'session_%'); 624 | $sth = $dbh->prepare($sql); 625 | $sth->execute; 626 | while (my @array = $sth->fetchrow_array) { 627 | my $table = $array[0]; 628 | $tables = "$tables $table,"; 629 | } 630 | $sth->finish; 631 | $tables =~ s/,$//; 632 | return $tables; 633 | } 634 | 635 | =head2 delete_merged_session_table 636 | 637 | Deletes the session merged table if it exists. 638 | 639 | =cut 640 | 641 | sub delete_merged_session_table { 642 | my ($sql, $sth); 643 | eval{ 644 | $sql = "DROP TABLE IF EXISTS session"; 645 | $sth = $dbh->prepare($sql); 646 | $sth->execute; 647 | $sth->finish; 648 | }; 649 | if ($@) { 650 | # Failed 651 | warn "[*] Drop table session failed...\n" if $DEBUG; 652 | return 1; 653 | } 654 | warn "[*] Dropped table session...\n" if $DEBUG; 655 | return 0; 656 | } 657 | 658 | =head2 merge_session_tables 659 | 660 | Creates a new session merge table 661 | 662 | =cut 663 | 664 | sub merge_session_tables { 665 | my $tables = shift; 666 | my ($sql, $sth); 667 | 668 | return 0 if ($FORMAT eq "any"); 669 | 670 | # Maybe check for != MRG_MyISAM - then exit 671 | 672 | if ($FORMAT eq "standard") { 673 | warn "[*] Creating standard session MERGE table\n" if $DEBUG; 674 | $sql = " \ 675 | CREATE TABLE session \ 676 | ( \ 677 | sid INT(0) UNSIGNED NOT NULL, \ 678 | sessionid BIGINT(20) UNSIGNED NOT NULL, \ 679 | start_time DATETIME NOT NULL, \ 680 | end_time DATETIME NOT NULL, \ 681 | duration INT(10) UNSIGNED NOT NULL, \ 682 | ip_proto TINYINT(3) UNSIGNED NOT NULL, \ 683 | ip_version TINYINT(3) UNSIGNED NOT NULL, \ 684 | src_ip DECIMAL(39,0) UNSIGNED, \ 685 | src_port SMALLINT UNSIGNED, \ 686 | dst_ip DECIMAL(39,0) UNSIGNED, \ 687 | dst_port SMALLINT UNSIGNED, \ 688 | src_pkts INT UNSIGNED NOT NULL, \ 689 | src_bytes INT UNSIGNED NOT NULL, \ 690 | dst_pkts INT UNSIGNED NOT NULL, \ 691 | dst_bytes INT UNSIGNED NOT NULL, \ 692 | src_flags TINYINT UNSIGNED NOT NULL, \ 693 | dst_flags TINYINT UNSIGNED NOT NULL, \ 694 | INDEX p_key (sid,sessionid), \ 695 | INDEX src_ip (src_ip), \ 696 | INDEX dst_ip (dst_ip), \ 697 | INDEX dst_port (dst_port), \ 698 | INDEX src_port (src_port), \ 699 | INDEX start_time (start_time) \ 700 | ) ENGINE=MERGE UNION=($tables) \ 701 | "; 702 | } 703 | if ($FORMAT eq "indexed") { 704 | warn "[*] Creating indexed session MERGE table\n" if $DEBUG; 705 | $sql = " \ 706 | CREATE TABLE session \ 707 | ( \ 708 | sid INT(10) UNSIGNED NOT NULL, \ 709 | sessionid BIGINT(20) UNSIGNED NOT NULL, \ 710 | start_time DATETIME NOT NULL, \ 711 | end_time DATETIME NOT NULL, \ 712 | duration INT(10) UNSIGNED NOT NULL, \ 713 | ip_proto TINYINT UNSIGNED NOT NULL, \ 714 | src_ip DECIMAL(39,0) UNSIGNED, \ 715 | src_port SMALLINT UNSIGNED, \ 716 | dst_ip DECIMAL(39,0) UNSIGNED, \ 717 | dst_port SMALLINT UNSIGNED, \ 718 | src_pkts INT UNSIGNED NOT NULL, \ 719 | src_bytes INT UNSIGNED NOT NULL, \ 720 | dst_pkts INT UNSIGNED NOT NULL, \ 721 | dst_bytes INT UNSIGNED NOT NULL, \ 722 | src_flags TINYINT UNSIGNED NOT NULL, \ 723 | dst_flags TINYINT UNSIGNED NOT NULL, \ 724 | ip_version TINYINT UNSIGNED NOT NULL, \ 725 | s_file VARCHAR(15) NOT NULL, \ 726 | s_byte BIGINT UNSIGNED NOT NULL, \ 727 | e_file VARCHAR(15) NOT NULL, \ 728 | e_byte BIGINT UNSIGNED NOT NULL, \ 729 | PRIMARY KEY (sid,sessionid), \ 730 | INDEX src_ip (src_ip), \ 731 | INDEX dst_ip (dst_ip), \ 732 | INDEX dst_port (dst_port), \ 733 | INDEX src_port (src_port), \ 734 | INDEX start_time (start_time) \ 735 | ) ENGINE=MERGE UNION=($tables) \ 736 | "; 737 | } 738 | 739 | eval { 740 | $sth = $dbh->prepare($sql); 741 | $sth->execute; 742 | $sth->finish; 743 | }; 744 | if ($@) { 745 | # Failed 746 | warn "[*] Create session MERGE table failed!\n" if $DEBUG; 747 | return 1; 748 | } 749 | return 0; 750 | } 751 | 752 | =head2 get_table_name 753 | 754 | makes a table name, format: session_$HOSTNAME_$DATE 755 | 756 | =cut 757 | 758 | sub get_table_name { 759 | my $DATE = `date --iso`; 760 | $DATE =~ s/\-//g; 761 | $DATE =~ s/\n$//; 762 | my $tablename = "session_" . sanitize_table_name($HOSTNAME) . "_" . "$DATE"; 763 | return $tablename; 764 | } 765 | 766 | =head2 checkif_table_exist 767 | 768 | Checks if a table exists. Takes $tablename as input and 769 | returns 1 if $tablename exists, and 0 if not. 770 | 771 | =cut 772 | 773 | sub checkif_table_exist { 774 | my $tablename = shift; 775 | my ($sql, $sth); 776 | eval { 777 | $sql = "select count(*) from $tablename where 1=0"; 778 | $dbh->do($sql); 779 | }; 780 | if ($dbh->err) { 781 | warn "Table $tablename does not exist.\n" if $DEBUG; 782 | return 0; 783 | } 784 | else{ 785 | return 1; 786 | } 787 | } 788 | 789 | =head2 recreate_merge_table 790 | 791 | Recreates the merge table. 792 | 793 | =cut 794 | 795 | sub recreate_merge_table { 796 | my $sessiontables = find_session_tables(); 797 | delete_merged_session_table(); 798 | merge_session_tables($sessiontables); 799 | } 800 | 801 | =head2 802 | 803 | Checks sanity of Dirs 804 | 805 | =cut 806 | 807 | sub check_dir { 808 | my $DIR = shift; 809 | if (-e $DIR) { 810 | print "[*] The directory $DIR exists.\n" if $DEBUG; 811 | } else { 812 | print "[E] ERROR: The directory $DIR does not exist!\n"; 813 | exit 1; 814 | } 815 | if (-r $DIR) { 816 | print "[*] The directory $DIR is readable.\n" if $DEBUG; 817 | } else { 818 | print "[E] ERROR: The directory $DIR is not readable!\n"; 819 | exit 1; 820 | } 821 | if (-w $DIR) { 822 | print "[*] The directory $DIR is writable.\n" if $DEBUG; 823 | } else { 824 | print "[E] ERROR: The directory $DIR is not writable!\n"; 825 | exit 1; 826 | } 827 | } 828 | 829 | =head2 game_over 830 | 831 | Terminates the program in a sainfull way. 832 | 833 | =cut 834 | 835 | sub game_over { 836 | warn "[*] Terminating...\n"; 837 | $dbh->disconnect; 838 | unlink ($PIDFILE); 839 | exit 0; 840 | } 841 | 842 | =head1 AUTHOR 843 | 844 | Edward Fjellskaal 845 | 846 | =head1 COPYRIGHT 847 | 848 | This library is free software, you can redistribute it and/or modify 849 | it under the same terms as Perl itself. 850 | 851 | =cut 852 | -------------------------------------------------------------------------------- /bin/m_carve.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/php 2 | 8 | # 9 | # 10 | # This program is free software; you can redistribute it and/or modify 11 | # it under the terms of the GNU General Public License as published by 12 | # the Free Software Foundation; either version 2 of the License, or 13 | # (at your option) any later version. 14 | # 15 | # This program is distributed in the hope that it will be useful, 16 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | # GNU General Public License for more details. 19 | # 20 | # You should have received a copy of the GNU General Public License 21 | # along with this program; if not, write to the Free Software 22 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 23 | # ---------------------------------------------------------------------- 24 | //Given a start and stop file, the location of the files and their pre-fix, 25 | //this function puts file names that fit the start-stop parameter and stores 26 | //the results in an array 27 | function carve($start, $stop, $dir, $pre){ 28 | 29 | //tokenize start and end file name by (.) 30 | $start_token = explode(".",$start); 31 | $stop_token = explode(".",$stop); 32 | 33 | //check to make sure file is in (prefix).(suffix) format (size 2) 34 | if(count($start_token)!=3 or count($stop_token)!=3){ 35 | print "Invalid file name format, must be two strings separated by a period (eg. cxt.12345)\n"; 36 | } 37 | else{ 38 | //assign start and end timestamp 39 | $start_tstamp = $start_token[2]; 40 | $stop_tstamp = $stop_token[2]; 41 | //store capture directory contents into variable 42 | $dircontents = list_dir($dir,$pre); 43 | //extract since it was passed from list_dir function 44 | extract($dircontents); 45 | //if first x digits match (in this case 6) then treat them as matches 46 | //and add to the results array 47 | $carve_results = array(); 48 | $j=$i=0; 49 | for($i; $i<(count($dircontents)); $i++){ 50 | 51 | if(substr_compare($start_tstamp,$dircontents[$i],0,5)==0){ 52 | if($dircontents[$i]>=$start_tstamp and $dircontents[$i]<=$stop_tstamp){ 53 | $carve_results[$j]=$dircontents[$i]; 54 | $j++; 55 | } 56 | } 57 | } 58 | } 59 | 60 | //sort results and return array 61 | sort($carve_results); 62 | return $carve_results; 63 | } 64 | 65 | //takes directory and file prefix and adds all files in the directory 66 | //with the given prefix to an array ($valid_files) 67 | function list_dir($directory,$pre){ 68 | 69 | $directory = $directory; 70 | $open_directory = opendir($directory); 71 | $valid_files=array(); 72 | while($filename = readdir($open_directory)){ 73 | $filesplit = explode(".", $filename); 74 | 75 | $check_prefix = $filesplit[0] .".". $filesplit[1]; 76 | if($check_prefix==$pre or $check_prefix == "openfpc-Default_Node.pcap"){ 77 | $valid_files[] = $filesplit[2]; 78 | } 79 | } 80 | closedir(); 81 | return $valid_files; 82 | } 83 | 84 | 85 | 86 | //Takes sorted list of files, the directory they are located in and the prefix of the 87 | //file name as arguments and retrieves each file's size and stores it in an array 88 | function get_sizes($files_array,$dir,$pre){ 89 | 90 | //var_dump($files_array); 91 | for($i=0;$i 241 | -------------------------------------------------------------------------------- /doc/COPYING: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /doc/INSTALL: -------------------------------------------------------------------------------- 1 | #### CxTracker INSTALL 2 | 3 | # TODO : apt-get install cxtracker 4 | 5 | # On a Ubuntu system: 6 | $ sudo apt-get install libnet-pcap-perl libgetopt-long-descriptive-perl git-core libdatetime-perl libpcap0.8 libpcap0.8-dev 7 | $ git clone git://github.com/gamelinux/cxtracker.git 8 | 9 | # C 10 | $ cd cxtracker/src/ 11 | $ make 12 | $ sudo cxtracker -h 13 | # You can copy cxtracker to /usr/local/sbin/ 14 | $ cp cxtracker /usr/local/sbin/ 15 | # One way to run cxtracker: 16 | $ cxtracker -i eth1 -d /nsm_data/sensor1/session/ -D 17 | 18 | # IF YOU USE cxtracker WITH SGUIL - you can stop here! 19 | # Just make sure that the sguil sancp agent is picking 20 | # up session files from the right location. 21 | 22 | ## IF YOU WANT TO PUT SESSION DATA INTO A DB ## 23 | # Maybe for your own project or for openfpc etc.# 24 | 25 | # cxtracker2db.pl - for storing session data to a DB 26 | # Edit cxtracker2db.pl to match db user and password if needed. 27 | 28 | # Some files and dirs that should be writable: 29 | $ mkdir /var/run/ 30 | $ touch /var/log/cxtracker2db.log 31 | 32 | # Prepare the mysql database 33 | GRANT USAGE ON *.* TO 'cxtracker'@'localhost' identified by 'cxtracker'; 34 | GRANT ALL ON cxtracker.* TO 'cxtracker'@'localhost' IDENTIFIED BY 'cxtracker'; 35 | FLUSH PRIVILEGES; 36 | 37 | CREATE DATABASE cxtracker; 38 | \u cxtracker 39 | 40 | # You need to add two function to mysql to handle IPv6 41 | # INET_ATON6 and INET_NTOA6: 42 | -----8<----- 43 | 44 | DELIMITER // 45 | CREATE FUNCTION INET_ATON6(n CHAR(39)) 46 | RETURNS DECIMAL(39) UNSIGNED 47 | DETERMINISTIC 48 | BEGIN 49 | RETURN CAST(CONV(SUBSTRING(n FROM 1 FOR 4), 16, 10) AS DECIMAL(39)) 50 | * 5192296858534827628530496329220096 -- 65536 ^ 7 51 | + CAST(CONV(SUBSTRING(n FROM 6 FOR 4), 16, 10) AS DECIMAL(39)) 52 | * 79228162514264337593543950336 -- 65536 ^ 6 53 | + CAST(CONV(SUBSTRING(n FROM 11 FOR 4), 16, 10) AS DECIMAL(39)) 54 | * 1208925819614629174706176 -- 65536 ^ 5 55 | + CAST(CONV(SUBSTRING(n FROM 16 FOR 4), 16, 10) AS DECIMAL(39)) 56 | * 18446744073709551616 -- 65536 ^ 4 57 | + CAST(CONV(SUBSTRING(n FROM 21 FOR 4), 16, 10) AS DECIMAL(39)) 58 | * 281474976710656 -- 65536 ^ 3 59 | + CAST(CONV(SUBSTRING(n FROM 26 FOR 4), 16, 10) AS DECIMAL(39)) 60 | * 4294967296 -- 65536 ^ 2 61 | + CAST(CONV(SUBSTRING(n FROM 31 FOR 4), 16, 10) AS DECIMAL(39)) 62 | * 65536 -- 65536 ^ 1 63 | + CAST(CONV(SUBSTRING(n FROM 36 FOR 4), 16, 10) AS DECIMAL(39)) 64 | ; 65 | END; 66 | // 67 | 68 | CREATE FUNCTION INET_NTOA6(n DECIMAL(39) UNSIGNED) 69 | RETURNS CHAR(39) 70 | DETERMINISTIC 71 | BEGIN 72 | DECLARE a CHAR(39) DEFAULT ''; 73 | DECLARE i INT DEFAULT 7; 74 | DECLARE q DECIMAL(39) UNSIGNED DEFAULT 0; 75 | DECLARE r INT DEFAULT 0; 76 | WHILE i DO 77 | -- DIV doesn't work with numbers > bigint 78 | SET q := FLOOR(n / 65536); 79 | SET r := n MOD 65536; 80 | SET n := q; 81 | SET a := CONCAT_WS(':', LPAD(CONV(r, 10, 16), 4, '0'), a); 82 | 83 | SET i := i - 1; 84 | END WHILE; 85 | 86 | SET a := TRIM(TRAILING ':' FROM CONCAT_WS(':', 87 | LPAD(CONV(n, 10, 16), 4, '0'), 88 | a)); 89 | 90 | RETURN a; 91 | 92 | END; 93 | // 94 | DELIMITER ; 95 | 96 | -----8<----- 97 | 98 | # Then: 99 | $ ./cxtracker2db.pl --hostname sensor1 --dir /nsm_data/sensor1/session/ --daemon 100 | -------------------------------------------------------------------------------- /doc/README: -------------------------------------------------------------------------------- 1 | #### CxTracker README 2 | # 3 | # Connection Tracker - is a passive network connection tracker 4 | # 5 | 6 | # About 7 | CxTracker (Connection Tracker) is a passive network connection tracker 8 | for profiling, history, auditing and network discovery. It can be used 9 | as an replacement for sancp in the sguil setup. It handles VLANs (2 layers) 10 | and IPv6 out of the box. 11 | 12 | # As is! 13 | This program is served 'as is'. We take no responsibility for anything :) 14 | 15 | # Install 16 | See INSTALL 17 | 18 | #### USE ### 19 | 20 | ### cxtracker (The C version) 21 | I use it now instead of sancp in my sguil setup. 22 | See '$ cxtracker --help' for updated info 23 | cxtracker --help 24 | Usage: 25 | $ cxtracker [options] 26 | 27 | OPTIONS: 28 | 29 | -? Help 30 | -V Version and compiled in options. 31 | -v Verbose output. 32 | -i Interface to sniff from. 33 | -f Output format line. See Format options. 34 | -b Berkley packet filter. 35 | -d Directory to write session files to. 36 | -D Enable daemon mode. 37 | -u User to drop priveleges to after daemonising. 38 | -g Group to drop priveleges to after daemonising. 39 | -T Direct to chroot into. 40 | -P Path to PID file (/var/run). 41 | -p Name of pidfile (cxtracker.pid). 42 | -r PCAP file to read. 43 | -w Dump PCAP to file with specified prefix. 44 | -F Flush output after every write to dump file. 45 | -s Roll over dump file based on size. 46 | -t Roll over dump file based on time intervals. 47 | -x Amount of space to leave free on disk. 48 | -A Write packets to directories by date (YYYY-MM-DD) 49 | 50 | # Example 51 | Running cxtracker in foreground: 52 | Only IPv4 traffic: 53 | ./cxtracker -d /nsm_data/sensor1/sancp -u nsm -g nsm -i eth1 -b 'ip' 54 | 55 | Only VLAN and IPv4 traffic: 56 | ./cxtracker -d /nsm_data/sensor1/sancp -u nsm -g nsm -i eth1 -b 'vlan and ip' 57 | 58 | vlan and IPv4 and IPv6: 59 | ./cxtracker -d /nsm_data/sensor1/sancp -u nsm -g nsm -i eth1 60 | -------------------------------------------------------------------------------- /doc/ROADMAP: -------------------------------------------------------------------------------- 1 | ## cxtracker roadmap 2 | 3 | # Version 2.0 4 | * We need to support other methods for fast packetcapture/0copy etc. 5 | * libpcap 1.0.0+ support and pcap_set_buffer_size etc. 6 | * support for using afpacket on linux 7 | 8 | # Version 1.5 9 | * Limiting packet-capture on bytes or packets in a session: 10 | - Drop capturing after XXXX packets or YYYY bytes 11 | - based on sessions 12 | - configurable for client and server separatly 13 | - Update pcap indexing to Cohere 14 | * Limiting packet-capture for know SSL/TLS/Encrypted traffic 15 | - Maybe check payload for content and if it matches within 16 | the first 20 packets, dont log anymore ?!!? 17 | - Maybe mark the IP+Port of the server as encrypted for 18 | future 'skip inspection' ? 19 | 20 | # Version 1.0 21 | * Sniff traffic from interface (-i) 22 | * Read pcap files (-r) 23 | * Specify output format (-f) 24 | * Specify BPF (-b) 25 | * Specify directory to write cxtracker logs to (-d) 26 | * Drop priveleges ( -u and -g) 27 | * Chroot (-T) 28 | * Specify pidfile (-p and -P) 29 | * Write pcap to file (-w) 30 | * Pcap roleover (-s and -t) 31 | * Pcap indexing in cxtracker output 32 | * pcap stats, like dropped packets etc. 33 | 34 | -------------------------------------------------------------------------------- /doc/TODO: -------------------------------------------------------------------------------- 1 | ## TODO 2 | 3 | Before Version 1.0 4 | * #ifdef debug 5 | V* init script 6 | * Optimize as much as possible 7 | * Read pcap files (-r) 8 | V* debian package 9 | * rpm for RHEL/CentOS 5.x 10 | V* pcap stats, like dropped packets etc. 11 | 12 | 13 | Befor Version 2.0 14 | * Config file and parsing 15 | * PCAP capture of network traffic 16 | * Indexing of PCAPS 17 | * Limiting packet capture on bytes or packets 18 | in a connection if one wants. 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /doc/sguil-notes.txt: -------------------------------------------------------------------------------- 1 | Filenames are like this: stats.interface.unixtimestamp: 2 | /nsm_data/hostname/sancp/stats.peth0.1244209823 3 | 4 | Each file contains 1 or more lines like this: 5 | ----8<---- 6 | 5343839945096828433|2009-06-05 13:48:14|2009-06-05 13:48:14|0|17|1361045767|56694|1361043554|53|1|48|1|96|0|0 7 | 5343839945096976838|2009-06-05 13:48:14|2009-06-05 13:48:14|0|17|1361045767|60603|1361043554|53|1|45|1|166|0|0 8 | 5343839945097042436|2009-06-05 13:48:14|2009-06-05 13:48:14|0|17|1361045767|40189|1361043554|53|1|45|1|166|0|0 9 | 5343839945097049070|2009-06-05 13:48:14|2009-06-05 13:48:35|21|6|1361045767|49528|3252367494|80|4|0|0|0|2|0 10 | ----8<---- 11 | The fields mean: 12 | sancp-connection-ID| ISO START TIME | ISO END TIME |duration time|protocol|src_ip|src_port|src_ip|src_port|src_packets|src_bytes|dst_packets|dst_bytes|src_flags|dst_flags 13 | 14 | 15 | # Conforms with out sguil sancp table schema 16 | format stats sancp_id,start_time_gmt,stop_time_gmt,duration,ip_proto,src_ip_decimal,src_port,dst_ip_decimal,dst_port,src_pkts,src_bytes,dst_pkts,dst_bytes,sflags,dflags 17 | 18 | -------------------------------------------------------------------------------- /etc/init.d/cxtracker-redhat: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Example Init file for cxtracker on Redhat systems 4 | # Contrib from: Jeremy Hoel 5 | # 6 | # chkconfig: 2345 40 60 7 | # description: cxtracker is a session capture tool 8 | # 9 | # processname: cxtracker 10 | 11 | 12 | source /etc/rc.d/init.d/functions 13 | source /etc/sysconfig/network 14 | 15 | ### Check that networking is up. 16 | [ "${NETWORKING}" == "no" ] && exit 0 17 | 18 | # To filter out ip6 which Sguil cant handle... 19 | BPF="ip" 20 | INT="eth1" 21 | 22 | ### Default variables 23 | SYSCONFIG="/etc/sysconfig/cxtracker" 24 | 25 | ### Read configuration 26 | [ -r "$SYSCONFIG" ] && source "$SYSCONFIG" 27 | 28 | RETVAL=0 29 | prog="/usr/bin/cxtracker" 30 | desc="Session tracking" 31 | 32 | PIDFILE="/var/run/cxtracker-${INT}.pid" 33 | OUTPUT="/var/log/snort/sguil/sancp/" 34 | OPTIONS="-u sguil -g sguil" 35 | META="sguil" 36 | 37 | 38 | start() { 39 | echo -n $"Starting $desc ($prog): " 40 | CX_OPTS="-D -f $META -i $INT -d $OUTPUT -b $BPF $OPTIONS -p $PIDFILE" 41 | daemon $prog $CX_OPTS 42 | RETVAL=$? 43 | echo 44 | return $RETVAL 45 | } 46 | 47 | stop() { 48 | echo -n $"Shutting down $desc ($prog): " 49 | killproc $prog 50 | RETVAL=$? 51 | echo 52 | [ $RETVAL -eq 0 ] && rm -f $PIDFILE 53 | return $RETVAL 54 | } 55 | 56 | restart() { 57 | stop 58 | start 59 | } 60 | 61 | 62 | case "$1" in 63 | start) 64 | start 65 | ;; 66 | stop) 67 | stop 68 | ;; 69 | restart) 70 | restart 71 | ;; 72 | *) 73 | echo $"Usage: $0 {start|stop|restart}" 74 | RETVAL=1 75 | esac 76 | 77 | exit $RETVAL 78 | -------------------------------------------------------------------------------- /etc/init.d/cxtracker-ubuntu: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Example init file for debian/ubuntu systems 3 | ### BEGIN INIT INFO 4 | # Provides: cxtracker 5 | # Required-Start: $network 6 | # Required-Stop: $network 7 | # Default-Start: 2 3 4 5 8 | # Default-Stop: 0 1 6 9 | # Short-Description: Network Connection Tracker 10 | # Description: This is a network security tool designed to 11 | # collect statistical information about network traffic 12 | ### END INIT INFO 13 | 14 | CXT_INTERFACE="eth0" 15 | 16 | # Source function library. 17 | if [ -r /etc/default/cxtracker ]; then 18 | . /etc/default/cxtracker 19 | fi 20 | 21 | # We'll add up all the options above and use them 22 | NAME=cxtracker 23 | DAEMON=/usr/bin/$NAME 24 | PIDFILE=/var/run/${NAME}-${CXT_INTERFACE}.pid 25 | 26 | # See how we were called. 27 | case "$1" in 28 | start) 29 | # chown -R ${CXT_UID}.${CXT_GID} ${CXT_ARCHIVE_DIR}/ 30 | echo -n "Starting $NAME ..." 31 | $DAEMON $CXT_ARCHIVE_DIR $CXT_INTERFACE $CXT_USER $CXT_GROUP $CXT_BPF >> /var/log/$NAME.log 2>&1 & 32 | PID1=$! 33 | echo "$PID1" > $PIDFILE 34 | echo " done." 35 | ;; 36 | stop) 37 | echo -n "Stopping $NAME ..." 38 | start-stop-daemon --oknodo --stop --quiet --pidfile=$PIDFILE --exec $DAEMON 39 | rm -f $PIDFILE 2>&1 & 40 | echo " done." 41 | ;; 42 | status) 43 | # Show the stats/counters 44 | if [ -s $PIDFILE ]; then 45 | echo -n "$NAME running with PID " 46 | cat $PIDFILE 47 | #echo -n "Dumping $NAME stats" 48 | #kill -USR1 `cat $PIDFILE` 49 | #echo " done." 50 | else 51 | echo "$NAME not running!" 52 | fi 53 | ;; 54 | now) 55 | # Dump all on-going connections 56 | echo -n "Dumping $NAME connections going on right now" 57 | kill -USR2 `cat $PIDFILE` 58 | echo " done." 59 | ;; 60 | hup) 61 | # Make certain all is running 62 | echo -n "HUP'ing $NAME " 63 | # chown -R ${CXT_UID}.${CXT_GID} ${CXT_ARCHIVE_DIR}/ 64 | kill -HUP `cat $PIDFILE` 65 | echo " done." 66 | ;; 67 | restart) 68 | $0 stop 69 | $0 start 70 | ;; 71 | force-reload) 72 | $0 stop 73 | $0 start 74 | ;; 75 | *) 76 | echo "Usage: $0 {start|stop|restart|status|now|hup|force-reload}" 77 | exit 1 78 | esac 79 | 80 | exit 0 81 | -------------------------------------------------------------------------------- /etc/init.d/default/cxtracker: -------------------------------------------------------------------------------- 1 | # Example default file for Debian/Ubuntu systems 2 | # Set main configuration options here 3 | CXT_ARCHIVE_DIR="-d /nsm_data/`hostname -s`/cxtracker -f nsmf " 4 | CXT_INTERFACE="-i eth1" 5 | CXT_USER="-u nsm" 6 | CXT_GROUP="-g nsm" 7 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | #Makefile 2 | 3 | OCFLAGS=-g -O3 -Wall -Wextra 4 | DCFLAGS=-g 5 | PCFLAGS=-g -pg 6 | LIBS=-lpcap 7 | LDFLAGS= 8 | 9 | SRC= cxtracker.c cxtracker.h \ 10 | format.c format.h \ 11 | ip.c ip.h 12 | 13 | OBJS= ip.o format.o cxtracker.o 14 | 15 | TARGETS=cxtracker 16 | 17 | all:${TARGETS} 18 | 19 | cxtracker: $(OBJS) 20 | $(CC) $(OBJS) -o cxtracker $(LIBS) $(LDFLAGS) 21 | 22 | debug: $(OBJS) 23 | $(CC) $(OBJS) -o cxtracker $(LIBS) $(DCFLAGS) $(LDFLAGS) 24 | 25 | profile: $(OBJS) 26 | $(CC) $(OBJS) -o cxtracker $(LIBS) $(PCFLAGS) $(LDFLAGS) 27 | 28 | clean: 29 | -rm -f ${OBJS} ${TARGETS} 30 | 31 | .c.o: 32 | $(CC) -c -Wall $(DEFS) $(OCFLAGS) $< 33 | 34 | -------------------------------------------------------------------------------- /src/cxtracker.c: -------------------------------------------------------------------------------- 1 | /* 2 | ** This file is a part of cxtracker. 3 | ** 4 | ** Copyright (C) 2009, Redpill Linpro 5 | ** Copyright (C) 2009, Edward Fjellskål 6 | ** Copyright (C) 2010 - 2013, Edward Fjellskål 7 | ** Copyright (C) 2011 - 2013, Ian Firns 8 | ** 9 | ** This program is free software; you can redistribute it and/or modify 10 | ** it under the terms of the GNU General Public License as published by 11 | ** the Free Software Foundation; either version 2 of the License, or 12 | ** (at your option) any later version. 13 | ** 14 | ** This program is distributed in the hope that it will be useful, 15 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | ** GNU General Public License for more details. 18 | ** 19 | ** You should have received a copy of the GNU General Public License 20 | ** along with this program; if not, write to the Free Software 21 | ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 22 | ** 23 | */ 24 | 25 | /* I N C L U D E S **********************************************************/ 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include "cxtracker.h" 48 | #include "format.h" 49 | 50 | /* G L O B A L E S **********************************************************/ 51 | u_int64_t cxtrackerid; 52 | struct timeval tstamp; 53 | u_short vlanid; 54 | 55 | pcap_t *handle; 56 | pcap_dumper_t *dump_handle; 57 | struct bpf_program cfilter; 58 | 59 | connection *bucket[BUCKET_SIZE]; 60 | connection *cxtbuffer = NULL; 61 | 62 | static char *dev,*chroot_dir,*output_format; 63 | static char dpath[STDBUF] = "./"; 64 | static char *group_name, *user_name, *true_pid_name; 65 | static char *pidfile = "cxtracker.pid"; 66 | static char *pidpath = "/var/run"; 67 | static int verbose, inpacket, intr_flag, use_syslog, dump_with_flush; 68 | static int mode; 69 | static char datedir = 0; 70 | static char *read_file; 71 | static uint64_t read_file_offset = 0; 72 | 73 | static uint64_t roll_size; 74 | static uint64_t roll_off_size = 0; 75 | static time_t roll_time; 76 | static time_t roll_time_last; 77 | static uint64_t dump_file_offset = 0; 78 | static char *dump_file_prefix; 79 | static char dump_file[STDBUF]; 80 | //uint64_t max_cxt = 0; 81 | //uint64_t cxt_alloc = 0; 82 | //uint64_t cxt_free = 0; 83 | 84 | static char *rollover_names[] = { 85 | "unknown", 86 | 87 | "kilobytes", 88 | "megabytes", 89 | "gigabytes", 90 | "terabytes", 91 | 92 | "seconds", 93 | "minutes", 94 | "hours", 95 | "days" 96 | }; 97 | 98 | ip_config_t ip_config; 99 | 100 | 101 | /* I N T E R N A L P R O T O T Y P E S ************************************/ 102 | void move_connection (connection*, connection**); 103 | inline int cx_track(ip_t *ip_src, uint16_t src_port, ip_t *ip_dst, uint16_t dst_port,uint8_t ip_proto,uint32_t p_bytes,uint8_t tcpflags,struct timeval tstamp, int af); 104 | void got_packet (u_char *useless,const struct pcap_pkthdr *pheader, const u_char *packet); 105 | void end_sessions(); 106 | void cxtbuffer_write(); 107 | void cxtbuffer_free(connection *c); 108 | void game_over(); 109 | void exit_clean(int code); 110 | void check_interupt(); 111 | void dump_active(); 112 | void set_end_sessions(); 113 | 114 | 115 | int dump_file_open(); 116 | int dump_file_roll(); 117 | int dump_file_close(); 118 | int pcap_roll_off(); 119 | int pcap_roll_off_recursive(); 120 | 121 | int pcap_roll_off() 122 | { 123 | /*rolling off is set*/ 124 | /*do we need to roll?*/ 125 | struct statvfs mystatvfs; 126 | while(1) 127 | { 128 | if(!statvfs(dpath, &mystatvfs)) 129 | { 130 | /*we can look at the partition*/ 131 | if((mystatvfs.f_bavail) > (roll_off_size / (mystatvfs.f_bsize))) 132 | return 0; 133 | if(pcap_roll_off_recursive(dpath, STDBUF)) 134 | return 1; 135 | } 136 | else 137 | { 138 | /*we can't look at the partition*/ 139 | return 1; 140 | } 141 | } 142 | return 0; 143 | } 144 | 145 | int pcap_roll_off_recursive(char dirpath[], int len) 146 | { 147 | struct dirent **mydirs; 148 | int h = 0; 149 | int i = 0; 150 | int smallest = -1; 151 | time_t mymtime = 0; 152 | h = scandir(dirpath, &mydirs, NULL, alphasort); 153 | if (h < 0) 154 | perror("scandir"); 155 | else 156 | { 157 | for(i = 0; i < h; i++) 158 | { 159 | if((mydirs[i]->d_type == DT_DIR) && ((strncmp(mydirs[i]->d_name,".",2)!=0) && (strncmp(mydirs[i]->d_name,"..",3)!=0) && (strncmp(mydirs[i]->d_name,"failed",7)!=0))) 160 | { 161 | char pathname[len]; 162 | snprintf(pathname, len, "%s%s/", dirpath, mydirs[i]->d_name); 163 | if(pcap_roll_off_recursive(pathname, len)) 164 | return 1; 165 | rmdir(pathname); 166 | return 0; 167 | } 168 | } 169 | for(i = 0; i < h; i++) 170 | { 171 | if(!(mydirs[i]->d_type == DT_DIR) && ((strncmp(mydirs[i]->d_name,".",2)!=0) && (strncmp(mydirs[i]->d_name,"..",3)!=0))) 172 | { 173 | char unlinkname[len]; 174 | snprintf(unlinkname, len, "%s%s", dirpath, mydirs[i]->d_name); 175 | struct stat mystat; 176 | stat(unlinkname, &mystat); 177 | if((mymtime > (mystat.st_mtime)) || (mymtime == 0)) 178 | { 179 | if((strncmp(mydirs[i]->d_name,".",2)!=0) && (strncmp(mydirs[i]->d_name,"..",3)!=0) && (strncmp(mydirs[i]->d_name,"stats.",6)!=0)) 180 | { 181 | /*New oldest file / smallest mtime*/ 182 | smallest = i; 183 | mymtime = mystat.st_mtime; 184 | } 185 | } 186 | } 187 | } 188 | if(smallest!=-1) 189 | { 190 | /*got oldest file, unlink*/ 191 | char unlinkname[len]; 192 | snprintf(unlinkname, len, "%s%s", dirpath, mydirs[smallest]->d_name); 193 | printf("Maximum disk usage reached; removing: %s\n", unlinkname); 194 | unlink(unlinkname); 195 | for (i = 0; i < h; i++) 196 | free(mydirs[i]); 197 | free(mydirs); 198 | return 0; 199 | } 200 | if(h>2) 201 | return 1; /*directory has stuff in it we can't delete*/ 202 | else 203 | return 0; /*directory is empty and should be deleted*/ 204 | } 205 | return 1; 206 | } 207 | 208 | 209 | void got_packet (u_char *useless,const struct pcap_pkthdr *pheader, const u_char *packet) { 210 | if ( intr_flag != 0 ) { check_interupt(); } 211 | inpacket = 1; 212 | 213 | /* on OpenBSD the ts struct of the bpf header has a different definition 214 | * than a timeval so assigning to timeval members individually */ 215 | tstamp.tv_sec = (long) pheader->ts.tv_sec; 216 | tstamp.tv_usec = (long) pheader->ts.tv_usec; 217 | 218 | /* are we dumping */ 219 | if (mode & MODE_DUMP) { 220 | time_t now = time(NULL); 221 | 222 | /* check if we should roll on time */ 223 | if( ( roll_time != 0 ) && 224 | ( now >= (roll_time_last + roll_time) ) ) 225 | { 226 | roll_time_last = now; 227 | printf("Rolling on time.\n"); 228 | dump_file_roll(); 229 | if(roll_off_size) 230 | if(pcap_roll_off()) 231 | { 232 | printf("Error pruning pcaps from disk.\n"); 233 | exit_clean(1); /*error*/ 234 | } 235 | } 236 | 237 | dump_file_offset = (uint64_t)ftell((FILE *)dump_handle); 238 | 239 | /* check if we should roll on size */ 240 | if ( (roll_size > 0) && 241 | (dump_file_offset >= roll_size) ) 242 | { 243 | printf("Rolling on size.\n"); 244 | dump_file_roll(); 245 | if(roll_off_size) 246 | if(pcap_roll_off()) 247 | { 248 | printf("Error pruning pcaps from disk.\n"); 249 | exit_clean(1); /*error*/ 250 | } 251 | } 252 | /*check if we should roll off disk*/ 253 | /*if(roll_off_size) 254 | pcap_roll_off();*/ 255 | /*moved to right after dump_file_roll();*/ 256 | 257 | 258 | /* write the packet */ 259 | pcap_dump((u_char *)dump_handle, pheader, packet); 260 | 261 | if ( dump_with_flush ) 262 | pcap_dump_flush(dump_handle); 263 | } 264 | else if ( mode & MODE_FILE ) { 265 | read_file_offset = (uint64_t)ftell(pcap_file(handle)) - pheader->caplen - 16; 266 | } 267 | 268 | /* printf("[*] Got network packet...\n"); */ 269 | ether_header *eth_hdr; 270 | eth_hdr = (ether_header *) (packet); 271 | u_short eth_type; 272 | eth_type = ntohs(eth_hdr->eth_ip_type); 273 | int eth_header_len; 274 | eth_header_len = ETHERNET_HEADER_LEN; 275 | vlanid = 0; 276 | 277 | if ( eth_type == ETHERNET_TYPE_8021Q ) { 278 | /* printf("[*] ETHERNET TYPE 8021Q\n"); */ 279 | eth_type = ntohs(eth_hdr->eth_8_ip_type); 280 | eth_header_len +=4; 281 | vlanid = ntohs(eth_hdr->eth_8_vid); 282 | if ( eth_type == ETHERNET_TYPE_8021Q ) { 283 | /* printf("[*] ETHERNET TYPE 8021Q in 8021Q\n"); */ 284 | eth_type = ntohs(eth_hdr->eth_82_ip_type); 285 | eth_header_len +=4; 286 | vlanid = ntohs(eth_hdr->eth_82_vid); 287 | } 288 | } 289 | 290 | else if ( eth_type == (ETHERNET_TYPE_802Q1MT|ETHERNET_TYPE_802Q1MT2|ETHERNET_TYPE_802Q1MT3|ETHERNET_TYPE_8021AD) ) { 291 | /* printf("[*] ETHERNET TYPE 802Q1MT\n"); */ 292 | eth_type = ntohs(eth_hdr->eth_82_ip_type); 293 | eth_header_len +=8; 294 | vlanid = ntohs(eth_hdr->eth_82_vid); 295 | } 296 | 297 | /* zero-ise our structure, simplifies our hashing later on */ 298 | int ip_tracked = 0; 299 | ip_t *ip_src = calloc(1, sizeof(ip_t)); 300 | ip_t *ip_dst = calloc(1, sizeof(ip_t)); 301 | 302 | if ( eth_type == ETHERNET_TYPE_IP ) { 303 | /* printf("[*] Got IPv4 Packet...\n"); */ 304 | ip4_header *ip4; 305 | ip4 = (ip4_header *) (packet + eth_header_len); 306 | 307 | ip_set(&ip_config, ip_src, &ip4->ip_src, AF_INET); 308 | ip_set(&ip_config, ip_dst, &ip4->ip_dst, AF_INET); 309 | 310 | if ( ip4->ip_p == IP_PROTO_TCP ) { 311 | tcp_header *tcph; 312 | tcph = (tcp_header *) (packet + eth_header_len + (IP_HL(ip4)*4)); 313 | /* printf("[*] IPv4 PROTOCOL TYPE TCP:\n"); */ 314 | ip_tracked = cx_track(ip_src, tcph->src_port, ip_dst, tcph->dst_port, ip4->ip_p, pheader->len, tcph->t_flags, tstamp, AF_INET); 315 | } 316 | else if (ip4->ip_p == IP_PROTO_UDP) { 317 | udp_header *udph; 318 | udph = (udp_header *) (packet + eth_header_len + (IP_HL(ip4)*4)); 319 | /* printf("[*] IPv4 PROTOCOL TYPE UDP:\n"); */ 320 | ip_tracked = cx_track(ip_src, udph->src_port, ip_dst, udph->dst_port, ip4->ip_p, pheader->len, 0, tstamp, AF_INET); 321 | } 322 | else if (ip4->ip_p == IP_PROTO_ICMP) { 323 | icmp_header *icmph; 324 | icmph = (icmp_header *) (packet + eth_header_len + (IP_HL(ip4)*4)); 325 | /* printf("[*] IP PROTOCOL TYPE ICMP\n"); */ 326 | ip_tracked = cx_track(ip_src, icmph->s_icmp_id, ip_dst, icmph->s_icmp_id, ip4->ip_p, pheader->len, 0, tstamp, AF_INET); 327 | } 328 | else { 329 | /* printf("[*] IPv4 PROTOCOL TYPE OTHER: %d\n",ip4->ip_p); */ 330 | ip_tracked = cx_track(ip_src, ip4->ip_p, ip_dst, ip4->ip_p, ip4->ip_p, pheader->len, 0, tstamp, AF_INET); 331 | } 332 | } 333 | 334 | else if ( eth_type == ETHERNET_TYPE_IPV6) { 335 | /* printf("[*] Got IPv6 Packet...\n"); */ 336 | ip6_header *ip6; 337 | ip6 = (ip6_header *) (packet + eth_header_len); 338 | 339 | ip_set(&ip_config, ip_src, &ip6->ip_src, AF_INET6); 340 | ip_set(&ip_config, ip_dst, &ip6->ip_dst, AF_INET6); 341 | 342 | if ( ip6->next == IP_PROTO_TCP ) { 343 | tcp_header *tcph; 344 | tcph = (tcp_header *) (packet + eth_header_len + IP6_HEADER_LEN); 345 | /* printf("[*] IPv6 PROTOCOL TYPE TCP:\n"); */ 346 | ip_tracked = cx_track(ip_src, tcph->src_port, ip_dst, tcph->dst_port, ip6->next, pheader->len, tcph->t_flags, tstamp, AF_INET6); 347 | } 348 | else if (ip6->next == IP_PROTO_UDP) { 349 | udp_header *udph; 350 | udph = (udp_header *) (packet + eth_header_len + IP6_HEADER_LEN); 351 | /* printf("[*] IPv6 PROTOCOL TYPE UDP:\n"); */ 352 | ip_tracked = cx_track(ip_src, udph->src_port, ip_dst, udph->dst_port, ip6->next, pheader->len, 0, tstamp, AF_INET6); 353 | } 354 | else if (ip6->next == IP6_PROTO_ICMP) { 355 | //icmp6_header *icmph; 356 | //icmph = (icmp6_header *) (packet + eth_header_len + IP6_HEADER_LEN); 357 | 358 | /* printf("[*] IPv6 PROTOCOL TYPE ICMP\n"); */ 359 | ip_tracked = cx_track(ip_src, ip6->hop_lmt, ip_dst, ip6->hop_lmt, ip6->next, pheader->len, 0, tstamp, AF_INET6); 360 | } 361 | else { 362 | /* printf("[*] IPv6 PROTOCOL TYPE OTHER: %d\n",ip6->next); */ 363 | ip_tracked = cx_track(ip_src, ip6->next, ip_dst, ip6->next, ip6->next, pheader->len, 0, tstamp, AF_INET6); 364 | } 365 | } 366 | 367 | if ( ip_tracked == 0 ) 368 | { 369 | if (ip_src != NULL) ip_free(ip_src); 370 | if (ip_dst != NULL) ip_free(ip_dst); 371 | } 372 | 373 | inpacket = 0; 374 | return; 375 | (void) useless; 376 | /* else { */ 377 | /* printf("[*] ETHERNET TYPE : %x\n", eth_hdr->eth_ip_type); */ 378 | /* return; */ 379 | /* } */ 380 | } 381 | 382 | int cx_track(ip_t *ip_src, uint16_t src_port,ip_t *ip_dst, uint16_t dst_port, 383 | uint8_t ip_proto, uint32_t p_bytes, uint8_t tcpflags,struct timeval tstamp, int af) { 384 | 385 | connection *cxt = NULL; 386 | connection *head = NULL; 387 | 388 | /* for non-ipv6 addresses, indexes 1, 2 and 3 are zero and don't influence the hash */ 389 | uint64_t hash = ip_hash(ip_src, ip_dst, src_port, dst_port, ip_proto, BUCKET_SIZE); 390 | 391 | cxt = bucket[hash]; 392 | head = cxt; 393 | 394 | while ( cxt != NULL ) { 395 | if ( cxt->s_port == src_port && cxt->d_port == dst_port 396 | && cxt->vlanid == vlanid 397 | && ip_cmp(cxt->s_ip, ip_src) == 0 398 | && ip_cmp(cxt->d_ip, ip_dst) == 0 ) 399 | { 400 | cxt->s_tcpFlags |= tcpflags; 401 | cxt->s_total_bytes += p_bytes; 402 | cxt->s_total_pkts += 1; 403 | cxt->last_pkt_time = tstamp; 404 | 405 | if ( mode & MODE_DUMP ) 406 | { 407 | cxt->last_offset = dump_file_offset + p_bytes; 408 | snprintf(cxt->last_dump, STDBUF, "%s", dump_file); 409 | } 410 | else if ( mode & MODE_FILE ) 411 | { 412 | cxt->last_offset = read_file_offset + p_bytes; 413 | snprintf(cxt->last_dump, STDBUF, "%s", read_file); 414 | } 415 | 416 | return 0; 417 | } 418 | else if ( cxt->d_port == src_port && cxt->s_port == dst_port 419 | && cxt->vlanid == vlanid 420 | && ip_cmp(cxt->s_ip, ip_dst) == 0 421 | && ip_cmp(cxt->d_ip, ip_src) == 0 ) 422 | { 423 | cxt->d_tcpFlags |= tcpflags; 424 | cxt->d_total_bytes += p_bytes; 425 | cxt->d_total_pkts += 1; 426 | cxt->last_pkt_time = tstamp; 427 | 428 | if ( mode & MODE_DUMP ) 429 | { 430 | cxt->last_offset = dump_file_offset + p_bytes; 431 | snprintf(cxt->last_dump, STDBUF, "%s", dump_file); 432 | } 433 | else if ( mode & MODE_FILE ) 434 | { 435 | cxt->last_offset = read_file_offset + p_bytes; 436 | snprintf(cxt->last_dump, STDBUF, "%s", read_file); 437 | } 438 | 439 | return 0; 440 | } 441 | cxt = cxt->next; 442 | } 443 | 444 | if ( cxt == NULL ) { 445 | cxtrackerid += 1; 446 | cxt = (connection*) calloc(1, sizeof(connection)); 447 | //cxt_alloc++; 448 | if (head != NULL ) { 449 | head->prev = cxt; 450 | } 451 | /* printf("[*] New connection...\n"); */ 452 | cxt->cxid = cxtrackerid; 453 | cxt->ipversion = af; 454 | cxt->vlanid = vlanid; 455 | cxt->s_tcpFlags = tcpflags; 456 | /* cxt->d_tcpFlags = 0x00; */ 457 | cxt->s_total_bytes = p_bytes; 458 | cxt->s_total_pkts = 1; 459 | /* cxt->d_total_bytes = 0; */ 460 | /* cxt->d_total_pkts = 0; */ 461 | cxt->start_time = tstamp; 462 | 463 | if ( mode & MODE_DUMP ) 464 | { 465 | cxt->start_offset = dump_file_offset; 466 | cxt->last_offset = cxt->start_offset + p_bytes; 467 | snprintf(cxt->start_dump, STDBUF, "%s", dump_file); 468 | snprintf(cxt->last_dump, STDBUF, "%s", dump_file); 469 | } 470 | else if ( mode & MODE_FILE ) 471 | { 472 | cxt->start_offset = read_file_offset; 473 | cxt->last_offset = cxt->start_offset + p_bytes; 474 | snprintf(cxt->start_dump, STDBUF, "%s", read_file); 475 | snprintf(cxt->last_dump, STDBUF, "%s", read_file); 476 | } 477 | else 478 | { 479 | cxt->start_offset = -1; 480 | cxt->last_offset = -1; 481 | } 482 | 483 | cxt->last_pkt_time = tstamp; 484 | 485 | cxt->s_ip = ip_src; 486 | cxt->d_ip = ip_dst; 487 | /* This should be Zeroed due to calloc */ 488 | /* 489 | if (af == AF_INET) { 490 | cxt->s_ip.s6_addr32[1] = 0; 491 | cxt->s_ip.s6_addr32[2] = 0; 492 | cxt->s_ip.s6_addr32[3] = 0; 493 | cxt->d_ip.s6_addr32[1] = 0; 494 | cxt->d_ip.s6_addr32[2] = 0; 495 | cxt->d_ip.s6_addr32[3] = 0; 496 | } 497 | */ 498 | cxt->s_port = src_port; 499 | cxt->d_port = dst_port; 500 | cxt->proto = ip_proto; 501 | cxt->next = head; 502 | //cxt->prev = NULL; 503 | 504 | /* New connections are pushed on to the head of bucket[s_hash] */ 505 | bucket[hash] = cxt; 506 | 507 | /* Return value should be 1, telling "ip_tracked" not to try 508 | * to free the memory allocated for ip_src and ip_dst */ 509 | return 1; 510 | } 511 | 512 | /* Should never be here! */ 513 | return 0; 514 | } 515 | 516 | /* 517 | This sub marks sessions as ENDED on different criterias: 518 | */ 519 | 520 | void end_sessions() { 521 | 522 | connection *cxt; 523 | struct timeval check_time; 524 | check_time.tv_sec = time(NULL); 525 | //check_time.tv_usec = 0; 526 | int cxkey, xpir; 527 | uint32_t curcxt = 0; 528 | uint32_t expired = 0; 529 | cxtbuffer = NULL; 530 | 531 | for ( cxkey = 0; cxkey < BUCKET_SIZE; cxkey++ ) { 532 | cxt = bucket[cxkey]; 533 | xpir = 0; 534 | while ( cxt != NULL ) { 535 | curcxt++; 536 | /* TCP */ 537 | if ( cxt->proto == IP_PROTO_TCP ) { 538 | /* FIN from both sides */ 539 | if ( cxt->s_tcpFlags & TF_FIN && cxt->d_tcpFlags & TF_FIN && (check_time.tv_sec - cxt->last_pkt_time.tv_sec) > 5 ) { 540 | xpir = 1; 541 | } 542 | /* RST from eather side */ 543 | else if ( (cxt->s_tcpFlags & TF_RST || cxt->d_tcpFlags & TF_RST) && (check_time.tv_sec - cxt->last_pkt_time.tv_sec) > 5) { 544 | xpir = 1; 545 | } 546 | /* if not a complete TCP 3-way handshake */ 547 | else if ( ( ( !(cxt->s_tcpFlags&TF_SYN) ) && ( !(cxt->s_tcpFlags&TF_ACK) ) ) || ( 548 | ( ( !(cxt->d_tcpFlags&TF_SYN) ) && ( !(cxt->d_tcpFlags&TF_ACK) ) ) && 549 | ( (check_time.tv_sec - cxt->last_pkt_time.tv_sec) > 30) 550 | ) 551 | ) { 552 | xpir = 1; 553 | } 554 | /* Ongoing timout */ 555 | //else if ( (cxt->s_tcpFlags&TF_SYNACK || cxt->d_tcpFlags&TF_SYNACK) && ((check_time - cxt->last_pkt_time) > 120)) { 556 | // xpir = 1; 557 | //} 558 | else if ( (check_time.tv_sec - cxt->last_pkt_time.tv_sec) > 600 ) { 559 | xpir = 1; 560 | } 561 | } 562 | else if ( cxt->proto == IP_PROTO_UDP && (check_time.tv_sec - cxt->last_pkt_time.tv_sec) > 60 ) { 563 | xpir = 1; 564 | } 565 | else if ( cxt->proto == IP_PROTO_ICMP || cxt->proto == IP6_PROTO_ICMP ) { 566 | if ( (check_time.tv_sec - cxt->last_pkt_time.tv_sec) > 60 ) { 567 | xpir = 1; 568 | } 569 | } 570 | else if ( (check_time.tv_sec - cxt->last_pkt_time.tv_sec) > 300 ) { 571 | xpir = 1; 572 | } 573 | 574 | if ( xpir == 1 ) { 575 | expired++; 576 | xpir = 0; 577 | connection *tmp = cxt; 578 | if (cxt == cxt->next) { 579 | cxt->next = NULL; 580 | } 581 | cxt = cxt->next; 582 | move_connection(tmp, &bucket[cxkey]); 583 | } else { 584 | cxt = cxt->next; 585 | } 586 | } 587 | } 588 | /* printf("Expired: %u of %u total connections:\n",expired,curcxt); */ 589 | } 590 | 591 | void move_connection (connection *cxt_from, connection **bucket_ptr_from) { 592 | /* remove cxt from bucket */ 593 | connection *prev = cxt_from->prev; /* OLDER connections */ 594 | connection *next = cxt_from->next; /* NEWER connections */ 595 | if ( prev == NULL ) { 596 | /* beginning of list */ 597 | *bucket_ptr_from = next; 598 | /* not only entry */ 599 | if ( next ) 600 | next->prev = NULL; 601 | } else if ( next == NULL ) { 602 | /* at end of list! */ 603 | prev->next = NULL; 604 | } else { 605 | /* a node */ 606 | prev->next = next; 607 | next->prev = prev; 608 | } 609 | 610 | /* add cxt to expired list cxtbuffer 611 | * - if head is null -> head = cxt; 612 | */ 613 | cxt_from->next = cxtbuffer; /* next = head */ 614 | cxt_from->prev = NULL; 615 | cxtbuffer = cxt_from; /* head = cxt. result: newhead = cxt->oldhead->list... */ 616 | } 617 | 618 | void cxtbuffer_write () { 619 | 620 | if ( cxtbuffer == NULL ) { return; } 621 | connection *next; 622 | next = NULL; 623 | FILE *cxtFile; 624 | char cxtfname[4096]; 625 | char *bname; 626 | 627 | if (mode == MODE_FILE) { 628 | bname = strdup(basename(read_file)); 629 | snprintf(cxtfname, sizeof(cxtfname), "%sstats.%s.%ld", dpath, bname, (long) tstamp.tv_sec); 630 | free(bname); 631 | } else { 632 | snprintf(cxtfname, sizeof(cxtfname), "%sstats.%s.%ld", dpath, dev, (long) tstamp.tv_sec); 633 | } 634 | cxtFile = fopen(cxtfname, "w"); 635 | 636 | if (cxtFile == NULL) { 637 | printf("[E] ERROR: Cant open file %s\n",cxtfname); 638 | /* Free them anyways! */ 639 | while ( cxtbuffer != NULL ) { 640 | next = cxtbuffer->next; 641 | 642 | cxtbuffer_free(cxtbuffer); 643 | cxtbuffer = next; 644 | } 645 | printf("[W] connections went to visit /dev/null\n"); 646 | } 647 | else { 648 | 649 | while ( cxtbuffer != NULL ) { 650 | format_write(cxtFile, cxtbuffer); 651 | 652 | next = cxtbuffer->next; 653 | cxtbuffer_free(cxtbuffer); 654 | cxtbuffer = next; 655 | } 656 | fclose(cxtFile); 657 | } 658 | cxtbuffer = NULL; 659 | } 660 | 661 | void cxtbuffer_free(connection *c) 662 | { 663 | if ( c == NULL ) 664 | return; 665 | 666 | if ( c->s_ip ) 667 | ip_free(c->s_ip); 668 | 669 | if ( c->d_ip ) 670 | ip_free(c->d_ip); 671 | 672 | free(c); 673 | c = NULL; 674 | //cxt_free++; 675 | } 676 | 677 | void end_all_sessions() { 678 | connection *cxt; 679 | int cxkey; 680 | int expired = 0; 681 | 682 | for ( cxkey = 0; cxkey < BUCKET_SIZE; cxkey++ ) { 683 | cxt = bucket[cxkey]; 684 | while ( cxt != NULL ) { 685 | expired++; 686 | connection *tmp = cxt; 687 | cxt = cxt->next; 688 | move_connection(tmp, &bucket[cxkey]); 689 | if ( cxt == NULL ) { 690 | bucket[cxkey] = NULL; 691 | } 692 | } 693 | } 694 | //printf("Expired: %d.\n",expired); 695 | } 696 | 697 | void bucket_keys_NULL() { 698 | int cxkey; 699 | 700 | for ( cxkey = 0; cxkey < BUCKET_SIZE; cxkey++ ) { 701 | bucket[cxkey] = NULL; 702 | } 703 | } 704 | 705 | void check_interupt() { 706 | if ( intr_flag == 1 ) { 707 | game_over(); 708 | } 709 | else if ( intr_flag == 2 ) { 710 | dump_active(); 711 | } 712 | else if ( intr_flag == 3 ) { 713 | set_end_sessions(); 714 | } 715 | else { 716 | intr_flag = 0; 717 | } 718 | } 719 | 720 | void set_end_sessions() { 721 | intr_flag = 3; 722 | if ( inpacket == 0 ) { 723 | end_sessions(); 724 | cxtbuffer_write(); 725 | intr_flag = 0; 726 | alarm(TIMEOUT); 727 | } 728 | } 729 | 730 | void game_over() { 731 | if ( inpacket == 0 ) { 732 | end_all_sessions(); 733 | cxtbuffer_write(); 734 | if (dev != NULL && strlen(dev) > 0) 735 | free(dev); 736 | //printf(" cxt_alloc: %lu\n",cxt_alloc); 737 | //printf(" cxt_free : %lu\n",cxt_free); 738 | exit_clean(0); 739 | } 740 | intr_flag = 1; 741 | } 742 | 743 | void dump_active() { 744 | if ( inpacket == 0 && intr_flag == 2 ) { 745 | end_all_sessions(); 746 | cxtbuffer_write(); 747 | intr_flag = 0; 748 | } else { 749 | intr_flag = 2; 750 | } 751 | } 752 | 753 | int dump_file_open() 754 | { 755 | char dump_file_path[STDBUF]; 756 | char dump_dir_path[STDBUF]; 757 | char datepath[] = "0000-00-00/"; 758 | /* calculate filename */ 759 | time_t now = time(NULL); 760 | 761 | memset(dump_file, 0, STDBUF); 762 | snprintf(dump_file, STDBUF, "%s.%lu", dump_file_prefix, (long unsigned int) now); 763 | 764 | /*implement date-based directories*/ 765 | if (datedir) 766 | { 767 | /*datedir is set*/ 768 | struct tm * timeptr; 769 | timeptr = gmtime(&now); 770 | 771 | //char datepath[] = "0000-00-00/"; 772 | 773 | snprintf(datepath, 12, "%4d-%02d-%02d/", (1900 + (int)(timeptr->tm_year)),(1+(int)(timeptr->tm_mon)),((int)(timeptr->tm_mday))); 774 | 775 | } 776 | 777 | if ( strcmp(dpath, "./") != 0 ) 778 | { 779 | if(datedir) 780 | { 781 | snprintf(dump_dir_path, STDBUF, "%s%s", dpath, datepath); 782 | snprintf(dump_file_path, STDBUF, "%s%s.%lu", dump_dir_path, dump_file_prefix, (long unsigned int) now); 783 | if(mkdir(dump_dir_path, (S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH))) 784 | { 785 | if(errno != EEXIST) 786 | { 787 | exit_clean(1); //"Directory cannot be created!" 788 | } 789 | } 790 | } 791 | else 792 | snprintf(dump_file_path, STDBUF, "%s%s.%lu", dpath, dump_file_prefix, (long unsigned int) now); 793 | } 794 | else 795 | { 796 | if(datedir) 797 | { 798 | snprintf(dump_file_path, STDBUF, "%s%s.%lu", datepath, dump_file_prefix, (long unsigned int) now); 799 | if(mkdir(datepath, (S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH))) 800 | { 801 | if(errno != EEXIST) 802 | { 803 | exit_clean(1); //"Directory cannot be created!" 804 | } 805 | } 806 | } 807 | else 808 | snprintf(dump_file_path, STDBUF, "%s.%lu", dump_file_prefix, (long unsigned int) now); 809 | } 810 | 811 | // TODO: check if destination file already exists 812 | 813 | 814 | if ( (dump_handle=pcap_dump_open(handle, dump_file_path)) == NULL ) 815 | { 816 | exit_clean(1); 817 | } 818 | 819 | return SUCCESS; 820 | } 821 | 822 | int dump_file_roll() 823 | { 824 | dump_file_close(); 825 | dump_file_open(); 826 | 827 | return SUCCESS; 828 | } 829 | 830 | 831 | int dump_file_close() 832 | { 833 | if ( dump_handle != NULL ) { 834 | pcap_dump_flush(dump_handle); 835 | pcap_dump_close(dump_handle); 836 | dump_handle = NULL; 837 | } 838 | 839 | return SUCCESS; 840 | } 841 | 842 | 843 | static int set_chroot(void) { 844 | char *absdir; 845 | 846 | /* logdir = get_abs_path(logpath); */ 847 | 848 | /* change to the directory */ 849 | if ( chdir(chroot_dir) != 0 ) { 850 | printf("set_chroot: Can not chdir to \"%s\": %s\n",chroot_dir,strerror(errno)); 851 | } 852 | 853 | /* always returns an absolute pathname */ 854 | absdir = getcwd(NULL, 0); 855 | 856 | /* make the chroot call */ 857 | if ( chroot(absdir) < 0 ) { 858 | printf("Can not chroot to \"%s\": absolute: %s: %s\n",chroot_dir,absdir,strerror(errno)); 859 | } 860 | 861 | if ( chdir("/") < 0 ) { 862 | printf("Can not chdir to \"/\" after chroot: %s\n",strerror(errno)); 863 | } 864 | 865 | return 0; 866 | } 867 | 868 | static int drop_privs(void) { 869 | struct group *gr; 870 | struct passwd *pw; 871 | char *endptr; 872 | int i; 873 | int do_setuid = 0; 874 | int do_setgid = 0; 875 | unsigned long groupid = 0; 876 | unsigned long userid = 0; 877 | 878 | if ( group_name != NULL ) { 879 | do_setgid = 1; 880 | if( isdigit(group_name[0]) == 0 ) { 881 | gr = getgrnam(group_name); 882 | groupid = gr->gr_gid; 883 | } 884 | else { 885 | groupid = strtoul(group_name, &endptr, 10); 886 | } 887 | } 888 | 889 | if ( user_name != NULL ) { 890 | do_setuid = 1; 891 | do_setgid = 1; 892 | if ( isdigit(user_name[0]) == 0 ) { 893 | pw = getpwnam(user_name); 894 | userid = pw->pw_uid; 895 | } else { 896 | userid = strtoul(user_name, &endptr, 10); 897 | pw = getpwuid(userid); 898 | } 899 | 900 | if ( group_name == NULL ) { 901 | groupid = pw->pw_gid; 902 | } 903 | } 904 | 905 | if ( do_setgid ) { 906 | if ( (i = setgid(groupid)) < 0 ) { 907 | printf("Unable to set group ID: %s", strerror(i)); 908 | } 909 | } 910 | 911 | endgrent(); 912 | endpwent(); 913 | 914 | if ( do_setuid ) { 915 | if (getuid() == 0 && initgroups(user_name, groupid) < 0 ) { 916 | printf("Unable to init group names (%s/%lu)", user_name, groupid); 917 | } 918 | if ( (i = setuid(userid)) < 0 ) { 919 | printf("Unable to set user ID: %s\n", strerror(i)); 920 | } 921 | } 922 | return 0; 923 | } 924 | 925 | static int is_valid_path(char *path) { 926 | struct stat st; 927 | 928 | if ( path == NULL ) { 929 | return 0; 930 | } 931 | if ( stat(path, &st) != 0 ) { 932 | return 0; 933 | } 934 | if ( !S_ISDIR(st.st_mode) || access(path, W_OK) == -1 ) { 935 | return 0; 936 | } 937 | return 1; 938 | } 939 | 940 | static int create_pid_file(char *path, char *filename) { 941 | char filepath[STDBUF]; 942 | char *fp = NULL; 943 | char *fn = NULL; 944 | char pid_buffer[12]; 945 | struct flock lock; 946 | int rval; 947 | int fd; 948 | 949 | memset(filepath, 0, STDBUF); 950 | 951 | if ( !filename ) { 952 | fn = pidfile; 953 | } 954 | else { 955 | fn = filename; 956 | } 957 | 958 | if ( !path ) { 959 | fp = pidpath; 960 | } 961 | else { 962 | fp = path; 963 | } 964 | 965 | if ( is_valid_path(fp) ) { 966 | snprintf(filepath, STDBUF-1, "%s/%s", fp, fn); 967 | } 968 | else { 969 | printf("PID path \"%s\" isn't a writeable directory!", fp); 970 | } 971 | 972 | true_pid_name = strdup(filename); 973 | 974 | if ( (fd = open(filepath, O_CREAT | O_WRONLY, 975 | S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) == -1 ) { 976 | return ERROR; 977 | } 978 | 979 | /* pid file locking */ 980 | lock.l_type = F_WRLCK; 981 | lock.l_start = 0; 982 | lock.l_whence = SEEK_SET; 983 | lock.l_len = 0; 984 | 985 | if ( fcntl(fd, F_SETLK, &lock) == -1 ) { 986 | if ( errno == EACCES || errno == EAGAIN ) { 987 | rval = ERROR; 988 | } 989 | else { 990 | rval = ERROR; 991 | } 992 | close(fd); 993 | return rval; 994 | } 995 | 996 | snprintf(pid_buffer, sizeof(pid_buffer), "%d\n", (int) getpid()); 997 | if ( ftruncate(fd, 0) != 0 ) { return ERROR; } 998 | if ( write(fd, pid_buffer, strlen(pid_buffer)) != 0 ) { return ERROR; } 999 | 1000 | close(fd); 1001 | 1002 | return SUCCESS; 1003 | } 1004 | 1005 | int daemonize() { 1006 | pid_t pid; 1007 | int fd; 1008 | 1009 | pid = fork(); 1010 | 1011 | if ( pid > 0 ) { 1012 | exit_clean(0); /* parent */ 1013 | } 1014 | 1015 | use_syslog = 1; 1016 | if ( pid < 0 ) { 1017 | return ERROR; 1018 | } 1019 | 1020 | /* new process group */ 1021 | setsid(); 1022 | 1023 | /* close file handles */ 1024 | if ( (fd = open("/dev/null", O_RDWR)) >= 0 ) { 1025 | dup2(fd, 0); 1026 | dup2(fd, 1); 1027 | dup2(fd, 2); 1028 | if ( fd > 2 ) { 1029 | close(fd); 1030 | } 1031 | } 1032 | 1033 | if ( pidfile ) { 1034 | return create_pid_file(pidpath, pidfile); 1035 | } 1036 | 1037 | return SUCCESS; 1038 | } 1039 | 1040 | static int go_daemon() { 1041 | return daemonize(); 1042 | } 1043 | 1044 | static void banner() { 1045 | fprintf(stdout, "\n"); 1046 | fprintf(stdout, " cxtracker - v%s\n", VERSION); 1047 | fprintf(stdout, " - The lightweight network connection tracker,\n"); 1048 | fprintf(stdout, " packet logger and packet indexer!\n\n"); 1049 | fprintf(stdout, " Using %s\n", pcap_lib_version()); 1050 | fprintf(stdout, "\n"); 1051 | } 1052 | 1053 | static void usage(const char *program_name) { 1054 | fprintf(stdout, "\n"); 1055 | fprintf(stdout, "USAGE: %s [-options]\n", program_name); 1056 | fprintf(stdout, "\n"); 1057 | fprintf(stdout, " General Options:\n"); 1058 | fprintf(stdout, " -? You're reading it.\n"); 1059 | fprintf(stdout, " -V Version and compiled in options.\n"); 1060 | fprintf(stdout, " -v Verbose output.\n"); 1061 | fprintf(stdout, " -i Interface to sniff from.\n"); 1062 | fprintf(stdout, " -f Output format line. See Format options.\n"); 1063 | fprintf(stdout, " -b Berkley packet filter.\n"); 1064 | fprintf(stdout, " -d Directory to write session files to.\n"); 1065 | fprintf(stdout, " -D Enable daemon mode.\n"); 1066 | fprintf(stdout, " -u User to drop priveleges to after daemonising.\n"); 1067 | fprintf(stdout, " -g Group to drop priveleges to after daemonising.\n"); 1068 | fprintf(stdout, " -T Direct to chroot into.\n"); 1069 | fprintf(stdout, " -P Path to PID file (/var/run).\n"); 1070 | fprintf(stdout, " -p Name of pidfile (cxtracker.pid).\n"); 1071 | fprintf(stdout, " -r PCAP file to read.\n"); 1072 | fprintf(stdout, " -w Dump PCAP to file with specified prefix.\n"); 1073 | fprintf(stdout, " -F Flush output after every write to dump file.\n"); 1074 | fprintf(stdout, " -s Roll over dump file based on size.\n"); 1075 | fprintf(stdout, " -t Roll over dump file based on time intervals.\n"); 1076 | fprintf(stdout, " -x Amount of space to leave free on disk.\n"); 1077 | fprintf(stdout, " -A Write packets to directories by date (YYYY-MM-DD)\n"); 1078 | fprintf(stdout, "\n"); 1079 | fprintf(stdout, " Long Options:\n"); 1080 | fprintf(stdout, " --help Same as '?'\n"); 1081 | fprintf(stdout, " --version Same as 'V'\n"); 1082 | fprintf(stdout, " --interface Same as 'i'\n"); 1083 | fprintf(stdout, " --format Same as 'f'\n"); 1084 | fprintf(stdout, " --bpf Same as 'b'\n"); 1085 | fprintf(stdout, " --log-dir Same as 'd'\n"); 1086 | fprintf(stdout, " --daemonize Same as 'D'\n"); 1087 | fprintf(stdout, " --user Same as 'u'\n"); 1088 | fprintf(stdout, " --group Same as 'g'\n"); 1089 | fprintf(stdout, " --chroot-dir Same as 'T'\n"); 1090 | fprintf(stdout, " --pid-file Same as 'p'\n"); 1091 | fprintf(stdout, " --pcap-file Same as 'r'\n"); 1092 | fprintf(stdout, "\n"); 1093 | format_options(); 1094 | } 1095 | 1096 | int main(int argc, char *argv[]) { 1097 | 1098 | int ch, fromfile, setfilter, version, drop_privs_flag, daemon_flag, chroot_flag; 1099 | char *bpff, errbuf[PCAP_ERRBUF_SIZE]; 1100 | extern char *optarg; 1101 | char roll_metric = 0; 1102 | char roll_off_metric = 0; 1103 | char roll_type = GIGABYTES; 1104 | //char roll_off_type = GIGABYTES; 1105 | size_t roll_point = 2; 1106 | size_t roll_off = 0; 1107 | roll_size = roll_point * GIGABYTE; 1108 | 1109 | int long_option_index = 0; 1110 | static struct option long_options[] = { 1111 | {"help", 0, NULL, '?'}, 1112 | {"version", 0, NULL, 'V'}, 1113 | {"interface", 1, NULL, 'i'}, 1114 | {"format", 1, NULL, 'f'}, 1115 | {"bpf", 1, NULL, 'b'}, 1116 | {"log-dir", 1, NULL, 'd'}, 1117 | {"daemonize", 0, NULL, 'D'}, 1118 | {"user", 1, NULL, 'u'}, 1119 | {"group", 1, NULL, 'g'}, 1120 | {"chroot-dir", 1, NULL, 'T'}, 1121 | {"pid-file", 1, NULL, 'p'}, 1122 | {"pcap-file", 1, NULL, 'r'}, 1123 | {0, 0, 0, 0} 1124 | }; 1125 | 1126 | 1127 | bpf_u_int32 net_mask = 0; 1128 | ch = fromfile = setfilter = version = drop_privs_flag = daemon_flag = 0; 1129 | dev = "eth0"; 1130 | bpff = ""; 1131 | chroot_dir = "/tmp/"; 1132 | output_format = "standard"; 1133 | cxtbuffer = NULL; 1134 | cxtrackerid = 0; 1135 | dump_with_flush = inpacket = intr_flag = chroot_flag = 0; 1136 | mode = 0; 1137 | 1138 | signal(SIGTERM, game_over); 1139 | signal(SIGINT, game_over); 1140 | signal(SIGQUIT, game_over); 1141 | signal(SIGHUP, dump_active); 1142 | signal(SIGALRM, set_end_sessions); 1143 | 1144 | while( (ch=getopt_long(argc, argv, "?b:d:DT:f:g:i:p:P:r:u:vVw:s:t:x:A", long_options, &long_option_index)) != EOF ) 1145 | switch (ch) { 1146 | case 'i': 1147 | dev = strdup(optarg); 1148 | mode |= MODE_DEV; 1149 | break; 1150 | case 'b': 1151 | bpff = strdup(optarg); 1152 | break; 1153 | case 'v': 1154 | verbose = 1; 1155 | break; 1156 | case 'f': 1157 | output_format = strdup(optarg); 1158 | break; 1159 | case 'd': 1160 | snprintf(dpath, STDBUF, "%s", optarg); 1161 | 1162 | // normalise the directory path to ensure it's slash terminated 1163 | if( dpath[strlen(dpath)-1] != '/' ) 1164 | strncat(dpath, "/", STDBUF); 1165 | break; 1166 | case '?': 1167 | usage(argv[0]); 1168 | exit_clean(0); 1169 | break; 1170 | case 'V': 1171 | banner(); 1172 | exit_clean(0); 1173 | break; 1174 | case 'D': 1175 | daemon_flag = 1; 1176 | break; 1177 | case 'T': 1178 | chroot_flag = 1; 1179 | break; 1180 | case 'u': 1181 | user_name = strdup(optarg); 1182 | drop_privs_flag = 1; 1183 | break; 1184 | case 'g': 1185 | group_name = strdup(optarg); 1186 | drop_privs_flag = 1; 1187 | break; 1188 | case 'p': 1189 | pidfile = strdup(optarg); 1190 | break; 1191 | case 'P': 1192 | pidpath = strdup(optarg); 1193 | break; 1194 | case 'r': 1195 | read_file = strdup(optarg); 1196 | mode |= MODE_FILE; 1197 | dev = NULL; 1198 | break; 1199 | case 'w': 1200 | dump_file_prefix = strdup(optarg); 1201 | mode |= MODE_DUMP; 1202 | break; 1203 | case 'F': 1204 | dump_with_flush = 1; 1205 | break; 1206 | case 's': 1207 | sscanf(optarg, "%zu%c", &roll_point, &roll_metric); 1208 | 1209 | switch( tolower(roll_metric) ) { 1210 | case 'k': 1211 | roll_size = roll_point * KILOBYTE; 1212 | roll_type = KILOBYTES; 1213 | break; 1214 | case 'm': 1215 | roll_size = roll_point * MEGABYTE; 1216 | roll_type = MEGABYTES; 1217 | break; 1218 | case 'g': 1219 | roll_size = roll_point * GIGABYTE; 1220 | roll_type = GIGABYTES; 1221 | break; 1222 | case 't': 1223 | roll_size = roll_point * TERABYTE; 1224 | roll_type = TERABYTES; 1225 | break; 1226 | default: 1227 | printf("[*] Invalid size metric: %c\n", roll_metric ? roll_metric : '-'); 1228 | break; 1229 | } 1230 | 1231 | break; 1232 | case 't': 1233 | sscanf(optarg, "%zu%c", &roll_point, &roll_metric); 1234 | 1235 | switch( tolower(roll_metric) ) { 1236 | case 's': 1237 | roll_time = roll_point; 1238 | roll_type = SECONDS; 1239 | break; 1240 | case 'm': 1241 | roll_time = roll_point * 60; 1242 | roll_type = MINUTES; 1243 | break; 1244 | case 'h': 1245 | roll_time = roll_point * 60 * 60; 1246 | roll_type = HOURS; 1247 | break; 1248 | case 'd': 1249 | roll_time = roll_point * 60 * 60 * 24; 1250 | roll_type = DAYS; 1251 | break; 1252 | default: 1253 | printf("[*] Invalid size metric: %c\n", roll_metric ? roll_metric : '-'); 1254 | break; 1255 | } 1256 | break; 1257 | case 'x': 1258 | sscanf(optarg, "%zu%c", &roll_off, &roll_off_metric); 1259 | 1260 | switch( tolower(roll_off_metric) ) { 1261 | case 'k': 1262 | roll_off_size = roll_off * KILOBYTE; 1263 | //roll_off_type = KILOBYTES; 1264 | break; 1265 | case 'm': 1266 | roll_off_size = roll_off * MEGABYTE; 1267 | //roll_off_type = MEGABYTES; 1268 | break; 1269 | case 'g': 1270 | roll_off_size = roll_off * GIGABYTE; 1271 | //roll_off_type = GIGABYTES; 1272 | break; 1273 | case 't': 1274 | roll_off_size = roll_off * TERABYTE; 1275 | //roll_off_type = TERABYTES; 1276 | break; 1277 | default: 1278 | printf("[*] Invalid size metric: %c\n", roll_metric ? roll_metric : '-'); 1279 | break; 1280 | } 1281 | break; 1282 | case 'A': 1283 | datedir = 1; 1284 | break; 1285 | 1286 | default: 1287 | exit_clean(1); 1288 | break; 1289 | } 1290 | 1291 | errbuf[0] = '\0'; 1292 | 1293 | // validate the output format string 1294 | format_validate(output_format); 1295 | 1296 | // specify reading from a device OR a file and not both 1297 | if ( (mode & MODE_DEV) && (mode & MODE_FILE) ) 1298 | { 1299 | printf("[!] You must specify a device OR file to read from, not both.\n"); 1300 | usage(argv[0]); 1301 | exit_clean(1); 1302 | } 1303 | else if ( (mode & MODE_FILE) && read_file) { 1304 | /* Read from PCAP file specified by '-r' switch. */ 1305 | printf("[*] Reading from file %s", read_file); 1306 | if (!(handle = pcap_open_offline(read_file, errbuf))) { 1307 | printf("\n"); 1308 | printf("[*] Unable to open %s. (%s)\n", read_file, errbuf); 1309 | exit_clean(1); 1310 | } else { 1311 | printf(" - OK\n"); 1312 | } 1313 | 1314 | // in pcap_open_offline(), libpcap appears to use a static buffer 1315 | // for reading in the file. we must use memcpy's to ensure data 1316 | // persists as expected 1317 | if ( ip_init(&ip_config, IP_SET_MEMCPY) ) 1318 | { 1319 | printf("[!] Unable to initialise the IP library.\n"); 1320 | exit_clean(1); 1321 | } 1322 | else 1323 | printf("[*] IP library using \"memcpy\" set.\n"); 1324 | } 1325 | else if ( (mode & MODE_DEV) && dev) { 1326 | if (getuid()) { 1327 | printf("[*] You must be root..\n"); 1328 | exit_clean(1); 1329 | } 1330 | 1331 | printf("[*] Running cxtracker %s\n",VERSION); 1332 | 1333 | //errbuf[0] = '\0'; 1334 | /* look up an availible device if non specified */ 1335 | if (dev == 0x0) dev = pcap_lookupdev(errbuf); 1336 | printf("[*] Device: %s\n", dev); 1337 | 1338 | if ((handle = pcap_open_live(dev, SNAPLENGTH, 1, 500, errbuf)) == NULL) { 1339 | printf("[*] Error pcap_open_live: %s \n", errbuf); 1340 | exit_clean(1); 1341 | } 1342 | 1343 | // in pcap_open_live(), libpcap maintains a heap allocated buffer 1344 | // for reading off the wire. we can use pointer copies here for 1345 | // improved speed 1346 | if ( ip_init(&ip_config, IP_SET_MEMCPY) ) 1347 | { 1348 | printf("[*] Unable to initialise the IP library.\n"); 1349 | exit_clean(1); 1350 | } 1351 | else 1352 | printf("[*] IP library using \"memcpy\" set.\n"); 1353 | 1354 | if ( chroot_flag == 1 ) { 1355 | set_chroot(); 1356 | } 1357 | 1358 | if(daemon_flag) { 1359 | if(!is_valid_path(pidpath)) 1360 | printf("[*] PID path \"%s\" is bad, check privilege.",pidpath); 1361 | openlog("cxtracker", LOG_PID | LOG_CONS, LOG_DAEMON); 1362 | printf("[*] Daemonizing...\n\n"); 1363 | go_daemon(); 1364 | } 1365 | } 1366 | else 1367 | { 1368 | printf("[*] You must specify where to read from.\n"); 1369 | exit_clean(1); 1370 | } 1371 | 1372 | /* B0rk if we see an error... */ 1373 | if (strlen(errbuf) > 0) { 1374 | printf("[*] Error errbuf: %s \n", errbuf); 1375 | exit_clean(1); 1376 | } 1377 | 1378 | if(drop_privs_flag) { 1379 | printf("[*] Dropping privs...\n"); 1380 | drop_privs(); 1381 | } 1382 | 1383 | if ((pcap_compile(handle, &cfilter, bpff, 1 ,net_mask)) == -1) { 1384 | printf("[*] Error pcap_compile user_filter: %s\n", pcap_geterr(handle)); 1385 | exit_clean(1); 1386 | } 1387 | 1388 | if (pcap_setfilter(handle, &cfilter)) { 1389 | printf("[*] Unable to set pcap filter! (%s)\n", pcap_geterr(handle)); 1390 | } else { 1391 | pcap_freecode(&cfilter); // filter code not needed after setfilter 1392 | } 1393 | 1394 | /*go ahead and check disk space and prune if needed*/ 1395 | if(roll_off_size) 1396 | { 1397 | roll_off_size += roll_size; 1398 | /*set roll_off_size up by roll_size 1399 | this should help prevent overstepping max disk usage*/ 1400 | /*prune*/ 1401 | if(pcap_roll_off()) 1402 | { 1403 | printf("[*] Error pruning pcaps from disk.\n"); 1404 | exit_clean(1); /*error*/ 1405 | } 1406 | } 1407 | 1408 | // set up dump mode now as appropriate 1409 | if (mode & MODE_DUMP ) 1410 | { 1411 | if (datedir) 1412 | { 1413 | printf("[*] Writing traffic to %s%s/%s.*, rolling every %d %s\n", 1414 | dpath, "YYYY-MM-DD", dump_file_prefix, (int)roll_point, rollover_names[(int)roll_type]); 1415 | } 1416 | else 1417 | { 1418 | printf("[*] Writing traffic to %s%s.*, rolling every %d %s\n", 1419 | dpath, dump_file_prefix, (int)roll_point, rollover_names[(int)roll_type]); 1420 | } 1421 | dump_file_open(); 1422 | } 1423 | 1424 | bucket_keys_NULL(); 1425 | 1426 | alarm(TIMEOUT); 1427 | if (read_file) { 1428 | printf("[*] Reading packets...\n"); 1429 | } else { 1430 | printf("[*] Sniffing...\n"); 1431 | } 1432 | 1433 | roll_time_last = time(NULL); 1434 | 1435 | pcap_loop(handle,-1,got_packet,NULL); 1436 | 1437 | game_over(); 1438 | 1439 | return 0; 1440 | } 1441 | 1442 | void exit_clean(int code) 1443 | { 1444 | pcap_freecode(&cfilter); // filter code not needed after setfilter 1445 | 1446 | // clean up the pcap handle 1447 | if (handle) 1448 | pcap_close(handle); 1449 | 1450 | // clean up the custom formatter 1451 | format_clear(); 1452 | 1453 | exit(code); 1454 | } 1455 | -------------------------------------------------------------------------------- /src/cxtracker.h: -------------------------------------------------------------------------------- 1 | /* 2 | ** This file is a part of cxtracker. 3 | ** 4 | ** Copyright (C) 2009, Redpill Linpro 5 | ** Copyright (C) 2009, Edward Fjellskål 6 | ** 7 | ** This program is free software; you can redistribute it and/or modify 8 | ** it under the terms of the GNU General Public License as published by 9 | ** the Free Software Foundation; either version 2 of the License, or 10 | ** (at your option) any later version. 11 | ** 12 | ** This program is distributed in the hope that it will be useful, 13 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | ** GNU General Public License for more details. 16 | ** 17 | ** You should have received a copy of the GNU General Public License 18 | ** along with this program; if not, write to the Free Software 19 | ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 20 | ** 21 | */ 22 | 23 | #ifndef __CXTRACKER_H__ 24 | #define __CXTRACKER_H__ 25 | 26 | /* I N C L U D E S **********************************************************/ 27 | #include 28 | #include "ip.h" 29 | 30 | /* D E F I N E S ************************************************************/ 31 | #define VERSION "0.9.8.5" 32 | #define TIMEOUT 45 33 | #define BUCKET_SIZE 65537 34 | #define SNAPLENGTH 65535 35 | 36 | #define MODE_DEV 0x01 37 | #define MODE_FILE 0x02 38 | #define MODE_DUMP 0x10 39 | 40 | #define ETHERNET_TYPE_IP 0x0800 41 | #define ETHERNET_TYPE_ARP 0x0806 42 | #define ETHERNET_TYPE_IPV6 0x86dd 43 | #define ETHERNET_TYPE_8021Q 0x8100 44 | #define ETHERNET_TYPE_802Q1MT 0x9100 45 | #define ETHERNET_TYPE_802Q1MT2 0x9200 46 | #define ETHERNET_TYPE_802Q1MT3 0x9300 47 | #define ETHERNET_TYPE_8021AD 0x88a8 48 | 49 | #define IP_PROTO_TCP 6 50 | #define IP_PROTO_UDP 17 51 | #define IP_PROTO_ICMP 1 52 | #define IP6_PROTO_ICMP 58 53 | 54 | #define IP4_HEADER_LEN 20 55 | #define IP6_HEADER_LEN 40 56 | #define TCP_HEADER_LEN 20 57 | #define UDP_HEADER_LEN 8 58 | #define ICMP_HEADER_LEN 4 59 | #define ETHERNET_HEADER_LEN 14 60 | #define ETHERNET_8021Q_HEADER_LEN 18 61 | #define ETHERNET_802Q1MT_HEADER_LEN 22 62 | 63 | #define TF_FIN 0x01 64 | #define TF_SYN 0x02 65 | #define TF_RST 0x04 66 | #define TF_PUSH 0x08 67 | #define TF_ACK 0x10 68 | #define TF_URG 0x20 69 | #define TF_ECE 0x40 70 | #define TF_CWR 0x80 71 | #define TF_SYNACK 0x12 72 | #define TF_NORESERVED (TF_FIN|TF_SYN|TF_RST|TF_PUSH|TF_ACK|TF_URG) 73 | #define TF_FLAGS (TF_FIN|TF_SYN|TF_RST|TF_ACK|TF_URG|TF_ECE|TF_CWR) 74 | 75 | #define KILOBYTE 1<<10 76 | #define MEGABYTE 1<<20 77 | #define GIGABYTE 1<<30 78 | #define TERABYTE 1<<40 79 | 80 | 81 | #define SUCCESS 0 82 | #define ERROR 1 83 | #define STDBUF 1024 84 | 85 | /* D A T A S T R U C T U R E S *********************************************/ 86 | 87 | /* 88 | * Ethernet header 89 | */ 90 | 91 | typedef struct _ether_header { 92 | u_char ether_dst[6]; /* destination MAC */ 93 | u_char ether_src[6]; /* source MAC */ 94 | 95 | union 96 | { 97 | struct etht 98 | { 99 | u_short ether_type; /* ethernet type (normal) */ 100 | } etht; 101 | 102 | struct qt 103 | { 104 | u_short eth_t_8021; /* ethernet type/802.1Q tag */ 105 | u_short eth_t_8_vid; 106 | u_short eth_t_8_type; 107 | } qt; 108 | 109 | struct qot 110 | { 111 | u_short eth_t_80212; /* ethernet type/802.1QinQ */ 112 | u_short eth_t_82_mvid; 113 | u_short eth_t_82_8021; 114 | u_short eth_t_82_vid; 115 | u_short eth_t_82_type; 116 | } qot; 117 | } vlantag; 118 | 119 | #define eth_ip_type vlantag.etht.ether_type 120 | 121 | #define eth_8_type vlantag.qt.eth_t_8021 122 | #define eth_8_vid vlantag.qt.eth_t_8_vid 123 | #define eth_8_ip_type vlantag.qt.eth_t_8_type 124 | 125 | #define eth_82_type vlantag.qot.eth_t_80212 126 | #define eth_82_mvid vlantag.qot.eth_t_82_mvid 127 | #define eth_82_8021 vlantag.qot.eth_t_82_8021 128 | #define eth_82_vid vlantag.qot.eth_t_82_vid 129 | #define eth_82_ip_type vlantag.qot.eth_t_82_type 130 | 131 | } ether_header; 132 | 133 | /* 134 | * IPv4 header 135 | */ 136 | 137 | typedef struct _ip4_header { 138 | uint8_t ip_vhl; /* version << 4 | header length >> 2 */ 139 | uint8_t ip_tos; /* type of service */ 140 | uint16_t ip_len; /* total length */ 141 | uint16_t ip_id; /* identification */ 142 | uint16_t ip_off; /* fragment offset field */ 143 | uint8_t ip_ttl; /* time to live */ 144 | uint8_t ip_p; /* protocol */ 145 | uint16_t ip_csum; /* checksum */ 146 | uint32_t ip_src; /* source address */ 147 | uint32_t ip_dst; /* dest address */ 148 | } ip4_header; 149 | 150 | #define IP_RF 0x8000 /* reserved fragment flag */ 151 | #define IP_DF 0x4000 /* dont fragment flag */ 152 | #define IP_MF 0x2000 /* more fragments flag */ 153 | #define IP_OFFMASK 0x1fff /* mask for fragmenting bits */ 154 | #define IP_HL(ip4_header) (((ip4_header)->ip_vhl) & 0x0f) 155 | #define IP_V(ip4_header) (((ip4_header)->ip_vhl) >> 4) 156 | 157 | /* 158 | * IPv6 header 159 | */ 160 | 161 | typedef struct _ip6_header { 162 | uint32_t vcl; /* version, class, and label */ 163 | uint16_t len; /* length of the payload */ 164 | uint8_t next; /* next header 165 | * Uses the same flags as 166 | * the IPv4 protocol field */ 167 | uint8_t hop_lmt; /* hop limit */ 168 | struct in6_addr ip_src; /* source address */ 169 | struct in6_addr ip_dst; /* dest address */ 170 | } ip6_header; 171 | 172 | /* 173 | * TCP header 174 | */ 175 | 176 | typedef struct _tcp_header { 177 | uint16_t src_port; /* source port */ 178 | uint16_t dst_port; /* destination port */ 179 | uint32_t t_seq; /* sequence number */ 180 | uint32_t t_ack; /* acknowledgement number */ 181 | uint8_t t_offx2; /* data offset, rsvd */ 182 | uint8_t t_flags; /* tcp flags */ 183 | uint16_t t_win; /* window */ 184 | uint16_t t_csum; /* checksum */ 185 | uint16_t t_urgp; /* urgent pointer */ 186 | } tcp_header; 187 | 188 | #define TCP_OFFSET(tcp_header) (((tcp_header)->t_offx2 & 0xf0) >> 4) 189 | #define TCP_X2(tcp_header) ((tcp_header)->t_offx2 & 0x0f) 190 | #define TCP_ISFLAGSET(tcp_header, flags) (((tcp_header)->t_flags & (flags)) == (flags)) 191 | 192 | /* 193 | * UDP header 194 | */ 195 | 196 | typedef struct _udp_header { 197 | uint16_t src_port; /* source port */ 198 | uint16_t dst_port; /* destination port */ 199 | uint16_t len; /* length of the payload */ 200 | uint16_t csum; /* checksum */ 201 | } udp_header; 202 | 203 | /* 204 | * ICMP header 205 | */ 206 | 207 | typedef struct _icmp_header { 208 | uint8_t type; 209 | uint8_t code; 210 | uint16_t csum; 211 | union 212 | { 213 | uint8_t pptr; 214 | 215 | struct in_addr gwaddr; 216 | 217 | struct idseq 218 | { 219 | uint16_t id; 220 | uint16_t seq; 221 | } idseq; 222 | 223 | int sih_void; 224 | 225 | struct pmtu 226 | { 227 | uint16_t ipm_void; 228 | uint16_t nextmtu; 229 | } pmtu; 230 | 231 | struct rtradv 232 | { 233 | uint8_t num_addrs; 234 | uint8_t wpa; 235 | uint16_t lifetime; 236 | } rtradv; 237 | } icmp_hun; 238 | 239 | #define s_icmp_pptr icmp_hun.pptr 240 | #define s_icmp_gwaddr icmp_hun.gwaddr 241 | #define s_icmp_id icmp_hun.idseq.id 242 | #define s_icmp_seq icmp_hun.idseq.seq 243 | #define s_icmp_void icmp_hun.sih_void 244 | #define s_icmp_pmvoid icmp_hun.pmtu.ipm_void 245 | #define s_icmp_nextmtu icmp_hun.pmtu.nextmtu 246 | #define s_icmp_num_addrs icmp_hun.rtradv.num_addrs 247 | #define s_icmp_wpa icmp_hun.rtradv.wpa 248 | #define s_icmp_lifetime icmp_hun.rtradv.lifetime 249 | 250 | union 251 | { 252 | /* timestamp */ 253 | struct ts 254 | { 255 | uint32_t otime; 256 | uint32_t rtime; 257 | uint32_t ttime; 258 | } ts; 259 | 260 | /* IP header for unreach */ 261 | struct ih_ip 262 | { 263 | ip4_header *ip; 264 | /* options and then 64 bits of data */ 265 | } ip; 266 | 267 | struct ra_addr 268 | { 269 | uint32_t addr; 270 | uint32_t preference; 271 | } radv; 272 | 273 | uint32_t mask; 274 | 275 | char data[1]; 276 | 277 | } icmp_dun; 278 | #define s_icmp_otime icmp_dun.ts.otime 279 | #define s_icmp_rtime icmp_dun.ts.rtime 280 | #define s_icmp_ttime icmp_dun.ts.ttime 281 | #define s_icmp_ip icmp_dun.ih_ip 282 | #define s_icmp_radv icmp_dun.radv 283 | #define s_icmp_mask icmp_dun.mask 284 | #define s_icmp_data icmp_dun.data 285 | } icmp_header; 286 | 287 | typedef struct _icmp6_header { 288 | uint8_t type; /* type field */ 289 | uint8_t code; /* code field */ 290 | uint16_t csum; /* checksum field */ 291 | union 292 | { 293 | uint32_t icmp6_data32[1]; /* type-specific field */ 294 | uint16_t icmp6_data16[2]; /* type-specific field */ 295 | uint8_t icmp6_data8[4]; /* type-specific field */ 296 | } icmp6_data; 297 | #define icmp6_id icmp6_data.icmp6_data16[0] /* echo request/reply */ 298 | #define icmp6_seq icmp6_data.icmp6_data16[1] /* echo request/reply */ 299 | } icmp6_header; 300 | 301 | #define ICMP6_UNREACH 1 302 | #define ICMP6_BIG 2 303 | #define ICMP6_TIME 3 304 | #define ICMP6_PARAMS 4 305 | #define ICMP6_ECHO 128 306 | #define ICMP6_REPLY 129 307 | 308 | /* Minus 1 due to the 'body' field */ 309 | #define ICMP6_MIN_HEADER_LEN (sizeof(ICMP6Hdr) ) 310 | 311 | /* 312 | * Structure for connections 313 | */ 314 | 315 | typedef struct _connection { 316 | u_int64_t cxid; /* connection id */ 317 | int ipversion; /* IP version (4/6) */ 318 | u_short vlanid; /* VLAN id if any */ 319 | u_int8_t proto; /* IP protocoll type */ 320 | ip_t *s_ip; /* source address */ 321 | ip_t *d_ip; /* destination address */ 322 | 323 | u_int16_t s_port; /* source port */ 324 | u_int16_t d_port; /* destination port */ 325 | u_int64_t s_total_pkts; /* total source packets */ 326 | u_int64_t s_total_bytes; /* total source bytes */ 327 | u_int64_t d_total_pkts; /* total destination packets */ 328 | u_int64_t d_total_bytes; /* total destination bytes */ 329 | u_int8_t s_tcpFlags; /* tcpflags sent by source */ 330 | u_int8_t d_tcpFlags; /* tcpflags sent by destination */ 331 | char start_dump[STDBUF]; /* dump file of starting packet */ 332 | int64_t start_offset; /* byte offset of the starting packet */ 333 | struct timeval start_time; /* connection start time */ 334 | char last_dump[STDBUF]; /* dump file of last packet */ 335 | int64_t last_offset; /* byte offset of the last packet */ 336 | struct timeval last_pkt_time; /* last seen packet time */ 337 | 338 | struct _connection *prev; /* Pointer to prev connection */ 339 | struct _connection *next; /* Pointer to next connection */ 340 | } connection; 341 | 342 | typedef enum { 343 | KILOBYTES = 1, 344 | MEGABYTES, 345 | GIGABYTES, 346 | TERABYTES, 347 | 348 | SECONDS, 349 | MINUTES, 350 | HOURS, 351 | DAYS 352 | } rollover_type; 353 | 354 | 355 | 356 | #endif /* __CXTRACKER_H__ */ 357 | -------------------------------------------------------------------------------- /src/format.c: -------------------------------------------------------------------------------- 1 | /* 2 | ** Copyright (C) 2011 - 2013, Ian Firns 3 | ** Copyright (C) 2011 - 2013, Edward Fjellskål 4 | ** 5 | ** 6 | ** This program is free software; you can redistribute it and/or modify 7 | ** it under the terms of the GNU General Public License as published by 8 | ** the Free Software Foundation; either version 2 of the License, or 9 | ** (at your option) any later version. 10 | ** 11 | ** This program is distributed in the hope that it will be useful, 12 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | ** GNU General Public License for more details. 15 | ** 16 | ** You should have received a copy of the GNU General Public License 17 | ** along with this program; if not, write to the Free Software 18 | ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 19 | ** 20 | */ 21 | 22 | #include "format.h" 23 | 24 | #include 25 | #include 26 | 27 | 28 | /* 29 | * Structure for custom output formats 30 | */ 31 | typedef struct _format_s { 32 | void (*func)(FILE *, const struct _connection *, const char *); 33 | char *prefix; 34 | struct _format_s *next; 35 | } format_t; 36 | 37 | format_t *custom = NULL; 38 | 39 | void format_function_append(format_t **head, void (*func)(FILE *, const struct _connection *, char *), char *prefix); 40 | void format_free(format_t **head); 41 | void format_write_cxid(FILE *fd, const connection *cxt, const char *prefix); 42 | void format_write_time_start(FILE *fd, const connection *cxt, const char *prefix); 43 | void format_write_utime_start(FILE *fd, const connection *cxt, const char *prefix); 44 | void format_write_unixtime_start(FILE *fd, const connection *cxt, const char *prefix); 45 | void format_write_unixutime_start(FILE *fd, const connection *cxt, const char *prefix); 46 | void format_write_time_end(FILE *fd, const connection *cxt, const char *prefix); 47 | void format_write_utime_end(FILE *fd, const connection *cxt, const char *prefix); 48 | void format_write_unixtime_end(FILE *fd, const connection *cxt, const char *prefix); 49 | void format_write_unixutime_end(FILE *fd, const connection *cxt, const char *prefix); 50 | void format_write_time_duration(FILE *fd, const connection *cxt, const char *prefix); 51 | //void format_write_unixutime_duration(FILE *fd, const connection *cxt, const char *prefix); 52 | void format_write_ip_protocol(FILE *fd, const connection *cxt, const char *prefix); 53 | void format_write_vlan_id(FILE *fd, const connection *cxt, const char *prefix); 54 | void format_write_ip_family(FILE *fd, const connection *cxt, const char *prefix); 55 | void format_write_ip_source(FILE *fd, const connection *cxt, const char *prefix); 56 | void format_write_ip_source_hex(FILE *fd, const connection *cxt, const char *prefix); 57 | void format_write_ip_source_numeric(FILE *fd, const connection *cxt, const char *prefix); 58 | void format_write_ip_source_fqdn(FILE *fd, const connection *cxt, const char *prefix); 59 | void format_write_ip_destination(FILE *fd, const connection *cxt, const char *prefix); 60 | void format_write_ip_destination_hex(FILE *fd, const connection *cxt, const char *prefix); 61 | void format_write_ip_destination_numeric(FILE *fd, const connection *cxt, const char *prefix); 62 | void format_write_ip_destination_fqdn(FILE *fd, const connection *cxt, const char *prefix); 63 | void format_write_ip_port_source(FILE *fd, const connection *cxt, const char *prefix); 64 | void format_write_ip_port_destination(FILE *fd, const connection *cxt, const char *prefix); 65 | void format_write_packets_source(FILE *fd, const connection *cxt, const char *prefix); 66 | void format_write_packets_destination(FILE *fd, const connection *cxt, const char *prefix); 67 | void format_write_bytes_source(FILE *fd, const connection *cxt, const char *prefix); 68 | void format_write_bytes_destination(FILE *fd, const connection *cxt, const char *prefix); 69 | void format_write_tcp_flags_source(FILE *fd, const connection *cxt, const char *prefix); 70 | void format_write_tcp_flags_destination(FILE *fd, const connection *cxt, const char *prefix); 71 | void format_write_pcap_file_start(FILE *fd, const connection *cxt, const char *prefix); 72 | void format_write_pcap_offset_start(FILE *fd, const connection *cxt, const char *prefix); 73 | void format_write_pcap_file_end(FILE *fd, const connection *cxt, const char *prefix); 74 | void format_write_pcap_offset_end(FILE *fd, const connection *cxt, const char *prefix); 75 | void format_write_newline(FILE *fd, const connection *cxt, const char *prefix); 76 | void format_write_custom(FILE *fd, const connection *cxt, const char *prefix); 77 | 78 | void format_options() 79 | { 80 | fprintf(stdout, " Format Options:\n"); 81 | fprintf(stdout, " %%cxd unique cxtracker ID\n"); 82 | fprintf(stdout, " %%stm start time [gmtime]\n"); 83 | fprintf(stdout, " %%stmu start time [gmtime.usec]\n"); 84 | fprintf(stdout, " %%stu start time [unix timestamp]\n"); 85 | fprintf(stdout, " %%stuu start time [unix timestamp.usec]\n"); 86 | fprintf(stdout, " %%etm end time [gmtime]\n"); 87 | fprintf(stdout, " %%etmu end time [gmtime.usec]\n"); 88 | fprintf(stdout, " %%etu end time [unix timestamp]\n"); 89 | fprintf(stdout, " %%etuu end time [unix timestamp.usec]\n"); 90 | fprintf(stdout, " %%dur duration [seconds]\n"); 91 | // fprintf(stdout, " %%dur duration [seconds.usec]\n"); 92 | fprintf(stdout, " %%vln VLAN ID\n"); 93 | fprintf(stdout, " %%ver IP family\n"); 94 | fprintf(stdout, " %%pro protocol\n"); 95 | fprintf(stdout, " %%sin source IP [IPv4 = integer, IPv6 = literal]\n"); 96 | fprintf(stdout, " %%sip source IP [IPv4/IPv6 = literal]\n"); 97 | fprintf(stdout, " %%six source IP [IPv4/IPv6 = hex]\n"); 98 | fprintf(stdout, " %%sih source IP [IPv4/IPv6 = host lookup]\n"); 99 | fprintf(stdout, " %%din destination IP [IPv4 = integer, IPv6 = literal]\n"); 100 | fprintf(stdout, " %%dip destination IP [IPv4/IPv6 = literal]\n"); 101 | fprintf(stdout, " %%dix destination IP [IPv4/IPv6 = hex]\n"); 102 | fprintf(stdout, " %%dih destination IP [IPv4/IPv6 = host lookup]\n"); 103 | fprintf(stdout, " %%spt source port\n"); 104 | fprintf(stdout, " %%dpt destination port\n"); 105 | fprintf(stdout, " %%spk total packets sent from the source IP during the session\n"); 106 | fprintf(stdout, " %%dpk total packets sent from the destination IP during the session\n"); 107 | fprintf(stdout, " %%sby total bytes send from the source IP during the session\n"); 108 | fprintf(stdout, " %%dby total bytes send from the destination IP during the session\n"); 109 | fprintf(stdout, " %%sfl cumulative source IP TCP flags sent during the session\n"); 110 | fprintf(stdout, " %%dfl cumulative destination IP TCP flags sent during the session\n"); 111 | fprintf(stdout, " %%spf pcap file containing start packet in session\n"); 112 | fprintf(stdout, " %%spo pcap file offset of start packet in session\n"); 113 | fprintf(stdout, " %%epf pcap file containing last packet in session\n"); 114 | fprintf(stdout, " %%epo pcap file offset of last packet in session\n"); 115 | fprintf(stdout, " %%nn Newline\n"); 116 | fprintf(stdout, "\n"); 117 | fprintf(stdout, " Format Meta-Options:\n"); 118 | fprintf(stdout, " standard Standard formatted output compatible with Sguil and OpenFPC etc.\n"); 119 | fprintf(stdout, " indexed Pcap-indexed formatted output compatible with Echidna etc.\n"); 120 | fprintf(stdout, "\n"); 121 | } 122 | 123 | void format_validate(const char *format) 124 | { 125 | /* 126 | */ 127 | char *format_qualified = NULL; 128 | 129 | const char *fp_s; 130 | const char *fp_e; 131 | 132 | void (*func)(FILE *, const struct _connection *, char *) = NULL; 133 | int match = 0; 134 | int format_length = 0; 135 | 136 | int use_standard = 0; 137 | 138 | // Check for depricated options first 139 | if ( strncmp(format, "sguil", 5) == 0 140 | || strncmp(format, "openfpc", 7) == 0 141 | || strncmp(format, "nsmf", 4) == 0 ) { 142 | fprintf(stdout, "[w] Predefined format %s is depricated, use \'standard\' instead.\n", format); 143 | 144 | use_standard = 1; 145 | } 146 | // check for pre-packaged options first 147 | if ( strncmp(format, "standard", 8) == 0 || use_standard ) 148 | format_qualified = strdup("%cxd|%stm|%etm|%dur|%pro|%sin|%spt|%din|%dpt|%spk|%sby|%dpk|%dby|%sfl|%dfl"); 149 | else if ( strncmp(format, "indexed", 7) == 0 ) 150 | format_qualified = strdup("%cxd|%stm|%etm|%dur|%pro|%sip|%spt|%dip|%dpt|%spk|%sby|%dpk|%dby|%sfl|%dfl|%ver|%spf|%spo|%epf|%epo"); 151 | else 152 | format_qualified = strdup(format); 153 | 154 | if ( NULL == format_qualified ) 155 | { 156 | fprintf(stderr, "FATAL: Unable to allocate memory for the custom formatter!\n"); 157 | exit(1); 158 | } 159 | 160 | format_length = strlen(format_qualified); 161 | 162 | // set up our iterators 163 | fp_s = format_qualified; 164 | fp_e = format_qualified; 165 | 166 | printf("[*] Using output format: %s\n", format_qualified); 167 | 168 | while ( (fp_e-format_qualified) < format_length ) 169 | { 170 | // check if it's time to match 171 | if ( strncmp(fp_e, "%", 1) == 0 ) 172 | { 173 | if ( strncmp(fp_e, "%cxd", 4) == 0 ) 174 | { 175 | match = 4; 176 | func = (void *)&format_write_cxid; 177 | } 178 | else if ( strncmp(fp_e, "%stmu", 5) == 0 ) 179 | { 180 | match = 5; 181 | func = (void *)&format_write_utime_start; 182 | } 183 | else if ( strncmp(fp_e, "%stm", 4) == 0 ) 184 | { 185 | match = 4; 186 | func = (void *)&format_write_time_start; 187 | } 188 | else if ( strncmp(fp_e, "%stuu", 5) == 0 ) 189 | { 190 | match = 5; 191 | func = (void *)&format_write_unixutime_start; 192 | } 193 | else if ( strncmp(fp_e, "%stu", 4) == 0 ) 194 | { 195 | match = 4; 196 | func = (void *)&format_write_unixtime_start; 197 | } 198 | else if ( strncmp(fp_e, "%etmu", 5) == 0 ) 199 | { 200 | match = 5; 201 | func = (void *)&format_write_utime_end; 202 | } 203 | else if ( strncmp(fp_e, "%etm", 4) == 0 ) 204 | { 205 | match = 4; 206 | func = (void *)&format_write_time_end; 207 | } 208 | else if ( strncmp(fp_e, "%etuu", 5) == 0 ) 209 | { 210 | match = 5; 211 | func = (void *)&format_write_unixutime_end; 212 | } 213 | else if ( strncmp(fp_e, "%etu", 4) == 0 ) 214 | { 215 | match = 4; 216 | func = (void *)&format_write_unixtime_end; 217 | } 218 | else if ( strncmp(fp_e, "%dur", 4) == 0 ) 219 | { 220 | match = 4; 221 | func = (void *)&format_write_time_duration; 222 | } 223 | else if ( strncmp(fp_e, "%ver", 4) == 0 ) 224 | { 225 | match = 4; 226 | func = (void *)&format_write_ip_family; 227 | } 228 | else if ( strncmp(fp_e, "%vln", 4) == 0 ) 229 | { 230 | match = 4; 231 | func = (void *)&format_write_vlan_id; 232 | } 233 | else if ( strncmp(fp_e, "%pro", 4) == 0 ) 234 | { 235 | match = 4; 236 | func = (void *)&format_write_ip_protocol; 237 | } 238 | else if ( strncmp(fp_e, "%sin", 4) == 0 ) 239 | { 240 | match = 4; 241 | func = (void *)&format_write_ip_source_numeric; 242 | } 243 | else if ( strncmp(fp_e, "%sip", 4) == 0 ) 244 | { 245 | match = 4; 246 | func = (void *)&format_write_ip_source; 247 | } 248 | else if ( strncmp(fp_e, "%six", 4) == 0 ) 249 | { 250 | match = 4; 251 | func = (void *)&format_write_ip_source_hex; 252 | } 253 | else if ( strncmp(fp_e, "%sih", 4) == 0 ) 254 | { 255 | match = 4; 256 | func = (void *)&format_write_ip_source_fqdn; 257 | } 258 | else if ( strncmp(fp_e, "%din", 4) == 0 ) 259 | { 260 | match = 4; 261 | func = (void *)&format_write_ip_destination_numeric; 262 | } 263 | else if ( strncmp(fp_e, "%dip", 4) == 0 ) 264 | { 265 | match = 4; 266 | func = (void *)&format_write_ip_destination; 267 | } 268 | else if ( strncmp(fp_e, "%dix", 4) == 0 ) 269 | { 270 | match = 4; 271 | func = (void *)&format_write_ip_destination_hex; 272 | } 273 | else if ( strncmp(fp_e, "%dih", 4) == 0 ) 274 | { 275 | match = 4; 276 | func = (void *)&format_write_ip_destination_fqdn; 277 | } 278 | else if ( strncmp(fp_e, "%spt", 4) == 0 ) 279 | { 280 | match = 4; 281 | func = (void *)&format_write_ip_port_source; 282 | } 283 | else if ( strncmp(fp_e, "%dpt", 4) == 0 ) 284 | { 285 | match = 4; 286 | func = (void *)&format_write_ip_port_destination; 287 | } 288 | else if ( strncmp(fp_e, "%spk", 4) == 0 ) 289 | { 290 | match = 4; 291 | func = (void *)&format_write_packets_source; 292 | } 293 | else if ( strncmp(fp_e, "%dpk", 4) == 0 ) 294 | { 295 | match = 4; 296 | func = (void *)&format_write_packets_destination; 297 | } 298 | else if ( strncmp(fp_e, "%sby", 4) == 0 ) 299 | { 300 | match = 4; 301 | func = (void *)&format_write_bytes_source; 302 | } 303 | else if ( strncmp(fp_e, "%dby", 4) == 0 ) 304 | { 305 | match = 4; 306 | func = (void *)&format_write_bytes_destination; 307 | } 308 | else if ( strncmp(fp_e, "%sfl", 4) == 0 ) 309 | { 310 | match = 4; 311 | func = (void *)&format_write_tcp_flags_source; 312 | } 313 | else if ( strncmp(fp_e, "%dfl", 4) == 0 ) 314 | { 315 | match = 4; 316 | func = (void *)&format_write_tcp_flags_destination; 317 | } 318 | else if ( strncmp(fp_e, "%spf", 4) == 0 ) 319 | { 320 | match = 4; 321 | func = (void *)&format_write_pcap_file_start; 322 | } 323 | else if ( strncmp(fp_e, "%spo", 4) == 0 ) 324 | { 325 | match = 4; 326 | func = (void *)&format_write_pcap_offset_start; 327 | } 328 | else if ( strncmp(fp_e, "%epf", 4) == 0 ) 329 | { 330 | match = 4; 331 | func = (void *)&format_write_pcap_file_end; 332 | } 333 | else if ( strncmp(fp_e, "%epo", 4) == 0 ) 334 | { 335 | match = 4; 336 | func = (void *)&format_write_pcap_offset_end; 337 | } 338 | else if ( strncmp(fp_e, "%nn", 3) == 0 ) 339 | { 340 | match = 3; 341 | func = (void *)&format_write_newline; 342 | } 343 | } 344 | 345 | if ( match > 0 ) 346 | { 347 | char *prefix = NULL; 348 | 349 | // check if we have prefix 350 | if ( fp_e > fp_s ) 351 | { 352 | prefix = calloc(1, sizeof(char) * (fp_e - fp_s + 1)); 353 | if ( NULL == prefix ) 354 | { 355 | fprintf(stderr, "FATAL: Unable to allocate memory for the custom formatter!\n"); 356 | free(format_qualified); 357 | exit(1); 358 | } 359 | 360 | strncpy(prefix, fp_s, fp_e-fp_s); 361 | } 362 | else 363 | prefix = strdup(""); 364 | 365 | fp_e += match; 366 | fp_s = fp_e; 367 | 368 | format_function_append(&custom, func, prefix); 369 | 370 | match = 0; 371 | } 372 | else 373 | fp_e++; 374 | } 375 | 376 | // check if we have prefix 377 | if ( fp_e > fp_s ) 378 | { 379 | char *prefix = calloc(1, sizeof(char) * (fp_e - fp_s + 1)); 380 | if ( NULL == prefix ) 381 | { 382 | fprintf(stderr, "FATAL: Unable to allocate memory for the custom formatter!\n"); 383 | free(format_qualified); 384 | exit(1); 385 | } 386 | 387 | strncpy(prefix, fp_s, fp_e-fp_s); 388 | prefix[fp_e-fp_s+1] = '\0'; 389 | 390 | format_function_append(&custom, (void *)&format_write_custom, prefix); 391 | } 392 | 393 | // clean up after ourselves; 394 | free(format_qualified); 395 | } 396 | 397 | void format_function_append(format_t **head, void (*func)(FILE *, const struct _connection *, char *), char *prefix) 398 | { 399 | format_t *iter = *head; 400 | format_t *item; 401 | 402 | item = calloc(1, sizeof(format_t)); 403 | 404 | if ( NULL == item ) 405 | { 406 | fprintf(stderr, "FATAL: Unable to allocate memory for the custom formatter!\n"); 407 | exit(1); 408 | } 409 | 410 | item->func = (void *)func; 411 | item->prefix = prefix; 412 | 413 | // if head is empty then add first item 414 | if ( NULL == iter ) 415 | { 416 | *head = item; 417 | } 418 | // otherwise append to the tail 419 | else 420 | { 421 | while (NULL != iter->next) 422 | iter = iter->next; 423 | 424 | iter->next = item; 425 | } 426 | } 427 | 428 | void format_free(format_t **head) 429 | { 430 | format_t *iter = *head; 431 | format_t *item; 432 | 433 | if( NULL == iter) 434 | return; 435 | 436 | item = iter; 437 | 438 | for (; NULL != item; iter = iter->next, item = iter) 439 | { 440 | free(item); 441 | } 442 | } 443 | 444 | 445 | void format_write(FILE *fd, const connection *cxt) 446 | { 447 | const format_t *iter = custom; 448 | 449 | // run through our custom format table 450 | while (iter != NULL) 451 | { 452 | iter->func(fd, cxt, iter->prefix); 453 | iter = iter->next; 454 | } 455 | 456 | // finish off the line 457 | fprintf(fd, "\n"); 458 | } 459 | 460 | 461 | // 462 | // DEDICATED FORMAT FUNCTIONS 463 | // 464 | 465 | void format_write_cxid(FILE *fd, const connection *cxt, const char *prefix) 466 | { 467 | fprintf(fd, "%s%ld%09ju", prefix, (long) cxt->start_time.tv_sec, cxt->cxid); 468 | } 469 | 470 | void format_write_time_start(FILE *fd, const connection *cxt, const char *prefix) 471 | { 472 | char t[80]; 473 | 474 | strftime(t, 80, "%F %H:%M:%S", gmtime(&cxt->start_time.tv_sec)); 475 | fprintf(fd, "%s%s", prefix, t); 476 | } 477 | 478 | void format_write_utime_start(FILE *fd, const connection *cxt, const char *prefix) 479 | { 480 | char t[80]; 481 | 482 | strftime(t, 80, "%F %H:%M:%S", gmtime(&cxt->start_time.tv_sec)); 483 | fprintf(fd, "%s%s.%lu", prefix, t, cxt->start_time.tv_usec); 484 | } 485 | 486 | void format_write_unixtime_start(FILE *fd, const connection *cxt, const char *prefix) 487 | { 488 | fprintf(fd, "%s%lu", prefix, (long) cxt->start_time.tv_sec); 489 | } 490 | 491 | void format_write_unixutime_start(FILE *fd, const connection *cxt, const char *prefix) 492 | { 493 | fprintf(fd, "%s%lu.%lu", prefix, (long) cxt->start_time.tv_sec, (long) cxt->start_time.tv_usec); 494 | } 495 | 496 | void format_write_time_end(FILE *fd, const connection *cxt, const char *prefix) 497 | { 498 | char t[80]; 499 | 500 | strftime(t, 80, "%F %H:%M:%S", gmtime(&cxt->last_pkt_time.tv_sec)); 501 | fprintf(fd, "%s%s", prefix, t); 502 | } 503 | 504 | void format_write_utime_end(FILE *fd, const connection *cxt, const char *prefix) 505 | { 506 | char t[80]; 507 | 508 | strftime(t, 80, "%F %H:%M:%S", gmtime(&cxt->last_pkt_time.tv_sec)); 509 | fprintf(fd, "%s%s.%lu", prefix, t, cxt->last_pkt_time.tv_usec); 510 | } 511 | 512 | void format_write_unixtime_end(FILE *fd, const connection *cxt, const char *prefix) 513 | { 514 | fprintf(fd, "%s%lu", prefix, (long) cxt->last_pkt_time.tv_sec); 515 | } 516 | 517 | void format_write_unixutime_end(FILE *fd, const connection *cxt, const char *prefix) 518 | { 519 | fprintf(fd, "%s%lu.%lu", prefix, (long) cxt->last_pkt_time.tv_sec, (long) cxt->last_pkt_time.tv_usec); 520 | } 521 | 522 | void format_write_time_duration(FILE *fd, const connection *cxt, const char *prefix) 523 | { 524 | fprintf(fd, "%s%ld", prefix, (long) (cxt->last_pkt_time.tv_sec - cxt->start_time.tv_sec)); 525 | } 526 | 527 | void format_write_ip_family(FILE *fd, const connection *cxt, const char *prefix) 528 | { 529 | fprintf(fd, "%s%d", prefix, ip_family_get(cxt->s_ip)); 530 | } 531 | 532 | void format_write_ip_protocol(FILE *fd, const connection *cxt, const char *prefix) 533 | { 534 | fprintf(fd, "%s%u", prefix, cxt->proto); 535 | } 536 | 537 | void format_write_vlan_id(FILE *fd, const connection *cxt, const char *prefix) 538 | { 539 | fprintf(fd, "%s%d", prefix, cxt->vlanid); 540 | } 541 | 542 | void format_write_ip_source_numeric(FILE *fd, const connection *cxt, const char *prefix) 543 | { 544 | char ip_s[IP_ADDRMAX]; 545 | 546 | if ( ip_ntop(cxt->s_ip, ip_s, IP_ADDRMAX, IP_NUMERIC_DEC) ) 547 | perror("Something died in ip_ntop for src"); 548 | 549 | fprintf(fd, "%s%s", prefix, ip_s); 550 | } 551 | 552 | void format_write_ip_source_fqdn(FILE *fd, const connection *cxt, const char *prefix) 553 | { 554 | char ip_s[IP_ADDRMAX]; 555 | 556 | if ( ip_ntop(cxt->s_ip, ip_s, IP_ADDRMAX, 0) ) 557 | perror("Something died in ip_ntop for src"); 558 | 559 | fprintf(fd, "%s%s", prefix, ip_s); 560 | } 561 | 562 | void format_write_ip_source(FILE *fd, const connection *cxt, const char *prefix) 563 | { 564 | char ip_s[IP_ADDRMAX]; 565 | 566 | if ( ip_ntop(cxt->s_ip, ip_s, IP_ADDRMAX, IP_NUMERIC) ) 567 | perror("Something died in ip_ntop for src"); 568 | 569 | fprintf(fd, "%s%s", prefix, ip_s); 570 | } 571 | 572 | void format_write_ip_source_hex(FILE *fd, const connection *cxt, const char *prefix) 573 | { 574 | char ip_s[IP_ADDRMAX]; 575 | 576 | if ( ip_ntop(cxt->s_ip, ip_s, IP_ADDRMAX, IP_NUMERIC_HEX) ) 577 | perror("Something died in ip_ntop for src"); 578 | 579 | fprintf(fd, "%s%s", prefix, ip_s); 580 | } 581 | 582 | void format_write_ip_destination_numeric(FILE *fd, const connection *cxt, const char *prefix) 583 | { 584 | char ip_s[IP_ADDRMAX]; 585 | 586 | if ( ip_ntop(cxt->d_ip, ip_s, IP_ADDRMAX, IP_NUMERIC_DEC) ) 587 | perror("Something died in ip_ntop for dest"); 588 | 589 | fprintf(fd, "%s%s", prefix, ip_s); 590 | } 591 | 592 | void format_write_ip_destination_fqdn(FILE *fd, const connection *cxt, const char *prefix) 593 | { 594 | char ip_s[IP_ADDRMAX]; 595 | 596 | if ( ip_ntop(cxt->d_ip, ip_s, IP_ADDRMAX, 0) ) 597 | perror("Something died in ip_ntop for dest"); 598 | 599 | fprintf(fd, "%s%s", prefix, ip_s); 600 | } 601 | 602 | void format_write_ip_destination(FILE *fd, const connection *cxt, const char *prefix) 603 | { 604 | char ip_s[IP_ADDRMAX]; 605 | 606 | if ( ip_ntop(cxt->d_ip, ip_s, IP_ADDRMAX, IP_NUMERIC) ) 607 | perror("Something died in ip_ntop for dest"); 608 | 609 | fprintf(fd, "%s%s", prefix, ip_s); 610 | } 611 | 612 | void format_write_ip_destination_hex(FILE *fd, const connection *cxt, const char *prefix) 613 | { 614 | char ip_s[IP_ADDRMAX]; 615 | 616 | if ( ip_ntop(cxt->d_ip, ip_s, IP_ADDRMAX, IP_NUMERIC_HEX) ) 617 | perror("Something died in ip_ntop for dest"); 618 | 619 | fprintf(fd, "%s%s", prefix, ip_s); 620 | } 621 | 622 | void format_write_ip_port_source(FILE *fd, const connection *cxt, const char *prefix) 623 | { 624 | fprintf(fd, "%s%u", prefix, ntohs(cxt->s_port)); 625 | } 626 | 627 | void format_write_ip_port_destination(FILE *fd, const connection *cxt, const char *prefix) 628 | { 629 | fprintf(fd, "%s%u", prefix, ntohs(cxt->d_port)); 630 | } 631 | 632 | void format_write_packets_source(FILE *fd, const connection *cxt, const char *prefix) 633 | { 634 | fprintf(fd, "%s%ju", prefix, cxt->s_total_pkts); 635 | } 636 | 637 | void format_write_packets_destination(FILE *fd, const connection *cxt, const char *prefix) 638 | { 639 | fprintf(fd, "%s%ju", prefix, cxt->d_total_pkts); 640 | } 641 | 642 | void format_write_bytes_source(FILE *fd, const connection *cxt, const char *prefix) 643 | { 644 | fprintf(fd, "%s%ju", prefix, cxt->s_total_bytes); 645 | } 646 | 647 | void format_write_bytes_destination(FILE *fd, const connection *cxt, const char *prefix) 648 | { 649 | fprintf(fd, "%s%ju", prefix, cxt->d_total_bytes); 650 | } 651 | 652 | void format_write_tcp_flags_source(FILE *fd, const connection *cxt, const char *prefix) 653 | { 654 | fprintf(fd,"%s%u", prefix, cxt->s_tcpFlags); 655 | } 656 | 657 | void format_write_tcp_flags_destination(FILE *fd, const connection *cxt, const char *prefix) 658 | { 659 | fprintf(fd,"%s%u", prefix, cxt->d_tcpFlags); 660 | } 661 | 662 | void format_write_pcap_offset_start(FILE *fd, const connection *cxt, const char *prefix) 663 | { 664 | fprintf(fd, "%s%lld", prefix, (long long int)cxt->start_offset); 665 | } 666 | 667 | void format_write_pcap_file_start(FILE *fd, const connection *cxt, const char *prefix) 668 | { 669 | fprintf(fd, "%s%s", prefix, cxt->start_dump); 670 | } 671 | 672 | void format_write_pcap_offset_end(FILE *fd, const connection *cxt, const char *prefix) 673 | { 674 | fprintf(fd, "%s%lld", prefix, (long long int)cxt->last_offset); 675 | } 676 | 677 | void format_write_pcap_file_end(FILE *fd, const connection *cxt, const char *prefix) 678 | { 679 | fprintf(fd, "%s%s", prefix, cxt->last_dump); 680 | } 681 | 682 | void format_write_newline(FILE *fd, const connection *cxt, const char *prefix) 683 | { 684 | fprintf(fd, "\n"); 685 | return; 686 | (void) cxt; 687 | (void) prefix; 688 | } 689 | 690 | void format_write_custom(FILE *fd, const connection *cxt, const char *prefix) 691 | { 692 | fprintf(fd, "%s", prefix); 693 | return; 694 | (void) cxt; 695 | } 696 | 697 | void format_clear() 698 | { 699 | format_t *iter = custom; 700 | 701 | // clean up our custom formatter 702 | while (iter != NULL) 703 | { 704 | custom = iter; 705 | 706 | iter = iter->next; 707 | 708 | if ( custom->prefix ) 709 | free(custom->prefix); 710 | 711 | free(custom); 712 | } 713 | } 714 | -------------------------------------------------------------------------------- /src/format.h: -------------------------------------------------------------------------------- 1 | /* 2 | ** Copyright (C) 2011 - 2013, Ian Firns 3 | ** 4 | ** 5 | ** This program is free software; you can redistribute it and/or modify 6 | ** it under the terms of the GNU General Public License as published by 7 | ** the Free Software Foundation; either version 2 of the License, or 8 | ** (at your option) any later version. 9 | ** 10 | ** This program is distributed in the hope that it will be useful, 11 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | ** GNU General Public License for more details. 14 | ** 15 | ** You should have received a copy of the GNU General Public License 16 | ** along with this program; if not, write to the Free Software 17 | ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | ** 19 | */ 20 | 21 | #ifndef __FORMAT_H__ 22 | #define __FORMAT_H__ 23 | 24 | #include 25 | #include "cxtracker.h" 26 | 27 | void format_write(FILE *fd, const connection *cxt); 28 | void format_validate(const char *format); 29 | void format_clear(void); 30 | void format_options(void); 31 | 32 | #endif /* __FORMAT_H__ */ 33 | -------------------------------------------------------------------------------- /src/ip.c: -------------------------------------------------------------------------------- 1 | /* 2 | ** Copyright (C) 2011 - 2013, Ian Firns 3 | ** 4 | ** 5 | ** This program is free software; you can redistribute it and/or modify 6 | ** it under the terms of the GNU General Public License as published by 7 | ** the Free Software Foundation; either version 2 of the License, or 8 | ** (at your option) any later version. 9 | ** 10 | ** This program is distributed in the hope that it will be useful, 11 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | ** GNU General Public License for more details. 14 | ** 15 | ** You should have received a copy of the GNU General Public License 16 | ** along with this program; if not, write to the Free Software 17 | ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | ** 19 | */ 20 | 21 | #include "ip.h" 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #ifdef __APPLE__ 29 | #include 30 | #endif /* __APPLE__ */ 31 | #ifdef linux 32 | #include 33 | #endif /* linux */ 34 | 35 | // private functions 36 | 37 | /** 38 | * \brief Set IP address from raw byte representation by copying the pointer only 39 | * \param Structure for writing resulting IP 40 | * \param Array of integers describing the IP numerically in network byte order 41 | * \param The IP family type (eg. AF_INET, AF_INET6) 42 | */ 43 | void ip_set_raw_with_pointer_copy(ip_t *dst, const void *src, int family); 44 | 45 | /** 46 | * \brief Set IP address from raw byte representation using a memcpy 47 | * \param Structure for writing resulting IP 48 | * \param Array of integers describing the IP numerically in network byte order 49 | * \param The IP family type (eg. AF_INET, AF_INET6) 50 | */ 51 | void ip_set_raw_with_memcpy(ip_t *dst, const void *src, int family); 52 | 53 | /** 54 | * \brief Set obfuscated IP address from raw byte representation using a memcpy 55 | * \param Structure for writing resulting IP 56 | * \param Array of integers describing the IP numerically in network byte order 57 | * \param The IP family type (eg. AF_INET, AF_INET6) 58 | */ 59 | void ip_set_raw_with_obfuscate(ip_t *dst, const void *src, int family); 60 | 61 | 62 | 63 | 64 | void ip_free(ip_t *ip) 65 | { 66 | if( NULL != ip ) 67 | { 68 | if( NULL != ip->addr ) 69 | free(ip->addr); 70 | 71 | free(ip); 72 | }; 73 | } 74 | 75 | ip_t *ip_alloc(const char *ip) 76 | { 77 | ip_t *ret; 78 | 79 | if( NULL == ip ) 80 | return NULL; 81 | 82 | if( (ret=calloc(1, sizeof(ip_t))) == NULL ) 83 | return NULL; 84 | 85 | if( ip_pton(ret, ip) != 1 ) 86 | { 87 | ip_free(ret); 88 | return NULL; 89 | } 90 | 91 | return ret; 92 | } 93 | 94 | ip_t *ip_alloc_raw(const void *ip, int family) 95 | { 96 | ip_t *ret; 97 | 98 | if( NULL == ip || (family != AF_INET && family != AF_INET6) ) 99 | return NULL; 100 | 101 | if( (ret=calloc(1, sizeof(ip_t))) == NULL ) 102 | return NULL; 103 | 104 | ip_set_raw_with_memcpy(ret, ip, family); 105 | 106 | return ret; 107 | } 108 | 109 | int ip_init(ip_config_t *config, int mode) 110 | { 111 | if( NULL == config ) 112 | return 1; 113 | 114 | if( mode == IP_SET_MEMCPY ) 115 | { 116 | config->set = (void *)&ip_set_raw_with_memcpy; 117 | return 0; 118 | } 119 | 120 | if( mode == IP_SET_POINTER_COPY ) 121 | { 122 | config->set = (void *)&ip_set_raw_with_pointer_copy; 123 | return 0; 124 | } 125 | 126 | if( mode == IP_SET_OBFUSCATE ) 127 | { 128 | config->set = (void *)&ip_set_raw_with_obfuscate; 129 | return 0; 130 | } 131 | 132 | return 1; 133 | } 134 | 135 | void ip_set(ip_config_t *config, ip_t *dst, const void *src, int family) 136 | { 137 | // we want speed so no NULL checks are performed here. be careful 138 | config->set(dst, src, family); 139 | } 140 | 141 | void ip_set_raw_with_pointer_copy(ip_t *dst, const void *src, int family) 142 | { 143 | dst->family = family; 144 | dst->bits = (family == AF_INET) ? 32 : 128; 145 | dst->addr = (ip_addr_t *)src; 146 | } 147 | 148 | void ip_set_raw_with_memcpy(ip_t *dst, const void *src, int family) 149 | { 150 | dst->family = family; 151 | 152 | if( dst->addr == NULL ) 153 | dst->addr = calloc(1, sizeof(ip_addr_t)); 154 | 155 | if( family == AF_INET ) 156 | { 157 | dst->bits = 32; 158 | memcpy(dst->addr->ip8, src, sizeof(struct in_addr)); 159 | } 160 | else if( family == AF_INET6 ) 161 | { 162 | dst->bits = 128; 163 | memcpy(dst->addr->ip8, src, sizeof(struct in6_addr)); 164 | } 165 | } 166 | 167 | void ip_set_raw_with_obfuscate(ip_t *dst, const void *src, int family) 168 | { 169 | ip_set_raw_with_memcpy(dst, src, family); 170 | } 171 | 172 | 173 | int ip_pton(ip_t *ip, const char *name) 174 | { 175 | const char *mask; 176 | char ip_buf[IP_ADDRMAX]; 177 | int bits = -1; 178 | 179 | /* check for and extract a mask in CIDR short form only */ 180 | if( (mask=strchr(name, (int)'/')) == NULL ) 181 | mask = name + strlen(name); 182 | else 183 | bits = atoi(mask); 184 | 185 | strncpy(ip_buf, name, mask-name); 186 | 187 | struct addrinfo *ai; 188 | struct addrinfo hints; 189 | memset(&hints, '\0', sizeof(hints)); 190 | 191 | hints.ai_flags = AI_NUMERICHOST; 192 | hints.ai_family = PF_UNSPEC; 193 | 194 | int ret; 195 | if( (ret=getaddrinfo(name, NULL, &hints, &ai)) != 0 ) 196 | return ret; 197 | 198 | ip->family = ai->ai_family; 199 | 200 | /* set up the bits if we haven't determined them yet */ 201 | if( bits < 0 ) 202 | bits = (ai->ai_family == AF_INET) ? 32 : 128; 203 | 204 | ip->bits = (uint8_t)bits; 205 | 206 | if( ai->ai_family == AF_INET ) 207 | memcpy(ip->addr->ip8, &((struct sockaddr_in *)ai->ai_addr)->sin_addr.s_addr, ai->ai_addrlen); 208 | else if( ai->ai_family == AF_INET6 ) 209 | memcpy(ip->addr->ip8, ((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr.s6_addr, ai->ai_addrlen); 210 | else 211 | { 212 | freeaddrinfo(ai); 213 | return EAI_FAMILY; // The address family was no recognized 214 | } 215 | 216 | // XXX: debug segfault 217 | //freeaddrinfo(ai); 218 | return 0; 219 | } 220 | 221 | int ip_ntop(const ip_t *ip, char *buf, int buflen, int flags) 222 | { 223 | if( NULL == ip || NULL == ip->addr ) 224 | return EAI_NONAME; // the IP is not known or the IP is null 225 | 226 | // printf("FLAGS = %d\n", flags); 227 | int ret; 228 | 229 | if( ip->family == AF_INET ) 230 | { 231 | if( flags & IP_NUMERIC_HEX ) 232 | { 233 | snprintf(buf, buflen, "%02x%02x%02x%02x", ip->addr->ip8[0], ip->addr->ip8[1], ip->addr->ip8[2], ip->addr->ip8[3]); 234 | } 235 | else if( flags & IP_NUMERIC_DEC ) 236 | { 237 | snprintf(buf, buflen, "%u", ntohl(ip->addr->ip32[0])); 238 | } 239 | else 240 | { 241 | struct sockaddr_in sin; 242 | int lflags = ( flags & ( IP_NUMERIC | IP_NUMERIC_DEC | IP_NUMERIC_HEX ) ) ? NI_NUMERICHOST : 0; 243 | 244 | sin.sin_family = AF_INET; 245 | memcpy(&sin.sin_addr.s_addr, ip->addr->ip8, sizeof(struct in_addr)); 246 | 247 | if( (ret=getnameinfo((struct sockaddr *)&sin, sizeof(sin), buf, buflen, NULL, 0, lflags)) ) 248 | return ret; 249 | } 250 | } 251 | else if( ip->family == AF_INET6 ) 252 | { 253 | if( flags & IP_NUMERIC_HEX ) 254 | { 255 | snprintf(buf, buflen, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", 256 | ip->addr->ip8[0], ip->addr->ip8[1], ip->addr->ip8[2], ip->addr->ip8[3], 257 | ip->addr->ip8[4], ip->addr->ip8[5], ip->addr->ip8[6], ip->addr->ip8[7], 258 | ip->addr->ip8[8], ip->addr->ip8[9], ip->addr->ip8[10], ip->addr->ip8[11], 259 | ip->addr->ip8[12], ip->addr->ip8[13], ip->addr->ip8[14], ip->addr->ip8[15]); 260 | } 261 | else 262 | { 263 | struct sockaddr_in6 sin; 264 | int lflags = ( flags & ( IP_NUMERIC | IP_NUMERIC_DEC | IP_NUMERIC_HEX ) ) ? NI_NUMERICHOST : flags; 265 | 266 | sin.sin6_family = AF_INET6; 267 | memcpy(&sin.sin6_addr.s6_addr, ip->addr->ip8, sizeof(struct in6_addr)); 268 | 269 | if( (ret=getnameinfo((struct sockaddr *)&sin, sizeof(sin), buf, IP_ADDRMAX, NULL, 0, lflags)) ) 270 | return ret; 271 | } 272 | } 273 | else 274 | return EAI_FAMILY; // The requested address family is not supported at all 275 | 276 | // XXX: hack 277 | // getnameinfo is returning addresses with weird appendage in the form 278 | // of "%number" where the number which i think should be an interface or 279 | // similar. for now let's just nuke it. 280 | // 281 | // eg. fe80::202:b3ff:fe1e:8329%4212018 282 | char *hack; 283 | if( (hack = strchr(buf, '%')) ) 284 | *hack = '\0'; 285 | 286 | return 0; 287 | } 288 | 289 | const char *ip_ntops(const ip_t *ip, int flags) 290 | { 291 | static char ip_buf[IP_ADDRMAX]; 292 | 293 | if( ip_ntop(ip, ip_buf, IP_ADDRMAX, flags) ) 294 | snprintf(ip_buf, IP_ADDRMAX, "%s%c", "unknown", '\0'); 295 | 296 | return ip_buf; 297 | } 298 | 299 | int ip_isloopback(const ip_t *ip) 300 | { 301 | if( NULL == ip || NULL == ip->addr ) 302 | return 0; 303 | 304 | // 127.0.0.0/8 is IPv4 loopback 305 | if( ip_family_get(ip) == AF_INET ) 306 | return ( ip->addr->ip8[0] == 0x7f ); 307 | 308 | // first 64 bits should be 0 309 | if( ip->addr->ip32[0] || ip->addr->ip32[1] ) 310 | return 0; 311 | 312 | // ::7f00:0/104 is ipv4 compatible ipv6 313 | // ::1 is the IPv6 loopback 314 | if( ip->addr->ip32[2] == 0 ) 315 | return ( ( ip->addr->ip8[12] == 0x7f ) || 316 | ( ntohl(ip->addr->ip32[3]) == 0x1 ) ); 317 | 318 | // ::ffff:127.0.0.0/104 is IPv4 loopback mapped over IPv6 319 | if( ntohl(ip->addr->ip32[2]) == 0xffff ) 320 | return ( ip->addr->ip8[12] == 0x7f ); 321 | 322 | return 0; 323 | } 324 | 325 | int ip_ismapped(const ip_t *ip) 326 | { 327 | if( NULL == ip || NULL == ip->addr ) 328 | return 0; 329 | 330 | if( ip_family_get(ip) == AF_INET ) 331 | return 0; 332 | 333 | // first 80 bits should be 0 334 | // next 16 should be 1 335 | if( ip->addr->ip32[0] || 336 | ip->addr->ip32[1] || 337 | ( ntohl(ip->addr->ip32[2]) != 0xffff && 338 | ip->addr->ip32[2] != 0 ) ) 339 | return 0; 340 | 341 | return 1; 342 | } 343 | 344 | 345 | void ip_obfuscate(ip_t *ob, const ip_t *ip) 346 | { 347 | /* 348 | uint32 *ob_p, *ip_p; 349 | int index, i; 350 | uint8_t mask = 0; 351 | */ 352 | 353 | if( NULL == ob || NULL == ip ) 354 | return; 355 | /* 356 | ob_p = ob->ip32; 357 | ip_p = ip->ip32; 358 | 359 | // Build the netmask by converting "val" into 360 | // the corresponding number of bits that are set 361 | index = (int)ceil(ob->bits / 32.0) - 1; 362 | 363 | for(i = 0; i < 32- (ob->bits - (index * 32)); i++) 364 | mask = (mask<<1) + 1; 365 | 366 | ip_p[index] = htonl((ntohl(ip_p[index]) & mask)); 367 | 368 | // 0 off the start of the IP 369 | while ( index > 0 ) ip_p[--index] = 0; 370 | 371 | // OR remaining pieces 372 | ip_p[0] |= ob_p[0]; 373 | ip_p[1] |= ob_p[1]; 374 | ip_p[2] |= ob_p[2]; 375 | ip_p[3] |= ob_p[3]; 376 | */ 377 | } 378 | 379 | 380 | -------------------------------------------------------------------------------- /src/ip.h: -------------------------------------------------------------------------------- 1 | /* 2 | ** Copyright (C) 2011 - 2013, Ian Firns 3 | ** 4 | ** 5 | ** This program is free software; you can redistribute it and/or modify 6 | ** it under the terms of the GNU General Public License as published by 7 | ** the Free Software Foundation; either version 2 of the License, or 8 | ** (at your option) any later version. 9 | ** 10 | ** This program is distributed in the hope that it will be useful, 11 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | ** GNU General Public License for more details. 14 | ** 15 | ** You should have received a copy of the GNU General Public License 16 | ** along with this program; if not, write to the Free Software 17 | ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | ** 19 | */ 20 | 21 | #ifndef __IP_H__ 22 | #define __IP_H__ 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #if (defined(__unix__) || defined(unix)) && !defined(USG) 30 | #include 31 | #endif 32 | #ifdef BSD 33 | #include 34 | #endif /* BSD */ 35 | 36 | #define IP_ADDRMAX NI_MAXHOST 37 | 38 | // TODO: raise these values to deconflict with the NI_* flags 39 | #define IP_FQDN 0x00 40 | #define IP_NUMERIC 0x01 41 | #define IP_NUMERIC_DEC 0x02 42 | #define IP_NUMERIC_HEX 0x04 43 | #define IP_OBFUSCATE 0x20 44 | 45 | #define IP_SET_POINTER_COPY 0x01 46 | #define IP_SET_MEMCPY 0x02 47 | #define IP_SET_OBFUSCATE 0x04 // implies memcpy 48 | 49 | typedef struct _ip_addr_s { 50 | union 51 | { 52 | uint8_t u_addr8[16]; 53 | uint16_t u_addr16[8]; 54 | uint32_t u_addr32[4]; 55 | uint64_t u_addr64[2]; 56 | } ip; 57 | #define ip8 ip.u_addr8 58 | #define ip16 ip.u_addr16 59 | #define ip32 ip.u_addr32 60 | #define ip64 ip.u_addr64 61 | } ip_addr_t; 62 | 63 | typedef struct _ip_s { 64 | int16_t family; // IP family 65 | uint8_t bits; // bits used for masking 66 | ip_addr_t *addr; 67 | } ip_t; 68 | 69 | 70 | typedef struct _ip_config_s { 71 | void (*set)(ip_t*, const void *, int); 72 | } ip_config_t; 73 | 74 | 75 | // 76 | // MEMBER ACCESS 77 | 78 | static inline int16_t ip_family_get(const ip_t *ip) 79 | { 80 | return ip->family; 81 | } 82 | 83 | static inline uint8_t ip_bits_get(const ip_t *ip) 84 | { 85 | return ip->bits; 86 | } 87 | 88 | static inline void ip_bits_set(ip_t *ip, uint8_t bits) 89 | { 90 | if ( NULL == ip ) 91 | return; 92 | 93 | if (bits > 128) 94 | return; 95 | 96 | ip->bits = bits; 97 | } 98 | 99 | // 100 | // MANAGER FUNCTIONS 101 | int ip_init(ip_config_t *config, int mode); 102 | 103 | /** 104 | * \brief Set IP address based on existing IP structure 105 | * \param Structure for writing resulting IP 106 | * \param Structure for sourcing IP from 107 | */ 108 | void ip_set(ip_config_t *config, ip_t *dst, const void *src, int family); 109 | 110 | // 111 | // ALLOCATORS AND SETTERS 112 | 113 | /** 114 | * \brief Allocate IP address 115 | * \param character array describing the IP literally or numerically 116 | * \return Structure containing numeric representation of IP 117 | */ 118 | ip_t *ip_alloc(const char *ip); 119 | 120 | /** 121 | * \brief Allocate IP address from raw numeric representation 122 | * \param Array of integers describing the IP numerically in network byte order 123 | * \return Structure containing numeric representation of IP 124 | */ 125 | ip_t *ip_alloc_raw(const void *ip, int family); 126 | 127 | /** 128 | * \brief Allocate IP address 129 | * \param Pre-allocated structure created with ip_alloc() or similar 130 | */ 131 | void ip_free(ip_t *); 132 | 133 | 134 | 135 | // 136 | // COMPARISONS AND CHECKS 137 | 138 | /** 139 | * \brief Checks if the IP address is set 140 | * \param structure containing numeric representation of IP 141 | * \return Non-zero if IP is set. Zero (0) otherwise. 142 | * 143 | * An IP is considered set when all appropriate values for it's family have 144 | * been set to a non-zero value. The following cases are conisdered zero 145 | * addresses: 146 | * * IPV4 - 0.0.0.0 147 | * * IPV6 - 0000:0000:0000:0000:0000:0000:0000:0000:0000, ::, or similar. 148 | */ 149 | static inline int ip_isset(const ip_t *ip) 150 | { 151 | return ( ip && ip->addr && 152 | ( 153 | ( 154 | ( ip->addr->ip32[0] ) || ( 155 | ( ip->family == AF_INET6 ) && 156 | ( ip->addr->ip32[1] || 157 | ip->addr->ip32[2] || 158 | ip->addr->ip32[3] || 159 | ip->bits != 128 ) 160 | ) 161 | ) || ( 162 | ( ip->family == AF_INET ) && 163 | ( ip->bits != 32 ) 164 | ) 165 | ) 166 | ); 167 | } 168 | 169 | 170 | /** 171 | * \brief Checks if the IP describes the loopback address 172 | * \param structure containing numeric representation of IP 173 | * \return Non-zero if IP is a loopback address. Zero (0) otherwise 174 | * 175 | * An IP is considered set when all appropriate values for it's family have 176 | * been set to a non-zero value. The following cases are conisdered zero 177 | * addresses: 178 | * * IPV4 - 0.0.0.0 179 | * * IPV6 - 0000:0000:0000:0000:0000:0000:0000:0000:0000, ::, or similar. 180 | */ 181 | int ip_isloopback(const ip_t *ip); 182 | 183 | /** 184 | * \brief Checks if the IP describes an IPv4 mapped to IPv6 address 185 | * \param structure containing numeric representation of IP 186 | * \return Non-zero if IP is a mapped address. Zero (0) otherwise 187 | * 188 | * An IPv4 mapped to IPv6 address has the first 80 bits set to zero (0), 189 | * followed by 16 bits set to one (1). The final 32-bits represent the 190 | * IPv4 address. A literal example of an IPv4 mapped IPv6 address has looks 191 | * like: 192 | * * 0000:0000:0000:0000:0000::ffff::192.168.10.44 193 | */ 194 | int ip_ismapped(const ip_t *); 195 | 196 | static inline int _ip_ip4_cmp(uint32_t ip1, uint32_t ip2) 197 | { 198 | uint32_t ip1_h = ntohl(ip1); 199 | uint32_t ip2_h = ntohl(ip2); 200 | 201 | if ( ip1_h < ip2_h ) 202 | return -1; 203 | 204 | if ( ip1_h > ip2_h ) 205 | return 1; 206 | 207 | return 0; 208 | } 209 | 210 | static inline int _ip_ip6_cmp(const ip_t *ip1, const ip_t *ip2) 211 | { 212 | int ret; 213 | 214 | if ( (ret = _ip_ip4_cmp(ip1->addr->ip32[0], ip2->addr->ip32[0])) ) 215 | return ret; 216 | 217 | if ( (ret = _ip_ip4_cmp(ip1->addr->ip32[1], ip2->addr->ip32[1])) ) 218 | return ret; 219 | 220 | if ( (ret = _ip_ip4_cmp(ip1->addr->ip32[2], ip2->addr->ip32[2])) ) 221 | return ret; 222 | 223 | if ( (ret = _ip_ip4_cmp(ip1->addr->ip32[3], ip2->addr->ip32[3])) ) 224 | return ret; 225 | 226 | return ret; 227 | } 228 | 229 | static inline int ip_cmp(const ip_t *ip1, const ip_t *ip2) 230 | { 231 | int ip1_set = ip_isset(ip1); 232 | int ip2_set = ip_isset(ip2); 233 | int ip1_family = ip_family_get(ip1); 234 | int ip2_family = ip_family_get(ip2); 235 | 236 | if ( ! (ip1_set && ip2_set) ) 237 | return 0; 238 | else if ( ! ip2_set ) 239 | return 1; 240 | else if ( ! ip1_set ) 241 | return -1; 242 | 243 | if ( (ip1_family == AF_INET) && (ip2_family == AF_INET) ) 244 | { 245 | return _ip_ip4_cmp(ip1->addr->ip32[0], ip2->addr->ip32[0]); 246 | } 247 | else if ( (ip1_family == AF_INET6) && (ip2_family == AF_INET6) ) 248 | { 249 | return _ip_ip6_cmp(ip1, ip2); 250 | } 251 | else if (ip2_family == AF_INET6) 252 | { 253 | return -1; 254 | } 255 | 256 | return 1; 257 | } 258 | 259 | /** 260 | * \brief Checks if an IP is contained in a network 261 | * \param IP network to search in 262 | * \param IP to search for 263 | * \return Non-zero if IP is the loopback address. Zero (0) otherwise 264 | */ 265 | static inline int ip_contains(const ip_t *haystack, const ip_t *needle) 266 | { 267 | uint32_t remainder; 268 | int i; 269 | int bits = ip_bits_get(haystack); 270 | int words = bits / 32; 271 | bits = 32 - (bits % 32); 272 | 273 | for (i=0; iaddr->ip32[i] != needle->addr->ip32[i] ) 275 | return 0; 276 | 277 | if ( bits == 32 ) 278 | return 1; 279 | 280 | remainder = ntohl(needle->addr->ip32[i]); 281 | remainder >>= bits; 282 | remainder <<= bits; 283 | 284 | return (ntohl(haystack->addr->ip32[i]) == remainder) ? 1 : 0; 285 | } 286 | 287 | /** 288 | * \brief Checks if the IP describes the loopback address 289 | * \param structure containing numeric representation of IP 290 | * \return Non-zero if IP is the loopback address. Zero (0) otherwise 291 | * 292 | * An IP is considered set when all appropriate values for it's family have 293 | * been set to a non-zero value. The following cases are conisdered zero 294 | * addresses: 295 | * * IPV4 - 0.0.0.0 296 | * * IPV6 - 0000:0000:0000:0000:0000:0000:0000:0000:0000, ::, or similar. 297 | */ 298 | static inline uint64_t ip_hash(const ip_t *ip1, const ip_t *ip2, uint16_t sp, uint16_t dp, uint8_t pr, uint64_t size) 299 | { 300 | if ( ip_family_get(ip1) == AF_INET6 ) 301 | { 302 | return ( ip1->addr->ip32[0] + ip1->addr->ip32[1] + 303 | ip1->addr->ip32[2] + ip1->addr->ip32[3] + 304 | ip2->addr->ip32[0] + ip2->addr->ip32[1] + 305 | ip2->addr->ip32[2] + ip2->addr->ip32[3] + 306 | sp + dp + pr ) % size; 307 | } 308 | 309 | return ( ip1->addr->ip32[0] + ip2->addr->ip32[0] + sp + dp + pr ) % size; 310 | } 311 | 312 | // 313 | // UTILITIES 314 | 315 | /** 316 | * \brief Obfuscate an IP address 317 | * \param Reference to obfuscated IP 318 | * \param Reference to source IP 319 | * 320 | * Assists in avoiding disclosure of real network IP addresses by obfuscating 321 | * for the purpose of public demonstration. The obfuscated IP's are not 322 | * guaranteed to conform to IEEE standards. 323 | */ 324 | void ip_obfuscate(ip_t *ob, const ip_t *ip); 325 | 326 | /** 327 | * \brief Converts IP from presentation to numeric format. 328 | * \param IP storage buffer 329 | * \param Buffer containing IP in presentation format 330 | * \return Non-zero if IP is the loopback address. Zero (0) otherwise 331 | */ 332 | int ip_pton(ip_t *ip, const char *name); 333 | 334 | /** 335 | * \brief Converts from numeric to presentation format using a supplied buffer 336 | * \param IP to be converted 337 | * \param Buffer to store presentation format into 338 | * \param Length of buffer 339 | * \param Conversion flags (see below) 340 | * \return Non-zero if IP is the loopback address. Zero (0) otherwise 341 | */ 342 | int ip_ntop(const ip_t *ip, char *buf, int buflen, int flags); 343 | 344 | /** 345 | * \brief Converts from numeric to presentation format using a static buffer 346 | * \param IP to be converted 347 | * \param Conversion flags (see below) 348 | * \return Reference to static buffer holding literal representation of IP 349 | * 350 | * Conversion flags: 351 | * 352 | */ 353 | const char *ip_ntops(const ip_t *ip, int flags); 354 | 355 | #endif /* __IP_H__ */ 356 | -------------------------------------------------------------------------------- /src/smallcxt.c: -------------------------------------------------------------------------------- 1 | /* 2 | ** This file is a part of cxtracker. 3 | ** 4 | ** Copyright (C) 2009, Redpill Linpro 5 | ** Copyright (C) 2009, Edward Fjellskål 6 | ** 7 | ** This program is free software; you can redistribute it and/or modify 8 | ** it under the terms of the GNU General Public License as published by 9 | ** the Free Software Foundation; either version 2 of the License, or 10 | ** (at your option) any later version. 11 | ** 12 | ** This program is distributed in the hope that it will be useful, 13 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | ** GNU General Public License for more details. 16 | ** 17 | ** You should have received a copy of the GNU General Public License 18 | ** along with this program; if not, write to the Free Software 19 | ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 20 | ** 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include "cxtracker.h" 33 | #include 34 | 35 | /* G L O B A L E S **********************************************************/ 36 | u_int64_t cxtrackerid; 37 | time_t timecnt,tstamp; 38 | pcap_t *handle; 39 | connection *bucket[BUCKET_SIZE], *cxtfree; 40 | connection *cxtbuffer = NULL; 41 | static char src_s[INET6_ADDRSTRLEN], dst_s[INET6_ADDRSTRLEN]; 42 | static char *dev,*dpath; 43 | static int verbose, inpacket, intr_flag, gameover, use_syslog; 44 | static uint64_t freecnt, buffercnt, cxtcnt; 45 | 46 | /* I N T E R N A L P R O T O T Y P E S ************************************/ 47 | void move_connection (connection*, connection**, connection**); 48 | void clear_connection (connection*); 49 | void cx_track(struct in6_addr ip_src,uint16_t src_port,struct in6_addr ip_dst,uint16_t dst_port,uint8_t ip_proto,uint16_t p_bytes,uint8_t tcpflags,time_t tstamp, int af); 50 | void got_packet (u_char *useless,const struct pcap_pkthdr *pheader, const u_char *packet); 51 | void end_sessions(); 52 | void cxtbuffer_write(); 53 | void game_over(); 54 | void check_interupt(); 55 | void dump_active(); 56 | void set_end_sessions(); 57 | 58 | 59 | void got_packet (u_char *useless,const struct pcap_pkthdr *pheader, const u_char *packet) { 60 | if ( intr_flag != 0 ) { check_interupt(); } 61 | inpacket = 1; 62 | tstamp = time(NULL); 63 | u_short p_bytes; 64 | 65 | /* printf("[*] Got network packet...\n"); */ 66 | ether_header *eth_hdr; 67 | eth_hdr = (ether_header *) (packet); 68 | u_short eth_type; 69 | eth_type = ntohs(eth_hdr->eth_ip_type); 70 | int eth_header_len; 71 | eth_header_len = ETHERNET_HEADER_LEN; 72 | 73 | if ( eth_type == ETHERNET_TYPE_8021Q ) { 74 | /* printf("[*] ETHERNET TYPE 8021Q\n"); */ 75 | eth_type = ntohs(eth_hdr->eth_8_ip_type); 76 | eth_header_len +=4; 77 | } 78 | 79 | else if ( eth_type == (ETHERNET_TYPE_802Q1MT|ETHERNET_TYPE_802Q1MT2|ETHERNET_TYPE_802Q1MT3|ETHERNET_TYPE_8021AD) ) { 80 | /* printf("[*] ETHERNET TYPE 802Q1MT\n"); */ 81 | eth_type = ntohs(eth_hdr->eth_82_ip_type); 82 | eth_header_len +=8; 83 | } 84 | 85 | if ( eth_type == ETHERNET_TYPE_IP ) { 86 | /* printf("[*] Got IPv4 Packet...\n"); */ 87 | ip4_header *ip4; 88 | ip4 = (ip4_header *) (packet + eth_header_len); 89 | p_bytes = (ip4->ip_len - (IP_HL(ip4)*4)); 90 | struct in6_addr ip_src, ip_dst; 91 | ip_src.s6_addr32[0] = ip4->ip_src; 92 | ip_dst.s6_addr32[0] = ip4->ip_dst; 93 | 94 | 95 | if ( ip4->ip_p == IP_PROTO_TCP ) { 96 | tcp_header *tcph; 97 | tcph = (tcp_header *) (packet + eth_header_len + (IP_HL(ip4)*4)); 98 | /* printf("[*] IPv4 PROTOCOL TYPE TCP:\n"); */ 99 | cx_track(ip_src, tcph->src_port, ip_dst, tcph->dst_port, ip4->ip_p, p_bytes, tcph->t_flags, tstamp, AF_INET); 100 | inpacket = 0; 101 | return; 102 | } 103 | else if (ip4->ip_p == IP_PROTO_UDP) { 104 | udp_header *udph; 105 | udph = (udp_header *) (packet + eth_header_len + (IP_HL(ip4)*4)); 106 | /* printf("[*] IPv4 PROTOCOL TYPE UDP:\n"); */ 107 | cx_track(ip_src, udph->src_port, ip_dst, udph->dst_port, ip4->ip_p, p_bytes, 0, tstamp, AF_INET); 108 | inpacket = 0; 109 | return; 110 | } 111 | else if (ip4->ip_p == IP_PROTO_ICMP) { 112 | icmp_header *icmph; 113 | icmph = (icmp_header *) (packet + eth_header_len + (IP_HL(ip4)*4)); 114 | /* printf("[*] IP PROTOCOL TYPE ICMP\n"); */ 115 | cx_track(ip_src, icmph->s_icmp_id, ip_dst, icmph->s_icmp_id, ip4->ip_p, p_bytes, 0, tstamp, AF_INET); 116 | inpacket = 0; 117 | return; 118 | } 119 | else { 120 | /* printf("[*] IPv4 PROTOCOL TYPE OTHER: %d\n",ip4->ip_p); */ 121 | cx_track(ip_src, ip4->ip_p, ip_dst, ip4->ip_p, ip4->ip_p, p_bytes, 0, tstamp, AF_INET); 122 | inpacket = 0; 123 | return; 124 | } 125 | } 126 | inpacket = 0; 127 | return; 128 | } 129 | 130 | void cx_track(struct in6_addr ip_src,uint16_t src_port,struct in6_addr ip_dst,uint16_t dst_port, 131 | uint8_t ip_proto,uint16_t p_bytes,uint8_t tcpflags,time_t tstamp, int af) { 132 | 133 | connection *cxt = NULL; 134 | connection *head = NULL; 135 | uint64_t hash; 136 | 137 | if (af == AF_INET) { 138 | hash = (( ip_src.s6_addr32[0] + ip_dst.s6_addr32[0] )) % BUCKET_SIZE; 139 | } else { 140 | /* Do we need an if? */ 141 | /* if (af == AF_INET6) { */ 142 | hash = (( ip_src.s6_addr32[0] + ip_src.s6_addr32[1] + ip_src.s6_addr32[2] + ip_src.s6_addr32[3] 143 | + ip_dst.s6_addr32[0] + ip_dst.s6_addr32[1] + ip_dst.s6_addr32[2] + ip_dst.s6_addr32[3] 144 | )) % BUCKET_SIZE; 145 | } 146 | 147 | cxt = bucket[hash]; 148 | head = cxt; 149 | 150 | while ( cxt != NULL ) { 151 | if (af == AF_INET) { 152 | if ( cxt->s_ip.s6_addr32[0] == ip_src.s6_addr32[0] && cxt->d_ip.s6_addr32[0] == ip_dst.s6_addr32[0] 153 | && cxt->s_port == src_port && cxt->d_port == dst_port ) { 154 | cxt->s_tcpFlags |= tcpflags; 155 | cxt->s_total_bytes += p_bytes; 156 | cxt->s_total_pkts += 1; 157 | cxt->last_pkt_time = tstamp; 158 | return; 159 | } 160 | else if ( memcmp(&cxt->s_ip,&ip_src,4) ) { 161 | cxt->d_tcpFlags |= tcpflags; 162 | cxt->d_total_bytes += p_bytes; 163 | cxt->d_total_pkts += 1; 164 | cxt->last_pkt_time = tstamp; 165 | return; 166 | } 167 | } else { 168 | /* Do we need an if ? */ 169 | /* if (af == AF_INET6) { */ 170 | if ( memcmp(&cxt->s_ip,&ip_src,16) && memcmp(&cxt->d_ip,&ip_dst,16) && 171 | cxt->s_port == src_port && cxt->d_port == dst_port ) { 172 | cxt->s_tcpFlags |= tcpflags; 173 | cxt->s_total_bytes += p_bytes; 174 | cxt->s_total_pkts += 1; 175 | cxt->last_pkt_time = tstamp; 176 | return; 177 | } else 178 | if ( memcmp(&cxt->s_ip,&ip_dst,16) && memcmp(&cxt->d_ip,&ip_src,16) && 179 | cxt->d_port == src_port && cxt->s_port == dst_port ) { 180 | cxt->d_tcpFlags |= tcpflags; 181 | cxt->d_total_bytes += p_bytes; 182 | cxt->d_total_pkts += 1; 183 | cxt->last_pkt_time = tstamp; 184 | return; 185 | } 186 | } 187 | cxt = cxt->next; 188 | } 189 | 190 | if ( cxt == NULL ) { 191 | u_int64_t cxtrackerid; 192 | cxtrackerid += 1; 193 | cxtcnt += 1; 194 | if (cxtfree != NULL) { 195 | /* Use a connection from cxtfree */ 196 | //move_connection(cxtfree, &cxtfree, cxt); 197 | // pop a connection from cxtfree 198 | cxt = cxtfree; 199 | cxtfree = cxtfree->next; 200 | //printf("[*] Re-used a connection from cxtfree...\n"); 201 | freecnt -= 1; 202 | }else{ 203 | /* Allocate memory for a new connection */ 204 | cxt = (connection*) calloc(1, sizeof(connection)); 205 | printf("[*] Allocated a new connection...\n"); 206 | } 207 | if (head != NULL ) { 208 | head->prev = cxt; 209 | } 210 | /* printf("[*] New connection...\n"); */ 211 | cxt->cxid = cxtrackerid; 212 | cxt->ipversion = af; 213 | cxt->s_tcpFlags = tcpflags; 214 | cxt->d_tcpFlags = 0x00; 215 | cxt->s_total_bytes = p_bytes; 216 | cxt->s_total_pkts = 1; 217 | cxt->d_total_bytes = 0; 218 | cxt->d_total_pkts = 0; 219 | cxt->start_time = tstamp; 220 | cxt->last_pkt_time = tstamp; 221 | 222 | cxt->s_ip = ip_src; 223 | cxt->d_ip = ip_dst; 224 | if (af == AF_INET) { 225 | cxt->s_ip.s6_addr32[1] = 0; 226 | cxt->s_ip.s6_addr32[2] = 0; 227 | cxt->s_ip.s6_addr32[3] = 0; 228 | cxt->d_ip.s6_addr32[1] = 0; 229 | cxt->d_ip.s6_addr32[2] = 0; 230 | cxt->d_ip.s6_addr32[3] = 0; 231 | } 232 | 233 | cxt->s_port = src_port; 234 | cxt->d_port = dst_port; 235 | cxt->proto = ip_proto; 236 | cxt->next = head; 237 | cxt->prev = NULL; 238 | 239 | /* New connections are pushed on to the head of bucket[s_hash] */ 240 | bucket[hash] = cxt; 241 | 242 | /* Return value should be X, telling to do fingerprinting */ 243 | return; 244 | } 245 | /* Should never be here! */ 246 | return; 247 | } 248 | 249 | void end_sessions() { 250 | 251 | connection *cxt; 252 | time_t check_time; 253 | check_time = time(NULL); 254 | int cxkey, xpir; 255 | uint32_t curcxt = 0; 256 | uint32_t expired = 0; 257 | //cxtbuffer = NULL; 258 | 259 | for ( cxkey = 0; cxkey < BUCKET_SIZE; cxkey++ ) { 260 | cxt = bucket[cxkey]; 261 | xpir = 0; 262 | while ( cxt != NULL ) { 263 | curcxt++; 264 | if ( (check_time - cxt->last_pkt_time) > 30 ) { 265 | xpir = 1; 266 | } 267 | if ( xpir == 1 ) { 268 | expired++; 269 | xpir = 0; 270 | connection *tmp = cxt; 271 | assert(cxt != cxt->next); 272 | cxt = cxt->next; 273 | move_connection(tmp, &bucket[cxkey], &cxtbuffer); 274 | cxtcnt -= 1; 275 | }else{ 276 | cxt = cxt->next; 277 | } 278 | } 279 | } 280 | fprintf(stderr, "[*] Expired: %u of %u total connections:\n",expired,curcxt); 281 | cxtbuffer_write(); 282 | // fprintf(stderr, "[*] End.\n"); 283 | printf("[*] cnxfree:%ju\tbucket:%ju\n",freecnt,cxtcnt); 284 | } 285 | 286 | void clear_connection (connection *cxt){ 287 | memset(cxt, 0, sizeof(*cxt)); 288 | /* 289 | cxt->cxid = 0; 290 | cxt->ipversion = 0; 291 | cxt->s_tcpFlags = 0x00; 292 | cxt->d_tcpFlags = 0x00; 293 | cxt->s_total_bytes = 0; 294 | cxt->s_total_pkts = 0; 295 | cxt->d_total_bytes = 0; 296 | cxt->d_total_pkts = 0; 297 | cxt->start_time = 0; 298 | cxt->last_pkt_time = 0; 299 | cxt->s_ip.s6_addr32[0] = 0; 300 | cxt->s_ip.s6_addr32[1] = 0; 301 | cxt->s_ip.s6_addr32[2] = 0; 302 | cxt->s_ip.s6_addr32[3] = 0; 303 | cxt->d_ip.s6_addr32[0] = 0; 304 | cxt->d_ip.s6_addr32[1] = 0; 305 | cxt->d_ip.s6_addr32[2] = 0; 306 | cxt->d_ip.s6_addr32[3] = 0; 307 | cxt->s_port = 0; 308 | cxt->d_port = 0; 309 | cxt->proto = 0; 310 | */ 311 | } 312 | 313 | /* move cxt from bucket to cxtbuffer 314 | * there are three cases usually: 315 | * either, we are in the middle of list. Update next and prev 316 | * or, we are at end of list, next==NULL, update prev->next = NULL 317 | 318 | */ 319 | void move_connection (connection *cxt_from, connection **bucket_ptr_from, connection **cxt_to ){ 320 | /* remove cxt from bucket */ 321 | connection *prev = cxt_from->prev; /* OLDER connections */ 322 | connection *next = cxt_from->next; /* NEWER connections */ 323 | if(prev == NULL){ 324 | // beginning of list 325 | *bucket_ptr_from = next; 326 | // not the only entry 327 | if(next) 328 | next->prev = NULL; 329 | } else if(next == NULL){ 330 | // at end of list! 331 | prev->next = NULL; 332 | } else { 333 | // a node. 334 | prev->next = next; 335 | next->prev = prev; 336 | } 337 | 338 | /* add cxt to expired list cxtbuffer 339 | - if head is null -> head = cxt; */ 340 | cxt_from->next = *cxt_to; // next = head 341 | cxt_from->prev = NULL; 342 | *cxt_to = cxt_from; // head = cxt. result: newhead = cxt->oldhead->list... 343 | } 344 | 345 | /* flush connection buffer to output */ 346 | void cxtbuffer_write () { 347 | 348 | if ( cxtbuffer == NULL ) { return; } 349 | connection *next, *oldhead; 350 | next = NULL; 351 | 352 | while ( cxtbuffer != NULL ) { 353 | oldhead = cxtfree; 354 | next = cxtbuffer->next; 355 | 356 | // free connection: 357 | //pop from cxtbuffer, push to cxtfree 358 | cxtfree = cxtbuffer; 359 | cxtbuffer = next; 360 | 361 | freecnt += 1; 362 | //clear_connection(cxtfree); 363 | cxtfree->next = oldhead; 364 | //printf("[*] cxtfree'd a connection\n"); 365 | //debug = NULL; 366 | } 367 | 368 | // if (head != NULL ) { free(head); } 369 | /* just write something*/ 370 | // fprintf(stderr, "Done...\n"); 371 | } 372 | 373 | void check_interupt() { 374 | if ( intr_flag == 1 ) { 375 | game_over(); 376 | } 377 | /* 378 | else if ( intr_flag == 2 ) { 379 | dump_active(); 380 | } 381 | */ 382 | else if ( intr_flag == 3 ) { 383 | set_end_sessions(); 384 | } 385 | else { 386 | intr_flag = 0; 387 | } 388 | } 389 | 390 | void set_end_sessions() { 391 | intr_flag = 3; 392 | if ( inpacket == 0 ) { 393 | end_sessions(); 394 | cxtbuffer_write(); 395 | intr_flag = 0; 396 | alarm(TIMEOUT); 397 | } 398 | } 399 | 400 | void game_over() { 401 | if ( inpacket == 0 ) { 402 | //end_all_sessions(); 403 | cxtbuffer_write(); 404 | pcap_close(handle); 405 | exit (0); 406 | } 407 | intr_flag = 1; 408 | } 409 | 410 | void add_connections() { 411 | int cxkey; 412 | connection *cxt; 413 | 414 | for ( cxkey = 0; cxkey < BUCKET_SIZE * 9; cxkey++ ) { 415 | freecnt += 1; 416 | cxt = (connection*) calloc(1, sizeof(connection)); 417 | //clear_connection(cxt); // already calloced! 418 | if (cxtfree != NULL) { 419 | cxt->next = cxtfree; 420 | cxtfree->prev = cxt; 421 | }else{ 422 | cxt->next = NULL; 423 | } 424 | cxt->prev = NULL; 425 | 426 | cxtfree = cxt; 427 | } 428 | } 429 | 430 | int main(int argc, char *argv[]) { 431 | 432 | int ch, fromfile, setfilter, version, drop_privs_flag, daemon_flag = 0; 433 | int use_syslog = 0; 434 | struct in_addr addr; 435 | struct bpf_program cfilter; 436 | char *bpff, errbuf[PCAP_ERRBUF_SIZE], *user_filter; 437 | char *net_ip_string; 438 | bpf_u_int32 net_mask; 439 | dev = "eth0"; 440 | bpff = ""; 441 | dpath = "/tmp"; 442 | cxtbuffer = NULL; 443 | cxtrackerid = 9999999999; 444 | inpacket = gameover = 0; 445 | freecnt = buffercnt = cxtcnt = 0; 446 | timecnt = time(NULL); 447 | 448 | if (getuid()) { 449 | printf("[*] You must be root..\n"); 450 | return (1); 451 | } 452 | printf("[*] Running cxtracker...\n"); 453 | 454 | signal(SIGTERM, game_over); 455 | signal(SIGINT, game_over); 456 | signal(SIGQUIT, game_over); 457 | signal(SIGALRM, set_end_sessions); 458 | /* alarm(TIMEOUT); */ 459 | 460 | while ((ch = getopt(argc, argv, "b:d:D:g:i:p:P:u:v")) != -1) 461 | switch (ch) { 462 | case 'i': 463 | dev = strdup(optarg); 464 | break; 465 | case 'b': 466 | bpff = strdup(optarg); 467 | break; 468 | case 'v': 469 | verbose = 1; 470 | break; 471 | case 'd': 472 | dpath = strdup(optarg); 473 | break; 474 | case 'D': 475 | daemon_flag = 1; 476 | break; 477 | default: 478 | exit(1); 479 | break; 480 | } 481 | 482 | errbuf[0] = '\0'; 483 | /* look up an availible device if non specified */ 484 | if (dev == 0x0) dev = pcap_lookupdev(errbuf); 485 | printf("[*] Device: %s\n", dev); 486 | 487 | if ((handle = pcap_open_live(dev, 65535, 1, 500, errbuf)) == NULL) { 488 | printf("[*] Error pcap_open_live: %s \n", errbuf); 489 | exit(1); 490 | } 491 | else if ((pcap_compile(handle, &cfilter, bpff, 1 ,net_mask)) == -1) { 492 | printf("[*] Error pcap_compile user_filter: %s\n", pcap_geterr(handle)); 493 | exit(1); 494 | } 495 | 496 | pcap_setfilter(handle, &cfilter); 497 | add_connections(); 498 | 499 | /* B0rk if we see an error... */ 500 | if (strlen(errbuf) > 0) { 501 | printf("[*] Error errbuf: %s \n", errbuf); 502 | exit(1); 503 | } 504 | 505 | alarm(TIMEOUT); 506 | printf("[*] Sniffing...\n\n"); 507 | pcap_loop(handle,-1,got_packet,NULL); 508 | 509 | pcap_close(handle); 510 | return(0); 511 | } 512 | 513 | --------------------------------------------------------------------------------