Path calculation, network diagram

This commit is contained in:
Ian Gulliver
2020-07-26 18:47:38 +00:00
parent 57778feb75
commit f21d10aac8
2 changed files with 74 additions and 25 deletions

40
main.go
View File

@@ -25,20 +25,18 @@ func main() {
} }
log.Printf("Network interface: %s", link.Attrs().Name) log.Printf("Network interface: %s", link.Attrs().Name)
upstream, err := getUpstream(link.Attrs().Name)
if err != nil {
log.Fatal(err)
}
log.Printf("Connected to %s on %s", upstream.Name, upstream.Port)
api, err := findAPI(link) api, err := findAPI(link)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
log.Printf("UniFi API URL: %s", api) log.Printf("UniFi API URL: %s", api)
/*
upstream, err := getUpstream(link.Attrs().Name)
if err != nil {
log.Fatal(err)
}
log.Printf("Connected to %s [%s] on %s", upstream.device, upstream.addr, upstream.port)
*/
unifi, err := NewClient(api) unifi, err := NewClient(api)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
@@ -54,18 +52,22 @@ func main() {
log.Fatal(err) log.Fatal(err)
} }
for _, dev := range devices { upstream_dev := devices[upstream.Name]
log.Printf("%#v", dev) if upstream_dev == nil {
log.Fatalf("Can't find my upstream in UniFi device list")
} }
upstream_dev.PathHop([]*Device{}, map[string]*Device{})
upstream_dev.LogHop("", true)
} }
type upstream struct { type Upstream struct {
device string Name string
port string Port string
addr net.Addr MAC string
} }
func getUpstream(intName string) (*upstream, error) { func getUpstream(intName string) (*Upstream, error) {
intr, err := net.InterfaceByName(intName) intr, err := net.InterfaceByName(intName)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -95,16 +97,16 @@ func getUpstream(intName string) (*upstream, error) {
return nil, err return nil, err
} }
ret := &upstream { ret := &Upstream {
addr: addr, MAC: strings.ToUpper(addr.String()),
} }
for _, tlv := range lldpFrame.Optional { for _, tlv := range lldpFrame.Optional {
switch tlv.Type { switch tlv.Type {
case lldp.TLVTypePortDescription: case lldp.TLVTypePortDescription:
ret.port = strings.ToLower(string(tlv.Value)) ret.Port = strings.ToLower(string(tlv.Value))
case lldp.TLVTypeSystemName: case lldp.TLVTypeSystemName:
ret.device = string(tlv.Value) ret.Name = string(tlv.Value)
} }
} }

View File

@@ -11,6 +11,7 @@ import (
"net/http/cookiejar" "net/http/cookiejar"
"os" "os"
"path" "path"
"sort"
"strings" "strings"
"time" "time"
@@ -28,7 +29,9 @@ type Device struct {
MAC string `json:"mac"` MAC string `json:"mac"`
// Constructed fields // Constructed fields
Neighbors map[string]*Device Neighbors map[string]*Device // all device peers
Beyond map[string]*Device // device peers that we reach through this device
Path []*Device
// Intermediate data; ignore // Intermediate data; ignore
Ports []*Port `json:"port_table"` Ports []*Port `json:"port_table"`
@@ -157,24 +160,30 @@ func (c *Client) ListDevices() (map[string]*Device, error) {
ret := map[string]*Device{} ret := map[string]*Device{}
for _, dev := range device_resp.Data { for _, dev := range device_resp.Data {
dev.Neighbors = map[string]*Device{}
dev.Beyond = map[string]*Device{}
dev.Path = []*Device{}
dev.MAC = strings.ToUpper(dev.MAC) dev.MAC = strings.ToUpper(dev.MAC)
ret[dev.MAC] = dev
if ret[dev.Name] != nil {
return nil, fmt.Errorf("Duplicate UniFi device names: %s", dev.Name)
}
ret[dev.Name] = dev
} }
// Second loop after the table is populated // Second loop after the table is populated
for _, dev := range ret { for _, dev := range ret {
dev.Neighbors = map[string]*Device{}
for _, port := range dev.Ports { for _, port := range dev.Ports {
for _, lldp := range port.LLDP { for _, lldp := range port.LLDP {
lldp.MAC = strings.ToUpper(lldp.MAC) lldp.MAC = strings.ToUpper(lldp.MAC)
neighbor := ret[lldp.MAC] neighbor := ret[lldp.Name]
if neighbor == nil { if neighbor == nil {
continue continue
} }
dev.Neighbors[neighbor.MAC] = neighbor dev.Neighbors[neighbor.Name] = neighbor
} }
} }
dev.Ports = nil dev.Ports = nil
@@ -183,6 +192,44 @@ func (c *Client) ListDevices() (map[string]*Device, error) {
return ret, nil return ret, nil
} }
func (d *Device) PathHop(path []*Device, visited map[string]*Device) {
newPath := append(path, d)
d.Path = newPath
visited[d.Name] = d
for _, neighbor := range d.Neighbors {
if visited[neighbor.Name] != nil {
// Cycle
continue
}
d.Beyond[neighbor.Name] = neighbor
neighbor.PathHop(newPath, visited)
}
}
func (d *Device) LogHop(prefix string, last bool) {
log.Printf("%s+- %s", prefix, d.Name)
names := []string{}
for _, next := range d.Beyond {
names = append(names, next.Name)
}
sort.Strings(names)
i := 0
for _, name := range names {
next := d.Beyond[name]
if last {
next.LogHop(fmt.Sprintf("%s ", prefix), i == len(names) - 1)
} else {
next.LogHop(fmt.Sprintf("%s| ", prefix), i == len(names) - 1)
}
i++
}
}
func loadCreds() (*Creds, error) { func loadCreds() (*Creds, error) {
fh, err := os.OpenFile(credPath(), os.O_RDONLY, 0) fh, err := os.OpenFile(credPath(), os.O_RDONLY, 0)
if err != nil { if err != nil {