1package App::Netdisco::SSHCollector::Platform::ASA;
2
3=head1 NAME
4
5App::Netdisco::SSHCollector::Platform::ASA
6
7=head1 DESCRIPTION
8
9Collect IPv4 ARP and IPv6 neighbor entries from Cisco ASA devices.
10
11You will need the following configuration for the user to automatically enter
12C<enable> status after login:
13
14 aaa authorization exec LOCAL auto-enable
15
16To use an C<enable> password separate from the login password, add an
17C<enable_password> under C<device_auth> tag in your configuration file:
18
19 device_auth:
20   - tag: sshasa
21     driver: cli
22     platform: ASA
23     only: '192.0.2.1'
24     username: oliver
25     password: letmein
26     enable_password: myenablepass
27
28=cut
29
30use strict;
31use warnings;
32
33use Dancer ':script';
34use Expect;
35use Moo;
36
37=head1 PUBLIC METHODS
38
39=over 4
40
41=item B<arpnip($host, $ssh)>
42
43Retrieve ARP and neighbor entries from device. C<$host> is the hostname or IP
44address of the device. C<$ssh> is a Net::OpenSSH connection to the device.
45
46Returns a list of hashrefs in the format C<{ mac =E<gt> MACADDR, ip =E<gt> IPADDR }>.
47
48=back
49
50=cut
51
52sub arpnip {
53    my ($self, $hostlabel, $ssh, $args) = @_;
54
55    debug "$hostlabel $$ arpnip()";
56
57    my ($pty, $pid) = $ssh->open2pty;
58    unless ($pty) {
59        debug "unable to run remote command [$hostlabel] " . $ssh->error;
60        return ();
61    }
62    my $expect = Expect->init($pty);
63
64    my ($pos, $error, $match, $before, $after);
65    my $prompt;
66
67    if ($args->{enable_password}) {
68       $prompt = qr/>/;
69       ($pos, $error, $match, $before, $after) = $expect->expect(10, -re, $prompt);
70
71       $expect->send("enable\n");
72
73       $prompt = qr/Password:/;
74       ($pos, $error, $match, $before, $after) = $expect->expect(10, -re, $prompt);
75
76       $expect->send( $args->{enable_password} ."\n" );
77    }
78
79    $prompt = qr/#\s*$/;
80    ($pos, $error, $match, $before, $after) = $expect->expect(10, -re, $prompt);
81
82    $expect->send("terminal pager 2147483647\n");
83    ($pos, $error, $match, $before, $after) = $expect->expect(5, -re, $prompt);
84
85    $expect->send("show names\n");
86    ($pos, $error, $match, $before, $after) = $expect->expect(60, -re, $prompt);
87    my @names = split(m/\n/, $before);
88
89    $expect->send("show arp\n");
90    ($pos, $error, $match, $before, $after) = $expect->expect(60, -re, $prompt);
91    my @lines = split(m/\n/, $before);
92
93    my @arpentries = ();
94
95    # ifname 192.0.2.1 0011.2233.4455 123
96    my $linereg = qr/[A-z0-9\-\.]+\s([A-z0-9\-\.]+)\s
97                     ([0-9a-fA-F]{4}\.[0-9a-fA-F]{4}\.[0-9a-fA-F]{4})/x;
98
99    foreach my $line (@lines) {
100        if ($line =~ $linereg) {
101            my ($ip, $mac) = ($1, $2);
102            if ($ip !~ m/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/) {
103                foreach my $name (@names) {
104                    if ($name =~ qr/name\s([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})\s([\w-]*)/x) {
105                        if ($ip eq $2) {
106                            $ip = $1;
107                        }
108                    }
109                }
110            }
111            if ($ip =~ m/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/) {
112                push @arpentries, { mac => $mac, ip => $ip };
113            }
114        }
115    }
116
117    # start ipv6
118    $expect->send("show ipv6 neighbor\n");
119    ($pos, $error, $match, $before, $after) = $expect->expect(60, -re, $prompt);
120
121    @lines = split(m/\n/, $before);
122
123    # IPv6 age MAC state ifname
124    $linereg = qr/([0-9a-fA-F\:]+)\s+[0-9]+\s
125                     ([0-9a-fA-F]{4}\.[0-9a-fA-F]{4}\.[0-9a-fA-F]{4})/x;
126
127    foreach my $line (@lines) {
128        if ($line =~ $linereg) {
129            my ($ip, $mac) = ($1, $2);
130            push @arpentries, { mac => $mac, ip => $ip };
131        }
132    }
133    # end ipv6
134
135    $expect->send("exit\n");
136    $expect->soft_close();
137
138    return @arpentries;
139}
140
1411;
142