2017-04-10 18:54:12 +02:00
|
|
|
package meshviewer
|
2016-03-07 02:29:31 +01:00
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"strings"
|
2017-04-10 18:54:12 +02:00
|
|
|
|
|
|
|
"github.com/FreifunkBremen/yanic/runtime"
|
2016-03-07 02:29:31 +01:00
|
|
|
)
|
|
|
|
|
2017-01-20 22:27:44 +01:00
|
|
|
// Graph a struct for all links between the nodes
|
2016-03-07 02:29:31 +01:00
|
|
|
type Graph struct {
|
|
|
|
Version int `json:"version"`
|
|
|
|
Batadv struct {
|
2016-06-16 18:03:45 +02:00
|
|
|
Directed bool `json:"directed"`
|
|
|
|
Graph []string `json:"graph"`
|
|
|
|
Nodes []*GraphNode `json:"nodes"`
|
|
|
|
Links []*GraphLink `json:"links"`
|
2016-03-07 02:29:31 +01:00
|
|
|
} `json:"batadv"`
|
|
|
|
}
|
|
|
|
|
2017-01-20 22:27:44 +01:00
|
|
|
// GraphNode small struct of a node for the graph struct
|
2016-05-16 11:41:45 +02:00
|
|
|
type GraphNode struct {
|
2016-06-16 18:03:45 +02:00
|
|
|
ID string `json:"id"`
|
|
|
|
NodeID string `json:"node_id"`
|
2016-05-16 11:41:45 +02:00
|
|
|
}
|
2017-01-20 22:27:44 +01:00
|
|
|
|
|
|
|
// GraphLink a struct for the link between two nodes
|
2016-03-07 02:29:31 +01:00
|
|
|
type GraphLink struct {
|
2016-06-16 21:17:06 +02:00
|
|
|
Source int `json:"source"`
|
|
|
|
Target int `json:"target"`
|
|
|
|
VPN bool `json:"vpn"`
|
|
|
|
TQ float32 `json:"tq"`
|
|
|
|
Bidirect bool `json:"bidirect"`
|
2016-03-07 02:29:31 +01:00
|
|
|
}
|
|
|
|
|
2017-01-20 22:27:44 +01:00
|
|
|
// GraphBuilder a temporaty struct during fill the graph from the node neighbours
|
|
|
|
type graphBuilder struct {
|
2016-03-07 12:05:53 +01:00
|
|
|
macToID map[string]string // mapping from MAC address to node id
|
2017-03-06 09:52:39 +01:00
|
|
|
idToMac map[string]string // mapping from node id to one MAC address
|
2016-03-07 12:05:53 +01:00
|
|
|
links map[string]*GraphLink // mapping from $idA-$idB to existing link
|
|
|
|
vpn map[string]interface{} // IDs/addresses of VPN servers
|
2016-03-07 02:29:31 +01:00
|
|
|
}
|
|
|
|
|
2017-01-20 22:27:44 +01:00
|
|
|
// BuildGraph transform from nodes (Neighbours) to Graph
|
2017-04-10 18:54:12 +02:00
|
|
|
func BuildGraph(nodes *runtime.Nodes) *Graph {
|
2017-01-20 22:27:44 +01:00
|
|
|
builder := &graphBuilder{
|
2016-03-07 02:29:31 +01:00
|
|
|
macToID: make(map[string]string),
|
2017-03-06 09:52:39 +01:00
|
|
|
idToMac: make(map[string]string),
|
2016-03-07 02:29:31 +01:00
|
|
|
links: make(map[string]*GraphLink),
|
2016-03-07 12:05:53 +01:00
|
|
|
vpn: make(map[string]interface{}),
|
|
|
|
}
|
|
|
|
|
2016-03-07 02:29:31 +01:00
|
|
|
builder.readNodes(nodes.List)
|
|
|
|
|
2016-05-16 11:41:45 +02:00
|
|
|
graph := &Graph{Version: 1}
|
|
|
|
graph.Batadv.Directed = false
|
2017-01-20 22:27:44 +01:00
|
|
|
graph.Batadv.Nodes, graph.Batadv.Links = builder.extract()
|
2016-03-07 02:29:31 +01:00
|
|
|
return graph
|
|
|
|
}
|
|
|
|
|
2017-04-10 18:54:12 +02:00
|
|
|
func (builder *graphBuilder) readNodes(nodes map[string]*runtime.Node) {
|
2016-03-07 02:29:31 +01:00
|
|
|
// Fill mac->id map
|
2016-06-16 18:03:45 +02:00
|
|
|
for sourceID, node := range nodes {
|
2016-03-17 11:51:45 +01:00
|
|
|
if nodeinfo := node.Nodeinfo; nodeinfo != nil {
|
2016-05-11 21:30:54 +02:00
|
|
|
// is VPN address?
|
|
|
|
if nodeinfo.VPN {
|
2016-06-16 18:03:45 +02:00
|
|
|
builder.vpn[sourceID] = nil
|
2016-05-11 21:30:54 +02:00
|
|
|
}
|
2016-07-13 17:36:01 +02:00
|
|
|
|
2017-03-06 09:52:39 +01:00
|
|
|
if len(nodeinfo.Network.Mac) > 0 {
|
2017-03-07 11:47:41 +01:00
|
|
|
builder.idToMac[sourceID] = nodeinfo.Network.Mac
|
2017-03-06 09:52:39 +01:00
|
|
|
}
|
|
|
|
|
2016-07-13 17:36:01 +02:00
|
|
|
// Batman neighbours
|
2016-06-16 18:03:45 +02:00
|
|
|
for _, batinterface := range nodeinfo.Network.Mesh {
|
2016-04-29 08:25:45 +02:00
|
|
|
interfaces := batinterface.Interfaces
|
|
|
|
addresses := append(append(interfaces.Other, interfaces.Tunnel...), interfaces.Wireless...)
|
2016-03-17 11:51:45 +01:00
|
|
|
|
2016-04-29 08:25:45 +02:00
|
|
|
for _, sourceAddress := range addresses {
|
2016-06-16 18:03:45 +02:00
|
|
|
builder.macToID[sourceAddress] = sourceID
|
2016-03-07 12:05:53 +01:00
|
|
|
}
|
2016-03-07 02:29:31 +01:00
|
|
|
}
|
2017-06-30 07:44:39 +02:00
|
|
|
|
2016-03-07 02:29:31 +01:00
|
|
|
}
|
2016-07-13 17:36:01 +02:00
|
|
|
|
|
|
|
// Iterate over local MAC addresses from LLDP
|
|
|
|
if neighbours := node.Neighbours; neighbours != nil {
|
2017-01-20 22:27:44 +01:00
|
|
|
for sourceAddr := range neighbours.LLDP {
|
2016-07-13 17:36:01 +02:00
|
|
|
builder.macToID[sourceAddr] = sourceID
|
|
|
|
}
|
|
|
|
}
|
2016-03-07 02:29:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Add links
|
2016-06-16 18:03:45 +02:00
|
|
|
for sourceID, node := range nodes {
|
2017-04-10 18:54:12 +02:00
|
|
|
if node.Online {
|
2016-06-22 09:16:06 +02:00
|
|
|
if neighbours := node.Neighbours; neighbours != nil {
|
2016-07-13 17:36:01 +02:00
|
|
|
// Batman neighbours
|
2016-06-22 09:16:06 +02:00
|
|
|
for _, batadvNeighbours := range neighbours.Batadv {
|
|
|
|
for targetAddress, link := range batadvNeighbours.Neighbours {
|
|
|
|
if targetID, found := builder.macToID[targetAddress]; found {
|
|
|
|
builder.addLink(targetID, sourceID, link.Tq)
|
|
|
|
}
|
2016-03-07 02:29:31 +01:00
|
|
|
}
|
|
|
|
}
|
2017-06-30 07:44:39 +02:00
|
|
|
// Wifi neighbours
|
|
|
|
for _, wifiNeighbours := range neighbours.WifiNeighbours {
|
|
|
|
for targetAddress, link := range wifiNeighbours.Neighbours {
|
|
|
|
if targetID, found := builder.macToID[targetAddress]; found {
|
2017-07-06 10:10:24 +02:00
|
|
|
linkActive := link.Noise + link.Inactive + link.Signal
|
|
|
|
if linkActive > 0 {
|
|
|
|
builder.addLink(targetID, sourceID, link.Signal/linkActive)
|
|
|
|
}
|
2017-06-30 07:44:39 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Babel neighbours
|
|
|
|
for _, babelNeighbours := range neighbours.Babel {
|
|
|
|
for _, link := range babelNeighbours {
|
|
|
|
if targetID, found := builder.macToID[link.Address]; found {
|
|
|
|
builder.addLink(targetID, sourceID, 1)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-07-13 17:36:01 +02:00
|
|
|
// LLDP
|
|
|
|
for _, neighbours := range neighbours.LLDP {
|
2017-01-20 22:27:44 +01:00
|
|
|
for targetAddress := range neighbours {
|
2016-07-13 17:36:01 +02:00
|
|
|
if targetID, found := builder.macToID[targetAddress]; found {
|
|
|
|
builder.addLink(targetID, sourceID, 255)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-03-07 02:29:31 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-06 09:52:39 +01:00
|
|
|
type graphNodeCache struct {
|
|
|
|
idToMac map[string]string
|
|
|
|
idToIndex map[string]int
|
|
|
|
count int
|
|
|
|
Nodes []*GraphNode
|
|
|
|
}
|
2016-03-07 02:29:31 +01:00
|
|
|
|
2017-03-06 09:52:39 +01:00
|
|
|
func newGraphNodeCache(idToMac map[string]string) *graphNodeCache {
|
|
|
|
return &graphNodeCache{
|
|
|
|
idToMac: idToMac,
|
|
|
|
idToIndex: make(map[string]int),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (gn *graphNodeCache) getIndex(nodeID string) int {
|
|
|
|
index, ok := gn.idToIndex[nodeID]
|
|
|
|
if !ok {
|
|
|
|
node := &GraphNode{
|
|
|
|
ID: gn.idToMac[nodeID],
|
2016-05-16 11:41:45 +02:00
|
|
|
NodeID: nodeID,
|
|
|
|
}
|
2017-03-06 09:52:39 +01:00
|
|
|
gn.Nodes = append(gn.Nodes, node)
|
|
|
|
gn.idToIndex[nodeID] = gn.count
|
|
|
|
index = gn.count
|
|
|
|
gn.count++
|
2016-05-16 11:41:45 +02:00
|
|
|
}
|
2017-03-06 09:52:39 +01:00
|
|
|
return index
|
|
|
|
}
|
|
|
|
|
|
|
|
func (builder *graphBuilder) extract() ([]*GraphNode, []*GraphLink) {
|
|
|
|
links := make([]*GraphLink, len(builder.links))
|
|
|
|
cache := newGraphNodeCache(builder.idToMac)
|
2016-06-16 21:17:06 +02:00
|
|
|
|
|
|
|
// collect links
|
2017-03-06 09:52:39 +01:00
|
|
|
i := 0
|
2016-05-16 11:41:45 +02:00
|
|
|
for key, link := range builder.links {
|
2016-06-16 21:17:06 +02:00
|
|
|
pos := strings.IndexByte(key, '-')
|
2017-03-06 09:52:39 +01:00
|
|
|
link.Source = cache.getIndex(key[:pos])
|
|
|
|
link.Target = cache.getIndex(key[pos+1:])
|
2016-06-16 21:17:06 +02:00
|
|
|
links[i] = link
|
|
|
|
i++
|
2016-03-07 02:29:31 +01:00
|
|
|
}
|
2017-03-06 09:52:39 +01:00
|
|
|
return cache.Nodes, links
|
2016-03-07 02:29:31 +01:00
|
|
|
}
|
|
|
|
|
2017-01-20 22:27:44 +01:00
|
|
|
func (builder *graphBuilder) isVPN(ids ...string) bool {
|
2016-03-07 12:05:53 +01:00
|
|
|
for _, id := range ids {
|
|
|
|
if _, found := builder.vpn[id]; found {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2017-01-20 22:27:44 +01:00
|
|
|
func (builder *graphBuilder) addLink(targetID string, sourceID string, linkTq int) {
|
2016-03-20 12:09:32 +01:00
|
|
|
// Sort IDs to generate the key
|
2016-03-07 02:29:31 +01:00
|
|
|
var key string
|
2016-06-16 18:03:45 +02:00
|
|
|
if strings.Compare(sourceID, targetID) > 0 {
|
|
|
|
key = fmt.Sprintf("%s-%s", sourceID, targetID)
|
2016-03-07 02:29:31 +01:00
|
|
|
} else {
|
2016-06-16 18:03:45 +02:00
|
|
|
key = fmt.Sprintf("%s-%s", targetID, sourceID)
|
2016-03-07 02:29:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
var tq float32
|
|
|
|
if linkTq > 0 {
|
|
|
|
tq = float32(1.0 / (float32(linkTq) / 255.0))
|
|
|
|
}
|
|
|
|
|
|
|
|
if link, ok := builder.links[key]; !ok {
|
|
|
|
builder.links[key] = &GraphLink{
|
2016-06-16 18:03:45 +02:00
|
|
|
VPN: builder.isVPN(sourceID, targetID),
|
|
|
|
TQ: tq,
|
2016-03-07 02:29:31 +01:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Use lowest of both link qualities
|
|
|
|
if tq < link.TQ {
|
|
|
|
link.TQ = tq
|
|
|
|
}
|
|
|
|
link.Bidirect = true
|
|
|
|
}
|
|
|
|
}
|