1package App::Netdisco::SSHCollector::Platform::FreeBSD;
2
3=head1 NAME
4
5App::Netdisco::SSHCollector::Platform::FreeBSD
6
7=head1 DESCRIPTION
8
9Collect ARP entries from FreeBSD routers.
10
11This collector uses "C<arp>" as the command for the arp utility on your
12system.  If you wish to specify an absolute path, then add an C<arp_command>
13item to your configuration:
14
15 device_auth:
16   - tag: sshfreebsd
17     driver: cli
18     platform: FreeBSD
19     only: '192.0.2.1'
20     username: oliver
21     password: letmein
22     arp_command: '/usr/sbin/arp'
23
24=cut
25
26use strict;
27use warnings;
28
29use Dancer ':script';
30use Expect;
31use Moo;
32
33=head1 PUBLIC METHODS
34
35=over 4
36
37=item B<arpnip($host, $ssh)>
38
39Retrieve ARP entries from device. C<$host> is the hostname or IP address
40of the device. C<$ssh> is a Net::OpenSSH connection to the device.
41
42Returns a list of hashrefs in the format C<{ mac =E<gt> MACADDR, ip =E<gt> IPADDR }>.
43
44=back
45
46=cut
47
48sub arpnip {
49    my ($self, $hostlabel, $ssh, $args) = @_;
50
51    debug "$hostlabel $$ arpnip()";
52
53    my ($pty, $pid) = $ssh->open2pty;
54    unless ($pty) {
55        debug "unable to run remote command [$hostlabel] " . $ssh->error;
56        return ();
57    }
58    my $expect = Expect->init($pty);
59
60    my ($pos, $error, $match, $before, $after);
61    my $prompt = qr/\$/;
62
63    ($pos, $error, $match, $before, $after) = $expect->expect(10, -re, $prompt);
64
65    my $command = ($args->{arp_command} || 'arp');
66    $expect->send("$command -n -a\n");
67    ($pos, $error, $match, $before, $after) = $expect->expect(5, -re, $prompt);
68
69    my @arpentries = ();
70    my @lines = split(m/\n/, $before);
71
72    # ? (192.0.2.1) at fe:ed:de:ad:be:ef on igb0_vlan2 expires in 658 seconds [vlan]
73    my $linereg = qr/\s+\((\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\)\s+at\s+([a-fA-F0-9:]{17})\s+on/;
74
75    foreach my $line (@lines) {
76        if ($line =~ $linereg) {
77            my ($ip, $mac) = ($1, $2);
78            push @arpentries, { mac => $mac, ip => $ip };
79        }
80    }
81
82    $expect->send("exit\n");
83    $expect->soft_close();
84
85    return @arpentries;
86}
87
881;
89