diff --git a/README.md b/README.md index 274c94a..a4b4355 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ switch# wireshark
Examples: [syntax] wireshark - + wireshark ip any any wireshark ip 192.168.25.2 any wireshark ip 192.168.25.2 192.168.30.20 Gi1/0/1 @@ -29,7 +29,15 @@ switch# wireshark
wireshark udp 192.168.25.2 any wireshark udp 192.168.25.2 192.168.30.20:53 Gi1/0/1 wireshark udp 192.168.25.2:53 192.168.30.20 Gi1/0/1 40 10 - + + [syntax] wireshark erspan + wireshark erspan ip any any + wireshark erspan ip any any 172.33.11.23 Gi1/0/1 + wireshark erspan ip any any 172.33.11.23 Gi1/0/1 2.2.2.2 + wireshark erspan ip any any 172.33.11.23 Gi1/0/1 2.2.2.2 50 + wireshark erspan ip any any 172.33.11.23 Gi1/0/1 2.2.2.2 50 rx + wireshark erspan --debug tcp any any 172.33.11.23 + ***If you want display pcap on cli examples: wireshark filter @@ -45,3 +53,5 @@ switch# wireshark
![Image of Help](https://github.com/settlej/Simple_Packet_Capture/blob/master/screen_shots/normal.gif)

# Debug Run ![Image of Help](https://github.com/settlej/Simple_Packet_Capture/blob/master/screen_shots/debug.gif)

+# ERSPAN Run +![Image of Help](https://github.com/settlej/Simple_Packet_Capture/blob/master/screen_shots/erspandemo.gif)

diff --git a/capture_program.tcl b/capture_program.tcl index a6266db..cf9c3eb 100644 --- a/capture_program.tcl +++ b/capture_program.tcl @@ -24,7 +24,24 @@ # # Simple Cisco Packet Capture. -proc progressbar {cur tot count} { + +############################################################################### +# +# Created to simplify packet capture program on Cisco devices that support +# native packet capture. Many Cisco platforms require different commands +# to start packet capture. There was also the issue of requiring +# multiple commands up to 14 different commands to start capturing. This is +# depending on how granularity of capture. This project was created to simplify +# said operations in a "common" user friendly interface on any supported +# platform. Capture can happen in just 2 user inputs (initial deceleration and +# approving capture confirmation) +# +############################################################################### + + +# prints progressbar to count down time until program is done captureing +# progress is based on the $duration variable. +proc progressbar {cur tot} { # if you don't want to redraw all the time, uncomment and change ferquency #if {$cur % ($tot/300)} { return } # set to total width of progress bar @@ -32,32 +49,38 @@ proc progressbar {cur tot count} { set half [expr {$total/2}] set percent [expr {100.*$cur/$tot}] set val (\ [format "%6.2f%%" $percent]\ ) - #set str "[string repeat \b 50] ) |[string repeat = [ - # expr {round($percent*$total/100)}]][ - # string repeat { } [expr {$total-round($percent*$total/100)}]]| [string repeat \b 52]" - #set str "[string range $str 0 $half]$val[string range $str [expr {$half+[string length $val]-1}] end]" - set str "|[string repeat = [expr {round($percent*$total/100)}]][string repeat { } [expr {$total-round($percent*$total/100)}]]|[string repeat \b 110]" - set str "$val [string range $str 0 $half][string range $str [expr {$half+[string length $val]-1}] end]" - puts -nonewline stderr $str + # '\b' is backspace in tcl on the terminal to continue using same terminal line for progress bar progression, by placing the cursor to the beginning + set bar "|[string repeat = [expr {round($percent*$total/100)}]][string repeat { } [expr {$total-round($percent*$total/100)}]]|[string repeat \b 110]" + set bar "$val [string range $bar 0 $half][string range $bar [expr {$half+[string length $val]-1}] end]" + puts -nonewline stderr $bar } +# function to clear screen but only displaying capture section proc clear_screen {} { + # requires enabling 8 bit support which is not on by default in vty lines ios_config "line vty 0 15" "international" puts "\x1B\x5B\x32\x4A\x1B\x5B\x30\x3B\x30\x48" + # remove once cleared ios_config "line vty 0 15" "no international" } +# function to display string only when debug variable set to "1" proc debugputs {msg} { if {$::debug == 1} { puts "\[DEBUG\]\: $msg" } } +# function to send to debug checker for display or not then execute IOS command without using "exec" tcl command +# Some IOS commands requried confirmation and "exec" puts command in blocking state until done, which is not +# useful when needing to see output on stdout. proc noexec_perform {event} { debugputs $event $event } +# function to send to debug checker for display or not then execute IOS command using "exec" tcl command +# exec allows for output capture to variables. proc perform {event {iosconfig { }} } { debugputs $event if {$iosconfig != { }} { @@ -67,81 +90,143 @@ proc perform {event {iosconfig { }} } { } } -proc gatherinformation_and_begin_capture {version protocol ipsource {ipdest any} {sinterface nothing} {duration {}} {size {}} } { +proc getlargest_interface {interfacelist} { + # declaring starting numbers + set currentmax 0 + set largestinterface "" + foreach {interface} $interfacelist { + # if interface is longer than previous largest known interface, declare new interface as largest + if {[expr [string bytelength $interface] > $currentmax]} { + set largestinterface $interface + set currentmax [string bytelength $interface] + } + } + return $currentmax +} + +# function to parse and search inital capture declaration, if any missing info request via question dialog +proc gatherinformation_and_begin_capture {version protocol ipsource ipdest {sinterface nothing} {duration {}} {size {}} {mtu 172}} { if {$sinterface == "nothing"} { + # If interface not provide at start ask for interface or control-plane capture puts "\nCapture Type:\n 1.\) Interface \[default\]\n 2.\) Control-Plane" + # puts nonewline will not include "\n" at the end but you need to do a "flush" to empty the stdout channel for print on screen puts -nonewline "\nSelection: " flush stdout + # Ask user input with "gets", it will look at the standard-in channel + # depending if type is interface vs control-plane capture will determint capture commands gets stdin {ctype} switch $ctype { "1" {set ctype "Interface"} "2" {set ctype "control"} default {set ctype "Interface"}} - # get list of interfaces if {$ctype == "Interface"} { puts "\nAvailable Interfaces:" + # Grab interfaces from "show ip interface brief" and using regex to grap interface names into a list set foundinterfaces [regexp -all -inline {[A-Za-z-]+\d\/?\d?\/?\d?\/?\d?\d?} [exec "show ip int br"]] - #set foundinterfaces [regexp -all -inline {([A-Za-z]+\d\/)+[\d\/]+} [exec "show ip int br"]] - foreach {a b c d e f g} [join $foundinterfaces ", "] {puts "$a $b $c $d $e $f $g"} + # find longest interface name for padding on display, used in format below + set largestinterfacesize [expr [getlargest_interface $foundinterfaces] + 2] + foreach {a b c d e} [join $foundinterfaces " "] { + # add padding for clean looking screen display + set a [format {%-*s} $largestinterfacesize $a] + set b [format {%-*s} $largestinterfacesize $b] + set c [format {%-*s} $largestinterfacesize $c] + set d [format {%-*s} $largestinterfacesize $d] + set e [format {%-*s} $largestinterfacesize $e] + # creates 5 column print based on padding + puts "$a $b $c $d $e"} set i 0 puts " " while {$i < 1} { - puts "Which interface to packet capture \[exact syntax needed\]?" + puts "Which interface to packet capture \[exact name needed\]?" puts -nonewline "\nSelection: " flush stdout gets stdin {sinterface} if {$sinterface == "exit"} {incr i} else {} if {$i > 0} {continue} else {} + # simple error checking via searching interface list with its exact name from user input if {[lsearch -exact $foundinterfaces "$sinterface"] == -1} { puts "\nInterface Not found...\n"} else {incr i} } } } else { - switch -glob $sinterface { - [c|C]ontrol* {set ctype control} - default {set ctype Interface} - }} - + # if original argument includes a $sinterface based on what was defined set the capture-type (ctype) + switch -glob $sinterface { + [c|C]ontrol* {set ctype control} + default {set ctype Interface} + } + } + # if duration or size were not initially defined ask user if { $duration == {} || $size == {} } { - if {$duration == {}} { - puts -nonewline "\nHow long to run capture? <1-300 seconds> \[Default=20\] : " - flush stdout - gets stdin {duration} - if {[string trim $duration] > 0} { - } else {set duration 20;} - } - if {$size == {}} { - puts -nonewline "\nMax Capture size? <1-50 MB> \[Default=10\] : " - flush stdout - gets stdin {size} - if {[string trim $size] > 0} {} else {set size 10} - } - startcapture $version $protocol $ipsource $ipdest $ctype $sinterface $duration $size + if {$duration == {}} { + set valid_num 0 + while {$valid_num == 0} { + puts -nonewline "\nHow long to run capture? <5-300 seconds> \[Default=20\] : " + flush stdout + gets stdin {duration} + if {$duration == ""} { + set duration 20 + incr valid_num + } + set results [verify_number_range [string trim $duration] 5 300] + if {$results == 0} { + puts "Invalid Duration Time" + } else { + incr valid_num + } + } + } + if {$size == {}} { + set valid_num 0 + while {$valid_num == 0} { + puts -nonewline "\nMax Capture size? <1-50 MB> \[Default=10\] : " + flush stdout + gets stdin {size} + if {$size == ""} { + set size 10 + incr valid_num + } + set results [verify_number_range [string trim $size] 1 50] + if {$results == 0} { + puts "Invalid Size" + } else { + incr valid_num + } + } + if {[string trim $size] > 0} {} else {set size 10} + } + startcapture $version $protocol $ipsource $ipdest $ctype $sinterface $duration $size $mtu } else { - startcapture $version $protocol $ipsource $ipdest $ctype $sinterface $duration $size + startcapture $version $protocol $ipsource $ipdest $ctype $sinterface $duration $size $mtu } } -proc versionsearch {version protocol ipsource ipdest ctype sinterface duration size} { +# redirect program to function that houses commands needed to do packet capture based on paltform version +proc versionsearch {version protocol ipsource ipdest ctype sinterface duration size mtu} { + # -glob will provide regex searching via wildcard matching "*" + # if no match inform platform is unsupported switch -glob $version { - 9* {capture_commands9000 $protocol $ipsource $ipdest $ctype $sinterface $duration $size} - 44* {capture_commands4400 $protocol $ipsource $ipdest $ctype $sinterface $duration $size} - 1004 {capture_commands4400 $protocol $ipsource $ipdest $ctype $sinterface $duration $size} - 45* {capture_commands4500 $protocol $ipsource $ipdest $ctype $sinterface $duration $size} - 38* {capture_commands3800 $protocol $ipsource $ipdest $ctype $sinterface $duration $size} - 3* {capture_commands3000 $protocol $ipsource $ipdest $ctype $sinterface $duration $size} - 100* {capture_commands1000 $protocol $ipsource $ipdest $ctype $sinterface $duration $size} + 9* {capture_commands9000 $protocol $ipsource $ipdest $ctype $sinterface $duration $size $mtu} + 44* {capture_commands4400 $protocol $ipsource $ipdest $ctype $sinterface $duration $size $mtu} + 1004 {capture_commands4400 $protocol $ipsource $ipdest $ctype $sinterface $duration $size $mtu} + 45* {capture_commands4500 $protocol $ipsource $ipdest $ctype $sinterface $duration $size $mtu} + 38* {capture_commands3800 $protocol $ipsource $ipdest $ctype $sinterface $duration $size $mtu} + 3* {capture_commands3000 $protocol $ipsource $ipdest $ctype $sinterface $duration $size $mtu} + 100* {capture_commands1000 $protocol $ipsource $ipdest $ctype $sinterface $duration $size $mtu} default {puts "Unsupported Version!"} } } -proc startcapture {version protocol ipsource ipdest ctype sinterface duration size} { +# function to provide confirmation before execution of capture commands +proc startcapture {version protocol ipsource ipdest ctype sinterface duration size mtu} { + # provided print out on screen what the capture will 'capture' and what are the limitations + # Includes warnings and termination messages if user requrements are in question or unreasonable puts "Capture Overview\n" + # 'repeat' will print the '-' based on the number of times declared next to it puts [string repeat - 37] if {$sinterface != "nothing" || $sinterface == "control"} { - puts "Capture: $sinterface \nDuration: $duration Sec \nFile Size: $size MB" + puts "Capture: $sinterface \nDuration: $duration Sec \nFile Size: $size MB \nMax Packet Size: $mtu bytes" } else { - puts "Capture Type: Control-Plane \nDuration: $duration Sec \nFile Size: $size MB" + puts "Capture Type: Control-Plane \nDuration: $duration Sec \nFile Size: $size MB\nMax Packet Size: $mtu bytes" } if { $ipsource == "any" && $ipdest == "any" } { puts "Capture ACL: $protocol any any" @@ -165,6 +250,7 @@ proc startcapture {version protocol ipsource ipdest ctype sinterface duration si set flashsize $result set bytesizefree [lindex [regexp -all -inline {\S+} [lindex [split $flashsize {(}] 1]] 0] set bytesizefree "${bytesizefree}.0" + # want to make sure there is enough storage if {[expr [expr $bytesizefree - $size] < 10000]} { puts "***TERMINATING! Not enought Free Space Available*** "; return } @@ -182,11 +268,12 @@ proc startcapture {version protocol ipsource ipdest ctype sinterface duration si flush stdout gets stdin {start} switch -glob $start { - y* { versionsearch $version $protocol $ipsource $ipdest $ctype $sinterface $duration $size} + y* { versionsearch $version $protocol $ipsource $ipdest $ctype $sinterface $duration $size $mtu} default { puts "\nCanceling!" } } } - + +# function to build acl format to create acl to filter captured packets proc acl_generator {protocol ipsource ipdest} { if { $ipsource == "any" && $ipdest == "any" } { set any_s_d " permit $protocol any any" @@ -208,8 +295,6 @@ proc acl_generator {protocol ipsource ipdest} { set source_dest "permit $protocol host [lindex $sourcesplit 0] eq [lindex $sourcesplit 1] host [lindex $destsplit 0] eq [lindex $destsplit 1]" set dest_source "permit $protocol host [lindex $destsplit 0] eq [lindex $destsplit 1] host [lindex $sourcesplit 0] eq [lindex $sourcesplit 1]" } - - } else { if { [regexp {\:} $ipsource] || [regexp {\:} $ipdest] } { if { [regexp {\:} $ipsource] } { @@ -223,7 +308,6 @@ proc acl_generator {protocol ipsource ipdest} { set dest_source "permit $protocol $ipdest host [lindex $sourcesplit 0] eq [lindex $sourcesplit 1]" } } else { - # set destsplit [ split $ipdest {:} ] if {[regexp -nocase {any} $ipdest] } { set source_dest "permit $protocol $ipdest host [lindex $sourcesplit 0] eq [lindex $sourcesplit 1]" set dest_source "permit $protocol host [lindex $sourcesplit 0] eq [lindex $sourcesplit 1] $ipdest" @@ -248,8 +332,8 @@ proc acl_generator {protocol ipsource ipdest} { } } } else { - set source_dest "permit ip host $ipsource host $ipdest" - set dest_source "permit ip host $ipdest host $ipsource" + set source_dest "permit $protocol host $ipsource host $ipdest" + set dest_source "permit $protocol host $ipdest host $ipsource" } } else { if { $ipsource == "any" && $ipdest != "any" } { @@ -268,8 +352,8 @@ proc acl_generator {protocol ipsource ipdest} { set dest_source " permit $protocol host $ipdest any" } } else { - set source_dest "permit ip any host $ipdest" - set dest_source "permit ip host $ipdest any" + set source_dest "permit $protocol any host $ipdest" + set dest_source "permit $protocol host $ipdest any" } } else { if { $protocol == "tcp" || $protocol == "udp" } { @@ -287,8 +371,8 @@ proc acl_generator {protocol ipsource ipdest} { set dest_source " permit $protocol host $ipsource any" } } else { - set source_dest "permit ip host $ipsource any" - set dest_source "permit ip $ipdest any host $ipsource" + set source_dest "permit $protocol host $ipsource any" + set dest_source "permit $protocol any host $ipsource" } } } @@ -300,8 +384,73 @@ proc acl_generator {protocol ipsource ipdest} { } } -proc capture_commands3000 { protocol ipsource ipdest ctype sinterface duration size} { +# function to provide status of capture session +proc check_capture_status {} { + switch -glob $::version { + 356* {set status [exec "show monitor capture point POINT"]} + default {set status [exec "show monitor capture CAPTURE"]} } + if {[regexp {Inactive} $status]} { + # capture is not operational + return 0 + } else { + # capture is operational + return 1 + } + puts status +} + +# function to display moving progress bar +# will check to see if capture is actually running, +# usually will run for the full duration time period +proc run_progress_bar {total} { + set finished "false" + set startcheck 0 + set started_succesfully -1 + for {set i 0} {$i <= [expr $total]} {incr i} { + if {$i == 0} {puts "\nStarting!\n"} + progressbar $i $total + flush stdout + if {$finished == "true"} { return } + after 1000 + if {$i == 1} {set startcheck [check_capture_status] + if {$startcheck == 1} { + set started_succesfully 1 + } else { + after 2000 + set startcheck [check_capture_status] + if {$startcheck == 1} { + set started_succesfully 1 + } else { + set started_succesfully 0 + puts "\nFailed to start capture, check logs and investigate\n" + } + } + } + if { [expr {($i % 3) == 0}]} { + #check every 3 seconds if completed or never started. + set currentstatus [check_capture_status] + # If started_successfully is 1 then capture was "Active" based on show command + # is currentstatus changes to 0 then capture is "Inactive" based on show command + # if 1 and 0 then capture may have reached size limit and early terminated capture + # else 0 and 0 then capture didn't seem to start 3 secs into start + if { $started_succesfully == 1 && $currentstatus == 0} {set i [expr $total - 1]; set finished "true"} + if { $started_succesfully == 0 && $currentstatus == 0 && $i < [expr $total - 2]} { + puts "" + puts "Capture didn't seem to start, please check logs" + set i [expr $total + 1] + set finished "true" + } + } + } +} + +# function for capture commands on 3000 series switches except for 3850 +proc capture_commands3000 { protocol ipsource ipdest ctype sinterface duration size mtu} { perform "monitor capture point stop all" + if {[file exists flash:CAPTURE.pcap]} { + debugputs "Deleting flash:CAPTURE.pcap" + file delete -force -- flash:CAPTURE.pcap + } perform "no access-list 199" iosconfig set aclresults [split [acl_generator $protocol $ipsource $ipdest] ":"] if {[llength $aclresults] == 1} { @@ -324,31 +473,36 @@ proc capture_commands3000 { protocol ipsource ipdest ctype sinterface duration s # <256-102400 Kbytes> perform "monitor capture buffer BUFF limit duration $duration" # <1-2000 sec> - debugputs "\(INFO\) Max MTU capture set to 172" - perform "monitor capture buffer BUFF max-size 172" + debugputs "\(INFO\) Max MTU capture set to $mtu" + perform "monitor capture buffer BUFF max-size $mtu" if {$ctype == "control"} { perform "monitor capture point ip process-switched POINT both" } else { perform "monitor capture point ip cef POINT $sinterface both" - } + } perform "monitor capture point associate POINT BUFF" # show monitor capture buffer BUFF parameters # or show monitor capture buffer BUFF dump perform "monitor capture point start POINT" - set total $duration - for {set i 0 } {$i <= $total} {incr i} {if {$i == 0} {puts "Starting!\n"}; progressbar $i $total $duration; flush stdout ; after 1000; incr duration -1} + run_progress_bar $duration puts "" debugputs "\(INFO\) Checking capture status" set check [perform "show monitor capture point POINT"] set stop 0 while {$stop < 2} { - if {[regexp {Inactive} $check]} { - set stop 3; - } else {puts "Compiling packet capture to pcap format..."; after 3000; set check [exec "show monitor capture point POINT"]; incr stop} + # if the progress bar is done, need to check if the device correctly stops the capture, possible it needs to convert + # capture to pcap output. If the capture is still running after 2 intervals of 3 seconds (6 sec) then manually stop capture + if {[regexp {Inactive} $check]} { + set stop 3; + } else { + puts "Compiling packet capture to pcap format..."; after 3000; set check [exec "show monitor capture point POINT"]; incr stop + } } perform "monitor capture point stop POINT" - debugputs "\(INFO\) Exporting capture to flash:CAPTURE.pcap" + puts "Exporting capture to flash:CAPTURE.pcap" + # export packets in memory to flash as a pcap file perform "monitor capture buffer BUFF export flash:CAPTURE.pcap" + # delete all capture commands and ACL if {$ctype == "control"} { perform "no monitor capture point ip process-switched POINT both" } else { @@ -361,7 +515,7 @@ proc capture_commands3000 { protocol ipsource ipdest ctype sinterface duration s } - +# 4500 switches need a secondary interface to get bi-directional traffic (read on capture_commands4500 function comments) proc find_dest_next_hop_interface {destlookupnexthop} { set lookup [exec "show ip cef $destlookupnexthop detail | i nexthop"] set nexthopinterface [lindex [split $lookup ] end] @@ -376,19 +530,27 @@ proc find_dest_next_hop_interface {destlookupnexthop} { return $nexthopinterface } } - -proc capture_commands4500 { protocol ipsource ipdest ctype sinterface duration size} { + +# function to perform capturing commands on the catalyst 4500 platform +proc capture_commands4500 { protocol ipsource ipdest ctype sinterface duration size mtu} { perform "no ip access-list extended CAPTURE-FILTER" iosconfig - if {[regexp {any} $ipsource] && [regexp {any} $ipdest]} { + # 4500 seems to have any 'inbound' only limitation for capturing TCP traffic. + # In order to capture bi-directional traffic need to capture on 2nd interface inbound where its going and coming. + # Because 2 interfaces are monitored there needs to be filtering to capture only traffic destined to monitor interface + # and if 'any any' is defined then you might capture lots of irrelivant information. Single SOURCE IP is a protection + # for busy uplink interfaces if monitoring an access port + if {[regexp {any} $ipsource] && [regexp {any} $ipdest] && $ctype != "control"} { puts "\n" puts "***FAILURE*** Both IP source and IP destination are \"any any\", needs to have single SOURCE IP. \n\n\[ENDING PROGRAM\]" perform "no monitor capture CAPTURE" perform "no ip access-list extended CAPTURE-FILTER" iosconfig perform "no class-map CAPTURE_CLASS_MAP" iosconfig - return + return } - if {$ctype != "control"} { + # using destlookupnexthop funtion to search and return nexthop interface to monitor + # if no destination ip is provide look for the default route nexthop interface + # else provide the next hop for the destination ip provided if {[regexp {any} $ipdest]} { set destlookupnexthop "0.0.0.0/0" } else { @@ -405,12 +567,11 @@ proc capture_commands4500 { protocol ipsource ipdest ctype sinterface duration s } else { puts "\n" puts "***FAILURE*** Failed to find Reverse Interface!!" - puts "\n" - puts "\[ENDING PROGRAM\]" + puts "\n" + puts "\[ENDING PROGRAM\]" return } } - set aclresults [split [acl_generator $protocol $ipsource $ipdest] ":"] if {[llength $aclresults] == 1} { set any_s_d [lindex $aclresults 0] @@ -419,20 +580,21 @@ proc capture_commands4500 { protocol ipsource ipdest ctype sinterface duration s set dest_source [lindex $aclresults 1] } if { [info exists any_s_d] } { - ios_config "ip access-list ex CAPTURE-FILTER" "$any_s_d" - debugputs "ip access-list ex CAPTURE-FILTER $any_s_d" + ios_config "ip access-list extended CAPTURE-FILTER" "$any_s_d" + debugputs "ip access-list extended CAPTURE-FILTER $any_s_d" set captureacl "$any_s_d" } else { - ios_config "ip access-list ex CAPTURE-FILTER" "$source_dest" "$dest_source" - debugputs "ip access-list ex CAPTURE-FILTER" + ios_config "ip access-list extended CAPTURE-FILTER" "$source_dest" "$dest_source" + debugputs "ip access-list extended CAPTURE-FILTER" debugputs " $source_dest" debugputs " $dest_source" set captureacl "$source_dest \n $dest_source" } perform "no monitor capture CAPTURE" - perform "no class-map CAPTURE_CLASS_MAP" iosconfig + perform "no class-map CAPTURE_CLASS_MAP" iosconfig if {[file exists bootflash:CAPTURE.pcap]} { + debugputs "Deleting bootflash:CAPTURE.pcap" file delete -force -- bootflash:CAPTURE.pcap } ios_config "class-map CAPTURE_CLASS_MAP" "match access-group name CAPTURE-FILTER" @@ -443,10 +605,10 @@ proc capture_commands4500 { protocol ipsource ipdest ctype sinterface duration s if {$ctype == "control"} { noexec_perform "monitor capture CAPTURE class-map CAPTURE_CLASS_MAP" noexec_perform "monitor capture CAPTURE file location bootflash:CAPTURE.pcap buffer-size $buffersize size $size control-plane both " - noexec_perform "monitor capture CAPTURE limit duration $duration packet-len 172" + noexec_perform "monitor capture CAPTURE limit duration $duration packet-len $mtu" } else { noexec_perform "monitor capture CAPTURE class-map CAPTURE_CLASS_MAP" - noexec_perform "monitor capture CAPTURE limit packet-len 172" + noexec_perform "monitor capture CAPTURE limit packet-len $mtu" noexec_perform "monitor capture CAPTURE interface $sinterface both file location bootflash:CAPTURE.pcap buffer-size $buffersize size $size limit duration $duration" if {[info exists additionalinterface]} { noexec_perform "monitor capture CAPTURE interface $additionalinterface in" @@ -454,15 +616,18 @@ proc capture_commands4500 { protocol ipsource ipdest ctype sinterface duration s } noexec_perform "monitor capture CAPTURE start" - set total $duration - for {set i 0 } {$i <= $total} {incr i} {if {$i == 0} {puts "Starting!\n"}; progressbar $i $total $duration; flush stdout ; after 1000; incr duration -1} + run_progress_bar $duration puts "" set check [perform "show monitor capture CAPTURE"] set stop 0 while {$stop < 2} { - if {[regexp {Inactive} $check]} { - set stop 3; - } else {puts "Compiling packet capture to pcap format..."; after 3000; set check [exec "show monitor capture CAPTURE"]; incr stop} + # if the progress bar is done, need to check if the device correctly stops the capture, possible it needs to convert + # capture to pcap output. If the capture is still running after 2 intervals of 3 seconds (6 sec) then manually stop capture + if {[regexp {Inactive} $check]} { + set stop 3; + } else { + puts "Compiling packet capture to pcap format..."; after 3000; set check [exec "show monitor capture CAPTURE"]; incr stop + } } perform "monitor capture CAPTURE stop" after 1000 @@ -475,7 +640,7 @@ proc capture_commands4500 { protocol ipsource ipdest ctype sinterface duration s } -proc capture_commands9000 { protocol ipsource ipdest ctype sinterface duration size} { +proc capture_commands9000 { protocol ipsource ipdest ctype sinterface duration size mtu} { perform "no ip access-list extended CAPTURE-FILTER" iosconfig set aclresults [split [acl_generator $protocol $ipsource $ipdest] ":"] if {[llength $aclresults] == 1} { @@ -485,43 +650,47 @@ proc capture_commands9000 { protocol ipsource ipdest ctype sinterface duration s set dest_source [lindex $aclresults 1] } if { [info exists any_s_d] } { - ios_config "ip access-list ex CAPTURE-FILTER" "$any_s_d" - debugputs "ip access-list ex CAPTURE-FILTER $any_s_d" + ios_config "ip access-list extended CAPTURE-FILTER" "$any_s_d" + debugputs "ip access-list extended CAPTURE-FILTER $any_s_d" set captureacl "$any_s_d" } else { - ios_config "ip access-list ex CAPTURE-FILTER" "$source_dest" "$dest_source" - debugputs "ip access-list ex CAPTURE-FILTER" + ios_config "ip access-list extended CAPTURE-FILTER" "$source_dest" "$dest_source" + debugputs "ip access-list extended CAPTURE-FILTER" debugputs " $source_dest" debugputs " $dest_source" set captureacl "$source_dest \n $dest_source" } perform "no monitor capture CAPTURE" - puts "" + puts "" if {[file exists flash:CAPTURE.pcap]} { + debugputs "Deleting flash:CAPTURE.pcap" file delete -force -- flash:CAPTURE.pcap } set buffsize [expr $size * 1000] noexec_perform "monitor capture CAPTURE access-list CAPTURE-FILTER" noexec_perform "monitor capture CAPTURE file location flash:CAPTURE.pcap buffer-size $size size $size" - noexec_perform "monitor capture CAPTURE limit duration $duration packet-len 172" + noexec_perform "monitor capture CAPTURE limit duration $duration packet-len $mtu" if {$ctype == "control"} { noexec_perform "monitor capture CAPTURE control-plane both" } else { noexec_perform "monitor capture CAPTURE interface $sinterface both" } noexec_perform "monitor capture CAPTURE start" - set total $duration - for {set i 0 } {$i <= $total} {incr i} {if {$i == 0} {puts "Starting!\n"}; progressbar $i $total $duration; flush stdout ; after 1000; incr duration -1} + run_progress_bar $duration puts "" set check [perform "show monitor capture CAPTURE"] set stop 0 while {$stop < 2} { - if {[regexp {Inactive} $check]} { - set stop 3; - } else {puts "Compiling packet capture to pcap format..."; after 3000; set check [exec "show monitor capture CAPTURE"]; incr stop} + # if the progress bar is done, need to check if the device correctly stops the capture, possible it needs to convert + # capture to pcap output. If the capture is still running after 2 intervals of 3 seconds (6 sec) then manually stop capture + if {[regexp {Inactive} $check]} { + set stop 3; + } else { + puts "Compiling packet capture to pcap format..."; after 3000; set check [exec "show monitor capture CAPTURE"]; incr stop + } } - puts "Exporting capture to flash:CAPTURE.pcap" + puts "Exporting capture to flash:CAPTURE.pcap \(Warning slow\)" perform "monitor capture CAPTURE stop" perform "no monitor capture CAPTURE" perform "no ip access-list extended CAPTURE-FILTER" iosconfig @@ -530,7 +699,8 @@ proc capture_commands9000 { protocol ipsource ipdest ctype sinterface duration s finish_statement } -proc capture_commands3800 { protocol ipsource ipdest ctype sinterface duration size} { +# function for 3850 platform +proc capture_commands3800 { protocol ipsource ipdest ctype sinterface duration size mtu} { perform "no ip access-list extended CAPTURE-FILTER" iosconfig set aclresults [split [acl_generator $protocol $ipsource $ipdest] ":"] if {[llength $aclresults] == 1} { @@ -540,49 +710,49 @@ proc capture_commands3800 { protocol ipsource ipdest ctype sinterface duration s set dest_source [lindex $aclresults 1] } if { [info exists any_s_d] } { - ios_config "ip access-list ex CAPTURE-FILTER" "$any_s_d" - debugputs "ip access-list ex CAPTURE-FILTER $any_s_d" + ios_config "ip access-list extended CAPTURE-FILTER" "$any_s_d" + debugputs "ip access-list extended CAPTURE-FILTER $any_s_d" set captureacl "$any_s_d" } else { - ios_config "ip access-list ex CAPTURE-FILTER" "$source_dest" "$dest_source" - debugputs "ip access-list ex CAPTURE-FILTER" + ios_config "ip access-list extended CAPTURE-FILTER" "$source_dest" "$dest_source" + debugputs "ip access-list extended CAPTURE-FILTER" debugputs " $source_dest" debugputs " $dest_source" set captureacl "$source_dest \n $dest_source" } - perform "no monitor capture CAPTURE" perform "no class-map CAPTURE_CLASS_MAP" iosconfig - if {[file exists flash:CAPTURE.pcap]} { + debugputs "Deleting flash:CAPTURE.pcap" file delete -force -- flash:CAPTURE.pcap } ios_config "class-map CAPTURE_CLASS_MAP" "match access-group name CAPTURE-FILTER" debugputs "class-map CAPTURE_CLASS_MAP" - debugputs " match access-group name CAPTURE-FILTER" + debugputs " match access-group name CAPTURE-FILTER" if {$ctype == "control"} { noexec_perform "monitor capture CAPTURE class-map CAPTURE_CLASS_MAP " - noexec_perform "monitor capture CAPTURE limit packet-len 172" + noexec_perform "monitor capture CAPTURE limit packet-len $mtu" noexec_perform "monitor capture CAPTURE file location flash:CAPTURE.pcap buffer-size $size limit duration $duration" noexec_perform "monitor capture CAPTURE control-plane both " - } else { noexec_perform "monitor capture CAPTURE class-map CAPTURE_CLASS_MAP" - noexec_perform "monitor capture CAPTURE limit packet-len 172" + noexec_perform "monitor capture CAPTURE limit packet-len $mtu" noexec_perform "monitor capture CAPTURE file location flash:CAPTURE.pcap buffer-size $size limit duration $duration" noexec_perform "monitor capture CAPTURE interface $sinterface both " } - noexec_perform "monitor capture CAPTURE start" - set total $duration - for {set i 0 } {$i <= $total} {incr i} {if {$i == 0} {puts "\n"}; progressbar $i $total $duration; flush stdout ; after 1000; incr duration -1} + run_progress_bar $duration puts "" set check [perform "show monitor capture CAPTURE"] set stop 0 while {$stop < 2} { - if {[regexp {Inactive} $check]} { - set stop 3; - } else {puts "Compiling packet capture to pcap format..."; after 3000; set check [exec "show monitor capture CAPTURE"]; incr stop} + # if the progress bar is done, need to check if the device correctly stops the capture, possible it needs to convert + # capture to pcap output. If the capture is still running after 2 intervals of 3 seconds (6 sec) then manually stop capture + if {[regexp {Inactive} $check]} { + set stop 3; + } else { + puts "Compiling packet capture to pcap format..."; after 3000; set check [exec "show monitor capture CAPTURE"]; incr stop + } } perform "monitor capture CAPTURE stop" perform "no monitor capture CAPTURE" @@ -592,7 +762,9 @@ proc capture_commands3800 { protocol ipsource ipdest ctype sinterface duration s finish_statement } -proc capture_commands4400 { protocol ipsource ipdest ctype sinterface duration size} { +# function to run capture commands for 4400 platform +proc capture_commands4400 { protocol ipsource ipdest ctype sinterface duration size mtu} { + # make sure not left over config from previous session perform "no ip access-list extended CAPTURE-FILTER" iosconfig set aclresults [split [acl_generator $protocol $ipsource $ipdest] ":"] if {[llength $aclresults] == 1} { @@ -602,53 +774,55 @@ proc capture_commands4400 { protocol ipsource ipdest ctype sinterface duration s set dest_source [lindex $aclresults 1] } if { [info exists any_s_d] } { - ios_config "ip access-list ex CAPTURE-FILTER" "$any_s_d" - debugputs "ip access-list ex CAPTURE-FILTER $any_s_d" - set captureacl "$any_s_d" + ios_config "ip access-list extended CAPTURE-FILTER" "$any_s_d" + debugputs "ip access-list extended CAPTURE-FILTER $any_s_d" + set captureacl "$any_s_d" } else { - ios_config "ip access-list ex CAPTURE-FILTER" "$source_dest" "$dest_source" - debugputs "ip access-list ex CAPTURE-FILTER" + ios_config "ip access-list extended CAPTURE-FILTER" "$source_dest" "$dest_source" + debugputs "ip access-list extended CAPTURE-FILTER" debugputs " $source_dest" debugputs " $dest_source" - set captureacl "$source_dest \n $dest_source" + set captureacl "$source_dest \n $dest_source" } - + # make sure no capture session is running by deleting it perform "no monitor capture CAPTURE" - puts "" - + puts "" if {[file exists flash:CAPTURE.pcap]} { + debugputs "Deleting flash:CAPTURE.pcap" file delete -force -- flash:CAPTURE.pcap } - if {$ctype == "control"} { - noexec_perform "monitor capture CAPTURE limit packet-len 172 duration $duration" + noexec_perform "monitor capture CAPTURE limit packet-len $mtu duration $duration" noexec_perform "monitor capture CAPTURE access-list CAPTURE-FILTER buffer size $size control-plane both" - } else { - noexec_perform "monitor capture CAPTURE limit packet-len 172 duration $duration " + noexec_perform "monitor capture CAPTURE limit packet-len $mtu duration $duration " noexec_perform "monitor capture CAPTURE access-list CAPTURE-FILTER buffer size $size interface $sinterface both" } noexec_perform "monitor capture CAPTURE start" - set total $duration - for {set i 0 } {$i <= $total} {incr i} {if {$i == 0} {puts "Starting!\n"}; progressbar $i $total $duration; flush stdout ; after 1000; incr duration -1} + run_progress_bar $duration puts "" set check [perform "show monitor capture CAPTURE"] set stop 0 while {$stop < 2} { - if {[regexp {Inactive} $check]} { - set stop 3; - } else {puts "Compiling packet capture to pcap format..."; after 3000; set check [exec "show monitor capture CAPTURE"]; incr stop} + # if the progress bar is done, need to check if the device correctly stops the capture, possible it needs to convert + # capture to pcap output. If the capture is still running after 2 intervals of 3 seconds (6 sec) then manually stop capture + if {[regexp {Inactive} $check]} { + set stop 3; + } else { + puts "Compiling packet capture to pcap format..."; after 3000; set check [exec "show monitor capture CAPTURE"]; incr stop + } } debugputs "\(INFO\) Exporting capture to bootflash:CAPTURE.pcap" perform "monitor capture CAPTURE stop" perform "monitor capture CAPTURE export bootflash:CAPTURE.pcap" + # delete all capture commands and ACL perform "no monitor capture CAPTURE" perform "no ip access-list extended CAPTURE-FILTER" iosconfig puts "\nDone!\n" finish_statement } -proc capture_commands1000 { protocol ipsource ipdest ctype sinterface duration size} { +proc capture_commands1000 { protocol ipsource ipdest ctype sinterface duration size mtu} { perform "no ip access-list extended CAPTURE-FILTER" iosconfig set aclresults [split [acl_generator $protocol $ipsource $ipdest] ":"] if {[llength $aclresults] == 1} { @@ -658,22 +832,23 @@ proc capture_commands1000 { protocol ipsource ipdest ctype sinterface duration s set dest_source [lindex $aclresults 1] } if { [info exists any_s_d] } { - ios_config "ip access-list ex CAPTURE-FILTER" "$any_s_d" - debugputs "ip access-list ex CAPTURE-FILTER $any_s_d" + ios_config "ip access-list extended CAPTURE-FILTER" "$any_s_d" + debugputs "ip access-list extended CAPTURE-FILTER $any_s_d" set captureacl "$any_s_d" - } else { - ios_config "ip access-list ex CAPTURE-FILTER" "$source_dest" "$dest_source" - debugputs "ip access-list ex CAPTURE-FILTER" + } else { + ios_config "ip access-list extended CAPTURE-FILTER" "$source_dest" "$dest_source" + debugputs "ip access-list extended CAPTURE-FILTER" debugputs " $source_dest" debugputs " $dest_source" set captureacl "$source_dest \n $dest_source" - } + } perform "no monitor capture CAPTURE" if {[file exists flash:CAPTURE.pcap]} { + debugputs "Deleting flash:CAPTURE.pcap" file delete -force -- flash:CAPTURE.pcap - } - noexec_perform "monitor capture CAPTURE limit packet-len 172" + } + noexec_perform "monitor capture CAPTURE limit packet-len $mtu" noexec_perform "monitor capture CAPTURE access-list CAPTURE-FILTER" noexec_perform "monitor capture CAPTURE buffer size $size" noexec_perform "monitor capture CAPTURE limit duration $duration" @@ -682,21 +857,26 @@ proc capture_commands1000 { protocol ipsource ipdest ctype sinterface duration s noexec_perform "monitor capture CAPTURE control-plane both" } else { noexec_perform "monitor capture CAPTURE interface $sinterface both" - } + } noexec_perform "monitor capture CAPTURE start" - set total $duration - for {set i 0 } {$i <= $total} {incr i} {if {$i == 0} {puts "Starting!\n"}; progressbar $i $total $duration; flush stdout ; after 1000; incr duration -1} + # diplay a progress bar passing how long it will run + run_progress_bar $duration puts "" set check [perform "show monitor capture CAPTURE"] set stop 0 while {$stop < 2} { - if {[regexp {Inactive} $check]} { - set stop 3; - } else {puts "Compiling packet capture to pcap format..."; after 3000; set check [exec "show monitor capture CAPTURE"]; incr stop} + # if the progress bar is done, need to check if the device correctly stops the capture, possible it needs to convert + # capture to pcap output. If the capture is still running after 2 intervals of 3 seconds (6 sec) then manually stop capture + if {[regexp {Inactive} $check]} { + set stop 3; + } else { + puts "Compiling packet capture to pcap format..."; after 3000; set check [exec "show monitor capture CAPTURE"]; incr stop + } } debugputs "\(INFO\)Exporting capture to flash:CAPTURE.pcap" perform "monitor capture CAPTURE stop" perform "monitor capture CAPTURE export flash:CAPTURE.pcap" + # delete all capture commands and ACL perform "no monitor capture CAPTURE" perform "no ip access-list extended CAPTURE-FILTER" iosconfig puts "\nDone!\n" @@ -704,6 +884,10 @@ proc capture_commands1000 { protocol ipsource ipdest ctype sinterface duration s } proc finish_statement {} { + # find current logged in user using "who" and locating astriek * next to username + # sometimes the username is cut off if name is too long, so need to use secondary lookup via "aaa sessions" + # there are times when logged in multiple times to same device can cause multiple aaa sessions, so filter multiple results + # to first found username that matches if multiple returned set allusers [exec "who"] set who_user [foreach {line} [split $allusers "\n"] {if {[regexp {\*} $line]} { set foundx $line; set user [regexp -all -inline {\S+} $foundx]}}; lindex $user 4] set allusers [exec "show aaa sessions | i $who_user"] @@ -721,11 +905,13 @@ proc finish_statement {} { }; lindex $ip 1] puts [string repeat * 20] puts "Get copy of pcap from flash via \"scp\" or SecureFx" + # Display output for Windows Command Prompt, cut and paste to get pcap and opens wireshark set cmdhelper "Windows CMD \'scp $current_user" append cmdhelper "@" append cmdhelper [string range $current_ip 0 end-3] append cmdhelper ":CAPTURE.pcap . & .\\CAPTURE.pcap'" puts $cmdhelper + # Display output for Powershell, cut and paste to get pcap and opens wireshark set cmdhelper "Windows Powershell \'scp $current_user" append cmdhelper "@" append cmdhelper [string range $current_ip 0 end-3] @@ -736,86 +922,56 @@ proc finish_statement {} { proc cli_show_filters { version } { + # version will determine which help to display + # Using switch loop to display correct filter, glob argument provides a regex like match + # bootflash is string argument passed to cli_filter_help switch -glob $version { - 9* {flash_devices} - 45* {bootflash_devices} - 38* {flash_devices} - 3* {flash_devices} - 1004 {puts "CLI File Display Unsupported. Must SCP to local Computer!"} - 100* {flash_devices} - default {puts "CLI File Display Unsupported. Must SCP to local Computer!"} + 9* {cli_filter_help} + 45* {cli_filter_help bootflash} + 38* {cli_filter_help} + 356* {puts "CLI File Display on this device type is Unsupported. Must SCP to local Computer!"} + 1004 {puts "CLI File Display on this device type is Unsupported. Must SCP to local Computer!"} + 100* {cli_filter_help} + default {puts "CLI File Display on this device type is Unsupported. Must SCP to local Computer!"} } } -proc flash_devices {} { +proc cli_filter_help {{storage flash}} { puts " #Basic filters - show monitor capture file bootflash:CAPTURE.pcap brief - show monitor capture file flash:CAPTURE.pcap display-filter icmp - show monitor capture file flash:CAPTURE.pcap display-filter tcp - show monitor capture file flash:CAPTURE.pcap display-filter udp + show monitor capture file $storage:CAPTURE.pcap brief + show monitor capture file $storage:CAPTURE.pcap display-filter icmp + show monitor capture file $storage:CAPTURE.pcap display-filter tcp + show monitor capture file $storage:CAPTURE.pcap display-filter udp #Routing protocols - show monitor capture file flash:CAPTURE.pcap display-filter \"eigrp || ospf || tcp.port == 179\" + show monitor capture file $storage:CAPTURE.pcap display-filter \"eigrp || ospf || tcp.port == 179\" #Web traffic - show monitor capture file flash:CAPTURE.pcap display-filter \"tcp.port == 80 || tcp.port == 443\" + show monitor capture file $storage:CAPTURE.pcap display-filter \"tcp.port == 80 || tcp.port == 443\" #DHCP - show monitor capture file flash:CAPTURE.pcap display-filter dhcp + show monitor capture file $storage:CAPTURE.pcap display-filter dhcp #DNS - show monitor capture file flash:CAPTURE.pcap display-filter \"udp.port == 53\" + show monitor capture file $storage:CAPTURE.pcap display-filter \"udp.port == 53\" #Traffic to or from IP \[First example does src \'and\' dst for 1.1.1.1 use ip.addr\] - show monitor capture file flash:CAPTURE.pcap display-filter \"ip.addr == 1.1.1.1\" - show monitor capture file flash:CAPTURE.pcap display-filter \"ip.src_host == 1.1.1.1\" - show monitor capture file flash:CAPTURE.pcap display-filter \"ip.dst_host == 1.1.1.1\" + show monitor capture file $storage:CAPTURE.pcap display-filter \"ip.addr == 1.1.1.1\" + show monitor capture file $storage:CAPTURE.pcap display-filter \"ip.src_host == 1.1.1.1\" + show monitor capture file $storage:CAPTURE.pcap display-filter \"ip.dst_host == 1.1.1.1\" #TCP Rest Flag - Show monitor capture file flash:CAPTURE.pcap display-filter \"tcp.flags.reset == 1\" + Show monitor capture file $storage:CAPTURE.pcap display-filter \"tcp.flags.reset == 1\" " } -proc bootflash_devices {} { - puts " - - #Basic filters - show monitor capture file bootflash:CAPTURE.pcap brief - show monitor capture file bootflash:CAPTURE.pcap display-filter icmp - show monitor capture file bootflash:CAPTURE.pcap display-filter tcp - show monitor capture file bootflash:CAPTURE.pcap display-filter udp - - #Routing protocols - show monitor capture file bootflash:CAPTURE.pcap display-filter \"eigrp || ospf || tcp.port == 179\" - - #Web traffic - show monitor capture file bootflash:CAPTURE.pcap display-filter \"tcp.port == 80 || tcp.port == 443\" - - #DHCP - show monitor capture file bootflash:CAPTURE.pcap display-filter dhcp - - #DNS - show monitor capture file bootflash:CAPTURE.pcap display-filter \"udp.port == 53\" - - #Traffic to or from IP - \[First example does src \'and\' dst for 1.1.1.1 use ip.addr\] - show monitor capture file bootflash:CAPTURE.pcap display-filter \"ip.addr == 1.1.1.1\" - show monitor capture file bootflash:CAPTURE.pcap display-filter \"ip.src_host == 1.1.1.1\" - show monitor capture file bootflash:CAPTURE.pcap display-filter \"ip.dst_host == 1.1.1.1\" - - #TCP Rest Flag - Show monitor capture file bootflash:CAPTURE.pcap display-filter \"tcp.flags.reset == 1\" - - " -} - proc displayhelp {} { - #puts "\n\[HELP\]:\nProvide source and destination {ip|any} with optional interface + # Help output for methods and option for useage puts "\n\n Examples: \[syntax\] wireshark 20 sec 10 MB 172 mtu @@ -834,7 +990,15 @@ proc displayhelp {} { wireshark udp 192.168.25.2 any wireshark udp 192.168.25.2 192.168.30.20:53 Gi1/0/1 wireshark udp 192.168.25.2:53 192.168.30.20 Gi1/0/1 40 10 - + + \[syntax\] wireshark erspan \ + wireshark erspan ip any any + wireshark erspan ip any any 172.33.11.23 Gi1/0/1 + wireshark erspan ip any any 172.33.11.23 Gi1/0/1 2.2.2.2 + wireshark erspan ip any any 172.33.11.23 Gi1/0/1 2.2.2.2 50 + wireshark erspan ip any any 172.33.11.23 Gi1/0/1 2.2.2.2 50 rx + wireshark erspan --debug tcp any any 172.33.11.23 + ***If you want display pcap on cli examples: wireshark filter @@ -847,12 +1011,19 @@ proc displayhelp {} { } proc getversion {} { + # Find version of device, "show version" then regex find in output the version, + # Search for model number line that contain model type. Usually 3560, 9300, 9400 + # Foreach will loop through every line via "\n" from output and look for "model + # number", once found split the line via spaces in regex -inline -all and gather + # 3rd element. set get_version [exec "show ver"] foreach {line} [split $get_version "\n"] { if {[regexp -nocase "model number" $line]} { set found_version [lindex [regexp -all -inline {\S+} $line] 3] } } + # Most Platforms do not have 'model number' line, + # manual search for supported platforms via regex if {![info exists found_version]} { foreach {line} [split $get_version "\n"] { if {[regexp {(CSR1000V)|(C45\d\d)|(ASR100\d)|(ISR44\d\d)} $line]} { @@ -860,14 +1031,16 @@ proc getversion {} { } } } + # Stop program an inform user device will not work. + # Checking to see if found_version variable is created if {![info exists found_version]} { puts "Unable to determine platform. " return 0 } - + # If device is found get version number and save to $version variable if {[regexp {\d\d\d\d*} $found_version]} { + global version set version [lindex [regexp -inline {\d\d\d\d} $found_version] 0] - puts "Device version: $version" return "$version" } else { puts "Unable to determine platform. " @@ -875,41 +1048,390 @@ proc getversion {} { } } +proc erspan_16code {protocol ipsource ipdest erspandest {sinterface ""} {originip ""} {duration ""} {direction ""} {silent ""}} { + + if {$sinterface == ""} { + # If interface not provide at start ask for interface + puts "\nAvailable Interfaces:" + # Grab interfaces from "show ip interface brief" and using regex to grap interface names into a list + set foundinterfaces [regexp -all -inline {[A-Za-z-]+\d\/?\d?\/?\d?\/?\d?\d?} [exec "show ip int br"]] + # find longest interface name for padding on display, used in format below + set largestinterfacesize [expr [getlargest_interface $foundinterfaces] + 2] + foreach {a b c d e} [join $foundinterfaces " "] { + # add padding for clean looking screen display + set a [format {%-*s} $largestinterfacesize $a] + set b [format {%-*s} $largestinterfacesize $b] + set c [format {%-*s} $largestinterfacesize $c] + set d [format {%-*s} $largestinterfacesize $d] + set e [format {%-*s} $largestinterfacesize $e] + # creates 5 column print based on padding + puts "$a $b $c $d $e"} + set i 0 + puts " " + while {$i < 1} { + puts "Which interface to ERSPAN Monitor \[exact name needed\]?" + puts -nonewline "\nSelection: " + flush stdout + gets stdin {sinterface} + if {$i > 0} {continue} else {} + # simple error checking via searching interface list with its exact name from user input + if {[lsearch -exact $foundinterfaces "$sinterface"] == -1} { puts "\nInterface Not found...\n"} else {incr i} + } + } + if {$originip == ""} { + set ipaddress_available [perform "show ip int br | i Loopback"] + set valid_ip 0 + if {$ipaddress_available != "" && [lindex [regexp -all -inline {\S+} $ipaddress_available] 1] != "unassigned"} { + set originip [lindex [regexp -all -inline {\S+} $ipaddress_available] 1] + } else { + while {$valid_ip < 1} { + puts -nonewline "Unable to located loopback IP. What ip will the ERSPAN session use as source IP \[Default=1.1.1.1\] : " + flush stdout + gets stdin {originip} + if {$originip == ""} { + set originip "1.1.1.1" + } + set valid_ip [verify_valid_ip $originip] + } + } + } + if {$duration == ""} { + set valid_num 0 + while {$valid_num == 0} { + puts -nonewline "For safety measure how long to run ERSPAN, in seconds <5-300 recommended> \[Default 30\]: " + flush stdout + gets stdin {duration} + if {$duration == ""} { + set duration 30 + incr valid_num + } + set results [verify_number_range $duration 5 300] + if {$results == 0} { + puts "Invalid Duration Time" + } else { + incr valid_num + } + } + } + if {$direction == ""} { + set transmit 0 + while {$transmit == 0} { + puts -nonewline "Capture Direction (rx,tx,both) \[Default=both\]: " + flush stdout + gets stdin {direction} + if {$direction == ""} { + set direction both + incr transmit + } + if {[string trim $direction] == "rx" || [string trim $direction] == "tx" || [string trim $direction] == "both"} { + incr transmit + } else { + puts "Invalid direction" + } + } + } + puts "\n" + puts [string repeat * 50] + puts "ERSPAN Montior Interface: $sinterface " + puts "ERSPAN Direction Cap: $direction" + if { $ipsource == "any" && $ipdest == "any" } { + puts "ERSPAN ACL: $protocol any any" + } else { + puts "ERSPAN ACL: $protocol $ipsource $ipdest + $protocol $ipdest $ipsource" + } + puts "ERSPAN Destination: $erspandest " + puts "ERSPAN Origin IP: $originip " + puts "ERSPAN max duration: $duration sec" + puts [string repeat * 50] + puts "" + #puts [string repeat * 50] + puts "\n If ERSPAN is destined to local computer with wireshark in default location + you can open 'CMD' or 'Powershell' \: \"C:\\Program Files\\Wireshark\\Wireshark.exe\" -f \"ip proto 0x2f\"" + puts " \[IMPORTANT\] ERSPAN encapsulates with GRE headers, verify firewalls on network allow GRE traffic" + puts "" + #puts [string repeat * 50] + puts "" + if {[string trim $silent] != "silent"} { + puts -nonewline "Start? \[yes\|no\]: " + flush stdout + gets stdin {start} + switch -glob $start { + y* {puts "\nStarting..."} + default { puts "\nCanceling!"; return } + } + } else { puts "Silent Mode: on" } + # Monitor session variables + set monitor_session "monitor session 15 type erspan-source" + set monitor_description "description tcl created erspan via wireshark program" + if {[regexp {[V|v]lan} $sinterface]} { + set vlannum [string range $sinterface 4 end] + set monitor_source "source vlan $vlannum $direction" + } else { + set monitor_source "source interface $sinterface $direction" + } + set monitor_filter "filter ip access-group ERSPAN-FILTER" + set monitor_destination "destination" + set monitor_destip "ip address $erspandest" + set monitor_id "erspan-id 15" + set monitor_origin "origin ip address $originip" + set monitor_ttl "ip ttl 10" + set monitor_end "end" + # Applet EEM variables + set emergancy_timer [expr $duration + 5] + set applet_name "event manager applet ERSPAN_TIMER" + set applet_descr "description EMERGANCY ERSPAN stop timer" + set applet_event "event timer countdown time $emergancy_timer " + set applet_action1 "action 1.0 cli command \"en\"" + set applet_action2 "action 2.0 cli command \"config t\"" + set applet_action3 "action 3.0 cli command \"monitor session 15 type erspan-source\"" + set applet_action4 "action 4.0 cli command \"shutdown\"" + set applet_action5 "action 5.0 syslog msg \" STOPPING ERSAPN\"" + perform "no $applet_name" iosconfig + after 1000 + debugputs "$applet_name " + debugputs " $applet_descr " + debugputs " $applet_event" + debugputs " $applet_action1" + debugputs " $applet_action2" + debugputs " $applet_action3" + debugputs " $applet_action4" + debugputs " $applet_action5" + debugputs " exit" + ios_config $applet_name $applet_descr $applet_action1 $applet_action2 $applet_action3 $applet_action4 $applet_action5 $applet_descr "exit" + ios_config $applet_name $applet_event "exit" + perform "no ip access-list extended ERSPAN-FILTER" iosconfig + set aclresults [split [acl_generator $protocol $ipsource $ipdest] ":"] + if {[llength $aclresults] == 1} { + set any_s_d [lindex $aclresults 0] + } else { + set source_dest [lindex $aclresults 0] + set dest_source [lindex $aclresults 1] + } + if { [info exists any_s_d] } { + ios_config "ip access-list extended ERSPAN-FILTER" "$any_s_d" + debugputs "ip access-list extended ERSPAN-FILTER $any_s_d" + set erspanacl "$any_s_d" + } else { + ios_config "ip access-list extended ERSPAN-FILTER" "$source_dest" "$dest_source" + debugputs "ip access-list extended ERSPAN-FILTER" + debugputs " $source_dest" + debugputs " $dest_source" + set erspanacl "$source_dest \n $dest_source" + } + debugputs "$monitor_session" + debugputs " $monitor_description " + debugputs " $monitor_source " + debugputs " $monitor_filter " + debugputs " no shutdown" + debugputs " $monitor_destination" + debugputs " $monitor_destip" + debugputs " $monitor_id" + debugputs " $monitor_origin" + debugputs " $monitor_ttl " + debugputs " exit" + debugputs " $monitor_end" + ios_config $monitor_session $monitor_description $monitor_source $monitor_filter "no shut" $monitor_destination $monitor_destip $monitor_id $monitor_ttl monitor_end + ios_config $monitor_session $monitor_destination $monitor_origin + #puts "" + #puts [string repeat * 50] + #puts "\n If ERSPAN is destined to local computer with wireshark in default location + #you can open 'CMD' or 'Powershell' \: \"C:\\Program Files\\Wireshark\\Wireshark.exe\" -f \"ip proto 0x2f\"" + #puts "" + #puts [string repeat * 50] + puts "" + puts "Erspan session will run for $duration seconds" + puts "" + run_progress_bar $duration + puts "\n" + ios_config $monitor_session "shutdown" + debugputs "$monitor_session" + debugputs " shutdown" + perform "no $monitor_session" iosconfig + perform "no ip access-list extended ERSPAN-FILTER" iosconfig + perform "no event manager applet ERSPAN_TIMER" iosconfig + puts "\nDone!\n" +} + + +proc erspan_setup {protocol ipsource ipdest erspandest {sinterface ""} {originip ""} {duration ""} {direction ""} {silent ""}} { + # version will determine which help to display + # Using switch loop to display correct filter, glob argument provides a regex like match + # bootflash is string argument passed to cli_filter_help + set version [getversion] + puts "Device version: $version" + switch -glob $version { + 9* {erspan_16code $protocol $ipsource $ipdest $erspandest $sinterface $originip $duration $direction $silent} + 45* {erspan_4500_15code $protocol $ipsource $ipdest $erspandest $sinterface $originip $duration $direction $silent} + 38* {erspan_16code $protocol $ipsource $ipdest $erspandest $sinterface $originip $duration $direction $silent} + 1004 {erspan_16code $protocol $ipsource $ipdest $erspandest $sinterface $originip $duration $direction $silent} + 100* {cli_filter_help} + default {puts "ERSPAN setup not supported on this device!"} + } +} + +proc verify_valid_ip {ip} { + regexp {^(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(?:\.(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])){3}$} $ip match + if {[info exists match]} { + return 1 + } else { + return 0 + } +} + +proc verify_number_range {provided beginning ending} { + set bresult [string is digit -strict $beginning] + set eresult [string is digit -strict $ending] + set rprovided [string is digit -strict $provided] + if {$bresult == 0 || $eresult == 0 || $rprovided == 0 } { + return 0 + } else { + if {[expr $provided >= $beginning] == 1 && [expr $provided <= $ending] == 1} { + return 1 + } else { + return 0 + } + + } +} + +proc verify_valid_aclip {ip} { + if {$ip == "any"} { + return 1 + } + set ipverify [verify_valid_ip $ip] + if {$ipverify == 1} { + return 1 + } + if {[string match *:* $ip]} { + set dividedstring [split $ip :] + if {[lindex $dividedstring 0] == "any" && [string is digit -strict [lindex $dividedstring 1]]} { + return 1 + } + if {[verify_valid_ip [lindex $dividedstring 0]] && [string is digit -strict [lindex $dividedstring 1]]} { + return 1 + } + } else { + return 0 + } + return 0 +} proc main {} { + # If no arguments passed to program, display help function to provide help examples. + # Stop program after help is dispalyed. if {$::argc == 0} { displayhelp; return } set version [getversion] + # If 1 argument and if is the word 'filter' will dispaly helper function for show commands + # Stop program after help is dispalyed. if {$::argc == 1 && [lindex $::argv 0] == "filter"} { cli_show_filters $version; return } + if {([lindex $::argv 0] == "erspan" || [lindex $::argv 1] == "--debug") && $::argc < 5} { + puts "\nMissing one of the required arguments \ \ \ "; return + } else { + if {[lindex $::argv 0] == "erspan" && [lindex $::argv 1] == "--debug"} { + if {[lindex $::argv 0] == "erspan"} { + clear_screen + global debug; set debug 1 + eval erspan_setup [lrange $::argv 2 end] + return + } else { + puts "\nInvalid option arrangment"; return + } + } + } + if {[lindex $::argv 0] == "erspan" && $::argc < 5} { + puts "\nMissing one of the required arguments \ \ \ "; return + } elseif {[lindex $::argv 0] == "erspan" && $::argc > 10} { + puts "\nToo Many arguments. \ \ \ \ \ \ \"; return + } elseif {[lindex $::argv 0] == "erspan" && $::argc >= 5} { + global debug; set debug 0 + set valid_acl_sourceip 0 + set valid_acl_sourceip [verify_valid_aclip [lindex $::argv 2]] + if {$valid_acl_sourceip == 0} { + puts "Invalid ACL Source IP!" + return + } + set valid_acl_destip 0 + set valid_acl_destip [verify_valid_aclip [lindex $::argv 3]] + if {$valid_acl_destip == 0} { + puts "Invalid ACL Destination IP!" + return + } + set valid_erspan_destip 0 + set valid_erspan_destip [verify_valid_ip [lindex $::argv 4]] + if {$valid_erspan_destip == 0} { + puts "Invalid ERSPAN Destination IP!" + return + } + set valid_erspan_sourceip 0 + if {$::argc >= 7} { + set valid_erspan_sourceip [verify_valid_ip [lindex $::argv 6]] + if {$valid_erspan_sourceip == 0} { + puts "Invalid ERSPAN Source IP!" + return + } + if {$::argc == 8} { + set results [verify_number_range [lindex $::argv 7] 5 300] + if {$results == 0} { + puts "Invalid Duration Time" + return + } + } + } + clear_screen + eval erspan_setup [lrange $::argv 1 end] + return + } + #debug will display commands used during program run if {[lindex $::argv 0] == "--debug"} { if {[expr $::argc < 4]} { puts "\nMissing one of the required arguments \ \ \"; return } else { - clear_screen - global debug; set debug 1; puts "\[Debugging mode\]" if {$version == 0} { return } - if {[expr $::argc == 4]} { - gatherinformation_and_begin_capture $version [lindex $::argv 1] [lindex $::argv 2] [lindex $::argv 3] - } else { - gatherinformation_and_begin_capture $version [lindex $::argv 1] [lindex $::argv 2] [lindex $::argv 3] [lindex $::argv 4] [lindex $::argv 5] [lindex $::argv 6] + set valid_acl_sourceip 0 + set valid_acl_sourceip [verify_valid_aclip [lindex $::argv 2]] + if {$valid_acl_sourceip == 0} { + puts "Invalid ACL Source IP!" + return + } + set valid_acl_destip 0 + set valid_acl_destip [verify_valid_aclip [lindex $::argv 3]] + if {$valid_acl_destip == 0} { + puts "Invalid ACL Destination IP!" + return } + clear_screen + #turn on debug for stdout + global debug; set debug 1; puts "\[Debugging mode\]" + eval gatherinformation_and_begin_capture $version [lrange $::argv 1 end] } return } else { + # if protocol with src and dest, not provide, inform user and end program if {$::argc < 3} { - puts "\nMissing one of the required arguments \ \ \"; return + puts "\nMissing one of the required arguments \ \ \"; return } else { + #turn off debug for stdout global debug; set debug 0 - clear_screen if {$version == 0} { return } - if {[expr $::argc == 3]} { - gatherinformation_and_begin_capture $version [lindex $::argv 0] [lindex $::argv 1] [lindex $::argv 2] - } else { - gatherinformation_and_begin_capture $version [lindex $::argv 0] [lindex $::argv 1] [lindex $::argv 2] [lindex $::argv 3] [lindex $::argv 4] [lindex $::argv 5] + set valid_acl_sourceip 0 + set valid_acl_sourceip [verify_valid_aclip [lindex $::argv 1]] + if {$valid_acl_sourceip == 0} { + puts "Invalid ACL Source IP!" + return + } + set valid_acl_destip 0 + set valid_acl_destip [verify_valid_aclip [lindex $::argv 2]] + if {$valid_acl_destip == 0} { + puts "Invalid ACL Destination IP!" + return } + clear_screen + eval gatherinformation_and_begin_capture $version [lrange $::argv 0 end] } } } diff --git a/screen_shots/erspandemo.gif b/screen_shots/erspandemo.gif new file mode 100644 index 0000000..c302981 Binary files /dev/null and b/screen_shots/erspandemo.gif differ