$NetBSD: patch-ad,v 1.2 1998/08/07 11:10:55 agc Exp $

--- ../tkined/apps/ip_discover.tcl	Thu Sep 12 22:01:03 1996
+++ ../tkined/apps/ip_discover.tcl	Thu Mar  6 17:11:38 1997
@@ -21,6 +21,17 @@
 IpInit IP-Discover
 
 ##
+## Mapping sysObjectID to icons
+##
+
+set sysid2icon(1.3.6.1.4.1.311.1.1.3.1) pc.xbm
+set sysid2icon(1.3.6.1.4.1.9.1) cisco.xbm
+set sysid2icon(1.3.6.1.4.1.11.2.3.9.1) laser.xbm
+set sysid2icon(1.3.6.1.4.1.75.5.80.1.1) concent.xbm
+set sysid2icon(1.3.6.1.4.10.250.8) unixpc.xbm
+set sysid2icon(1.3.6.1.4.1.42.2) SUN-Server.xbm
+
+##
 ## These are the global parameters that control the discovering
 ## process. They are manipulated by the "Discover Parameter" proc.
 ##
@@ -30,7 +41,15 @@
 set columns 16
 set report true
 set debug false
-
+set communities {public private}
+set suppresdomain {.foo.bar.com}
+set parallel 10
+
+for {set i 1} {$i < 32} {incr i} {
+        set a [expr 0xffffffff << $i]
+        set b [expr ($a >> 24) & 0xff].[expr ($a >> 16) & 0xff].[expr ($a >> 8) & 0xff].[expr ($a) & 0xff]
+        set subnetbits($b) /[expr 32-$i]
+}
 
 ##
 ## During our icmp fire, we build up the following tables.
@@ -48,13 +67,30 @@
 ##
 
 ##
+## We want to do things in parallel, so this procedure comes handy
+##
+
+proc forsome {var count list code} {
+    upvar 1 $var sublist
+    set rel [expr $count - 1]
+    while {[llength $list]} {
+	set sublist [lrange $list 0 $rel]
+	uplevel 1 "$code"
+	set list [lrange $list $count end]
+    }
+}
+
+##
 ## Reset the global tables. This is now called before we start anything
 ## else to recover from abnormal terminated runs.
 ##
 
 proc reset {} {
-    catch {
-	unset nodes networks links ids trace mask fip name address gateways
+    foreach i {
+	nodes networks links ids trace mask fip name address gateways snmp
+	icon } {
+	global $i
+	catch "unset $i"
     }
 }
 
@@ -146,20 +182,23 @@
 ##
 
 proc netping { network } {
+    global parallel
     set result ""
+    set res ""
     if {[regexp "^\[0-9\]+\.\[0-9\]+\.\[0-9\]+$" $network] > 0} {
 	set hosts ""
 	for {set a4 1} {$a4<255} {incr a4} {
-	    append hosts " $network.$a4"
+	    lappend hosts "$network.$a4"
 	}
-	set result [icmp echo $hosts]
-    }
-    set res ""
-    foreach pr $result {
-	set pr_ip [lindex $pr 0]
-	set pr_time [lindex $pr 1]
-	if {$pr_time>=0} {
-	    lappend res $pr_ip
+	forsome lst $parallel $hosts {
+	    debug "** ping $lst"
+	    foreach pr [icmp echo $lst] {
+		set pr_ip [lindex $pr 0]
+		set pr_time [lindex $pr 1]
+		if {$pr_time>=0} {
+		    lappend res $pr_ip
+		}
+	    }
 	}
     }
     return $res
@@ -246,13 +285,13 @@
 }
 
 ##
-## Get a trace of the routes to very node. Store them in the global
+## Get a trace of the routes to every node. Store them in the global
 ## array trace. If we find nodes that were not discovered using the
 ## icmp fire, put them in the global table.
 ##
 
 proc discover_traces {} {
-    global ids nodes trace address
+    global ids nodes trace address parallel
     set count 0
     set start [clock seconds]
 
@@ -260,19 +299,14 @@
     foreach id [array names nodes] {
 	set ip $address($id)
 	lappend addrs $ip
-	if {[llength $addrs] > 255} {
-	    foreach route [trace_route $addrs] {
-		set id $ids([lindex $route 0])
-		set trace($id) [lindex $route 1]
-		incr count
-	    }
-	    set addrs ""
-	}
     }
-    foreach route [trace_route $addrs] {
-	set id $ids([lindex $route 0])
-	set trace($id) [lindex $route 1]
-	incr count
+    forsome lst $parallel $addrs {
+	debug "** trace $lst"
+	foreach route [trace_route $lst] {
+	    set id $ids([lindex $route 0])
+	    set trace($id) [lindex $route 1]
+	    incr count
+	}
     }
 
     # add new discovered nodes to the global tables
@@ -301,26 +335,22 @@
 ##
 
 proc discover_masks {} {
-    global nodes ids mask address
+    global nodes ids mask address parallel
     set count 0
     set start [clock seconds]
 
     set addrs ""
     foreach id [array names nodes] {
+	if {[info exists mask($id)]} continue
 	lappend addrs $address($id)
-	if {[llength $addrs] > 255} {
-	    foreach ipmask [icmp mask $addrs] {
-		set id $ids([lindex $ipmask 0])
-		set mask($id) [lindex $ipmask 1]
-		incr count
-	    }
-	    set addrs ""
-	}
     }
-    foreach ipmask [icmp mask $addrs] {
-	set id $ids([lindex $ipmask 0])
-	set mask($id) [lindex $ipmask 1]
-	incr count
+    forsome lst $parallel $addrs {
+	debug "** masks $lst"
+	foreach ipmask [icmp mask $lst] {
+	    set id $ids([lindex $ipmask 0])
+	    set mask($id) [lindex $ipmask 1]
+	    incr count
+	}
     }
     writeln "$count netmasks queried in [expr {[clock seconds]-$start}] seconds."
     flush stdout
@@ -334,31 +364,101 @@
 ## ===========================================================================
 ##
 
-proc discover_snmp_callback {id s e} {
-    global snmp
+proc discover_snmp_callback {id s e {v ""}} {
+    global snmp icon sysid2icon
     if {$e == "noError"} {
 	set snmp($id) [$s configure]
+	foreach i [array names sysid2icon] {
+		if {[string first $i [lindex $v 2]] == 0} {
+			set icon($id) $sysid2icon($i)
+		}
+	}
+	debug $v
     }
     $s destroy
 }
 
 proc discover_snmp {} {
-    global nodes address snmp
-    global icmp_retries icmp_timeout
+    global nodes address snmp communities ids parallel
+    global icmp_retries icmp_timeout mask gateways icon
+
     set start [clock seconds]
     mib load rfc1213.mib
-    foreach id [array names nodes] {
-        set ip $address($id)
-	if {[catch {snmp session -address $ip \
-		-retries $icmp_retries -timeout $icmp_timeout} s]} continue
+    foreach com $communities {
+	debug "** Looking for community $com"
+	set ips ""
+	foreach id [array names nodes] {
+	    if {[info exists snmp($id)]} continue
+	    lappend ips $address($id)
+	}
+	forsome ipl $parallel $ips {
+	    debug "** snmp community $com $ipl"
+	    foreach ip $ipl {
+		if {[catch {snmp session -address $ip -community $com \
+		    -retries $icmp_retries -timeout $icmp_timeout} s]} continue
+		if {[catch {
+		    $s get sysObjectID.0 "discover_snmp_callback $ids($ip) %S %E %V"
+		} msg]} {
+		    writeln "Oops: $ip get sysObjectID.0: $msg"
+		}
+		update
+		snmp wait
+	    }
+	}
+    }
+    foreach i [array names snmp] {
+	set s [eval snmp session $snmp($i)]
+	set l ""
+	catch {unset ifstate}
+	debug "** Walk [$s configure]"
+	if {[catch {$s walk x "ifIndex ifAdminStatus" {
+		set ifstate([lindex [lindex $x 0] 2]) [lindex [lindex $x 1] 2]
+	    } } msg]} {
+	    writeln "Oops: $msg from $s"
+	    continue
+	}
 	if {[catch {
-	    $s get sysObjectID.0 [list discover_snmp_callback $id "%S" "%E"]
-	} msg]} {
-	    writeln "Oops: $ip get sysObjectID.0: $msg"
+	    $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 {$ifstate($if) != "up"} continue
+		lappend l $a
+		set m [lindex [lindex $x 1] 2]
+		if {$a == $address($i)} { 
+		    set mask($i) $m 
+		} elseif {![info exists ids($a)]} {
+		    create_node $a
+		    set j $ids($a)
+		    set mask($j) $m
+		    set snmp($j) $snmp($i)
+		    if {[info exists icon($i)]} {
+debug "** id $j $a/$m icon $icon($i)"
+			set icon($j) $icon($i)
+		    }
+		    debug "** New interface: $j $a $m"
+		}
+	    } } msg]} {
+	    writeln "Oops: $msg from $s"
+	    continue
+	}
+		
+	if {[llength $l] > 1} {
+	    foreach j $l {
+		set ll "$j"
+		foreach k $l {
+		    if {$k != $j} {
+			lappend ll "$k"
+		    }
+		}
+		set gateways($ids($j)) $ll
+	    }
 	}
-        update
+	$s destroy
     }
-    snmp wait
+    
     set count [llength [array names snmp]]
     writeln "$count snmp agents queried in [expr {[clock seconds]-$start}] seconds."
     flush stdout
@@ -380,33 +480,26 @@
 ##
 
 proc discover_fips {} {
-    global nodes ids fip address
+    global nodes ids fip address parallel
     set count 0
     set start [clock seconds]
 
     set addrs ""
-    set idlist ""
+    set idx 0
     foreach id [array names nodes] {
 	lappend addrs $address($id)
-	lappend idlist $id
-	if {[llength $addrs] > 255} {
-	    set idx 0
-	    foreach ipfip [icmp ttl 32 $addrs] {
-		set id [lindex $idlist $idx]
-		set fip($id) [lindex $ipfip 0]
-		incr count
-		incr idx
-	    }
-	    set addrs ""
-	    set idlist ""
-	}
+	set idlist($idx) $id
+	incr idx
     }
     set idx 0
-    foreach ipfip [icmp ttl 32 $addrs] {
-	set id [lindex $idlist $idx]
-	set fip($id) [lindex $ipfip 0]
-	incr count
-	incr idx
+    forsome lst $parallel $addrs {
+	debug "** fips $lst"
+	foreach ipfip [icmp ttl 32 $lst] {
+	    set id $idlist($idx)
+	    set fip($id) [lindex $ipfip 0]
+	    incr count
+	    incr idx
+	}
     }
     writeln "$count ip addresses queried in [expr {[clock seconds]-$start}] seconds."
     flush stdout
@@ -513,6 +606,7 @@
     foreach id [array names nodes] {
 	set ip $address($id)
 	set netmask $mask($id)
+	if {$netmask == "0.0.0.0"} continue;
 
 	# Get the official network for this node.
 
@@ -526,22 +620,25 @@
 	    D { set bytes "" }
 	}
 	set net [join $bytes .]
-	if {![info exists table($net)]} { set table($net) $mmm }
+
+debug ">> $ip $netmask $net $mmm"
+
+	#if {![info exists table($net)]} { set table($net) $mmm }
 
 	# Sanity check for incorrect netmasks. Netmasks wider than
 	# the official network type don't make any sense. Ignore
 	# problems that are due to the loopback mask of the localhost.
 	
-	if {$netmask == "0.0.0.0"} continue;
 	if { [ip_network $net $netmask] != $net } {
 	    if {![catch {nslook [exec hostname]} localhost]} {
 		if {[string trim $localhost] == $ip} continue
 	    }
-	    set txt "Please check the netmask $netmask for $ip on class $class network $net."
+	    set txt "Please check the netmask $netmask for $ip on network $net."
 	    ined acknowledge $txt
 	    if {$report == "true"} {
 		writeln $txt
 	    }
+	    set mask($id) "0.0.0.0"
 	    continue
 	}
 
@@ -550,6 +647,21 @@
 	set subnet [ip_network $ip $netmask]
 	if {![info exists table($subnet)]} { set table($subnet) $netmask }
     }
+    foreach id [array names nodes] {
+	set ip $address($id)
+	set netmask $mask($id)
+	if {$netmask != "0.0.0.0"} continue;
+
+debug "))) $ip $netmask $mmm"
+	foreach net [array names table] {
+	    if {[ip_ismember $ip $net [lindex $table($net) 0]]} {
+		set mask($id) $table($net)
+debug "=== $ip $mask($id) $net "
+		break
+	    }
+	}
+
+    }
 
     if {[info exist table]} {
 
@@ -618,6 +730,7 @@
 		incr cons
 	    }
 	}
+        writeln "Network $net ($pros, $cons, $unk)." 
 	if {$cons > $pros} {
 	    set res [ined confirm \
   "The majority ($cons : $pros) of nodes on network $net have wide netmasks." \
@@ -745,6 +858,7 @@
 
     writeln "$count gateways discovered in [expr {[clock seconds]-$start}] seconds."
     flush stdout
+
 }
 
 
@@ -863,7 +977,7 @@
 ##
 
 proc merge_gateways {} {
-    global ids nodes networks links gateways address name
+    global ids nodes networks links gateways address name snmp
     set count 0
     set start [clock seconds]
 
@@ -871,7 +985,10 @@
 
     debug "** entering merge_gateways"
     foreach id [array names gateways] {
-	if {[llength $gateways($id)] < 2} continue
+	if {[llength $gateways($id)] < 2} {
+		unset gateways($id)
+		continue
+	}
 	debug "** gateways ($id) :\t $gateways($id)"
     }
 
@@ -888,8 +1005,6 @@
 
     foreach id [array names gateways] {
 
-	if {[llength $gateways($id)] < 2} continue
-
 	set myip [lindex $gateways($id) 0]
 	if {[info exists done($myip)]} {
 	    debug "** $myip skipped - $myip is already done"
@@ -952,12 +1067,16 @@
 	    # addresses for which there is no id. We get such beasts
 	    # when checking the returned udp address.
 	    
-	    if {![info exists ids($ip)]} continue
-	    
+	    if {![info exists ids($ip)]} {
+		writeln "skipping $ip for reasons we don't understand"
+		continue
+	    }
+
 	    set rid $ids($ip)
 	    catch {unset nodes($rid)}
 	    catch {unset address($rid)}
 	    catch {unset name($rid)}
+	    catch {unset snmp($rid)}
 	    
 	    # Adjust the links pointing to rid.
 	    
@@ -997,7 +1116,8 @@
 ##
 
 proc talk_to_ined {} {
-    global nodes networks links address snmp name
+    global nodes networks links address snmp name gateways subnetbits
+    global suppresdomain icon
     set count 0
     set start [clock seconds]
 
@@ -1018,6 +1138,9 @@
 		NODE {
 		    set addr [GetIpAddress $comp]
 		    set id_by_addr($addr) $id
+		    foreach a [ined attribute $id "allIP"] {
+			    set id_by_addr($a) $id
+		    }
 		}
 		NETWORK {
 		    set id_by_addr([ined address $comp]) $id
@@ -1045,13 +1168,22 @@
 	if {[info exists id_by_addr($ip)]} {
 	    set ined_id($id) $id_by_addr($ip)
 	} else {
+	    set nm "[lindex $name($id) 0]" 
+	    regsub "\(.*\)$suppresdomain\$" "$nm" {\1} nm
 	    set ined_id($id) [ined -noupdate create NODE]
 	    ined -noupdate address $ined_id($id) $ip
-	    ined -noupdate name $ined_id($id) "[lindex $name($id) 0]"
+	    ined -noupdate name $ined_id($id) "$nm"
 	    ined -noupdate label $ined_id($id) name
 	    ined -noupdate move $ined_id($id) [nextx] [nexty]
 	    if {[info exists snmp($id)]} {
 		ined -noupdate attribute $ined_id($id) "SNMP:Config" $snmp($id)
+		ined -noupdate color $ined_id($id) brown
+	    }
+	    if {[info exists icon($id)]} {
+		ined -noupdate icon $ined_id($id) $icon($id)
+	    }
+	    if {[info exists gateways($id)]} {
+		ined -noupdate attribute $ined_id($id) "allIP" $gateways($id)
 	    }
 	    set id_by_addr($ip) $ined_id($id)
 	    incr count
@@ -1074,11 +1206,12 @@
 	if {[info exists id_by_addr($ip)]} {
 	    set ined_id($id) $id_by_addr($ip) 
 	} else {
-	    set ined_id($id) [ined -noupdate create NETWORK 0 0 300 0]
-	    ined -noupdate name $ined_id($id) "$ip $type"
+	    set ined_id($id) [ined -noupdate create NETWORK 0 0 100 0]
+	    ined -noupdate name $ined_id($id) "$ip$subnetbits($networks($id))"
 	    ined -noupdate address $ined_id($id) $ip
 	    ined -noupdate label $ined_id($id) name
 	    ined -noupdate move $ined_id($id) [nextx] [nexty]
+	    ined -noupdate attribute $ined_id($id) "netmask" $networks($id)
 	    set id_by_addr($ip) $ined_id($id)
 	    incr count
 	}
@@ -1125,7 +1258,7 @@
 
 proc "Discover IP Network" {list} {
 
-    global nodes networks links ids trace mask fip name address gateways
+    global nodes networks links ids trace mask fip name address gateways snmp
     static nets
 
     reset
@@ -1171,15 +1304,16 @@
     foreach network $nets { discover_nodes $network }
     if {[info exists nodes]} {
 	discover_traces
-	discover_masks
 	discover_snmp
+	discover_masks
 	discover_fips
 	discover_networks
 	discover_gateways
 	discover_links
 	merge_gateways
 	talk_to_ined
-	unset nodes networks links ids trace mask fip name address
+	catch {unset nodes networks links ids trace mask fip name address}
+	catch {unset snmp}
 	catch {unset gateways}
     }
 
@@ -1195,7 +1329,7 @@
 
 proc "Discover Route" {list} {
 
-    global nodes networks links ids trace mask fip name address gateways
+    global nodes networks links ids trace mask fip name address gateways snmp
     static ips
 
     reset
@@ -1249,8 +1383,8 @@
 
     if {[info exists nodes]} {
 	discover_traces
-	discover_masks
 	discover_snmp
+	discover_masks
 	discover_fips
 	discover_networks
 	discover_gateways
@@ -1259,6 +1393,7 @@
 	talk_to_ined
 	unset nodes networks links ids trace mask fip name address
 	catch {unset gateways}
+	catch {unset snmp}
 	
     }
 
@@ -1276,6 +1411,9 @@
     global email_trace
     global report
     global debug
+    global communities
+    global suppresdomain
+    global parallel
 
     set result [ined request "IP-Discover Parameter" \
 	[list [list "# of ICMP retries:" $icmp_retries scale 1 10] \
@@ -1287,7 +1425,10 @@
           [list "Nodes per row:" $columns scale 10 40] \
           [list "Email Discover Routes:" $email_trace radio true false] \
           [list "Write Report:" $report radio true false] \
-          [list "Debug Mode:" $debug radio true false] ] \
+          [list "Debug Mode:" $debug radio true false]  \
+          [list "SNMP Communities:" $communities entry 10]  \
+          [list "Suppress Domain:" $suppresdomain entry 10]  \
+          [list "Parallelism:" $parallel scale 1 255] ] \
         [list "set values" cancel] ]
 
     if {[lindex $result 0] == "cancel"} return
@@ -1302,6 +1443,9 @@
     set email_trace      [lindex $result  8]
     set report           [lindex $result  9]
     set debug            [lindex $result 10]
+    set communities      [lindex $result 11]
+    set suppresdomain    [lindex $result 12]
+    set parallel	 [lindex $result 13]
 
     icmp -retries $icmp_retries
     icmp -timeout $icmp_timeout
@@ -1408,7 +1552,7 @@
 
 proc ined_create {host user} {
     global nodes networks links ids trace mask fip name address gateways
-    global hosts email_trace
+    global hosts email_trace snmp
 
     if {![info exists hosts($host)]} {
 	set hosts($host) $host
@@ -1425,8 +1569,8 @@
 	
 	if {[info exists nodes]} {
 	    discover_traces
-	    discover_masks
 	    discover_snmp
+	    discover_masks
 	    discover_fips
 	    discover_networks
 	    discover_gateways
@@ -1435,6 +1579,7 @@
 	    talk_to_ined
 	    unset nodes networks links ids trace mask fip name address
 	    catch {unset gateways}
+	    catch {unset snmp}
 	}
 	
 	writeln "discover finished in [expr {[clock seconds]-$start}] seconds"
