Initial commit
This commit is contained in:
226
main.go
Normal file
226
main.go
Normal file
@@ -0,0 +1,226 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/mdlayher/ethernet"
|
||||||
|
"github.com/mdlayher/lldp"
|
||||||
|
"github.com/mdlayher/raw"
|
||||||
|
"github.com/vishvananda/netlink"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
link, err := getLink()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
log.Printf("Network interface: %s", link.Attrs().Name)
|
||||||
|
|
||||||
|
api, err := findApi(link)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
type upstream struct {
|
||||||
|
device string
|
||||||
|
port string
|
||||||
|
addr net.Addr
|
||||||
|
}
|
||||||
|
|
||||||
|
func getUpstream(intName string) (*upstream, error) {
|
||||||
|
intr, err := net.InterfaceByName(intName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
sock, err := raw.ListenPacket(intr, 0x88cc /* LLDP ethertype */, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer sock.Close()
|
||||||
|
|
||||||
|
buf := make([]byte, intr.MTU)
|
||||||
|
bufLen, addr, err := sock.ReadFrom(buf)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ethFrame := ðernet.Frame{}
|
||||||
|
err = ethFrame.UnmarshalBinary(buf[:bufLen])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
lldpFrame := &lldp.Frame{}
|
||||||
|
err = lldpFrame.UnmarshalBinary(ethFrame.Payload)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ret := &upstream {
|
||||||
|
addr: addr,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tlv := range lldpFrame.Optional {
|
||||||
|
switch tlv.Type {
|
||||||
|
case lldp.TLVTypePortDescription:
|
||||||
|
ret.port = strings.ToLower(string(tlv.Value))
|
||||||
|
case lldp.TLVTypeSystemName:
|
||||||
|
ret.device = string(tlv.Value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func findApi(link netlink.Link) (string, error) {
|
||||||
|
addrs, err := netlink.AddrList(link, 2 /* AF_INET */)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
apis := []string{}
|
||||||
|
|
||||||
|
for _, addr := range addrs {
|
||||||
|
ip := binary.BigEndian.Uint32(addr.IPNet.IP.To4())
|
||||||
|
mask := binary.BigEndian.Uint32(addr.IPNet.Mask)
|
||||||
|
start := ip & mask
|
||||||
|
end := ip | (^mask & 0xffffffff)
|
||||||
|
|
||||||
|
addr_apis, err := findApis(start, end)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
apis = append(apis, addr_apis...)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(apis) == 0 {
|
||||||
|
return "", fmt.Errorf("no UniFi APIs found")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(apis) > 1 {
|
||||||
|
return "", fmt.Errorf("multiple APIs found TODO")
|
||||||
|
}
|
||||||
|
|
||||||
|
return apis[0], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func findApis(start_ip, end_ip uint32) ([]string, error) {
|
||||||
|
wg := sync.WaitGroup{}
|
||||||
|
wg.Add(int(end_ip - start_ip + 1))
|
||||||
|
|
||||||
|
ret := []string{}
|
||||||
|
|
||||||
|
for iter := start_ip; iter <= end_ip; iter++ {
|
||||||
|
go func(ip uint32) {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 500 * time.Millisecond)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
url := fmt.Sprintf(
|
||||||
|
"https://%d.%d.%d.%d/",
|
||||||
|
ip >> 24 & 0xff,
|
||||||
|
ip >> 16 & 0xff,
|
||||||
|
ip >> 8 & 0xff,
|
||||||
|
ip >> 0 & 0xff,
|
||||||
|
)
|
||||||
|
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
|
||||||
|
if err != nil {
|
||||||
|
// TODO: should return an outer error
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tr := &http.Transport{
|
||||||
|
TLSClientConfig: &tls.Config{
|
||||||
|
InsecureSkipVerify: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
client := http.Client{
|
||||||
|
Transport: tr,
|
||||||
|
}
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
// Probably not https
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
set_cookie := resp.Header["Set-Cookie"]
|
||||||
|
if len(set_cookie) != 1 || !strings.HasPrefix(set_cookie[0], "TOKEN=") {
|
||||||
|
// Probably not Unifi
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = append(ret, url)
|
||||||
|
}(iter)
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getLink() (netlink.Link, error) {
|
||||||
|
links, err := getLinks()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(links) == 0 {
|
||||||
|
return nil, fmt.Errorf("no links found (TODO)")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(links) > 1 {
|
||||||
|
return nil, fmt.Errorf("more than one link (TODO)")
|
||||||
|
}
|
||||||
|
|
||||||
|
return links[0], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getLinks() ([]netlink.Link, error) {
|
||||||
|
links, err := netlink.LinkList()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ret := []netlink.Link{}
|
||||||
|
|
||||||
|
for _, link := range links {
|
||||||
|
if link.Attrs().Flags & net.FlagUp == 0 {
|
||||||
|
// Link is down
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if link.Attrs().Flags & net.FlagLoopback != 0 {
|
||||||
|
// Link is loopback
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if link.Attrs().EncapType != "ether" {
|
||||||
|
// Not ethernet
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = append(ret, link)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user