1 #!/usr/bin/perl
2 package POE::Component::ArpWatch;
4 use strict;
5 use warnings;
7 BEGIN{
8 eval('use POE');
9 eval('use POE::Component::Pcap');
10 eval('use NetPacket::Ethernet qw( :types )');
11 eval('use NetPacket::ARP qw( :opcodes )');
12 }
14 END{
15 }
17 ## Map arp opcode #'s to strings
18 my %arp_opcodes = (
19 NetPacket::ARP::ARP_OPCODE_REQUEST, 'ARP Request',
20 NetPacket::ARP::ARP_OPCODE_REPLY, 'ARP Reply',
21 NetPacket::ARP::RARP_OPCODE_REQUEST, 'RARP Request',
22 NetPacket::ARP::RARP_OPCODE_REPLY, 'RARP Reply',
23 );
25 ##
26 ## POE::Component::ArpWatch->spawn(
27 ## [ Alias => 'arp_watch' ],
28 ## [ Device => 'eth0' ],
29 ## [ Dispatch => dispatch_state ],
30 ## [ Session => dispatch_session ],
31 ## )
32 ##
33 sub spawn {
34 my $class = shift;
35 my %args = @_;
37 $args{ Alias } ||= 'arp_watch';
39 POE::Session->create(
40 inline_states => {
41 _start => \&_start,
42 # _signal => \&_signal,
43 _stop => \&_stop,,
44 _dispatch => \&_dispatch,
45 set_dispatch => \&set_dispatch,
46 run => \&run,
47 pause => \&pause,
48 shutdown => \&shutdown,
49 },
50 args => [
51 $args{ Alias }, # ARG0
52 $args{ Device }, # ARG1
53 $args{ Dispatch },# ARG2
54 $args{ Session }, # ARG3
55 ],
56 );
58 return $args{ Alias };
59 }
61 sub _start {
62 my ($kernel, $heap, $session,
63 $alias, $device, $target_state, $target_session )
64 = @_[ KERNEL, HEAP, SESSION, ARG0..ARG3 ];
66 POE::Component::Pcap->spawn(
67 Alias => $alias . '_pcap',
68 Device => $device,
69 Filter => 'arp',
70 Dispatch => '_dispatch',
71 Session => $session,
72 );
74 $heap->{'pcap_session'} = $kernel->alias_resolve( $alias . '_pcap' );
76 ## Set alias for ourselves and remember it
77 $kernel->alias_set( $alias );
78 $heap->{Alias} = $alias;
80 ## Set dispatch target session and state if it was given
81 if( defined( $target_session ) ) {
82 $heap->{'target_session'} = $target_session;
83 $heap->{'target_state'} = $target_state;
84 }
85 }
87 sub set_dispatch {
88 my( $heap, $sender, $target ) = @_[ HEAP, SENDER, ARG0 ];
90 if( defined $target ) {
91 ## Remember whome to forward results to
92 $heap->{'target_session'} = $sender;
93 $heap->{'target_state'} = $target;
94 } else {
95 ## Clear target
96 delete $heap->{'target_session'};
97 delete $heap->{'target_state'};
98 }
99 }
101 sub run {
102 $_[KERNEL]->post( $_[HEAP]->{'pcap_session'} => 'run' );
103 }
105 sub pause {
106 $_[KERNEL]->post( $_[HEAP]->{'pcap_session'} => 'pause' );
107 }
109 sub _dispatch {
110 my( $kernel, $heap, $packets ) =
111 @_[ KERNEL, HEAP, ARG0 ];
113 if( exists $heap->{'target_session'} ) {
114 $kernel->post( $heap->{'target_session'},
115 $heap->{'target_state'},
116 _process_packet( @{ $_ } ) ) foreach( @{$packets} );
117 }
118 }
120 sub _signal {
121 # print "Got signal ", $_[ARG0], "\n";
123 $_[KERNEL]->post( pcap => 'shutdown' );
125 return 1
126 }
128 sub shutdown {
129 my ( $kernel, $heap, $session, $sender )
130 = @_[ KERNEL, HEAP, SESSION, SENDER ];
131 my $alias = $heap->{Alias};
133 # print "In shutdown for sid ", $session->ID, ", alias $alias\n";
134 # print "shutdown by ", $sender->ID, "\n";
136 $kernel->post( $heap->{'pcap_session'} => 'shutdown' );
138 $kernel->alias_remove( $alias );
140 # print "Out shutdown for sid ", $session->ID, ", alias $alias\n";
141 }
143 sub _stop {
144 my ( $kernel, $heap, $session ) = @_[ KERNEL, HEAP, SESSION ];
145 my $alias = $heap->{Alias};
147 # print "In state_stop for sid ", $session->ID, ", alias $alias\n";
149 # print "Out state_stop for sid ", $session->ID, ", alias $alias\n";
150 }
152 sub _process_packet {
153 my( $hdr, $pkt ) = @_;
155 my $arp =
156 NetPacket::ARP->decode( NetPacket::Ethernet->decode($pkt)->{data} );
158 ## Return hashref with apropriate fields
159 return {
160 type => $arp_opcodes{ $arp->{opcode} },
161 tv_sec => $hdr->{tv_sec},
162 tv_usec => $hdr->{tv_usec},
163 source_haddr => _phys( $arp->{sha} ),
164 source_ipaddr => _ipaddr( $arp->{spa} ),
165 target_haddr => _phys( $arp->{tha} ),
166 target_ipaddr => _ipaddr( $arp->{tpa} ),
167 }
168 }
170 ## Pretty printing subs for addresses
171 sub _ipaddr { join( ".", unpack( "C4", pack( "N", oct( "0x". shift ) ) ) ) }
172 sub _phys { join( ":", grep {length} split( /(..)/, shift ) ) }
174 # vim:ts=4:shiftwidth:expandtab
175 1;
177 __END__