configuration, scripting, Uncategorized, wireless topics

Script to batch change authentication server on Cisco wireless controllers

Task description:

-there are more than 100 Cisco wireless controllers in a region

– it old authentication radius servers 10.10.0.100 & 10.10.0.101 are approaching end-of-support and should be replaced by  new one 10.10.0.73.

– on each of wireless controller there are can be one or two wifi SSIDs which are using mentioned old radius servers. In this script they will be called “Office1” & “Office2”. Under their settings script will replace old radius servers by new one. Regex are being used to identify under which IDs “Office1” & “Office2” are configured on each particular WLC

– for SSH into each device Perl module Net::SSH::Expect is used

– script output can be logged by Linux tee command to verify that all done correctly

– script reads IP addresses of each WLC from a file, which is given as a parameter to a script:

./this_script.pl file_with_WLCs_IPs.txt | tee result.log

Script itself may be not perfect but done in a short time and describes an approach which can be optimized and made more advanced:


#!/usr/bin/perl
#script to update radius auth servers on local WLCs

use Net::SSH::Expect;
my $filename = $ARGV[0];

my $username=”your_username”;
my $password=”your_password”;
if (open(my $fh, ‘<:encoding(UTF-8)’, $filename)) {
while ( <$fh> ) {
chomp;
@fields = split(‘ ‘, $_);

$host1 = $fields[0];
my $ssh = Net::SSH::Expect->new (
host => $host1,
raw_pty => 1
);
print (“Getting into WLC $host1…n”);
$ssh->run_ssh() or print “SSH process couldn’t start: $!”;
$ssh->waitfor(‘Are you sure you want to continue connecting (yes/no)?’, 10) or print “n key already imported n”;
$ssh->send(“yesn”);
$ssh->waitfor(‘User:’, 20) or die “prompt ‘User’ not found after 20 second”;
$ssh->send(” “);
$ssh->send(“$username”);
$ssh->waitfor(‘Password:’, 7) or print “prompt ‘Password’ not found after 7 second”;
$ssh->send(“$password”);
$ssh->waitfor(‘(Cisco Controller)’, 7) or print “prompt ‘Cisco Controller’ not found”;
print ($ssh->exec(“config paging disable”));

print (“Checking ‘Office1’ WLAN id numbern”);
$ssh_session =$ssh->exec(“show wlan summary”);
print ($$ssh_session);

#cutting line with office WLAN information:
($matching) = $ssh_session=~ /([^n]*Office1+)/is;
print (“n$matching”);

my $wlan_id1;
$matching =~ m/(d+)/g;
if ($matching ne “”) {

$wlan_id1=$1;
}
else {
$wlan_id1=0;
}
$ssh_session =$ssh->exec(“show wlan summary”);
print ($$ssh_session);
#cutting line with Office2 WLAN information
($matching) = $ssh_session=~ /([^n]*Office2+)/is;
print (“n$matching”);
$matching =~ m/(d+)/g;

my $wlan_id2;
if ($matching ne “”) {
$wlan_id2=$1;

}
else {
$wlan_id2=0;
}

print (“nn print Office1 wifi id= $wlan_id1, Office2 id=$wlan_id2 n”);
$ssh_session =$ssh->exec(“show radius summary”);
print ($$ssh_session);
($matching) = $ssh_session=~ /([^n]*10.10.0.100+)/is;
print (“n$matching”);
$matching =~ m/(d+)/g;
my $rad_id100;
if ($matching ne “”) {
$rad_id100=$1;
}
else {
$rad_id100=0
}
$ssh_session =$ssh->exec(“show radius summary”);
print ($$ssh_session);
($matching) = $ssh_session=~ /([^n]*10.10.0.101+)/is;
print (“n$matching”);
$matching =~ m/(d+)/g;
my $rad_id101;
if ($matching ne “”) {
$rad_id101=$1;
}
else {
$rad_id101=0;
}
$ssh_session =$ssh->exec(“show radius summary”);
print ($$ssh_session);
($matching) = $ssh_session=~ /([^n]*10.10.0.73+)/is;
print (“n$matching”);
$matching =~ m/(d+)/g;

my $rad_id73;
if ($matching ne “”) {
$rad_id73=$1;
}
else {
$rad_id73=0;
}
print (“n rad_id100 = $rad_id100, rad_id101 = $rad_id101, rad_id73 = $rad_id73 “);

print(“n disabling mentioned WLANs “);
print ($ssh->exec(“config wlan disable $wlan_id1”));
print ($ssh->exec(“config wlan disable $wlan_id2”));
print(“n deleting all auth radius servers under related WLANs “);
print ($ssh->exec(“config wlan radius_server auth delete $wlan_id1 all”));
print ($ssh->exec(“config wlan radius_server auth delete $wlan_id2 all”));

print(“n deleting all auth radius servers under radius settings on WLC “);
print ($ssh->exec(“config radius auth delete $rad_id100”));
print ($ssh->exec(“config radius auth delete $rad_id101”));
print ($ssh->exec(“config radius auth delete $rad_id73”));

# to check the case that auth radius servers have been configured with id equal to 1, otherwise throw warning in log and configure new radius with id=30
if (($rad_id100==1) || ($rad_id101==1) || ($rad_id73==1)) {
print(“n adding new auth radius server (with id=1) under radius settings on WLC n”);
print ($ssh->exec(“config radius auth add 1 10.10.0.73 1812 ascii inf2_acs_key”));
print ($ssh->exec(“config radius auth disable 1”));
print ($ssh->exec(“config radius auth rfc3576 enable 1”));
print ($ssh->exec(“config radius auth retransmit-timeout 1 5”));
print ($ssh->exec(“config radius auth management 1 disable”));
print ($ssh->exec(“config radius auth network 1 enable”));
print ($ssh->exec(“config radius auth enable 1”));
print ($ssh->exec(“config wlan radius_server auth add $wlan_id1 1”));
print ($ssh->exec(“config wlan radius_server auth add $wlan_id2 1”));
print ($ssh->exec(“config wlan enable $wlan_id1”));
print ($ssh->exec(“config wlan enable $wlan_id2”));

}

else {
#my $min_rad_id = min ($rad_id100, $rad_id101, $rad_id73);
print(“n ADDING NEW RADIUS SERVER (WITH ID=30) UNDER RADIUS SETTINGS ON WLC n”);
print ($ssh->exec(“config radius auth add 30 10.10.0.73 1812 ascii inf2_acs_key”));
print ($ssh->exec(“config radius auth disable 30”));
print ($ssh->exec(“config radius auth rfc3576 enable 30”));
print ($ssh->exec(“config radius auth retransmit-timeout 30 5”));
print ($ssh->exec(“config radius auth management 30 disable”));
print ($ssh->exec(“config radius auth network 30 enable”));
print ($ssh->exec(“config radius auth enable 30”));
print ($ssh->exec(“config wlan radius_server auth add $wlan_id1 30”));
print ($ssh->exec(“config wlan radius_server auth add $wlan_id2 30”));
print ($ssh->exec(“config wlan enable $wlan_id1”));
print ($ssh->exec(“config wlan enable $wlan_id2”));
}

print(“n saving configuration file to flash memory n”);
print ($ssh->exec(“save config”));
print ($ssh->exec(“y”));
}
} else {
warn “Could not open file ‘$filename’ $!”;
}

 

monitoring, Uncategorized, wireless topics

monitor amount of users on Cisco access points

At this stage of wifi technology development high density of clients and access points can be seen quite often. Now is a task to avoid too much clients per access point (once I saw a report with ~90 clients on one access point) to offer reliable wifi service.

Amount of users per access points in certain moments of time can be gained via Cisco prime (PI – prime infrastructure, as it called by vendor). In my case this system is monitored by other persons and when they will restore its functionality is unclear. For such case or if someone don’t want to invest in Cisco prime, Bash script and SQL can be used to monitor amount of users per access points during the time:

1) login with Expect/Perl/Python script to WLC and monitor all output

2) issue command “show ap summary” and following result will be achieved (some columns are deleted for brevity):

(Cisco Controller) >show ap summary

Number of APs……………………………… 101

Global AP User Name………………………… mgmt
Global AP Dot1x User Name…………………… Not Configured

AP Name          AP Model                  Location           IP Address         Clients
——————    ——————–            —————-         —————        ——————–
235AP           AIR-CAP2702E-Z-K9    Stores               10.10.47.235          1
234AP           AIR-CAP2702E-Z-K9    Gnd F               10.10.47.234           1
233AP           AIR-CAP2702E-Z-K9    KP                     10.10.47.233           1
7213AP         AIR-CAP2702E-Z-K9    Aisle 4 – H5     10.105.82.26           0
7208AP         AIR-CAP2702E-Z-K9    Above Bench  10.105.82.33          3

3) this output can be saved in file, which afterwards can be used by a Bash and Perl scripts to push such data into MySQL on regular basis with help of cron on Linux.

4) the result looks good:

mysql> select dt,tm, sum(users_amount) from APs_users GROUP BY dt,tm;
+——————+————-+———————————+
|        dt            |        tm      | sum(users_amount) |
+——————+————-+————- ——————-+
| 2018-12-28  | 09:05:31 |              21                    |
| 2018-12-29  | 07:05:32 |              18                    |
| 2018-12-29  | 08:05:32 |              18                    |
+——————-+————+———————————+
3 rows in set (0.00 sec)

Access control lists, Uncategorized

Automation of ACL implementation on Cisco L3 switches

As you may know, implementation of ACLs on Cisco L3 switches is quite time consuming work. Per my understanding, inside Cisco company some automation is being used based on Java program.

Here is my version to automate this process by Perl. It is expected that ACL request  has been received in excel file. The program goes through cells of this file, gathers subnets and generates result in a certain form. This version is a kind of v1.0, as it is expects only IP data in the file and requires some editing for TCP/UDP case and related ports.

#!/usr/bin/perl
#usage: arg1=ACL VLAN nubmer; arg2=VLAN description; arg3=core1; arg4=core2; arg5=ACL_request#; arg6=location (site name); arg7=ACL_request_file
use strict;
use warnings;
use POSIX qw(strftime); # to generate current date in ISO format
use Net::Netmask; # to calculate wildcard mask and other subnetting operations
use Spreadsheet::Read qw(ReadData); # to read excel file
my $now = time();
my $tz = strftime(“%z”, localtime($now));
my $date_my = strftime(“%Y-%m-%d”, localtime($now));

#-Read script input, result is $VLAN_number, $VLAN_description, $core1, $core2—#

my ($VLAN_number, $VLAN_description, $core1, $core2, $request_number, $location, $ACL_request) = @ARGV;

if (not defined $VLAN_number) {
die “Need VLAN number\n”;
}

#—Read excel with ACL request———————————#
my $book = ReadData (“$ACL_request”);
my @rows = Spreadsheet::Read::rows($book->[1]);
#—Create a file with a name in a certain format, result is file with correct name—–#
my $filename=”$date_my\_$core1\_$core2\_VLAN$VLAN_number.acl”;
print $filename;
open(my $fh, ‘>’, “/tmp/$filename”);
print $fh “! **\n”;
print $fh “! ** Location: $location\n”;
print $fh “! ** Purpose: ACL for production VLAN $VLAN_description\n”;
print $fh “! ** VLAN Number: $VLAN_number\n”;
print $fh “! ** Applied on: $core1, $core2\n”;
print $fh “! **\n”;
print $fh “! ** Last change: $date_my\n”;
print $fh “! ** Changed by: Surname Name, department\n”;
print $fh “\n”;
# deploy all related rules for IN direction:
print $fh “ip access extend vlan$VLAN_number\_filter_in\n”;
print $fh “\n”;
print $fh “remark enabling ICMP on any direction, request#$request_number\n”;
print $fh “permit icmp any any\n”;
print $fh “\n”;
print $fh “remark enabling DNS/WINS/server for hotfixes/active directory traffic, request#$request_number\n”; # DNS/WINS/server for hotfixes/active directory servers (to be improved)
print $fh “\n”;
my $excel_cell=$book->[1]{cell}[3][1]; # read C1 cell where subnet should be
$excel_cell =~ s/\/.*//s; #remove subnet mask, e.g. /24 /25 etc
print $fh “remark enabling traffic for virtual gateways, request#$request_number\n”;
print $fh “permit ip $excel_cell 0.0.0.3 any\n”;

#-from line 11 till the end of excel read source and destination addresses and create ACLs
for (my $i=11; $i <= scalar (@rows); $i++) {
my $excel_cell_1=$book->[1]{cell}[3][$i]; # read source IP scope
my $excel_cell_2=$book->[1]{cell}[6][$i]; # read destination IP scope
my $excel_cell_3=$book->[1]{cell}[9][$i]; # read comment
my $excel_cell_4=$book->[1]{cell}[1][$i]; # read protocol, e.g. TCP/UDP/IP etc, if nothing is mentioned, then consider IP

if (defined $excel_cell_1 && defined $excel_cell_2 ) { # if source AND destination data exist then proceed with creation related ACL rule, otherwise just ignore it
if (index($excel_cell_1, ‘/’) != -1) {
$excel_cell_1 =~ s/\s+//g; # remove all empty spaces in the string
my $block_1 = Net::Netmask->new(“$excel_cell_1”);
my $wildcard_src = $block_1->hostmask();
}

if (index($excel_cell_2, ‘/’) != -1) {
# print “$excel_cell_2 contains subnet mask \n”;
$excel_cell_2 =~ s/\s+//g; # remove all empty spaces in the string
my $block_2 = Net::Netmask->new(“$excel_cell_2”);
my $wildcard_dst = $block_2->hostmask();
}
# the case when protocol is mentioned in an ACL request
if (index($excel_cell_4, ‘/’) != -1) {
print $fh “$excel_cell_3, request#$request_number\n”;
my $block_1 = Net::Netmask->new(“$excel_cell_1”);
my $wildcard_src = $block_1->hostmask();
my $network_src = $block_1->base();
my $block_2 = Net::Netmask->new(“$excel_cell_2”);
my $wildcard_dst = $block_2->hostmask();
my $network_dst = $block_2->base();
# my $block_1 = Net::Netmask->new(“$excel_cell_1”);
print $fh “permit $excel_cell_4 $network_src $wildcard_src $network_dst $wildcard_dst\n”;
}
# the case when protocol in not mentioned in an ACL request, considering IP
else {
print $fh “remark $excel_cell_3, request#$request_number\n”;
# my $block_2 = Net::Netmask->new(“$excel_cell_2”);
my $block_1 = Net::Netmask->new(“$excel_cell_1”);
my $wildcard_src = $block_1->hostmask();
my $network_src = $block_1->base();
my $block_2 = Net::Netmask->new(“$excel_cell_2”);
my $wildcard_dst = $block_2->hostmask();
my $network_dst = $block_2->base();
print $fh “permit ip $network_src $wildcard_src $network_dst $wildcard_dst\n”;

}

} #end of IF check that SRC and DST networks are provided in a certain line

#rules to filter critical ports:
$excel_cell=$book->[1]{cell}[3][1]; # reading data following “ACL-Request for subnet:”
if (index($excel_cell, ‘/’) != -1) {
$excel_cell =~ s/\s+//g; # remove all empty spaces in the string
my $block_1 = Net::Netmask->new(“$excel_cell”);
my $network=$block_1->base();
my $wildcard_src = $block_1->hostmask();

print $fh “\n”;
print $fh “remark filter critical ports, request#$request_number\n”;
print $fh “deny tcp $network $wildcard_src any eq 445\n”;
print $fh “deny tcp $network $wildcard_src any eq 1200\n”;
print $fh “deny tcp $network $wildcard_src any range 135 139\n”;
print $fh “deny udp $network $wildcard_src any eq 445\n”;
print $fh “deny udp $network $wildcard_src any eq 1434\n”;
print $fh “deny udp $network $wildcard_src any range 137 138\n”;
print $fh “\n”;

#rules to filter and log all other ports:
print $fh “remark filter and log all other traffic, request#$request_number\n”;
print $fh “deny ip any any log\n\n”;
print $fh “exit\n”;
}

print $fh “\n”;
print $fh “\n”;
# deploy all related rules for OUT direction:
print $fh “ip access extend vlan$VLAN_number\_filter_out\n”;
print $fh “\n”;
print $fh “remark enabling ICMP on any direction, request#$request_number\n”;
print $fh “permit icmp any any\n”;
print $fh “\n”;
print $fh “remark enabling DNS/WINS/server for hotfixes/active directory traffic, request#$request_number\n”; # DNS/WINS/server for hotfixes/active directory servers (to be improved)
print $fh “\n”;

$excel_cell=$book->[1]{cell}[3][1]; # reading data following “ACL-Request for subnet:”
if (index($excel_cell, ‘/’) != -1) {
# print “$excel_cell_1 contains subnet mask \n”;
$excel_cell =~ s/\s+//g; # remove all empty spaces in the string
my $block_1 = Net::Netmask->new(“$excel_cell”);
# my $wildcard_src = $block_1->hostmask();
my $network=$block_1->base();

print $fh “remark enabling traffic for virtual gateways, request#$request_number\n”;
print $fh “permit ip any $network 0.0.0.3\n”;
}

#from line 11 till the end of excel read source and destination addresses and create ACLs
for (my $i=11; $i <= scalar (@rows); $i++) {

my $excel_cell_1=$book->[1]{cell}[3][$i]; # read source IP scope
my $excel_cell_2=$book->[1]{cell}[6][$i]; # read destination IP scope
my $excel_cell_3=$book->[1]{cell}[9][$i]; # read comment
my $excel_cell_4=$book->[1]{cell}[1][$i]; # read protocol, e.g. TCP/UDP/IP etc, if nothing is mentioned, then consider IP

if (defined $excel_cell_1 && defined $excel_cell_2 ) { # if source AND destination data exist then proceed with creation related ACL rule, otherwise just ignore it
if (index($excel_cell_1, ‘/’) != -1) {
# print “$excel_cell_1 contains subnet mask \n”;
$excel_cell_1 =~ s/\s+//g; # remove all empty spaces in the string
my $block_1 = Net::Netmask->new(“$excel_cell_1”);
my $wildcard_src = $block_1->hostmask();
# my $network_src = $block_1->base();
# print “\nWildcard mask: “, $wildcard_src, “\n”;
}

if (index($excel_cell_2, ‘/’) != -1) {
# print “$excel_cell_2 contains subnet mask \n”;
$excel_cell_2 =~ s/\s+//g; # remove all empty spaces in the string
my $block_2 = Net::Netmask->new(“$excel_cell_2”);
my $wildcard_dst = $block_2->hostmask();
# print “\nWildcard mask: “, $wildcard_dst, “\n”;

}
# the case when protocol is mentioned in an ACL request
if (index($excel_cell_4, ‘/’) != -1) {
print $fh “remark $excel_cell_3, request#$request_number\n”;
my $block_1 = Net::Netmask->new(“$excel_cell_1”);
my $wildcard_src = $block_1->hostmask();
my $network_src = $block_1->base();
my $block_2 = Net::Netmask->new(“$excel_cell_2”);
my $wildcard_dst = $block_2->hostmask();
my $network_dst = $block_2->base();
# my $block_1 = Net::Netmask->new(“$excel_cell_1”);
print $fh “permit $excel_cell_4 $network_dst $wildcard_dst $network_src $wildcard_src\n”;
}
# the case when protocol in not mentioned in an ACL request, considering IP
else {
print $fh “remark $excel_cell_3, request#$request_number\n”;
# my $block_2 = Net::Netmask->new(“$excel_cell_2”);
my $block_1 = Net::Netmask->new(“$excel_cell_1”);
my $wildcard_src = $block_1->hostmask();
my $network_src = $block_1->base();
my $block_2 = Net::Netmask->new(“$excel_cell_2”);
my $wildcard_dst = $block_2->hostmask();
my $network_dst = $block_2->base();
print $fh “permit ip $network_dst $wildcard_dst $network_src $wildcard_src\n”;

}
}
} #end of IF check that SRC and DST networks are provided in a certain line
#rules to filter critical ports:
$excel_cell=$book->[1]{cell}[3][1]; # reading data following “ACL-Request for subnet:”
if (index($excel_cell, ‘/’) != -1) {
# print “$excel_cell_1 contains subnet mask \n”;
$excel_cell =~ s/\s+//g; # remove all empty spaces in the string
my $block_1 = Net::Netmask->new(“$excel_cell”);
my $network=$block_1->base();
my $wildcard_src = $block_1->hostmask();

print $fh “\n”;
print $fh “remark filter critical ports, request#$request_number\n”;
print $fh “deny tcp any eq 445 $network $wildcard_src\n”;
print $fh “deny tcp any eq 1200 $network $wildcard_src\n”;
print $fh “deny tcp any range 135 139 $network $wildcard_src\n”;
print $fh “deny udp any eq 445 $network $wildcard_src\n”;
print $fh “deny udp any eq 1434 $network $wildcard_src\n”;
print $fh “deny udp any range 137 138 $network $wildcard_src\n”;
print $fh “\n”;

#rules to filter and log all other ports:
print $fh “remark filter and log all other traffic, request#$request_number\n”;
print $fh “deny ip any any log\n\n”;
print $fh “exit\n”;
}

print $fh “\n\n”;
print $fh “interface vlan $VLAN_number\n”;
print $fh “ip access-group vlan$VLAN_number\_filter_in in\n”;
print $fh “ip access-group vlan$VLAN_number\_filter_out out\n”;
print $fh “end\n”;
close $fh;

Uncategorized, wireless topics

how to quickly update peer settings on guest anchor WLC

Lets imagine someone has a Cisco anchor for “guest wifi” with configured wrong(outdated) peers and there is a task to remove them all with minimum effort.

For that it is needed to

1)establish SSH to such anchor

2) issue command to get the list of all configured peers “show mobility summary”

(Cisco Controller) >show mobility summary

Mobility Protocol Port……………………… 16666
Default Mobility Domain…………………….. GUEST
Multicast Mode ……………………………. Disabled
Mobility Domain ID for 802.11r………………. 0x78e3
Mobility Keepalive Interval…………………. 10
Mobility Keepalive Count……………………. 3
Mobility Group Members Configured……………. 72
Mobility Control Message DSCP Value………….. 0

Controllers configured in the Mobility Group
MAC Address          IP Address        Group Name          Multicast               IP Status
00:06:f6:62:51:a0   10.20.30.5          guest1                    0.0.0.0     Control and Data Path Down
00:07:7d:c0:52:e0  192.168.20.80    guest2                   0.0.0.0      Control and Data Path Down
00:42:5a:55:53:40  192.168.30.77    guest3                    0.0.0.0     Control and Data Path Down

etc. 72 peers

3) output which was received on step 2),  save into a file (for example: /tmp/peers.txt) on Linux and use good old awk command to extract first column from the output:

awk ‘{print $1}’ /tmp/peers.txt

which will print only column with MAC addresses of peers.

4) prepend “config mobility group member delete” to each line with MAC address. To combine step 3) and step 4) into one step, issue following command:

awk ‘{print $1}’ /tmp/peers.txt | sed ‘s/^/config mobility group member delete /’

5) achieved result copy and paste into Cisco WLC’s command line, e.g.:

(Cisco Controller) >config mobility group member delete 00:06:f6:62:51:a0

(Cisco Controller) >config mobility group member delete 00:07:7d:c0:52:e0

(Cisco Controller) >config mobility group member delete 00:42:5a:55:53:40

etc… for 71 peers…