Skip to content

Instantly share code, notes, and snippets.

@hirose31
Last active August 29, 2015 13:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hirose31/9182442 to your computer and use it in GitHub Desktop.
Save hirose31/9182442 to your computer and use it in GitHub Desktop.
#!/usr/bin/env perl
use strict;
use warnings;
use Data::Dumper;
BEGIN {
sub p($) {
local $Data::Dumper::Indent = 1;
local $Data::Dumper::Deepcopy = 1;
local $Data::Dumper::Sortkeys = 1;
local $Data::Dumper::Terse = 1;
local $Data::Dumper::Useqq = 1;
local $Data::Dumper::Quotekeys = 0;
print STDERR Dumper($_[0]);
}
}
use Net::Pcap;
use Net::Pcap::Reassemble;
use NetPacket::Ethernet;
use NetPacket::IP;
use NetPacket::TCP;
use POSIX qw(strftime);
sub debug {
return unless $ENV{DEBUG};
my @m = @_;
chomp @m;
print STDERR "@_\n";
}
MAIN: {
my $pcapfile = shift @ARGV;
if (! -r $pcapfile) {
die "Cannot read: $pcapfile";
}
my $err;
my $pcap = Net::Pcap::open_offline($pcapfile, \$err)
or die "open_offline: $err";
### We can apply extra filters
# my $filter;
# pcap_compile($pcap, \$filter, "tcp port 6379", 1, 0);
# pcap_setfilter($pcap, $filter);
my $stream = {}; # need to initialize to pass reference of THIS variable
Net::Pcap::Reassemble::loop($pcap, -1, \&process_packet, {
stream => $stream,
});
# p $stream;
for my $tcp (sort {
$a->{start}[0]+$a->{start}[1]/1000000
<=>
$b->{start}[0]+$b->{start}[1]/1000000
} values %$stream) {
printf("%s.%06d\n",
strftime("%Y-%m-%d %H:%M:%S", localtime( $tcp->{start}[0] )),
$tcp->{start}[1],
);
print "req:\n".join(' ', @{ $tcp->{data}{req} })."\n";
print "res:\n".join(' ', @{ $tcp->{data}{res} })."\n";
}
exit;
}
sub process_packet {
my($opt, $header, $packet) = @_;
my $stream = $opt->{stream};
my $eth = NetPacket::Ethernet->decode($packet);
if ($eth->{type} != NetPacket::Ethernet::ETH_TYPE_IP) {
warn "Not a IP ethernet frame: $eth->{type}";
return;
}
my $ip = NetPacket::IP->decode($eth->{data});
if ($ip->{proto} != NetPacket::IP::IP_PROTO_TCP) {
warn "Not a TCP packet: $ip->{proto}";
return;
}
my $tcp = NetPacket::TCP->decode($ip->{data});
### rebuild TCP stream from TCP packets
if ($tcp->{flags} & SYN && !($tcp->{flags} & ACK)) {
my $next_seqnum = $tcp->{seqnum} + 1;
$stream->{ $next_seqnum } = {
start => [ $header->{tv_sec}, $header->{tv_usec} ],
end => [ 0, 0 ],
state => 'syn',
data => { req => [], res => [] },
};
} elsif ($tcp->{flags} & SYN && $tcp->{flags} & ACK) {
if (exists $stream->{ $tcp->{acknum} }) {
$stream->{ $tcp->{acknum} }{state} = 'syn+ack';
} else {
debug("Got syn+ack but no syn packet");
}
} elsif ($tcp->{flags} & ACK && length($tcp->{data}) > 0) {
if (exists $stream->{ $tcp->{seqnum} }) {
# client -> server
my $next_seqnum = $tcp->{seqnum} + length($tcp->{data});
$stream->{ $next_seqnum } = delete $stream->{ $tcp->{seqnum} };
$stream->{ $next_seqnum }{state} = 'ack';
push @{ $stream->{ $next_seqnum }{data}{req} }, $tcp->{data};
} elsif (exists $stream->{ $tcp->{acknum} }) {
# server -> client
my $next_seqnum = $tcp->{acknum};
$stream->{ $next_seqnum } = delete $stream->{ $tcp->{acknum} };
$stream->{ $next_seqnum }{state} = 'ack';
push @{ $stream->{ $next_seqnum }{data}{res} }, $tcp->{data};
} else {
debug("Got ack but no syn+ack packet");
}
} elsif ($tcp->{flags} & ACK && $tcp->{flags} & FIN) {
if (exists $stream->{ $tcp->{seqnum} }) {
my $next_seqnum = $tcp->{seqnum} + 1;
$stream->{ $next_seqnum } = delete $stream->{ $tcp->{seqnum} };
$stream->{ $next_seqnum }{state} = 'fin1';
} elsif (exists $stream->{ $tcp->{acknum} }) {
my $next_seqnum = $tcp->{acknum};
$stream->{ $next_seqnum }{state} = 'fin2';
} else {
debug("Got unknown FIN packet");
}
} elsif ($tcp->{flags} & ACK && length($tcp->{data}) == 0) {
if (exists $stream->{ $tcp->{seqnum} }) {
my $next_seqnum = $tcp->{seqnum};
$stream->{ $next_seqnum }{state} = 'closed';
$stream->{ $next_seqnum }{end} = [ $header->{tv_sec}, $header->{tv_usec} ];
} else {
; # no problem, multiple ack
}
}
}
__END__
### Cannot parse a pcap file generated by "-i any", so must specify correct interface name.
# tcpdump -i lo -s65535 tcp port 11211 -w memcached.pcap
(access to 127.0.0.1:11211)
$ parse-pcap.pl memcached.pcap
2014-02-24 14:11:58.451993
req:
set foo 0 0 3
FOO
res:
STORED
2014-02-24 14:11:59.732226
req:
get foo
res:
VALUE foo 0 3
FOO
END
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment