#! bpf_wish -f # # This is a bpf_wish script to monitor and display counts of ICMP # messages sent in response to multicast traffic (there should be # none). Modified by Steve Casner from the monipm script for IP # multicast traffic by Van Jacobson & Steve McCanne. # # $Header: monicmp $ # ------------------------------------------------------------- # This is a quick-reference for mouse button actions (described # in more detail below): # # Window left button middle button right button # ------ ----------- ------------- ------------ # overall start detail toggle addr/name - # detail start stripchart toggle addr/name delete window # stripchart - - delete window # # In addition to clicking the right button, typing 'q' in any # window makes it go away. If you type 'q' in the summary window, # everything goes away. # ------------------------------------------------------------- # # When you first start the program, it displays a bar chart of all # sources from which ICMP messages are being received in response to # multicast packets. The number to the left of the bar is the count # of ICMP messages received. The bar saturates at 1000, but the # number continues to count so counts up to 9999 can be accurately # read, and if overflow into the fifth digit occurs, this can be # detected because a few pixels of that digit will be visible. The # label to the right of the bar is the ICMP source address. # # For each new bar added to the chart when another source is ICMP # messages is heard, a new window is created to record a detail # barchart of the multicast transmissions that are triggering the ICMP # messages from that source. The labels in the detail barchart are # normally in the form " to /". (If # the port is missing it usually means that the ICMP resulted from an # IP fragment, or that the sender of the ICMP did not properly include # the IP header plus 64 bits.) Such ICMP messages are illegal, and # the offending sources should be upgraded or removed from the # presence of multicast traffic. ICMP messages may also result from # the TTL expiring when multicast packets are being sent across a # srcrt tunnel. In that case, the ICMP message will come from an # intervening router, and the label will be in the form " to # (srcrt to )". Such ICMP messages are legal, and usually result from a # (temporary) routing loop in the unicast routing along the srcrt # tunnel path. However, all srcrt tunnels should be eliminated, then # these ICMPs would not occur. # # If you left click on a bar in the top-level chart, it will deiconify # or expose the detail barchart window for that ICMP source. If that # window has been deleted, it will be recreated. If you left click on # the "total" bar in the top-level chart or on some bar in a detail # barchart, you get a stripchart of the associated ICMP message count. # By default the strip is updated every second and covers the most # recent 5 minutes of traffic. The number in the upper right of the # strip gives the full scale count of packets. The strip will rescale # if necessary to accommodate any increase in packet rate. (The # current version doesn't ever reduce its scale, however. If you find # all the data is scrunched near the bottom, delete the chart & start # a new one.) # # If you middle click on some bar, the label will toggle between an ip # address and hostname (hostnames are not the default because it takes # so damn long to translate from address to name, particularly under # SunOS, and this screws up the data collection). [Note that in this # version of the script it may not be a good idea to leave entries # toggled to the hostname because this screws up the sorting order # when new sources are inserted, if you care about the order.] # # If you right click in a detail barchart window or a stripchart # window, that window will be deleted and the data it contained will # be discarded. The window can be recreated to begin counting anew by # left clicking on the parent barchart. # # If you type 'L' or 'l' in the top-level chart, a nested listing of # all the charts will be output to stdout. Each chart is headed by a # timestamp for the first and last updates to that chart. # # ----------------------------------------------------------------- # # This script is invoked as: # # monicmp [-i interface] [bpf spec] # # Give the "-i" flag is you want to monitor on some other interface # than the first. E.g., "-i le1". If you only want to see ICMP # messages coming to your host, you can append a bpf filter spec: # # monicmp ip dst host & # # where "host" is the name or address of your host. # # ----------------------------------------------------------------- # following are update rate for barcharts (in ms), # max value for barcharts (in Kbits/s) and update rate # for stripcharts (in ms). set brate 3000 set bmax 1000 set srate 1000 # idle time (in multiples of 'brate') before inactive bars are deleted # this is set to 45 minutes since the imm & usenet sources run on that # cycle & we don't want to continuously delete then add them. set maxidle 900 proc pktAction { tap } { global cnt win set s [$tap ip.src] if {[info exists cnt($s)] == 0} { set cnt($s) 0 set win($s) 0 createbar $s } if {$win($s) != 0} { set d [$tap ip.dst] set data [$tap ip.data] scan $data %2x icmptype scan $data %*28s%4x frag set frag [expr $frag & 0x1fff] if {$icmptype == 3} { set mcast [hex2dot [string range $data 48 55]] if {$frag == 0} { scan $data %*60s%4x port set dat $s/$d/$mcast/$port } else { set dat $s/$d/$mcast/ } } else { set mcast [hex2dot [string range $data 72 79]] set tsrc [hex2dot [string range $data 64 71]] set tdst [hex2dot [string range $data 48 55]] if {$frag == 0} { scan $data %*84s%4x port set dat $s/$d/$mcast/$port/$tsrc/$tdst } else { set dat $s/$d/$mcast//$tsrc/$tdst } } if {[info exists cnt($dat)] == 0} { set cnt($dat) 0 if {$icmptype == 3} { createdbar "$d to $mcast/$port" $win($s) $s $dat } else { createdbar "$d to $mcast/$port \(srcrt $tsrc to $tdst\)" $win($s) $s $dat } } incr cnt($dat) 1 } incr cnt($s) 1 incr cnt(total) 1 } proc maxval {v} { if {$v < 7.5} { return 10 } if {$v < 37.5} { return 50 } if {$v < 75} { return 100 } if {$v < 375} { return 500 } if {$v < 750} { return 1000 } if {$v < 3750} { return 5000 } return 10000 } proc scup {w dat s m} { global cnt last set v [expr round(($cnt($dat)-$last($w))*$s)] if {$v>$m} { set m [maxval $v] $w configure -max $m -command "scup $w $dat $s $m" [winfo parent $w].l.r configure -text $m } $w set $v set last($w) $cnt($dat) } set nsc 0 proc createsc {dat max} { global sc nsc last srate cnt incr nsc set f .sc$nsc toplevel $f set last($f.s) $cnt($dat) stripchart $f.s -command "scup $f.s $dat [expr 1000./$srate] $max" \ -interval $srate \ -max $max -stripwidth 1 -width 1 \ -numstrips 300 -relief flat -striprelief flat \ -guaranteedrawing true -borderwidth 0 frame $f.l pack [label $f.l.l -text $dat] -side left pack [label $f.l.r -text "$max"] -side right pack $f.l -fill x pack $f.s bind $f <3> "destroy $f" bind $f.s <3> "destroy $f" bind $f.l <3> "destroy $f" bind $f.l.l <3> "destroy $f" bind $f.l.r <3> "destroy $f" bind $f {focus %W} bind $f q "destroy $f" $f.s start } proc addbar {w b t} { set f 0 foreach i [pack slaves $w] { if {$f>1} { set l [lindex [$i.l configure -text] 4] if {$t<$l} { pack $b -before $i -fill x return } } incr f } pack $b -fill x } proc flipname {w o n} { $w.l configure -text $n bind $w <2> "flipname $w \"$n\" \"$o\"" bind $w.b <2> "flipname $w \"$n\" \"$o\"" bind $w.l <2> "flipname $w \"$n\" \"$o\"" } proc ip2name {w a} { set i [string first " " $a] set j [string first " (srcrt " $a] set k [string last " to " $a] if {$i == -1} { set n [string tolower [gethostbyaddr $a]] if {$n == $a} { return } } else { if {$j == -1} { set b [string range $a 0 [expr $i-1]] set n [string tolower [gethostbyaddr $b]] if {$n == $b} { return } if {[string range $a $i end] == " total"} { set p [winfo parent $w] wm title $p "ICMPs from $n" wm iconname $p $n } set n $n[string range $a $i end] } else { set j [expr $j+7] set n [string tolower "[gethostbyaddr \ [string range $a 0 [expr $i-1]]][string range $a $i $j][gethostbyaddr \ [string range $a [expr $j+1] [expr $k-1]]] to [gethostbyaddr \ [string range $a [expr $k+4] [expr [string length $a]-2]]]\)"] } } flipname $w $a $n } proc hex2dot {x} { scan $x %2x%2x%2x%2x a b c d return $a.$b.$c.$d } set ndbar 0 proc createdbar {d dwin s dat} { global ndbar last idle cnt brate bmax win incr ndbar set f $dwin.$ndbar frame $f -relief sunken -borderwidth 1 set win($f) 0 set w $f.b set last($w) $cnt($dat) set idle($w) 0 bargraph $w -command "bup $dwin $w $dat" \ -interval $brate -max $bmax \ -tickinterval 0 -orient h -relief flat \ -borderwidth 0 pack append $f $w {left} pack append $f [label $f.l -text $d] {left fill} addbar $dwin $f $d if {$s != $dat} { bind $f "unset cnt($dat)" } bind $f <1> "createsc $dat 5" bind $f.b <1> "createsc $dat 5" bind $f.l <1> "createsc $dat 5" bind $f <2> "ip2name $f \"$d\"" bind $f.b <2> "ip2name $f \"$d\"" bind $f.l <2> "ip2name $f \"$d\"" bind $f <3> "destroy $dwin" bind $f.b <3> "destroy $dwin" bind $f.l <3> "destroy $dwin" $w start } set ndwin 0 proc createdwin {s b} { global ndwin win daytime if {$win($s) == 0} { incr ndwin set f .dwin$ndwin toplevel $f wm title $f "ICMPs from $s" wm iconname $f $s set win($s) $f set win($b) $f label $f.time -borderwidth 0 -relief flat -anchor c \ -text $daytime pack append $f $f.time {top} createdbar "$s total" $f $s $s bind $f "winzip $s $b" bind $f {focus %W} bind $f q "destroy $f" } else { wm iconify $win($s) wm deiconify $win($s) # Also expose? (instead of iconify/deiconify) } } proc winzip {s b} { global win set win($s) 0 set win($b) 0 } proc bup {f w dat} { global cnt last idle daytime set amt [expr round($cnt($dat))] if {$amt != 0} { if {$idle($w) == -1} { set p [winfo parent $w] addbar [winfo parent $p] $p \ [lindex [$p.l configure -text] 4] } if {$last($w) != $cnt($dat)} { $w set $amt set s [lindex [$f.time configure -text] 4] if {$s != $daytime} { set s [string range $s 0 15] set d $daytime if {[string range $s 0 9] == \ [string range $d 0 9]} { set d [string range $d 11 end] } $f.time configure -text "$s - $d" } } set idle($w) 0 # monicmp doesn't make use of this idle mechanism (yet). This code is # deleted to make sure that the "0 total" bar doesn't go away when # there are no ICMP messages seen. # } else { # if {[$w get] != 0} { # $w set 0 # } else { # if {$idle($w) != -1} { # global maxidle # incr idle($w) 1 # if {$idle($w)>$maxidle} { # pack forget [winfo parent $w] # set idle($w) -1 # } # } # } } set last($w) $cnt($dat) } set nbar 0 proc createbar {s} { global nbar last brate bmax idle win incr nbar set f .top.$nbar frame $f -relief sunken -borderwidth 1 set win($f) 0 set w $f.b set last($w) 0 set idle($w) 0 bargraph $w -command "bup .top $w $s" \ -interval $brate -max $bmax \ -tickinterval 0 -orient h -relief flat \ -borderwidth 0 pack append $f $w {left} pack append $f [label $f.l -text $s] {left fill} addbar .top $f $s if {$s == "total"} { bind $f <1> "createsc $s 5" bind $f.b <1> "createsc $s 5" bind $f.l <1> "createsc $s 5" } else { bind $f <1> "createdwin $s $f" bind $f.b <1> "createdwin $s $f" bind $f.l <1> "createdwin $s $f" bind $f <2> "ip2name $f $s" bind $f.b <2> "ip2name $f $s" bind $f.l <2> "ip2name $f $s" createdwin $s $f } $w start } proc listbar {f pf} { puts "$pf[$f.b get] [lindex [$f.l configure -text] 4]" } proc listwin {w pf} { global win set f 0 foreach i [pack slaves $w] { if {$f == 0} { puts "$pf[lindex [$i configure -text] 4]" } if {$f == 1} { listbar $i $pf } if {$f>1} { listbar $i $pf if {$win($i) != 0} { listwin $win($i) " $pf" } } incr f } } proc listall { } { puts "" listwin .top "" } proc date { } { global daytime set s [exec date] set daytime [string range $s 0 15][string range $s 19 27] after 60000 date } frame .top bind . q {destroy %W} bind . Q {destroy %W} bind . l listall bind . L listall bind . {focus %W} pack append . .top {top expand fill} date label .top.time -borderwidth 0 -relief flat -anchor c -text $daytime pack append .top .top.time {top} set cnt(total) 0 createbar total set tAction pktAction set tFilter "icmp and \ ((icmp\[0]=3 and icmp\[24]&0xF0=0xE0) or \ (icmp\[0]=11 and icmp\[28:2]=0x0183 and icmp\[36]&0xF0=0xE0)) " set tUserFilter "" set tSize 90 set tInterface "" for {set i 0} {$i<[llength $argv]} {incr i 1} { case [lindex $argv $i] in { -i { incr i 1 set tInterface [lindex $argv $i] } -* { puts stderr "monipm: unknown arg `[lindex $argv $i]'" exit 1 } default { if {$tUserFilter == ""} { set tUserFilter " and" } set tUserFilter "$tUserFilter [lindex $argv $i]" } } } if {$tInterface != ""} { tap pktTap $tAction -f "$tFilter$tUserFilter" -s $tSize -i $tInterface } { tap pktTap $tAction -f "$tFilter$tUserFilter" -s $tSize }