#!/usr/bin/perl -w # Fetch and display wireless stats from an Apple Airport Extreme Base Station. # Andre LaBranche, dre at mac dot com, 4/27/07 # Change History # 5/3/07 - Fixed SNMP MIB URL, added --installmib option use Switch; use Getopt::Long qw(:config permute bundling); use POSIX qw(strftime); # User Tuneables Start # We need to define base stations and their SNMP community names for querying. # For slight obscurity purposes, we store a base64 encoded version of the snmp # community name. Generate such a string at the command line like this: # echo "the password" | openssl enc -base64 # or just use the --encode option to this script. # e.g. airport --encode "my password" # Define base stations as shown here. You'll need the IP address and encoded # form of the SNMP community name. Specify as many base stations as you like. $basestations{"10.0.1.1"} = "dGhlIHBhc3N3b3JkCg=="; # Our friends. Store the MAC addresses like this (all caps, with spaces). If # we find a wireless client in this list, we'll display the specified name # instead of the MAC address. $friends{"00 17 F2 02 8E 96"} = "donk"; $friends{"00 17 F2 02 2F 97"} = "dreness"; # snmpwalk binary location $snmpbin = "/usr/bin/snmpwalk"; # User Tunables End # some variables @friends = keys %friends; # a list of friendly MAC addresses $fieldOrder = ""; # used to hold order of output fields # get and process command line arguments and options. We use Getopt::Long to # handle the command line help and verbose options. Anything else gets passed # to the &orderFields subroutine. This is so we can take these in order to # build the output format. GetOptions( 'v+' => \$debug, 'h|help' => \$printHelp, 'd' => \$delimited, '<>' => \&orderFields, 'installmib' => \&installmib, 'encode=s' => \$pwhash ); # process cli arguments / options &printCliHelp if ( defined $printHelp ); # be sure you've installed Apple's Airport MIB: # http://docs.info.apple.com/article.html?artnum=120227 if ( -e "/usr/share/snmp/mibs/airport-extreme.mib" ) { } #zee goggles! else { print "Can't find Airport MIB. Use --installmib to install it:\n"; #print "cd /usr/share/snmp/mibs ; sudo curl -L -O "; #print "http://supportdownload.apple.com/download.info.apple.com/"; #print "Apple_Support_Area/Apple_Software_Updates/Mac_OS_X/downloads/"; #print "061-0652.20030619.5ibjt/airport-extreme.mib\n"; die("Error: Missing SNMP MIB file.\n"); } # make sure we have work to do if ( !defined %basestations ) { print "Edit this script to configure base stations to monitor.\n"; exit 1; } if ( defined $pwhash ) { $base64 = `echo "$pwhash" | openssl enc -base64`; print "base64 encoded version of $pwhash is: $base64\n"; exit 0; } if ( !defined $debug ) { $debug = 0 } print "Debug level: $debug\n" if ( $debug gt 0 ); # if user passed in a field specification, $fieldOrder would be populated if ( defined $fieldOrder ) { # make an array of individual field spec characters @fields = split( //, $fieldOrder ); # test each character and load @infos with the corresponding field name foreach $a (@fields) { switch ($a) { case "n" { push @infos, "wirelessPhysAddress" }; case "y" { push @infos, "wirelessType" }; case "s" { push @infos, "wirelessStrength" }; case "r" { push @infos, "wirelessRate" }; case "o" { push @infos, "wirelessNoise" }; case "t" { push @infos, "wirelessNumTX" }; case "e" { push @infos, "wirelessNumRX" }; case "T" { push @infos, "wirelessNumTXErrors" }; case "E" { push @infos, "wirelessNumRXErrors" }; case "a" { push @infos, "wirelessTimeAssociated" }; case "i" { push @infos, "wirelessLastRefreshTime" }; }; } } if ( grep ( /wireless/, @infos ) ) { # user passed in some options, we'll use them } else { # User didn't specify any output fields, so set up default output @infos = ( 'wirelessPhysAddress', 'wirelessType', 'wirelessStrength', 'wirelessRate', 'wirelessNoise', 'wirelessTimeAssociated', 'wirelessLastRefreshTime' ); } print "Using data field order: [@infos]\n" if ( $debug gt 0 ); # We need to build a format string appropriate to the output field order we'll # be using. Pass &buildFormatStrings the list of fields, and it returns a # matching array of format strings (used for printf later). @formats = &buildFormatStrings(@infos); print "Using printf formats: [@formats]\n" if ( $debug gt 1 ); # Query each base station in turn. We'll build a hash in the form # $host{$key} = $value, where $host is the IP address, and $key is the name of # an SNMP object, with corresponding $value. foreach $host ( keys %basestations ) { chomp( $community = `echo $basestations{$host} | openssl enc -d -base64` ); # Build the SNMP command. Walk the entire airport MIB ... $snmpcmd = "$snmpbin -m AIRPORT-BASESTATION-3-MIB -Osq -v 2c "; # ... use the supplied snmp community and host names $snmpcmd = $snmpcmd . "-c \"$community\" \"$host\" "; # ... and root the search at this OID $snmpcmd = $snmpcmd . "SNMPv2-SMI::enterprises.apple.airport"; # Fire off the snmpwalk command. All the output is stored in @output. print "About to run SNMP command:\n$snmpcmd\n" if ( $debug gt 0 ); @output = `$snmpcmd`; chomp @output; # step through each line of output and organize the data foreach $line (@output) { # match the interesting bits $line =~ /^(.*?)\s(.*?)$/; $key = $1; $val = $2; # store the two values as a hash element, if we got them both $host{$key} = "$val" if ( ( defined $key ) && ( defined $val ) ); undef $key; undef $val; } # make sure we got useful output from the snmpwalk if ( !defined $host{"sysConfName.0"} ) { $snmpHelp = < "%-17s", 'wirelessType' => "%-4s", 'wirelessStrength' => "%-4s", 'wirelessRate' => "%-4s", 'wirelessNoise' => "%-5s", 'wirelessNumTX' => "%-10s", 'wirelessNumRX' => "%-10s", 'wirelessNumTXErrors' => "%-10s", 'wirelessNumRXErrors' => "%-10s", 'wirelessTimeAssociated' => "%-8s", 'wirelessLastRefreshTime' => "%-8s" ); %headings = ( 'wirelessPhysAddress' => "Name or Address ", 'wirelessType' => "Type", 'wirelessStrength' => "Str ", 'wirelessRate' => "Rate", 'wirelessNoise' => "Noise", 'wirelessNumTX' => "TX Frames ", 'wirelessNumRX' => "RX Frames ", 'wirelessNumTXErrors' => "TX Errors ", 'wirelessNumRXErrors' => "RX Errors ", 'wirelessTimeAssociated' => "Connected ", 'wirelessLastRefreshTime' => "Idle " ); foreach $a (@infos) { print "buildFormatStrings: got $a from infos...\n" if ( $debug gt 2 ); print "buildFormatStrings: $a is: $formats{$a}\n" if ( $debug gt 2 ); push( @formats, "$formats{$a}" ); } return @formats; } sub orderFields { $fieldOrder .= "$_[0]"; print "fieldOrder is: $fieldOrder\n" if ( ( defined $debug ) && ( $debug gt 1 ) ); } sub installmib { print "You will be prompted for your password.\n"; system("cd /usr/share/snmp/mibs ; sudo curl -L -O http://supportdownload.apple.com/download.info.apple.com/Apple_Support_Area/Apple_Software_Updates/Mac_OS_X/downloads/061-0652.20030619.5ibjt/airport-extreme.mib ; cd ~"); exit 0; };