File Coverage

blib/lib/Net/Analysis/EventLoop.pm
Criterion Covered Total %
statement 25 27 92.5
branch n/a
condition n/a
subroutine 9 9 100.0
pod n/a
total 34 36 94.4


line stmt bran cond sub pod time code
1             package Net::Analysis::EventLoop;
2             # $Id: EventLoop.pm 131 2005-10-02 17:24:31Z abworrall $
3              
4 8     8   92608 use 5.008000;
  8         27  
  8         441  
5             our $VERSION = '0.01';
6 8     8   52 use strict;
  8         17  
  8         235  
7 8     8   39 use warnings;
  8         54  
  8         252  
8              
9 8     8   49 use Carp qw(carp croak confess);
  8         10  
  8         515  
10              
11 8     8   7218 use NetPacket::Ethernet qw(:ALL);
  8         11551  
  8         1715  
12 8     8   8046 use NetPacket::IP qw(:ALL);
  8         23875  
  8         1457  
13 8     8   7251 use NetPacket::TCP qw(:ALL);
  8         14278  
  8         1251  
14 8     8   6738 use NetPacket::UDP qw(:ALL);
  8         6838  
  8         939  
15 8     8   11248 use Net::Pcap;
  0            
  0            
16             use Params::Validate qw(:all);
17              
18             use Net::Analysis::Packet qw(:all);
19              
20             #### Public methods
21             #
22             # {{{ new
23              
24             sub new {
25             my ($class) = shift;
26             my ($self) = bless ({pkt_number => 0}, $class);
27              
28             my %h = validate (@_, {dispatcher => { can => 'emit_event' }});
29              
30             $self->{dispatcher} = $h{dispatcher};
31              
32             return $self;
33             }
34              
35             # }}}
36              
37             # {{{ loop_file
38              
39             sub loop_file {
40             my ($self) = shift;
41             my %h = validate (@_, { filename => { type => SCALAR },
42             no_setup_teardown => { type => SCALAR,
43             default => 0} });
44              
45             my ($np_err);
46             my ($pcap_t) = Net::Pcap::open_offline ($h{filename}, \$np_err);
47              
48             carp "event_loop('$h{filename}') failed: '$np_err'\n" if (defined $np_err);
49             $self->_event_loop ($pcap_t, $h{no_setup_teardown});
50             }
51              
52             # }}}
53             # {{{ loop_net
54              
55             sub loop_net {
56             my ($self) = shift;
57             my %h = validate (@_, { filter => { type => SCALAR } });
58              
59             # See 'man Net::Pcap' for more details on these settings.
60             my $promiscuity = 0;
61             my $snaplen = 10240; # Must be >1540, else we will miss bytes
62             my $timeout_ms = 0;
63             my $optimize_filter = 1;
64              
65             my ($np_err, $net, $mask, $filter_t);
66              
67             my $dev = Net::Pcap::lookupdev(\$np_err);
68             Net::Pcap::lookupnet ($dev, \$net, \$mask, \$np_err);
69              
70             my $pcap_t = Net::Pcap::open_live($dev, $snaplen, $promiscuity,
71             $timeout_ms, \$np_err);
72              
73             if (defined $np_err) {
74             carp "loop_net(filter=>'$h{filter}') failed: '$np_err'\n";
75             }
76              
77             if (Net::Pcap::compile ($pcap_t, \$filter_t, $h{filter},
78             $optimize_filter, $net) == -1)
79             {
80             carp "unable to compile filter string '$h{filter}'\n";
81             }
82              
83             Net::Pcap::setfilter ($pcap_t, $filter_t);
84             $self->_event_loop ($pcap_t);
85             }
86              
87             # }}}
88              
89             # {{{ summary
90              
91             sub summary {
92             my ($self) = @_;
93              
94             print "---{ parse summary }---\n";
95             foreach (sort {$self->{n_pkts}{$b} <=> $self->{n_pkts}{$a}} keys %{$self->{n_pkts}})
96             {
97             printf " %-40.40s: % 7d\n", $_, $self->{n_pkts}{$_};
98             }
99             }
100              
101             # }}}
102              
103              
104             #### Private helper methods
105             #
106              
107             # {{{ _netpacket_packet_to_our_packet
108              
109             sub _netpacket_packet_to_our_packet {
110             my ($self, $wire_pkt, $wire_hdrs) = @_;
111              
112             # We assume ethernet capture ...
113             my ($eth_obj) = NetPacket::Ethernet->decode ($wire_pkt);
114              
115             # A flexible OO dispatch scheme is probably where this is heading ...
116              
117             if ($eth_obj->{type} == ETH_TYPE_IP) {
118             my $ip_obj = NetPacket::IP->decode($eth_obj->{data});
119              
120             if($ip_obj->{proto} == IP_PROTO_TCP) {
121             # Some ethernet frames come with padding; this confuses NetPacket,
122             # so strip it off here before parsing the IP payload as a TCP
123             # packet.
124             my $ip_data_len = $ip_obj->{len} - $ip_obj->{hlen} * 4;
125             if ($ip_data_len < length($ip_obj->{data})) {
126             substr ($ip_obj->{data}, $ip_data_len) = '';
127             }
128              
129             my $tcp_obj = NetPacket::TCP->decode ($ip_obj->{data});
130             #$self->{n_pkts}{"tcp_ok"}++;
131             # $ip_obj has the IP addresses
132             # $tcp_obj has the ports & TCP info, and the payload in {data}
133              
134             # Create a 'vendor-neutral' packet, in case we leave NetPacket
135             my $pkt = ["$ip_obj->{dest_ip}:$tcp_obj->{dest_port}",
136             "$ip_obj->{src_ip}:$tcp_obj->{src_port}",
137             $tcp_obj->{flags},
138             $tcp_obj->{data},
139             $tcp_obj->{seqnum},
140             $tcp_obj->{acknum},
141             $self->{pkt_number}++,
142             # These are turned into the object $pkt->{time}
143             $wire_hdrs->{tv_sec},
144             $wire_hdrs->{tv_usec},
145             ];
146             pkt_init($pkt);
147              
148             return $pkt;
149              
150             =pod
151              
152             return Net::Analysis::Packet->new
153             ({to => "$ip_obj->{dest_ip}:$tcp_obj->{dest_port}",
154             from => "$ip_obj->{src_ip}:$tcp_obj->{src_port}",
155             flags => $tcp_obj->{flags},
156             data => $tcp_obj->{data},
157             seqnum => $tcp_obj->{seqnum},
158             acknum => $tcp_obj->{acknum},
159             pkt_number => $self->{pkt_number}++,
160              
161             # These are turned into the object $pkt->{time}
162             tv_sec => $wire_hdrs->{tv_sec},
163             tv_usec => $wire_hdrs->{tv_usec},
164             } );
165              
166             =cut
167              
168             #} elsif ($ip_obj->{proto} == IP_PROTO_UDP) {
169             # We should handle these at some point ...
170             #$self->{n_pkts}{"SKIP_ip_proto_UDP"}++;
171             #} else {
172             #$self->{n_pkts}{"SKIP_ip_proto_$ip_obj->{proto}"}++;
173             }
174              
175             #} else {
176             # ARP ? AppleTalk ? SNMP ? IPv6 ? PPP ? Whatever, skip it
177             #$self->{n_pkts}{"SKIP_eth_pkt_type_$eth_obj->{type}"}++;
178             }
179              
180             return undef;
181             }
182              
183             # }}}
184             # {{{ _event_loop
185              
186             sub _event_loop {
187             my ($self, $pcap_t, $no_setup_teardown) = @_;
188              
189             unless ($no_setup_teardown) {
190             $self->{dispatcher}->emit_event (name => 'setup');
191             }
192              
193             while (1) {
194             my (%hdr);
195             my ($np_pkt) = Net::Pcap::next($pcap_t, \%hdr);
196             last if (!defined $np_pkt);
197              
198             if ($hdr{len} != $hdr{caplen}) {
199             warn "incomplete packet - use tcpdump with option '-S 2048'\n";
200             next;
201             }
202              
203             my $our_pkt = $self->_netpacket_packet_to_our_packet ($np_pkt, \%hdr);
204              
205             next if (!defined $our_pkt);
206              
207             # This will need re-jigging when we handle more than just TCP
208             $self->{dispatcher}->emit_event (name => '_internal_tcp_packet',
209             args => {pkt => $our_pkt});
210             }
211              
212             unless ($no_setup_teardown) {
213             $self->{dispatcher}->emit_event (name => 'teardown');
214             }
215             }
216              
217             # }}}
218              
219              
220             1;
221             __END__