1 #!/usr/bin/perl
2 package POE::Component::ArpWatch;
4 use strict;
5 use warnings;
6 use utf8;
8 BEGIN{
9 eval('use POE');
10 eval('use POE::Component::Pcap');
11 eval('use NetPacket::Ethernet qw( :types )');
12 eval('use NetPacket::ARP qw( :opcodes )');
13 }
15 END{
16 }
18 ## Map arp opcode #'s to strings
19 my %arp_opcodes = (
20 NetPacket::ARP::ARP_OPCODE_REQUEST, 'ARP Request',
21 NetPacket::ARP::ARP_OPCODE_REPLY, 'ARP Reply',
22 NetPacket::ARP::RARP_OPCODE_REQUEST, 'RARP Request',
23 NetPacket::ARP::RARP_OPCODE_REPLY, 'RARP Reply',
24 );
26 ##
27 ## POE::Component::ArpWatch->spawn(
28 ## [ Alias => 'arp_watch' ],
29 ## [ Device => 'eth0' ],
30 ## [ Dispatch => dispatch_state ],
31 ## [ Session => dispatch_session ],
32 ## )
33 ##
34 sub spawn {
35 my $class = shift;
36 my %args = @_;
38 $args{ Alias } ||= 'arp_watch';
40 POE::Session->create(
41 inline_states => {
42 _start => \&_start,
43 # _signal => \&_signal,
44 _stop => \&_stop,,
45 _dispatch => \&_dispatch,
46 set_dispatch => \&set_dispatch,
47 run => \&run,
48 pause => \&pause,
49 shutdown => \&shutdown,
50 },
51 args => [
52 $args{ Alias }, # ARG0
53 $args{ Device }, # ARG1
54 $args{ Dispatch },# ARG2
55 $args{ Session }, # ARG3
56 ],
57 );
59 return $args{ Alias };
60 }
62 sub _start {
63 my ($kernel, $heap, $session,
64 $alias, $device, $target_state, $target_session )
65 = @_[ KERNEL, HEAP, SESSION, ARG0..ARG3 ];
67 POE::Component::Pcap->spawn(
68 Alias => $alias . '_pcap',
69 Device => $device,
70 Filter => 'arp',
71 Dispatch => '_dispatch',
72 Session => $session,
73 );
75 $heap->{'pcap_session'} = $kernel->alias_resolve( $alias . '_pcap' );
77 ## Set alias for ourselves and remember it
78 $kernel->alias_set( $alias );
79 $heap->{Alias} = $alias;
81 ## Set dispatch target session and state if it was given
82 if( defined( $target_session ) ) {
83 $heap->{'target_session'} = $target_session;
84 $heap->{'target_state'} = $target_state;
85 }
86 }
88 sub set_dispatch {
89 my( $heap, $sender, $target ) = @_[ HEAP, SENDER, ARG0 ];
91 if( defined $target ) {
92 ## Remember whome to forward results to
93 $heap->{'target_session'} = $sender;
94 $heap->{'target_state'} = $target;
95 } else {
96 ## Clear target
97 delete $heap->{'target_session'};
98 delete $heap->{'target_state'};
99 }
100 }
102 sub run {
103 $_[KERNEL]->post( $_[HEAP]->{'pcap_session'} => 'run' );
104 }
106 sub pause {
107 $_[KERNEL]->post( $_[HEAP]->{'pcap_session'} => 'pause' );
108 }
110 sub _dispatch {
111 my( $kernel, $heap, $packets ) =
112 @_[ KERNEL, HEAP, ARG0 ];
114 if( exists $heap->{'target_session'} ) {
115 $kernel->post( $heap->{'target_session'},
116 $heap->{'target_state'},
117 _process_packet( @{ $_ } ) ) foreach( @{$packets} );
118 }
119 }
121 sub _signal {
122 # print "Got signal ", $_[ARG0], "\n";
124 $_[KERNEL]->post( pcap => 'shutdown' );
126 return 1
127 }
129 sub shutdown {
130 my ( $kernel, $heap, $session, $sender )
131 = @_[ KERNEL, HEAP, SESSION, SENDER ];
132 my $alias = $heap->{Alias};
134 # print "In shutdown for sid ", $session->ID, ", alias $alias\n";
135 # print "shutdown by ", $sender->ID, "\n";
137 $kernel->post( $heap->{'pcap_session'} => 'shutdown' );
139 $kernel->alias_remove( $alias );
141 # print "Out shutdown for sid ", $session->ID, ", alias $alias\n";
142 }
144 sub _stop {
145 my ( $kernel, $heap, $session ) = @_[ KERNEL, HEAP, SESSION ];
146 my $alias = $heap->{Alias};
148 # print "In state_stop for sid ", $session->ID, ", alias $alias\n";
150 # print "Out state_stop for sid ", $session->ID, ", alias $alias\n";
151 }
153 sub _process_packet {
154 my( $hdr, $pkt ) = @_;
156 my $arp =
157 NetPacket::ARP->decode( NetPacket::Ethernet->decode($pkt)->{data} );
159 ## Return hashref with apropriate fields
160 return {
161 type => $arp_opcodes{ $arp->{opcode} },
162 tv_sec => $hdr->{tv_sec},
163 tv_usec => $hdr->{tv_usec},
164 source_haddr => _phys( $arp->{sha} ),
165 source_ipaddr => _ipaddr( $arp->{spa} ),
166 target_haddr => _phys( $arp->{tha} ),
167 target_ipaddr => _ipaddr( $arp->{tpa} ),
168 }
169 }
171 ## Pretty printing subs for addresses
172 sub _ipaddr { join( ".", unpack( "C4", pack( "N", oct( "0x". shift ) ) ) ) }
173 sub _phys { join( ":", grep {length} split( /(..)/, shift ) ) }
175 1;
177 __END__