Showing posts with label Tcl. Show all posts
Showing posts with label Tcl. Show all posts

ARP table with logical and physical interfaces

In a layer-3 switching environment, the ARP table displayed with the show arp command lists the logical (L3) interfaces, for example the VLAN or BVI interface. This Tcl script displays the logical as well as physical interface associated with each IP/MAC address.

Martin Hecko gave me the idea for this script and helped to test it on a Catalyst switch. Thank you!

Shorter display of OSPF database

Recently I had to explore the behavior of Cisco IOS OSPF implementation and had to inspect OSPF database on routers in various areas. If you're only interested in the contents of the database (not in low-level troubleshooting), variety of LSA fields (including LS Age, Options, Checksum, Length ...) are just cluttering the printout, so I fine-tuned the show filter to exclude all the non-relevant fields, ending with show ip ospf database parameters | exclude LS|Options|Check|Len|(MTID:[ 0-9]+$) (the MTID field appears in IOS release 12.2SRC).To make the command more useful, I've changed it into a short Tcl script (using steps from the post explaining how to execute complex CLI commands from Tcl) stored in flash:ospfdb.tcl

set cmd {show ip ospf database }
append cmd $argv
append cmd { | excl LS|Options|Check|Len|(MTID:[ 0-9]+$)}
puts [exec $cmd]
… and defined alias exec ospfdb flash:ospfdb.tcl. I could then easily inspect the contents of various parts of OSPF database I was interested in, for example:
a3#ospfdb external 0.0.0.0
 
            OSPF Router with ID (10.0.1.3) (Process ID 1)
 
                Type-5 AS External Link States
 
  Link State ID: 0.0.0.0 (External Network Number )
  Advertising Router: 10.0.1.5
  Network Mask: /0
        Metric Type: 2 (Larger than any link state path)
        Metric: 1 
        Forward Address: 0.0.0.0
        External Route Tag: 1

Simple CLI extensions: handling special characters

Last week I've described how you can extend the exec-mode CLI commands with almost no knowledge of Tcl. A bit more work is required if your commands include Tcl special characters (quotes, braces or backslashes).

For example, to display all routes advertised by customers of AS X, you'd use the following show command: show ip bgp regexp _X_([0-9]+)(_\1)*$ (the regular expression is explained in the AS-path based filter of customer BGP routes post). This command cannot be entered as a Tcl string with variable substitution; Tcl would interpret the [ and \ characters. You could enter the whole command in curly braces, but then there would be no variable substitution that we need to insert command line parameters. To make Tcl happy, use the following Tcl commands:

  1. set cmd {first-part-of-command} stores the command prefix into the cmd variable;
  2. append cmd $argv appends the command line arguments to the command;
  3. append cmd {rest-of-command} appends the rest of the IOS exec command;
  4. puts [exec $cmd] executes the command and prints the results.

For example, the following code will display the customers of a BGP AS specified in the command line (after being stored in a flash file and defined in an alias, of course):

set cmd {show ip bgp regexp _}
append cmd $argv
append cmd {_([0-9]+)(_\1)*$}
puts [exec $cmd]

Simple extensions to exec-mode CLI

The various show filters available in Cisco IOS are a great tool to minimize the amount of printout you have to analyze, their only problem (from my perspective) is that you cannot make an alias out of them, as you usually have to supply one or more parameters to the show command and these parameters have to be inserted before the filter (and the alias command does not support replaceable parameters). You could solve the problem with Tcl shell, but I'm not sure many networking engineers are fluent Tcl programmers. Fortunately, the code you need is so simple anyone can create a working solution.

Follow these simple steps:

  1. Execute the show command you're interested in and fine-tune the filter. For example, I wanted to have a short display of IP interfaces produced with the show ip interface fa0/0 ¦ include address¦protocol command.
  2. Store the following line of Tcl code in a flash file: puts [exec "your-command"], replacing the arguments in your command with $argv (you can use this trick if you don't have an external file server handy). In my case, the flash:ipconfig.tcl file contained the following code:
  3. puts [exec "show ip interface $argv ¦ include address¦protocol"]
  4. Define a command alias: alias exec new-command tclsh file-in-flash, for example, alias exec ipconfig flash:ipconfig.tcl.
Now you can execute your new command and use command parameters to select the printout you want.
X1#ipconfig fa0/0
FastEthernet0/0 is up, line protocol is up
  Internet address is 172.16.0.1/24
  Broadcast address is 255.255.255.255
  Helper address is not set
  Network address translation is disabled

Replace the broken vertical bar in sample printouts with a vertical bar before using them.

Change the source IP address of an EEM SMTP session

I've got the following question from Levi:

I have a Tcl script that is used in conjunction with EEM to send email whenever the amount of CRC errors on a particular interface increases above a certain threshold. My problem is that the router uses the IP of the outgoing interface as the source IP when it communicates with the SMTP server. This particular interface happens to have a private IP. There's another interface with a public IP and I wanted to know how to get the router to use the public IP on the other interface when it's sending email generated by the TCL script.
There are several ways to solve this problem. If you use Tcl, you could write your own SMTP client and use the -myaddr parameter in the socket call to specify the source IP address. Those of us who prefer EEM applets are not so lucky, you have to use NAT to change the source IP address before the packet is sent toward the SMTP server.

This article is part of You've asked for it series.

Fix the "do" command

The do command available in configuration modes of Cisco IOS is probably one of the best features ever implemented in IOS, but you tend to continue typing the do keyword even in the exec mode, resulting in syntax errors. The alias command doesn't help as you cannot specify an empty command line. However, there is a Tcl-based workaround.Store the following Tcl code in flash:do.tcl:

puts [exec $argv]
Configure alias exec do tclsh flash:do.tcl and you can execute the do command from exec mode.

The simple solution does not page the output, a lot more work would be needed to implement the proper paging functionality

Continuous display of top CPU processes

When you have to monitor which processes consume router's CPU over a period of time, a Tcl script that emulates the Unix top command might come handy. Download it, install it and enjoy the view :)

If anyone has discovered a reliable technique that detects a keypress event (= character available on stdin) in the Tcl loop, please let me know. The Ctrl/C solution is a kludge.

Create structured e-mails from EEM applets

A few weeks ago I've described how to use the append show filter and more command to send e-mails containing multiple printouts from an EEM applet. A few hours after I've published the post, David Houser sent me a great EEM applet that used texts stored in flash: files to generate headings between various show commands. While his solution works perfectly (and gives you all the flexibility you want), it's a bit verbose and requires lots of small files that clutter your flash: memory. I've thus decided to write a small Tcl script that executes the Cisco IOS command specified in the command line and appends the command results together with a heading in an output file.

IOS scripting with Tcl available on almost all platforms

The Tcl scripting (invoked with tclsh command) is now available on almost all IOS-based platforms (apart from the low-end Catalyst switches). For high-end distributed platforms, use the 12.2(33)SRC release, for Catalyst 6500 the 12.2(33)SXH release. On most other platforms, you can use mainstream 12.4 release.

Tcl-based IOS backdoor

Andy Davis from the Information Risk Management has written an interesting "application note": how to create a backdoor to Cisco IOS using Tclsh (I've tested it and it works quite nicely). His backdoor implementation relies on a bug in Cisco IOS that allows a "hung" (or never-ending) Tclsh script to continue executing even after the user session has been disconnected (the only means of stopping such a script is with the clear line command). That bug has been fixed in recent IOS versions (I've tested that as well, the Tclsh script is killed as soon as the Telnet session is disconnected in IOS 12.4(15)T), but you can still use the same technique (although it might be a bit less convenient) if you convert the Tclsh script into an EEM policy and trigger it periodically with a timer event.

Predefine your own Tcl functions

If you want to have your own Tcl functions available when you start tclsh, you could use the scripting tcl init file configuration command that I've briefly mentioned in one of the previous posts. This command specifies a source file that is executed every time you start Tcl shell. The source file can contain function definitions, package declarations or any other Tcl code.

If you need to, you can specify multiple initialization files.

For example, if you'd like to implement a comfortable Tcl-based pinger (similar to the one Ethan Banks found in the Sadikhov forums, store the following Tcl code into the file flash:pinger.tcl

proc pinger { iplist } {
  foreach ip $iplist {
    if { [regexp "(!!!)" [exec "ping $ip timeout 1" ]] } {
      puts "$ip"
    } else { puts "$ip **** failed ***" }
  }
}
… and configure scripting tcl init flash:pinger.tcl. Now you can ping a number of hosts in a single operation:
R1#tclsh
R1(tcl)#pinger { 10.0.0.1 10.0.0.2 10.0.0.3 10.0.0.4 }
10.0.0.1
10.0.0.2
10.0.0.3 **** failed ***
10.0.0.4 **** failed ***

Display the names of the configured route-maps

I'm probably getting old … I keep forgetting the exact names (and capitalization) of route-maps I've configured on the router. The show route-maps command is way too verbose when I'm simply looking for the exact name of the route-map I want to use, so I wrote a Tcl script that displays the names of the route-maps configured on the router. If you add a -d switch, it also displays their descriptions (to be more precise, the first description configured in the route-map).

When using the -d switch, the script executes the show running command and might take a while to complete.

To use the script, download the routeMaps.tcl file (available from my web site) into the router's flash and follow the installation instructions in the source.

Here is a sample printout from one of my routers:
R1#show alias | include rm

  rm tclsh flash:routeMaps.tcl
R1#rm
LocPref
SetCommunity
TestRange
prepend
 
R1#rm -d
Route map name Description
========================================================================
LocPref
SetCommunity Sets time-based communities on local routes
TestRange
prepend

Phase 2: Upload text files through a Telnet session

In a previous post, I've described how you can use Tcl shell to upload text content into router's flash if the router has no connectivity to a suitable file server (or you don't have FTP or TFTP server handy).

The trick works flawlessly, but typing the same obscure Tcl commands gets tedious after a while, so the first time I had to use this solution to develop a Tcl script, I've quickly written another script that takes file name as the parameter and hides all the other murky details.

To use it, transfer the contents of storeFile.tcl (available from my web site) to the router's flash (using the previously described trick), follow the installation instructions in the source and you're ready to go.

Note: You can adapt the Tcl script to your needs; for example, you could add instructions to re-register EEM Tcl policy every time you upload the new code.

Implement “wc -l” in Cisco IOS

Sometimes it would be nice to have the full complement of Unix utilities available on Cisco IOS. That's not going to happen for a while, but we can use Tcl to make our life simpler in the meantime. Xavier Brouckaert, a regular contributor to my blog, has sent me the Tcl implementation of line counting utility (equivalent to wc -l on Unix).

First you have to define the wc Tcl procedure:

proc wc { cmd } { llength [split [exec $cmd] "\n" ] }
You can define the procedure interactively in Tclsh (but then you have to do it every time you start Tclsh) or you could store the code in a flash file and execute the file every time the Tclsh is started with the scripting tcl init filename global configuration command.

Once the wc procedure is defined, execute wc { IOS command } in Tclsh and you'll get the line count. For example, to get the number of directly connected routes use
wc { show ip route ¦ include ^C }

The include ^C filter includes all lines that start with letter C; in our case all directly connected routes

Obviously you could turn this idea into a full-blown Tclsh script that would accept CLI arguments … but I'll leave this as an exercise for the readers (you can probably tell I've been reading some academic literature lately :). However, if you find the time to write a more complete wc implementation on IOS, please do post the URL here.

Tabular display of OSPF external routes

I was testing OSPF external routes recently and wanted to have a comprehensive display of OSPF type-5 LSAs (not the too-verbose information IOS generates), so I created a Tcl script that displays type-5 (external) LSAs from the OSPF database in a tabular format. You can download it from my web site (installation instructions are included in the Tcl source).Here is a sample printout from one of my lab routers:

S1#ospfExternal
 
External OSPF routes for OSPF process ID 1
 
  Prefix Cost Tag ASBR Forward addr
==================================================================
> 10.1.0.1/32 10 E1 1 10.0.0.3
  10.1.0.1/32 2000 E1 1 10.0.0.11
> 10.1.0.2/32 5 E2 2 10.0.0.3
  10.1.0.2/32 200 E2 2 10.0.0.11

Generate terminal escape sequences from Tcl

One of my readers (who unfortunately prefered to stay anonymous, so I cannot give credit where it's due) figured out how to output escape sequences from IOS Tclsh: you have execute terminal international command first.

For example, to clear the screen and display red heading text, use the following commands:

exec terminal international;
puts "\033\[2J\033\[H\033\[1;31mHeader text\033\[m"
Obviously, you could easily use this functionality to build a nice full-screen menu system.
Notes
  • To output the ESC character, use the \033 code within the double quotes;
  • To output the left angle bracket, you have to use the \[ sequences, otherwise Tcl interprets the bracket as start of an expression;
  • The ANSI escape sequences (recognized by most terminal emulators) are documented on Wikipedia;

Copy the text files into router's flash through a Telnet session

Were you ever in a situation where a file that would have to be on the router was sitting on your laptop, but you couldn't store it into the router's flash across the Telnet session or through the console port?

If the file in question is a text file, and the router supports Tcl shell, danshtr documented an interesting trick: you create the file in Tclsh interpreter, cut-and-paste the text through the telnet session into a Tcl string and write the string to the file. If you want to have a more cryptic solution here it is:

  • Start tclsh;
  • Enter puts [open "flash:filename" w+] {. Do not hit the ENTER key at the end of the line
  • Copy-paste the file contents. The contents should not include unmatched curly right brackets (every curly right bracket has to be preceded by a matching curly left bracket).
  • After the file contents have been pasted, enter } and press ENTER.
  • End the tclsh session with tclquit.

Any idea how to generate binary output from Tclsh?

I've tried to port a simple Tcl full-screen editor to IOS and failed completely as IOS tclsh escapes control characters written by the puts command. For example, the following escape sequence should clear the screen, but as the ESCAPE character is displayed as ^[, it doesn't work:

router(tcl)#puts "\033\[2J"
^[[2J
router(tcl)#
Any ideas how to persuade the router to display raw binary data?

Can I combine EEM applets with Tcl shell?

When I've been describing the limitations of kron, ??? quickly asked an interesting question: “as I cannot insert extra input keystrokes with EEM applet, can I run a Tcl script from it with the action sequence cli command "tclsh script" command and use the typeahead function call to get around the limitation?” The only answer I could give at that time was “maybe” … and obviously it was time for a more thorough test. The short result is: YES, you can do it (at least in IOS release 12.4(15)T1).

… and here is the long description of the test. I've started by creating a small Tcl script (see below) that clears the counters on Loopback 0. As the clear counters command requires keyboard input and generates a syslog message, it was a perfect test case.

typeahead "y"
exec "clear counter loop 0"

I've copied this script into the flash:tcl/clearL0.tcl and tested it:

R1#tclsh flash:tcl/clearL0.tcl
%CLEAR-5-COUNTERS: Clear counter on interface Loopback0 by console

So far, so good. Next, I've created an EEM applet with no trigger …

event manager applet Clear
 event none
 action 1.0 cli command "enable"
 action 1.1 cli command "tclsh flash:tcl/clearL0.tcl"

… enabled the EEM CLI debugging and started it:

R1#debug event man action cli
Debug EEM action cli debugging is on
R1#event man run Clear
R1#
%HA_EM-6-LOG: Clear : DEBUG(cli_lib) : : CTL : cli_open called.
%HA_EM-6-LOG: Clear : DEBUG(cli_lib) : : OUT :
%HA_EM-6-LOG: Clear : DEBUG(cli_lib) : : OUT : R1>
%HA_EM-6-LOG: Clear : DEBUG(cli_lib) : : IN : R1>enable
%HA_EM-6-LOG: Clear : DEBUG(cli_lib) : : OUT :
%HA_EM-6-LOG: Clear : DEBUG(cli_lib) : : OUT : R1#
%HA_EM-6-LOG: Clear : DEBUG(cli_lib) : : IN : R1#tclsh flash:tcl/clearL0.tcl
%CLEAR-5-COUNTERS: Clear counter on interface Loopback0 by on vty0 (EEM:Clear)
%HA_EM-6-LOG: Clear : DEBUG(cli_lib) : : OUT :
%HA_EM-6-LOG: Clear : DEBUG(cli_lib) : : OUT : R1#
%HA_EM-6-LOG: Clear : DEBUG(cli_lib) : : CTL : cli_close called.

Great. It works. Let's move on: I've inserted a trigger into the EEM applet that ran the applet when an OSPF neighbor reached FULL adjacency:

event manager applet Clear
 event syslog pattern "OSPF-5-ADJCHG.*to FULL"
 action 1.0 cli command "enable"
 action 1.1 cli command "tclsh flash:tcl/clearL0.tcl"

And now for the final test: after I've enabled the serial interface, OSPF neighbors established adjacency …

R1(config-if)#interface ser 1/0
R1(config-if)#no shutdown
R1(config-if)#
%LINK-3-UPDOWN: Interface Serial1/0, changed state to up
%LINEPROTO-5-UPDOWN: Line protocol on Interface Serial1/0, changed state to up
%OSPF-5-ADJCHG: Process 1, Nbr 10.0.1.2 on Serial1/0 from LOADING to FULL, Loading Done
%CLEAR-5-COUNTERS: Clear counter on interface Loopback0 by on vty0 (EEM:Clear)

… and the counters on Loopback0 were cleared. Test completed :)

This article is part of You've asked for it series.

Persistent EEM variables

Someone has asked me a while ago whether it's possible to retain variable values between invocations of an EEM policy. Since a new copy of Tcl interpreter is started for each event, global variables obviously won't work; they are lost as soon as the Tcl policy is finished. A potential solution is to modify the router's configuration and save the values you wish to preserve in event manager environment, but that's a time-consuming process that interferes with whatever router configuration management process you have.

The real solution is based on the appl_setinfo and appl_reqinfo calls. They work, but like many other Tcl-related IOS features they are … well … weird.This time, the programmers managed to implement WORO (Write-Once-Read-Once) memory:

  • The value you want to preserve is saved with appl_setinfo key name data value function call. Keys must be unique; you can only set the same key once. If you try to set the value of a key multiple times, the function does not overwrite the previous value but fails.
  • You can read the value with appl_reqinfo key name function call. If the key value hasn't been set, it returns an empty string and sets the $_cerrno variable, otherwise it returns a list with 'data' as the first element and your value as the second list element (I have to admit I've seen simpler APIs :).
  • Once you read the key value, it's gone. You cannot read it twice.
If all this sounds a bit strange, don't worry; here's a working example:
::cisco::eem::event_register_cli sync no skip no pattern "show"

namespace import ::cisco::eem::*
namespace import ::cisco::lib::*

Set the variable value to zero (in case we haven't saved the value before) and read the previous value

set lastCnt 0
set getLastCnt [ appl_reqinfo key "showCounter" ]

If the first element in the list is 'data', then the second element is our value.

if { [ lindex $getLastCnt 0 ] == "data" } {
  set lastCnt [ lindex $getLastCnt 1 ]
}

Increase the counter and generate a syslog message

incr lastCnt
action_syslog priority info msg "Show command was executed $lastCnt times"

Save the new value of the counter to be retrieved by the next invocation of the same policy.

appl_setinfo key "showCounter" data $lastCnt

This article is part of You've asked for it series.