I have long wanted to put into practice the Cisco IOS capabilities that hide behind the
tclsh command and are present in almost every router and switch. But unfortunately, or maybe fortunately, I didn’t have to solve problems where the use of automation by the means of the device itself could somehow help, but there were never too many devices from Cisco under my control. Finally, fate threw me on a business trip from where it was necessary to manage the network, and in the hands of only a tablet with Wi-Fi and the 80th TCP port. This time I had to dictate commands by voice through the phone, but on arrival the task was solved using
Cisco IOS Scripting with Tcl .
Fortunately, TCL implementation in Cisco IOS is functional enough to solve this problem.
At first I went in search of a ready-made solution. Actually, Cisco itself offers access to its devices through a web interface, but such access does not have all the features of the console interface (as Cisco itself indicates), requires preliminary configuration (for example, Java for SDM), affects security, is not convenient for use ( for me) - in general, on all devices
no ip http server . It was also possible to pamper the SSH / telnet port on HTTP, but here’s the problem with the tablet: telnet hasn't been there for a long time in Windows, and ssh. We take into account that we may need to go as quickly as possible from any device where there is a web browser, let it be the phone of the beginning / mid-2000s.
We look at third-party ready-made solutions for remote access to the Cisco console -
this is probably the most popular article on this topic , but remember that we need at least telnet. As a result, we will form requirements: access via the web-interface, as simple as possible to run on the device (ideally with one team) - and we will write everything ourselves.
First, what happened - by the link at the end of the post, you can pick up a script that should be run on a remote device. Due to the fact that the implementation of TCL is for very many systems, so you can get access to Cisco IOS, Windows, Linux, FreeBSD (this is just what I checked myself). In the parameters, you must specify the address at which we will listen to incoming requests, and the port. If the parameters are not specified, then we listen to all the addresses and the first free port allocated to us by the system. There is a semblance of a hint (as in Cisco) when a question mark is in the parameters, the irony is that the hint does not work in the Cisco console, the question mark is intercepted by the Cisco console. The first line does not contain the standard
#! <Script path> , so we explicitly call
tclsh (for Cisco, it is impossible to do otherwise):
win>tclsh cws.tcl ? ABCD or * Listen ip address <cr> win>tclsh cws.tcl * ? <0-65535> Listen tcp port, 0 for first free <cr> win>tclsh cws.tcl * 8181 Listen on http://0.0.0.0:8181
For the Cisco console, we write the full path (via tftp), and you need to be in privileged mode. In tclsh mode, call the script through
source , but the command line parameters cannot be passed to the script:
cisco
For nix consoles, the parameter "*" is perceived as a wildcard parameter (wildcard), therefore it is necessary to screen it with "\ *" or enclose it in quotes.
After launch, you can enter the browser at the address you are listening to. The ascetic interface is a line for entering commands, a button for executing and stopping a script, and also a tclsh mode switch.

If the command being executed has something to output, then the output of the command will be shown at the beginning of the page. The tclsh mode executes the command as a TCL script — you can use the entire set of language commands. The input field is limited to 160 characters, the size is 40 characters (convenient for the tablet). The script stop button will finish its work on the device, we will not need to ask anyone for this when we finish our actions. Execution errors of the command itself are processed and output back to the web interface, communication and script errors are not caught at all, so standard console interpreter errors TCL can get out of the console (I tried to catch everything as much as possible, but maybe that’s left). It is also worth remembering that the script processes only one request at a time, and if you execute a command that requires interactive actions or a long execution period, then access for further control of the script will be lost. You can terminate the command being executed with the "&" character separated by a space, which will launch it in the background and return control back to the script, does not work for Cisco IOS.
The main feature of tclsh on Cisco is that all Cisco commands are interpreted as native by the interpreter, that is, in fact, we supplement the TCL language with all the functionality that a particular device command interface has - we don’t need to explicitly call commands using
exec , they are already are performed. Also, we will not be able to switch to configuration mode from the
conf t terminal, for this we need to use the command TCL addition from Cisco -
ios_config . For example, turn off the interface:
cisco
In great detail about this as always on
cisco.com .
On the contrary, in Windows all commands are not executed in the console, that is, they are perceived only as a separate executable file, so in order to execute, for example,
dir, you must explicitly call the console:
win>cmd /c dir c:\ C SYSTEM : A073-3CE1 c:\
With the functionality finished, now a little about implementation. To accept TCP connections, use the
socket command in server mode, the
-server key. If necessary, we transfer the address to listen to (if it is not then all addresses will be listened to) the
-myaddr key. Required parameters is the name of the callback procedure that will be called when the data is transmitted in the established connection, in our case it is
get_http . As I already wrote, errors when creating a connection are not checked, if something goes wrong the script will swear at the console. The open socket is saved to the
wsh variable, from which we get its parameters with the command
fconfigure $ wsh -sockname - the listened address and port to display them on the screen.
if { $argc == $i } then { if [string length $listenaddr] then { set wsh [socket -server get_http -myaddr $listenaddr $listenport] } else { set wsh [socket -server get_http $listenport] } set sockparam [fconfigure $wsh -sockname] puts "Listen on http://[lindex $sockparam 0]:[lindex $sockparam 2]" after $connwait set stopsrv 1 vwait stopsrv close $wsh set retcode $stopsrv } else { set retcode [expr $i + 100] } return $retcode
Then we call
vwait which is waiting for the change in the variable
stopsrv . Without this callback command, the procedure will not accept connections, the server as such will not listen on the port at all. In fact, only after calling this command, we switch to server mode: TCL goes into the internal loop where the connection requests are processed. The
stopsrvv variable
is needed to stop handling connections - stop the server, we will set it when you click the stop button in the web interface - TCL will exit the listening cycle, complete the callback procedure
get_http and only then execute the
close command following
vwait that closes the open socket Before
vwait , a protective mechanism: if we change our mind to go in or do not take any action for a long time, then after the time set in the variable
connwait = 15 minutes,
stopsrv will be installed and
vwait will continue to run. If we logged in, the
after is reinitialized again in the
get_http procedure.
A wrapper from
if is needed to correctly transfer the command line parameters to this block, the parameters themselves are checked earlier, that there and how you can see in the script itself.
We remember about the Cisco tclsh feature that Cisco commands are native to it, so instead of
exit which belongs specifically to Cisco and not TCL, we use
return - after executing the script on Cisco, a completion code will be issued to the console.
All the main work is done in the
get_http procedure, where the input parameters of HTTP requests are analyzed and the procedures for generating HTTP responses are called:
proc get_http { sockaddr ipaddr portaddr } { global stopsrv global connwait gets $sockaddr r flush $sockaddr set rs [string tolower [string trim $r]] after cancel set stopsrv 2 after $connwait set stopsrv 2 switch -regexp -- $rs { {^get\s*/close} { set body 1; set stopsrv 0 } {^get\s*/\s+} { puts "GET from $ipaddr:$portaddr"; after cancel set stopsrv 1 } {^get\s*/\?cmd=} { if { [regexp {/\?cmd=([^[:space:]^\&]*)(&tclsh)?} $r opt cmdline checked] && [string length $cmdline] } then { set cmdline [expandPercent $cmdline] if { ! [string length $checked] } then { catch "exec $cmdline" msgout } else { catch $cmdline msgout set checked {checked} } } } default {set mode 1; set body 2 } } response_http $sockaddr $mode response_html $sockaddr $body $cmdline $msgout $checked close $sockaddr }
The parameters of this procedure are: an open socket
sockaddr , where we will read from and where we will write, as well as the port
portaddr and the address of the
ipaddr client that has joined. We define the global variable
stopsrv to see it outside this procedure, it is necessary for
vwait . We read only the first line (we do not need the rest -
flush ), we expect that there will be a GET request. We check it in
switch and act in accordance with what the client gives us. If we do not know something, then we will return “HTTP / 1.0 501” (we cannot display the requested content). We respond to the correct request with "HTTP / 1.0 200".
All HTTP responses are generated in the
response_http procedure, and the HTML page is in
response_html . I will not describe these procedures, in them the linear code for outputting page markup - just a few lines of
puts $ sockaddr <text> and checking the conditions for what to output.
We respond to the following requested data:
- / - root - we print our page, and write to the device console who came to us - the address and port. Also cancel the pause after ;
- / close - terminate the script - set the stopsrv variable (in fact, "/ close?" flies from the client, as a request from a form without parameters);
- / cmd = <command> & tclsh - first open the text from the HTTP request - the processed line arrives to us where all spaces are replaced with "+", all non-standard characters (almost all characters except letters and numbers) are represented as% XX. To do this, we use expandPercent , almost invariably with TCL Wiki . Next, we execute the cleared command via catch and save the execution results in msgout , for output on our page. Calling the team in this way we catch possible errors of its execution. If the “tclsh” parameter is present, then we execute as is, if not, then we execute via exec - which gives us the effect of executing the command in the device console.
Right after we send the answers, we close the socket. The fact that we do not support “keep-alive” connections is reported in each HTTP response with the option “Connection: close” and indicating the version of “HTTP / 1.0”. The script in no way tries to meet the standards, I made the minimum possible processing, so that those browsers that were at hand (Opera 12, IE7, Chrome) reacted to the transmitted data normally, there could be surprises in those that were not at hand.
The language syntax feature directly on Cisco IOS is more related to the implementation of TCL version 8.3.4, and the latest version 8.5.12. For example, the convenient
switch ,
-matchvar, and
-nocase options are not implemented. In any case, you can write on any platform, just more strictly related to syntax and then there will be no problems with the transfer.
In the code, I could screw up a little, went overboard with the style somewhere, on the contrary, on some devices it might not start because of the features of these devices, but on the whole I looked pretty stable. The main thing when using is to remember that this is just a tool, and how to attach the head to it depends only on the one who applies this head.
The script can be
picked up by reference -
cws.tclTCL on Cisco -
www.cisco.com/en/US/docs/ios/12_3t/12_3t2/feature/guide/gt_tcl.htmlTCL Commands -
www.tcl.tk/man/tcl8.5/TclCmd/contents.htm