#!/usr/bin/perl ############################################################################### # Program: pkts2db.pl v.01 # Description: Reads a pcap file and puts header fields in a SQLite database # Dependencies: Perl, Net::Pcap, NetPacket, and SQLite # Development Platform: Windows 7 with Perl v5.10.1 and SQLite v3.6.22 # Release Date: 2010-03-05 # # Copyright 2010 Adam Bray # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . ############################################################################### # Modules use warnings; use strict; use Getopt::Long; use File::Basename; use Net::Pcap; use NetPacket::Ethernet; use NetPacket::IP; use NetPacket::TCP; use NetPacket::UDP; use NetPacket::ICMP; # Declare variables my $pcap; my $help; my $db; my $id = 1; my $err; my $filter_c; my $filter = "ip and tcp or udp or icmp"; # Get options GetOptions ("read|r=s" => \$pcap, "database|d=s" => \$db, "help|h" => \$help); &Help if $help || !defined($pcap && $db); # Open files to store header fields my $basename = fileparse($db, qr/\.[^.]+$/); open (IPOUT, ">${basename}_ip.csv"); open (TCPOUT, ">${basename}_tcp.csv"); open (UDPOUT, ">${basename}_udp.csv"); open (ICMPOUT, ">${basename}_icmp.csv"); # Read PCAP file my $packets = Net::Pcap::open_offline( $pcap, \$err ) || die "Error reading file: $err\n"; # Compilie and set filter Net::Pcap::compile( $packets, \$filter_c, $filter, 1, 0 ); Net::Pcap::setfilter( $packets, $filter_c ); # Process and then close $packets Net::Pcap::loop( $packets, -1, \&read_packets, '' ); Net::Pcap::close( $packets ); # Subroutine to process each packet sub read_packets { my ($user_data, $header, $packet) = @_; my $time = $header->{tv_sec}; my $timeu = $header->{tv_usec}; my $eth = NetPacket::Ethernet->decode($packet); my $ip = NetPacket::IP->decode($eth->{'data'}); my $tcp = NetPacket::TCP->decode($ip->{'data'}); my $udp = NetPacket::UDP->decode($ip->{'data'}); my $icmp = NetPacket::ICMP->decode($ip->{'data'}); # Format time for SQLite my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($time); $mon++; $sec = "0$sec" if ($sec < 10); $min= "0$min" if ($min < 10); $hour = "0$hour" if ($hour < 10); $mday = "0$mday" if ($mday < 10); $mon = "0$mon" if ($mon < 10); $time = $year + 1900 . "-$mon-${mday}T$hour:$min:$sec.$timeu"; # Declare variables to store header fields # Ethernet my ( $smac, $dmac, $ethertype ); # IP my ( $ipver, $iphlen, $ipflags, $ipfoffset, $iptos, $iplen, $ipid, $ipttl, $ipproto, $ipchksum, $ipsaddr, $ipdaddr, $ipopts ); # TCP my ( $sport, $dport, $tcpseqnum, $tcpacknum, $tcphlen, $tcpreserved, $tcpflags, $tcpwindow, $tcpurg, $tcpopts ); # UDP my ( $udplen ); # Ethernet and IP $smac = $eth->{'src_mac'}; $dmac = $eth->{'dest_mac'}; $ethertype = $eth->{'type'}; if ($ethertype == 0x0800) { $ipver = $ip->{'ver'}; $iphlen = $ip->{'hlen'} * 4; $ipflags = $ip->{'flags'}; $ipfoffset = $ip->{'foffset'}; $iptos = $ip->{'tos'}; $iplen = $ip->{'len'}; $ipid = $ip->{'id'}; $ipttl = $ip->{'ttl'}; $ipproto = $ip->{'proto'}; $ipchksum = $ip->{'cksum'}; $ipsaddr = $ip->{'src_ip'}; $ipdaddr = $ip->{'dest_ip'}; $ipopts = "1" if $ip->{'options'}; # No nulls foreach my $field ( $smac, $dmac, $ethertype, $ipver, $iphlen, $ipflags, $ipfoffset, $iptos, $iplen, $ipid, $ipttl, $ipproto, $ipchksum, $ipsaddr, $ipdaddr, $ipopts ) { $field = "0" if !defined $field; } # Create CSV lines and print to IP file my $line = "$id,$time,$smac,$dmac,$ethertype,$ipver,$iphlen,$iptos,$iplen,"; $line .= "$ipid,$ipflags,$ipfoffset,$ipttl,$ipproto,$ipchksum,$ipsaddr,$ipdaddr,$ipopts\n"; print IPOUT $line; } if (defined $ipproto && $ipproto == 6) { # TCP $sport = $tcp->{'src_port'}; $dport = $tcp->{'dest_port'}; $tcpseqnum = $tcp->{'seqnum'}; $tcpacknum = $tcp->{'acknum'}; $tcphlen = $tcp->{'hlen'} * 4; $tcpreserved = $tcp->{'reserved'}; $tcpflags = $tcp->{'flags'}; $tcpwindow = $tcp->{'winsize'}; $tcpurg = $tcp->{'urg'}; $tcpopts = "1" if $tcp->{'options'}; # No nulls foreach my $field ( $sport, $dport, $tcpseqnum, $tcpacknum, $tcphlen, $tcpreserved, $tcpflags, $tcpwindow, $tcpurg, $tcpopts) { $field = "0" if !defined $field; } # Create CSV lines and print to TCP file my $line = "$id,$sport,$dport,$tcpseqnum,$tcpacknum,$tcphlen,$tcpreserved,"; $line .= "$tcpflags,$tcpwindow,$tcpurg,$tcpopts\n"; print TCPOUT $line; } elsif (defined $ipproto && $ipproto == 17) { # UDP $sport = $udp->{'src_port'}; $dport = $udp->{'dest_port'}; $udplen = $udp->{'len'}; # No nulls foreach my $field ($sport, $dport, $udplen) { $field = "0" if !defined $field; } # Create CSV lines and print to UDP file my $line = "$id,$sport,$dport,$udplen\n"; print UDPOUT $line; } elsif (defined $ipproto && $ipproto == 1) { # ICMP my $type = $icmp->{'type'}; my $code = $icmp->{'code'}; # No nulls foreach my $field ($type, $code) { $field = "0" if !defined $field; } # Create CSV lines and print to ICMP file my $line = "$id,$type,$code\n"; print ICMPOUT $line; } $id++; } close IPOUT; close TCPOUT; close UDPOUT; close ICMPOUT; # Create IP table and then delete IP CSV file system("sqlite3 $db \"CREATE TABLE ip (id INTEGER PRIMARY KEY, time TEXT, smac TEXT, dmac TEXT, ethtype INTEGER, ver INTEGER, hlen INTEGER, tos INTEGER, len INTEGER, ipid INTEGER, flags INTEGER, foffset INTEGER, ttl INTEGER, proto INTEGER, chksum INTEGER, saddr TEXT, daddr TEXT, opts INTEGER);\""); system("sqlite3 -separator , $db \".import ${basename}_ip.csv ip\""); unlink("${basename}_ip.csv"); # Create TCP table and then delete TCP CSV file system("sqlite3 $db \"CREATE TABLE tcp (id INTEGER, sport INTEGER, dport INTEGER, seq INTEGER, ack INTEGER, hlen INTEGER, reserved INTEGER, flags INTEGER, window INTEGER, urgptr INTEGER, opts INTEGER, FOREIGN KEY (id) REFERENCES ip(id));\""); system("sqlite3 -separator , $db \".import ${basename}_tcp.csv tcp\""); unlink("${basename}_tcp.csv"); # Create UDP table and then delete UDP CSV file system("sqlite3 $db \"CREATE TABLE udp (id INTEGER, sport INTEGER, dport INTEGER, len INTEGER, FOREIGN KEY (id) REFERENCES ip(id));\""); system("sqlite3 -separator , $db \".import ${basename}_udp.csv udp\""); unlink("${basename}_udp.csv"); # Create ICMP table and then delete ICMP CSV file system("sqlite3 $db \"CREATE TABLE icmp (id INTEGER, type INTEGER, code INTEGER, FOREIGN KEY (id) REFERENCES ip(id));\""); system("sqlite3 -separator , $db \".import ${basename}_icmp.csv icmp\""); unlink("${basename}_icmp.csv"); # Help subroutine sub Help { print <