Wednesday 23 July 2014

Make a router become a syslog server using tcl script

 Code:
global my_sock
global my_data

proc Listener {port action} {
global my_sock
if {$action == "START"} {
set my_sock [socket -server on_connect $port]
} else {
if {[info exists my_sock]} {
puts "Closing my socket"
close $my_sock
}
}
return $my_sock
}

proc on_connect {newsock clientAddress clientPort} {
puts "socket is connected now"
fconfigure $newsock -blocking 0
fileevent $newsock readable [list handleInput $newsock]
}

proc handleInput {f} {
global my_data
if {[eof $f]} {
fileevent $f readable {}
close $f
return
}
set my_data [read -nonewline $f]
regsub -all {<[0-9]+>[0-9]+: } $my_data " " output
if {[string length $output]} {
puts stdout "$output"
}
}

Listener 9500 START
vwait my_sock

save the file to the router that will run the script:

 TCLRouter#copy tftp: flash:
Address or name of remote host [142.100.64.100]?
Source filename [syslogd_book.tcl]?
Destination filename [syslogd_book.tcl]?
%Warning:There is a file already existing with this name
Do you want to over write? [confirm]
Accessing tftp://142.100.64.100/syslogd_book.tcl...
Erase flash: before copying? [confirm]

TCLRouter#dir         
Directory of flash:/

    1  -rw-         757                    <no date>  syslogd_book.tcl


run the script on the router:
 TCLRouter#tclsh flash:syslogd_book.tcl

On R2, type syslog command to save logs to the router that is running the script via tcp port 9500:
R2:  logging host 12.12.12.1 transport tcp port 9500

will see the router is recording logs from R2:

TCLRouter#tclsh flash:syslogd_book.tcl
socket is connected now
 *Mar  1 00:26:51.763: %LINK-5-CHANGED: Interface Loopback1, changed state to administratively down
 *Mar  1 00:26:53.939: %LINK-3-UPDOWN: Interface Loopback1, changed state to up *Mar  1 00:26:54.939: %LINEPROTO-5-UPDOWN: Line protocol on Interface Loopback1, changed state to up *Mar  1 00:26:57.851: %SYS-6-LOGGINGHOST_STARTSTOP: Logging to host 12.12.12.1 started - reconnection
 *Mar  1 00:26:57.867: %LINK-5-CHANGED: Interface Loopback1, changed state to administratively down
 *Mar  1 00:26:58.867: %LINEPROTO-5-UPDOWN: Line protocol on Interface Loopback1, changed state to down
 *Mar  1 00:34:26.835: %SYS-5-CONFIG_I: Configured from console by console 



Another example explains the question you may be wondering how to store sys logs received from R2 into a file.

code:

global my_sock
global my_data
global my_mode
global my_file

proc Listener {port action filename} {
global my_sock
global my_file
global my_mode
set my_mode 0

if {$action == "START"} {
    set my_sock [socket -server on_connect $port]
    set my_mode 1
} elseif {$action == "STARTWriting"} {
    set my_sock [socket -server on_connect $port]
    set my_file [open $filename WRONLY]
    set my_mode 2
} else {
if {[info exists my_sock]} {
    puts "Closing my socket"
    close $my_sock
}
}
    return $my_sock
}

if {[info exists my_file]} {
#if the socket is really there, close it
puts "Closing my file"
close $my_file
}

proc on_connect {newsock clientAddress clientPort} {
    puts "socket is connected now"
    fconfigure $newsock -blocking 0
    fileevent $newsock readable [list handleInput $newsock]
}

proc handleInput {f} {
global my_data
global my_file
global my_mode

if {[eof $f]} {
    fileevent $f readable {}
    close $f
    return
}

set my_data [read -nonewline $f]
regsub -all {<[0-9]+>[0-9]+: } $my_data " " output

if {[string length $output]} {
    puts stdout "$output"
    if {[expr ($my_mode == 2)]} {
    puts $my_file $output
}
}
}

###################################
#check if input any
if {$argc == 0} {
puts "Usage: syslogd port filename"
puts "port is the TCP port to listing for incoming connection"
puts "filename is optional parameter to use for writing the syslog data"
return
}
set port [lindex $argv 0]

#check if input a port number
if {[expr (1 != [string is digit $port])]} {
puts "must provide a numeric port number"
return
}

#verify port is in the valid range
if ([expr (1 != (0 < $port))]) {
puts "port number too low"
return
}

#verify port is in the valid range
if ([expr (1 != ($port < 65536))]) {
puts "port number too high"
return
}

if {$argc == 1} {

#only provide port
Listener $port START 0
} elseif {$argc == 2} {

#save filename
set my_filename [lindex $argv 1]

#varify inputs
Listener $port STARTWriting $my_filename
} else {
puts "Usage: syslogd port filename"
puts "port is the TCP port to listing for incoming connection"
puts "filename is optional parameter to use for writing the syslog data"
return
}

exec "term esc 27"
vwait my_sock


run the script on TCLrouter:

TCLRouter#tclsh flash:syslogd_book2.tcl 9500 flash:syslog.txt
socket is connected now
 *Mar  1 00:17:52.487: %LINK-3-UPDOWN: Interface Loopback0, changed state to up
 *Mar  1 00:17:55.239: %LINK-5-CHANGED: Interface Loopback0, changed state to a
ministratively down
 *Mar  1 00:18:03.739: %SYS-6-LOGGINGHOST_STARTSTOP: Logging to host 12.12.12.1
started - CLI initiated


on R2, cancel recording logs to TCLrouter

r2(config)#no logging host 12.12.12.1 transport tcp port 9500


simultaneously, script will stop.

TCLRouter#

Connection to host lost.


a file was generated by the script

TCLRouter(tcl)#dir
Directory of flash:/

    1  -rw-        2258                    <no date>  syslogd_book2.tcl
    2  -rw-         308                    <no date>  syslog.txt


 check the file with logs

TCLRouter(tcl)#more flash:syslog.txt
 *Mar  1 00:17:52.487: %LINK-3-UPDOWN: Interface Loopback0, changed state to up
 *Mar  1 00:17:55.239: %LINK-5-CHANGED: Interface Loopback0, changed state to administratively down
 *Mar  1 00:18:03.739: %SYS-6-LOGGINGHOST_STARTSTOP: Logging to host 12.12.12.1 started - CLI initiated




On sender router, you may want to filter some kinds of syslog by using this code:

if [string match "*by console" $::orig_msg] {
return ""
} else {
return $::orig_msg


this code is used for filtering the syslog generated after exiting configuration mode. 

logging console filtered 
logging filter flash:filter3.tcl   


 you may also want to customize syslog by using tcl script. here is the code:

 set text $::format_string
set listp 0

while {$listp < [llength $::msg_args]} {
set beg [string first %s $text]
set end $beg
incr end
set text [string replace $text $beg $end [lindex $msg_args $listp]]
incr listp
}

return "$buginfseq$timestamp: %$facility-$severity-$mnemonic: $text"


save it as filter7.tcl,  setup global configurations for  "timestamp" and "sequence number"
service timestamps log datetime
service sequence-numbers 

exit configuration mode. syslog is displayed as follows:

sender#000077: *Mar  1 03:02:26: SYS-5-CONFIG_I: Configured from console by console

 

No comments:

Post a Comment