Path calculation, network diagram
This commit is contained in:
40
main.go
40
main.go
@@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
59
unifi.go
59
unifi.go
@@ -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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user