6 # ha:2345:respawn:/usr/local/sbin/harpy >>/var/log/harpy.log eth1 eth2
8 # ha:2345:respawn:/usr/local/sbin/harpy >>/var/log/harpy.log eth_intra
12 sub __KERNEL__ { 1; } # for "linux/socket.ph"
16 require "linux/if_ether.ph";
17 require "linux/socket.ph";
18 require "linux/sockios.ph";
19 require "linux/if_arp.ph";
21 sub SOCKADDR_SIZEOF { 16; }
33 my $a=unpack("H*",$n);
51 socket $sock,AF_PACKET(),SOCK_RAW(),ETH_P_ARP() or die "No ARP socket: $!";
53 local *ioctl_ifhwaddr=sub($)
57 my $buf=$ifname.("\x00"x0x1000);
58 ioctl $sock,SIOCGIFHWADDR(),$buf or die "ioctl($ifname,SIOCGIFHWADDR): $!";
59 my($trash,$sockaddr)=unpack("a16a16",$buf);
60 my($sa_len,$sa_data)=unpack("a2a6",$sockaddr);
64 local *ioctl_ifindex=sub($)
68 my $buf=$ifname.("\x00"x0x1000);
69 ioctl $sock,SIOCGIFINDEX(),$buf or die "ioctl($ifname,SIOCGIFINDEX): $!";
70 my($trash,$ifindex)=unpack("a16i",$buf);
74 my $hw=ioctl_ifhwaddr($ifname);
75 my $sockaddr_ll=pack "SniSCCa8", # struct sockaddr_ll:
76 AF_PACKET(), # unsigned short int sll_family;
77 ETH_P_ARP(), # unsigned short int sll_protocol;
78 ioctl_ifindex($ifname), # int sll_ifindex;
79 ARPHRD_ETHER(), # unsigned short int sll_hatype;
80 PACKET_BROADCAST(), # unsigned char sll_pkttype;
81 ETH_ALEN(), # unsigned char sll_halen;
82 $hw; # unsigned char sll_addr[8];
84 bind $sock,$sockaddr_ll or die "bind($ifname): $!";
86 return $sock,hw_ntoa($hw);
89 #struct ether_arp_frame {
90 # struct ether_header {
91 # u_int8_t ether_dhost[ETH_ALEN]; /* destination eth addr */
92 # u_int8_t ether_shost[ETH_ALEN]; /* source ether addr */
93 # u_int16_t ether_type; /* packet type ID field */
96 # struct arphdr { /* fixed-size header */
97 # unsigned short int ar_hrd; /* Format of hardware address. */
98 # unsigned short int ar_pro; /* Format of protocol address. */
99 # unsigned char ar_hln; /* Length of hardware address. */
100 # unsigned char ar_pln; /* Length of protocol address. */
101 # unsigned short int ar_op; /* ARP opcode (command). */
103 # u_int8_t arp_sha[ETH_ALEN]; /* sender hardware address */
104 # u_int8_t arp_spa[4]; /* sender protocol address */
105 # u_int8_t arp_tha[ETH_ALEN]; /* target hardware address */
106 # u_int8_t arp_tpa[4]; /* target protocol address */
112 my($dst_hw,$src_hw,$dst,$src)=@_;
114 my $msg=pack "a6a6nnnCCna6a4a6a4",
115 hw_aton($dst_hw), # $ether_dhost
116 hw_aton($src_hw), # $ether_shost
122 ARPOP_REPLY(), # $ar_op
123 hw_aton($src_hw), # $arp_sha
124 inet_aton($src), # $arp_spa
125 hw_aton($dst_hw), # $arp_tha
126 inet_aton($dst); # $arp_tpa
134 return if 42>length $msg;
148 )=unpack "a6a6nnnCCna6a4a6a4",$msg;
149 $V>=3 and print Data::Dumper->Dump([
176 return if $ar_op!=ARPOP_REQUEST();
177 return if $ar_hln!=6;
178 return if $ar_pln!=4;
179 my $tell=inet_ntoa $arp_spa;
180 my $who_has=inet_ntoa $arp_tpa;
181 my $tell_hw=hw_ntoa $ether_shost;
182 return $tell,$who_has,$tell_hw;
189 my $ifname=shift @ifnames;
190 if ($ifname=~/{(\d+)-(\d+)}/) {
191 push @ifnames,$`.$_.$' for $1..$2;
194 my($sock,$hw)=sock($ifname);
201 $V and print localtime()." START\n";
204 vec($rfds,fileno($_->{"sock"}),1)=1 for values(%socks);
205 my $got=select $rfds,undef(),undef(),undef();
206 die "Invalid select(2): ".Dumper($got) if !defined $got || $got<0;
208 while (my($ifname,$hash)=each(%socks)) {
209 next if !vec($rfds,fileno($hash->{"sock"}),1);
210 $V>=2 and print localtime()." got packet: $ifname\n";
212 defined(my $from_addr=recv $hash->{"sock"},$msg,0x1000,0) or die "recv($ifname): $!";
213 next if !(my($tell,$who_has,$tell_hw)=arp_unpack($msg));
214 $V and print localtime()." got: tell=$tell,who_has=$who_has,tell_hw=$tell_hw\n";
215 next if $tell eq $who_has; # do not reply to self-detection queries
216 my $msg_reply=arp_pack($tell_hw,$hash->{"hw"},$tell,$who_has);
217 send $hash->{"sock"},$msg_reply,0,$from_addr or die "send($ifname): $!";