#!/usr/bin/perl
$::PROGRAM = basename($0);
$::VERSION =
"0.01"
;
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 {
$|++;
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
;
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
) {
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"
;
}
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
$/;
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)
;
pcap_dump(
$dumper
,
$header
,
$packet
)
if
$dumper
;
my
$ethframe
= NetPacket::Ethernet->decode(
$packet
);
if
(
$ethframe
->{type} == ETH_TYPE_IP) {
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]) )
}