File Coverage

blib/lib/RAS/AS5200.pm
Criterion Covered Total %
statement 15 157 9.5
branch 1 56 1.7
condition 1 15 6.6
subroutine 4 11 36.3
pod 8 8 100.0
total 29 247 11.7


line stmt bran cond sub pod time code
1             ### AS5200.pm
2             ### PERL module for talking to a Cisco AS5200 access router
3             #########################################################
4              
5             package RAS::AS5200;
6             $VERSION = "1.04";
7              
8 1     1   675 use strict "subs"; use strict "refs";
  1     1   2  
  1         36  
  1         6  
  1         2  
  1         27  
9              
10             # This uses Net::Telnet to connect to the RAS
11 1     1   1492 use Net::Telnet ;
  1         57635  
  1         3353  
12              
13             # The name $ras will be used consistently as the
14             # reference to the RAS::AS5200 object we're handling
15              
16             # The constructor method, of course
17             sub new {
18 1     1 1 64 my($class) = shift ;
19 1         2 my($ras) = {} ;
20 1         7 %$ras = @_ ;
21              
22 1 50 33     4 unless ($ras->{hostname}) { warn "ERROR: ", (ref($class) || $class), " - Hostname not specified.\n"; return(); }
  1         72  
  1         6  
23 0           $ras->{'VERSION'} = $VERSION;
24 0 0         $ras->{prompt} = '\w*[>#]' unless $ras->{prompt};
25              
26 0   0       bless($ras, ref($class) || $class);
27             }
28              
29              
30             # for debugging - printenv() prints to STDERR
31             # the entire contents of %$ras
32             sub printenv {
33 0     0 1   my($ras) = shift;
34 0           while (($key,$value) = each(%$ras)) { warn "$key = $value\n"; }
  0            
35 0           return();
36             }
37              
38              
39             # run_command() is the heart of this module's functionality
40             # This runs the specified commands on the router and returns
41             # a list of refs to arrays containing the commands' output
42             sub run_command {
43 0     0 1   my($ras) = shift;
44 0           my(@returnlist);
45 0           my($prompt) = '/' . $ras->{prompt} . '$/';
46              
47 0           while ($command = shift) {
48 0           my($session) = new Net::Telnet;
49 0           $session->errmode("return");
50 0           $session->open($ras->{hostname});
51 0 0         if ($session->errmsg) {
52 0           warn "ERROR: ",ref($ras)," - Cannot connect to host $ras->{hostname} - ",$session->errmsg,"\n"; return(); }
  0            
53              
54             # If a login name is supplied, wait for the Login: prompt.
55             # Otherwise, send a blank password.
56             # This is a workaround for 2 changes in early versions of IOS 11
57             # 1. IOS 11.3 asks for login and password, 11.2 asks only for password
58             # 2. IOS 11.2 - the first password prompt fails to authenticate about
59             # 1/4 of the time, so it's best to send a blank password for the
60             # first password prompt
61 0 0         if ($ras->{login}) {
62 0           $session->waitfor('/Login: $/i');
63 0 0         if ($session->errmsg) {
64 0           warn "ERROR: ",ref($ras)," - Waiting for login prompt on host $ras->{hostname} - ",$session->errmsg,"\n"; return(); }
  0            
65 0           $session->print($ras->{login});
66             }
67             else {
68 0           $session->waitfor('/Password: $/i');
69 0 0         if ($session->errmsg) {
70 0           warn "ERROR: ",ref($ras)," - Waiting for password prompt on host $ras->{hostname} - ",$session->errmsg,"\n"; return(); }
  0            
71 0           $session->print("");
72             }
73 0           $session->waitfor('/Password: $/i');
74 0           $session->print($ras->{password});
75 0 0         if ($session->errmsg) {
76 0           warn "ERROR: ",ref($ras)," - Waiting for password prompt on host $ras->{hostname} - ",$session->errmsg,"\n"; return(); }
  0            
77              
78             # Okay, we're logged in. Get the command prompt.
79 0           $session->waitfor($prompt);
80 0 0         if ($session->errmsg) {
81 0           warn "ERROR: ",ref($ras)," - Waiting for command prompt on host $ras->{hostname} - ",$session->errmsg,"\n"; return(); }
  0            
82 0           my(@output);
83              
84             # If the command was prefixed with "ENABLE " then go into enable mode first.
85 0 0         if ($command =~ s/^ENABLE //) {
86 0           $session->print("enable");
87 0           $session->waitfor('/Password: $/');
88 0           $session->print($ras->{enablepassword});
89 0 0         if ($session->errmsg) {
90 0           warn "ERROR: ",ref($ras)," - Waiting for enable password prompt on host $ras->{hostname} - ",$session->errmsg,"\n"; return(); }
  0            
91 0           $session->waitfor($prompt);
92 0 0         if ($session->errmsg) {
93 0           warn "ERROR: ",ref($ras)," - Waiting for post-enable command prompt on host $ras->{hostname} - ",$session->errmsg,"\n"; return(); }
  0            
94             }
95              
96             # Send the command and keep paging down and grab the output
97 0           $session->print($command);
98 0           while (1) {
99 0           $session->print(""); my($line) = $session->getline;
  0            
100 0 0         if ($session->errmsg) {
101 0           warn "ERROR: ",ref($ras)," - Waiting on output from command \"$command\" on host $ras->{hostname} - ",$session->errmsg,"\n"; return(); }
  0            
102 0 0         if ($line eq "[confirm]") { $session->print("y"); next; }
  0            
  0            
103 0 0         if ($line =~ /^$ras->{prompt}$/) { $session->print("exit"); $session->close; last; }
  0            
  0            
  0            
104 0           $line =~ s/^\s?--More--\s*\010+\s+\010+//;
105 0           push(@output, $line);
106             }
107              
108 0           shift(@output); # Trim the echoed command
109 0           push(@returnlist, \@output);
110             } # end of shifting commands
111              
112             # We're returning a list of references to lists.
113             # Each ref points to an array containing the returned text
114             # from the command, and the list of refs corresponds
115             # to the list of commands we were given
116 0           return(@returnlist);
117             } # end of run_command
118              
119              
120             # usergrep() - takes a username and returns an array of
121             # ports on which the user was found
122             sub usergrep {
123 0     0 1   my($ras) = shift;
124 0 0         my($username) = shift; return() unless $username;
  0            
125 0 0         if ($ras->{truncateusernames}) { $username = substr($username,0,10); }
  0            
126 0           my($output) = $ras->run_command('show users');
127 0           my(@ports);
128              
129 0           foreach (@$output) {
130 0           my($port,$user);
131 0 0 0       next unless (/^\s+\d+ tty \d+\s/ || /^\s+Se\d+\:\d+\s/);
132 0           $port = unpack("x0 a12", $_) ; $port =~ s/^\s*\d* //; $port =~ s/\s*$//;
  0            
  0            
133 0           $user = unpack("x13 a10", $_); $user =~ s/^\s*//; $user =~ s/\s*$//;
  0            
  0            
134 0 0         ($user eq $username) && push(@ports,$port);
135             }
136 0           return(@ports);
137             }
138              
139              
140             # userports() returns a hash of arrays
141             # keys are the usernames of all users currently connected
142             # values are arrays of ports that that user in connected on
143             sub userports {
144 0     0 1   my($ras) = shift;
145 0           my($output) = $ras->run_command('show users');
146 0           my(%userports);
147              
148 0           foreach (@$output) {
149 0           my($port,$user);
150 0 0 0       next unless (/^\s+\d+ tty \d+\s/ || /^\s+Se\d+\:\d+\s/);
151 0           $port = unpack("x0 a12", $_) ; $port =~ s/^\s*\d* //; $port =~ s/\s*$//;
  0            
  0            
152 0           $user = unpack("x13 a10", $_); $user =~ s/^\s*//; $user =~ s/\s*$//;
  0            
  0            
153 0           push(@{$userports{$user}},$port);
  0            
154             }
155 0           return(%userports);
156             }
157              
158              
159             # portusage() returns an array: 1st element is the # of ports and the
160             # rest of the elements are usernames of users currently logged on
161             sub portusage {
162 0     0 1   my($ras) = shift;
163 0           my($interfaces,$connections) = $ras->run_command('sho isdn status','show users');
164 0           my(@users, $totalports);
165              
166 0           $totalports = 23 * scalar(grep(/^ISDN Serial\S+ interface$/, @$interfaces));
167              
168 0           foreach (@$connections) {
169 0           my($port,$user);
170 0 0 0       next unless (/^\s+\d+ tty \d+ / || /^\s+Se\d+\:\d+ /);
171 0           $user = unpack("x13 a10", $_); $user =~ s/^\s*(\S+)\s*$/$1/;
  0            
172 0 0         next if ($user =~ /^\s*$/);
173 0           push(@users,$user);
174             }
175              
176 0           return($totalports,@users);
177             }
178              
179              
180             # userkill() does a usergrep() and then disconnects the specified user
181             sub userkill {
182 0     0 1   my($ras) = shift;
183 0 0         my($username); $username = shift; return() unless $username;
  0            
  0            
184 0 0         if ($ras->{truncateusernames}) { $username = substr($username,0,10); }
  0            
185 0           my(@ports) = $ras->usergrep($username);
186 0 0         return('') unless @ports;
187              
188 0           my(@killcommands);
189 0           foreach (@ports) {
190 0 0         if (/^tty/) { push(@killcommands, "ENABLE clear line $_"); }
  0 0          
191 0           elsif (/^Se/) { push(@killcommands, "ENABLE clear int $_"); }
192             }
193              
194 0           $ras->run_command(@killcommands);
195 0           return(@ports);
196             }
197              
198              
199             # killexcessoutoctets() takes a bytelimit and if a interfaces
200             # outoctets/bytes exceeds that limit the interface is cleared and then the
201             # counters for the interface are reset. Used to stop radius counters
202             # wrapping ie. 32 bit signed int wraps into negative at about 2 gig
203             sub killexcessoutoctets {
204 0     0 1   my($ras) = shift;
205 0           my($bytelimit) = shift;
206 0           my %userports = $ras->userports;
207              
208 0           foreach $user (keys %userports) {
209 0           foreach $arraycnt (0 .. (@{$userports{$user}} - 1)) {
  0            
210 0           my($result) = $ras->run_command('show interface ' . $userports{$user}[$arraycnt]);
211 0           foreach (@$result) {
212 0 0         next unless (/\s+\d+ packets output, (\d+) bytes.+/);
213 0           my $outoctets = $1;
214 0 0         if ($outoctets > $bytelimit) {
215 0           $ras->run_command('ENABLE clear int ' . $userports{$user}[$arraycnt] , 'ENABLE clear counter ' . $userports{$user}[$arraycnt]);
216 0           next;
217             }
218             }
219             }
220             }
221 0           return(1);
222             }
223              
224             #############################################################
225              
226             1;#So PERL knows we're cool
227              
228             __END__;