11.10. Switch Port ControlSometimes it can be handy to turn up or down a switch port. For example, you may want to play a prank on an annoying co-worker. Other times, it may be a case of disabling a host that is infected with a virus and spewing packets all over the network. Whatever the case may be, it can be helpful to have something like this in your toolbox. Most people attach a serial cable from a laptop to the management port on the switch to configure it or manage it. To manage a switch via SNMP, you generally have to create a VLAN (which may encompass all the ports on the switch). This VLAN is configured with an IP address, which allows for SNMP access and control. The actual ability to manage a port comes via the Bridge MIB (RFC 1493). Most if not all switch vendors implement this MIB. Many vendors also have their own MIB that may enhance or extend the Bridge MIB, but we will focus on the RFC version to keep things generic. To successfully manage a switch port, you have to know the following bits of information:
The key to managing your switch ports is keeping track of which hosts are on which switch ports. Tobias Oetiker, creator of MRTG, created a Perl script called Cammer (http://people.ee.ethz.ch/~oetiker/webtools/mrtg/pub/contrib/cammer). Cammer displays which MAC addresses are on a switch, along with IP address information. It does this by querying the Address Resolution Protocol (ARP) table on a router and then matching this up with what's on your switch. Here's a sample run: $ ./cammer.pl public@switch public@router Fa0/9 1 00:11:43:17:06:8d 192.168.0.48 dhcp48.domain Fa0/3 1 00:60:f5:08:4e:3c 192.168.0.1 router.domain Fa0/8 1 00:60:47:40:fd:14 192.168.0.130 dhcp130.domain When running Cammer, you must pass it the community string for your switch and router. The first column is the interface name from the switch. The second column is the VLAN number, followed by the MAC address, IP address, and DNS name of the IP address (if available). Unfortunately, this script is Cisco-specific, but modifying it to operate with other vendors is doable. For our purposes, we modified the Cammer source a little bit. The output now looks like this: $ ./cammer2.pl public@switch public@router 192.168.0.148 1 (ifIndex = 10, ifName = Fa0/9) 00:11:43:17:06:8d 192.168.0.48 dhcp48.domain 1 (ifIndex = 4, ifName = Fa0/3) 00:60:f5:08:4e:3c 192.168.0.1 router.domain 1 (ifIndex = 9, ifName = Fa0/8) 00:60:47:40:fd:14 192.168.0.130 dhcp130.domain The display now shows the ifIndex in addition to the ifName. The ifIndex is used later in the script that does the actual port control. The output from running diff on the original Cammer version and the one we modified follows: 51d50 < 62a62,65 > my %OID_TO_NONARRAY = ( 'dot1dBasePortIfIndex' => "1.3.6.1.2.1.17.1.4.1.2",); > > > 129a133,134 > my %macsToIfIndex; > my %macsToIfName; 132a138 > #my $sws = SNMPv2c_Session->open ($opt{sw},$opt{swco}.'@'.$vlan,161) 141c147,152 < $port{$vlan}{$mac}=$port; --- > my $iid = snmpget($opt{swco}.'@'.$opt{sw},"$OID_TO_ NONARRAY{'dot1dBasePortIfIndex'}.$port"); > my $ifname = snmpget($opt{swco}.'@'.$opt{sw},"ifName.$iid"); > #my $ifname = snmpget($opt{swco}.'@'.$opt{sw},"$OID_TO_ NONARRAY{'ifName'}.$iid"); > $macsToIfIndex{$mac} = $iid; > $macsToIfName{$mac} = $ifname; > $port{$vlan}{$mac}=$port; 145c156,158 < sub { my($port,$if) = pretty(@_); --- > sub { > my($port,$if) = pretty(@_); > print "$port,$if\n"; 166c179 < push @{$output{$name}}, sprintf "%4s %-17s %-15s %s %s",$truevlan,$mac,$ip[0],$host[0],$quest; --- > push @{$output{$name}}, sprintf "%4s (ifIndex = %s, ifName = %s) %-17s %-15s %s %s",$truevlan,$macsToIfIndex{$mac},$macsToIfName{$mac},$mac,$ip[0],$host[0], $quest if($ip[0] ne ""); 168a182 > print "\n$opt{sw}\n"; 249d262 < The switch control Perl script that follows uses a few MIB objects from the Bridge MIB: #!/usr/bin/perl use SNMP; use Getopt::Long; GetOptions("mac=s" => \$gMac, "index=s" => \$gIndex, "action=s" => \$gAction, ); ($gMac,$gAction,$gIndex) = verifyInput($gMac,$gAction,$gIndex); &SNMP::initMib( ); &SNMP::loadModules(qw/BRIDGE-MIB/); my $host = "192.168.0.148"; my $roComm = "public"; my $rwComm = "private"; $roSession = new SNMP::Session(DestHost => $host, Community => $roComm, UseSprintValue => 1, Version=>2); die "session creation error: $SNMP::Session::ErrorStr" unless (defined $roSession); $rwSession = new SNMP::Session(DestHost => $host, Community => $rwComm, UseSprintValue => 1, Version=>2); die "session creation error: $SNMP::Session::ErrorStr" unless (defined $rwSession); findMac( ); sub findMac { my($discover) = @_; $vars = new SNMP::VarList(['dot1dTpFdbAddress'], ['dot1dTpFdbPort']); # get first row my ($mac, $port) = $roSession->getnext($vars); die $roSession->{ErrorStr} if ($roSession->{ErrorStr}); while (!$roSession->{ErrorStr} and $$vars[0]->tag eq "dot1dTpFdbAddress" || $$vars[0]->tag eq "dot1dBasePortIfIndex"){ my @tmac = $mac =~ m/(\w{1,2}) (\w{1,2}) (\w{1,2}) (\w{1,2}) (\w{1,2}) (\ w{1,2})/g; $mac = sanitizeMac(sprintf("%s:%s:%s:%s:%s:%s",@tmac)); if($gMac eq $mac){ # We found it my $ifnum = $roSession->get("dot1dBasePortIfIndex\.$port"); if($ifnum eq $gIndex){ doAction($gAction,$ifnum); }else{ print "$mac has moved to ifIndex $ifnum\n"; } last; } # keep going ($mac, $port) = $roSession->getnext($vars); } } sub doAction{ my ($action,$ifnum) = @_; my $ifname = $roSession->get("ifDescr\.$ifnum"); if($action eq "up"){ print "Turning $ifname $action (ifNum is $ifnum)..\n"; $rwSession->set([["ifAdminStatus", $ifnum, 1, "INTEGER"]]); }elsif($action eq "down"){ print "Turning $ifname $action (ifNum is $ifnum)...\n"; $rwSession->set([["ifAdminStatus", $ifnum, 2, "INTEGER"]]); } if($rwSession->{ErrorStr}){ print "An error occurred during processing: $rwSession->{ErrorStr}\n"; } } sub sanitizeMac{ my($mac) = @_; my @tmac = split(/:/,$mac); foreach my $byte (0..$#tmac){ $tmac[$byte] =~ s/^0//g; $tmac[$byte] = lc($tmac[$byte]); } $mac = sprintf("%s:%s:%s:%s:%s:%s",@tmac); return $mac; } sub verifyInput{ my($mac,$action,$index) = @_; if(($mac eq "" && $action eq "" && $index eq "")) { usage( ); exit; } if($action eq ""){ usage( ); exit; } $mac = sanitizeMac($mac); $action = lc($action); if($action ne "up" && $action ne "down"){ usage( ); exit; } return ($mac,$action,$index); } sub usage{ print "Usage:\t$0 --mac=0:f:0:d:55:a --index=10 --action=up\n"; print "\tSpecify a MAC adddress and the index in the interfaces MIB tree where this port lives on the switch. Action can be EITHER \"up\" OR \"down\"\n"; } Here are some key points about this script:
Here are some sample runs of the script: $ ./swcontrol.pl --mac=00:11:43:17:06:8d --index=10 --action=up Turning FastEthernet0/9 up (ifNum is 10).. $ ./swcontrol.pl --mac=00:11:43:17:06:8d --index=10 --action=down Turning FastEthernet0/9 down (ifNum is 10)... $ ./swcontrol.pl --mac=00:11:43:17:06:8d --index=11 --action=up 0:11:43:17:6:8d has moved to ifIndex 10 $ The last run shows output when the wrong index is used on the command line. This index check is used as a safety since ifIndex values can shift or move at any time. When this happens, you may need to rerun Cammer to update your mappings. Finally, here are some ways to enhance this script:
|