1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
|
#!/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 = "UDP";
$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<N>
Exit after receiving I<N> packets.
=item B<-i>, B<--interface> I<device>
Listen on the specified interface. If unspecified, the program will use
the interface returned by C<pcap_lookupdev()>.
=item B<-s>, B<--snaplen> I<L>
Capture I<L> bytes of data for each packet. Defaults to 256.
=item B<-w>, B<--writeto> I<file>
=back
=head1 DESCRIPTION
B<pcapdump> mimics the very basic features of B<tcpdump(1)> and provides
a good example of how to use C<Net::Pcap>.
=head1 AUTHOR
SE<eacute>bastien Aperghis-Tramoni, E<lt>sebastien@aperghis.netE<gt>
=head1 COPYRIGHT
Copyright (C) 2005, 2006, 2007, 2008, 2009 SE<eacute>bastien 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
|