[tkined] discovery code up for grabs...

Poul-Henning Kamp (phk@freebsd.org)
Fri, 13 Feb 1998 13:13:06 +0100

Hi gang!

I had to figure out a network and found the IP-discovery in tkined
somewhat wanting so I hacked my own together. I don't have time
to polish this stuff, but if anybody wants to pick up where I left
off and improve on this I think it would be worth while.

Poul-Henning

#!/bin/sh
# the next line restarts using scotty -*- tcl -*- \
exec scotty2.1.6 "$0" "$@"
#
# ip_world.tcl -
#
# This file contains code to position nodes on image maps based
# on some guesses where those machines are actually located.
#
# Copyright (c) 1994-1996 Technical University of Braunschweig.
#
# See the file "license.terms" for information on usage and redistribution
# of this file, and for a DISCLAIMER OF ALL WARRANTIES.

package require Tnm 2.1

ined size
LoadDefaults layout
IpInit IP-Trouble

set communities {public private}
set candidates ""

# Initialize a couple of lookup tables
set a ffffffff
for {set i 32} {$i >= 0} {incr i -1} {
set b [expr ( 0x$a >> 24) & 0xff]
lappend b [expr ( 0x$a >> 16) & 0xff]
lappend b [expr ( 0x$a >> 8) & 0xff]
lappend b [expr ( 0x$a >> 0) & 0xff]
set a [format %08x [expr 0x$a << 1]]
set bits2mask($i) [join $b .]
set ipmaskwidth([join $b .]) $i
set c ""
foreach j $b {
lappend c [expr 255^$j]
}
set c [join [lrange $c 1 end] .]
set bits2revmask($i) $c
set ipmaskwidth($c) $i
}

set x0 150
set y0 30
set dx 90
set dy 60
set lx 900
set cx $x0
set cy $y0
set ny 30
set dny 30

proc incr_ip {ip} {
set i [split $ip .]
set i0 [lindex $i 0]
set i1 [lindex $i 1]
set i2 [lindex $i 2]
set i3 [lindex $i 3]
incr i3
if {$i3 == 255} {incr i2 ; set i3 0}
if {$i2 == 255} {incr i1 ; set i3 0}
if {$i1 == 255} {incr i0 ; set i3 0}
if {$i0 == 255} { set i3 0}
return "$i0.$i1.$i2.$i3"
}

proc ip_net {ip mask} {
set i [split $ip .]
set m [split $mask .]
set r ""
append r [expr [lindex $i 0] & [lindex $m 0]]
append r "."
append r [expr [lindex $i 1] & [lindex $m 1]]
append r "."
append r [expr [lindex $i 2] & [lindex $m 2]]
append r "."
append r [expr [lindex $i 3] & [lindex $m 3]]
return $r
}

proc ip_default_net {ip} {
set i [lindex [split $ip .] 0]
if {$i < 128} { return "255.0.0.0" }
if {$i < 192} { return "255.255.0.0" }
return "255.255.255.0"
}

## Read back from the image to our data structures
proc "readback" { } {
global subnets bits2mask ipnumbers

catch {unset subnets}
catch {unset ipnumbers}
foreach i [ined retrieve] {
if {[lindex $i 0] == "LINK"} continue
if {[lindex $i 0] == "NETWORK"} {
set a [split [lindex $i 2] /]
set ip [lindex $a 0]
set subnets([list $ip $bits2mask([lindex $a 1])]) [lindex $i 1]
set managed([list $ip $bits2mask([lindex $a 1])]) [lindex $i 1]
continue
}
if {[lindex $i 0] == "NODE"} {
set a [ined address [lindex $i 1]]
set ipnumbers($a) [lindex $i 1]
set l [ined attribute [lindex $i 1] IP:Numbers]
foreach j $l {
set ipnumbers([lindex $j 0]) [lindex $i 1]
puts "readback IP $j"
}
continue
}
}
}

proc conn_to_net {id ip {mask ""}} {
global subnets ipmaskwidth ny dny

if {$mask == ""} {
set net ""
set width 0
set w 0
foreach i [array names subnets] {
set mask [lindex $i 1]
set w $ipmaskwidth($mask)
if {$w < $width} continue
if {[ip_net $ip $mask] == [lindex $i 0]} {
set net $i
set width $w
}
}
if {$net != ""} {
ined create LINK $id $subnets($net)
return
}
}
if {$mask == ""} {
set mask [ip_default_net $ip]
}
set net [ip_net $ip $mask]
set subn [list $net $mask]
if {![info exists subnets($subn)]} {
set subnets($subn) [ined create NETWORK 20 $ny 90 $ny]
incr ny $dny
ined name $subnets($subn) "$net/$ipmaskwidth($mask)"
ined label $subnets($subn) name
}
ined create LINK $id $subnets($subn)
}

##
## Try to get an SNMP session setup
##

proc PokeSnmp {id host ip} {
global communities snmp

set snmp($id) 0
foreach com $communities {
if {$snmp($id)} break
puts "Trying $id $host $ip $com"
if {[catch {snmp session -address $ip -community $com} s]} {
puts $s
} elseif {[catch {
$s get {sysObjectID.0 ifNumber.0} "discover_snmp_callback $id %S %E %V"
} msg]} {
puts "Oops: $ip get sysObjectID.0: $msg"
}
}
snmp wait

}

proc discover_snmp_callback {id s e {v1 ""} {v2 ""}} {
global snmp icon sysid2icon candidates ipnumbers x y dx dy lx x0

if {$e != "noError" || $snmp($id)} {
$s destroy
return
}

set snmp($id) 1
ined -noupdate attribute $id "SNMP:Config" [$s configure]
ined -noupdate attribute $id "ifNumber" [lindex $v2 2]
ined -noupdate color $id "blue"

if {[catch {$s walk x "ifIndex ifAdminStatus" {
puts ">> $x"
set ifstate([lindex [lindex $x 0] 2]) [lindex [lindex $x 1] 2]
} } msg]} {
puts "Oops: $msg from $s"
return
}

set inl [ined -noupdate attribute $id "IP:Numbers"]
$s walk x "ipAdEntAddr ipAdEntNetMask ipAdEntIfIndex" {
set a [lindex [lindex $x 0] 2]
if {$a == "0.0.0.0"} continue
if {$a == "1.1.1.1"} continue
if {$a == "127.0.0.1"} continue
set if [lindex [lindex $x 2] 2]
if {![info exists ifstate($if)]} continue
if {$ifstate($if) != "up"} continue
set m [lindex [lindex $x 1] 2]
set ipnumbers($a) [list $id $a $m $if]
conn_to_net $id $a $m
lappend inl [list $a $m]
puts "ipnumbers($a) $ipnumbers($a)"
}
ined -noupdate attribute $id "IP:Numbers" $inl
$s walk x "ipNetToMediaNetAddress ipNetToMediaPhysAddress" {
if {[lindex [lindex $x 1] 2] == "FF:FF:FF:FF:FF:FF"} continue
set ip [lindex [lindex $x 0] 2]
if {[info exists ipnumbers($ip)]} continue
if {[lsearch -exact $candidates $ip] == -1} {
lappend candidates [lindex [lindex $x 0] 2]
}
}
$s walk x "ipRouteNextHop" {
set ip [lindex [lindex $x 0] 2]
if {[info exists ipnumbers($ip)]} continue
if {[lsearch -exact $candidates $ip] == -1} {
lappend candidates [lindex [lindex $x 0] 2]
}
}
puts $candidates
$s destroy
}

##
## Trace a route to the given host. Returns the route as a tcl list.
##

proc PokeHost { host {maxlen 32} } {
global cx cy lx ly x0 y0 dx dy ipnumbers snmp
set ip $host

puts "PokeHost $host"

if {[regexp "^\[0-9\]+\.\[0-9\]+\.\[0-9\]+\.\[0-9\]+$" $host] > 0} {
set ip $host
} else {
set ip [nslook $host]
}

if {[info exists ipnumbers($ip)]} return

if {[catch {dns -server 195.8.128.1 name $ip} host]} {
set host ""
}
set ping [lindex [icmp echo $ip] 0]
if {[lindex $ping 1] < 0 && $host == ""} return

if {$host == ""} {
set host $ip
}
set id [ined -noupdate create NODE]
ined move $id $cx $cy
incr cx $dx
if {$cx > $lx} {
incr cy $dy
set cx $x0
}
regsub -nocase {.cybercity.dk} $host "" host
ined -noupdate address $id $ip
ined -noupdate label $id name
ined -noupdate name $id $host

set ipnumbers($ip) [list $id]
if {[lindex $ping 1] >= 0} {
ined -noupdate color $id "green"
}
update
PokeSnmp $id $host $ip
puts "---"
if {!$snmp($id)} {
conn_to_net $id $ip
set inl ""
lappend inl [list $ip ""]
set oip [lindex [lindex [icmp ttl 50 $ip] 0] 0]
if {$ip != $oip && $oip != "0.0.0.0"} {
conn_to_net $id $oip
lappend inl [list $oip ""]
}
ined -noupdate attribute $id "IP:Numbers" $inl
}
}

##
## Create a routing trace and map it on the world map.
##

proc "Poke Host" { list } {

global image image_x image_y image_mx image_my
static host

readback

if {![info exists host]} { set host "" }

# ask the user for some destination hosts

set result [ined request "Destination hosts:" \
[list [list Host: $host ] ] [list trace cancel] ]
if {[lindex $result 0] == "cancel"} return

foreach h [set host [lindex $result 1]] {
PokeHost $h
}
}

proc PokeNet {net} {
global bits2mask
set a [split $net /]
set m $bits2mask([lindex $a 1])
set n [lindex $a 0]
set i [incr_ip $n]
while 1 {
set j [incr_ip $i]
if {[ip_net $j $m] != $n} break
PokeHost $i
set i $j
}
}

proc "Poke Net" { list } {

global image image_x image_y image_mx image_my
static host

readback

foreach i $list {
if {[lindex $i 0] != "NETWORK"} continue
PokeNet [lindex $i 2]
}
return
}

proc "Next Host" { list } {

global candidates ipnumbers subnets bits2mask

readback

foreach n [array names subnets] {
if {![is_managed $subnets($n)]} continue
puts "Checking $n $subnets($n)"
puts "candidates before: $candidates"

set net [lindex $n 0]
set mask [lindex $n 1]
set temp $candidates
set candidates ""

foreach i $temp {
if {$i == "0.0.0.0"} continue
if {$i == "1.1.1.1"} continue
if {$i == "127.0.0.1"} continue
if {[info exists ipnumbers($i)]} continue
if {[llength [split $i .]] != 4} continue
puts $i
if {[ip_net $i $mask] == $net} {
PokeHost $i
} else {
lappend candidates $i
}
}
puts "candidates after: $candidates"
}
return
while {[llength $candidates]} {
set host [lindex $candidates 0]
set candidates [lrange $candidates 1 end]
if {$host == "0.0.0.0"} continue
if {$host == "1.1.1.1"} continue
if {$host == "127.0.0.1"} continue
if {[info exists ipnumbers($host)]} continue
PokeHost $host
}
}

proc set_manage {id} {
ined attribute $id Managed "true"
ined font $id 6x13bold
}

proc clr_manage {id} {
ined attribute $id Managed "false"
ined font $id 6x13
}

proc is_managed {id} {
if {[ined attribute $id Managed] == "true"} {return 1}
return 0
}

proc "Manage" { list } {
puts $list
foreach i $list {
set o [lindex $i 1]
if {[is_managed $o]} {
puts "$i already managed"
}
set_manage $o
}
}

proc "Unmanage" { list } {
puts $list
foreach i $list {
set o [lindex $i 1]
if {![is_managed $o]} {
puts "$i isn't managed"
}
clr_manage $o
}
}

##
## Some help about this script.
##

proc "Help PHK-Discovery" { list } {
ined browse "Help about PHK-Discovery" {
"TBD"
}
}

##
## Delete the menus created by this interpreter.
##

proc "Delete PHK-Discovery" { list } {
global menus
foreach id $menus { ined delete $id }
exit
}

set menus [ ined create MENU "PHK-Discovery" \
"Poke Host" \
"Poke Net" \
"Next Host" \
"Manage" \
"Unmanage" \
"Help PHK-Discovery" "Delete PHK-Discovery" ]

readback

--
Poul-Henning Kamp             FreeBSD coreteam member
phk@FreeBSD.ORG               "Real hackers run -current on their laptop."

--
!! This message is brought to you via the `tkined & scotty' mailing list.
!! Please do not reply to this message to unsubscribe. To subscribe or
!! unsubscribe, send a mail message to <tkined-request@ibr.cs.tu-bs.de>.
!! See http://wwwsnmp.cs.utwente.nl/~schoenw/scotty/ for more information.