#!/usr/bin/perl use strict; use Data::Hexdumper; use File::Basename; use Getopt::Long qw(:config no_auto_abbrev); use Net::Pcap qw(:functions); use NetPacket::Ethernet qw(:types); use NetPacket::IP qw(:protos); use NetPacket::TCP; use Pod::Usage; use Socket qw(inet_ntoa); $::PROGRAM = basename($0); $::VERSION = "0.01"; # globals my $dumper = undef; my %icmp = ( ICMP_ECHO => "echo", ICMP_ECHOREPLY => "echo-reply", ICMP_IREQ => "ireq", ICMP_IREQREPLY => "ireq-reply", ICMP_MASREQ => "mask", ICMP_MASKREPLY => "mask-reply", ICMP_PARAMPROB => "param-prob", ICMP_REDIRECT => "redirect", ICMP_ROUTERADVERT => "router-advert", ICMP_ROUTERSOLICIT => "router-solicit", ICMP_SOURCEQUENCH => "source-quench", ICMP_TIMXCEED => "time-exceeded", ICMP_TSTAMP => "timestamp", ICMP_TSTAMPREPLY => "timestamp-reply", ICMP_UNREACH => "unreachable", ); MAIN: { run(); } sub run { $|++; # get options my %options = ( count => 10, promisc => 0, snaplen => 256, timeout => 10, ); GetOptions(\%options, qw{ help|h! version|V! count|c=i interface|i=s promisc|p! snaplen|s=i writeto|w=s }) or pod2usage(); pod2usage({ -verbose => 2, -exitval => 0 }) if $options{help}; print "$::PROGRAM v$::VERSION\n" if $options{version}; my ($err, $net, $mask, $filter); my $dev = $options{interface} || pcap_lookupdev(\$err); my $filter_str = join " ", @ARGV; # open the interface my $pcap = pcap_open_live($dev, @options{qw(snaplen promisc timeout)}, \$err) or die "fatal: can't open network device $dev: $err ", "(do you have the privileges?)\n"; if ($filter_str) { # compile the filter pcap_compile($pcap, \$filter, $filter_str, 1, 0) == 0 or die "fatal: filter error\n"; pcap_setfilter($pcap, $filter); } if ($options{writeto}) { $dumper = pcap_dump_open($pcap, $options{writeto}) or die "fatal: can't write to file '$options{writeto}': $!\n"; } # print some information about the interface we're currently using pcap_lookupnet($dev, \$net, \$mask, \$err); print "listening on $dev (", dotquad($net), "/", dotquad($mask), ")", ", capture size $options{snaplen} bytes"; print ", filtering on $filter_str" if $filter_str; print $/; # enter the main loop pcap_loop($pcap, $options{count}, \&process_packet, ''); pcap_close($pcap); } sub process_packet { my ($user_data, $header, $packet) = @_; my ($proto, $payload, $src_ip, $src_port, $dest_ip, $dest_port, $flags); printf "packet: len=%s, caplen=%s, tv_sec=%s, tv_usec=%s\n", map { $header->{$_} } qw(len caplen tv_sec tv_usec); # dump the packet if asked to do so pcap_dump($dumper, $header, $packet) if $dumper; # decode the Ethernet frame my $ethframe = NetPacket::Ethernet->decode($packet); if ($ethframe->{type} == ETH_TYPE_IP) { # decode the IP payload my $ipframe = NetPacket::IP->decode($ethframe->{data}); $src_ip = $ipframe->{src_ip}; $dest_ip = $ipframe->{dest_ip}; if ($ipframe->{proto} == IP_PROTO_ICMP) { my $icmpframe = NetPacket::ICMP->decode($ipframe->{data}); $proto = "ICMP"; $payload = $icmpframe->{data}; } elsif ($ipframe->{proto} == IP_PROTO_TCP) { my $tcpframe = NetPacket::TCP->decode($ipframe->{data}); $proto = "TCP"; $src_port = $tcpframe->{src_port}; $dest_port = $tcpframe->{dest_port}; $payload = $tcpframe->{data}; $flags = flags_of($tcpframe->{flags}); } elsif ($ipframe->{proto} == IP_PROTO_UDP) { my $udpframe = NetPacket::UDP->decode($ipframe->{data}); $proto = "TCP"; $src_port = $udpframe->{src_port}; $dest_port = $udpframe->{dest_port}; $payload = $udpframe->{data}; } printf "IP:%s %s:%d -> %s:%d (%s)\n", $proto, $src_ip, $src_port, $dest_ip, $dest_port, $flags; print hexdump(data => $payload, start_position => 0) if length $payload; print $/; } } sub flags_of { my ($flags) = @_; my @strarr = (); push @strarr, "urg" if $flags & URG; push @strarr, "ack" if $flags & ACK; push @strarr, "psh" if $flags & PSH; push @strarr, "fin" if $flags & FIN; push @strarr, "syn" if $flags & SYN; push @strarr, "rst" if $flags & RST; push @strarr, "ece" if $flags & ECE; push @strarr, "cwr" if $flags & CWR; return join ",", @strarr } sub dotquad { return inet_ntoa( pack("I", $_[0]) ) } __END__ =head1 NAME pcapdump - Dump packets from the network =head1 SYNOPSIS pcapdump [-c count] [-i interface] [-s snaplen] [-w file] [expression] pcapdump --help pcapdump --version =head1 OPTIONS =over =item B<-c>, B<--count> I Exit after receiving I packets. =item B<-i>, B<--interface> I Listen on the specified interface. If unspecified, the program will use the interface returned by C. =item B<-s>, B<--snaplen> I Capture I bytes of data for each packet. Defaults to 256. =item B<-w>, B<--writeto> I =back =head1 DESCRIPTION B mimics the very basic features of B and provides a good example of how to use C. =head1 AUTHOR SEbastien Aperghis-Tramoni, Esebastien@aperghis.netE =head1 COPYRIGHT Copyright (C) 2005, 2006, 2007, 2008 SEbastien Aperghis-Tramoni. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut #### packet: len=70, caplen=70, tv_sec=1272916318, tv_usec=252147 IP:UDP 8.8.8.8:53 -> 10.0.0.2:37731 () 0x0000 : 31 4E 81 80 00 01 00 01 00 00 00 00 06 67 6F 6F : 1N...........goo 0x0010 : 67 6C 65 03 63 6F 6D 00 00 01 00 01 C0 0C 00 01 : gle.com......... 0x0020 : 00 01 00 00 01 2C 00 04 D1 55 E7 68 : .....,...U.h