[TASK] make yanic babel compatible (#104)
This commit is contained in:
parent
35d6933d31
commit
c20e216ed6
@ -15,7 +15,7 @@ func TestReadConfig(t *testing.T) {
|
||||
assert.NotNil(config)
|
||||
|
||||
assert.True(config.Respondd.Enable)
|
||||
assert.Equal([]string{"br-ffhb"}, config.Respondd.Interfaces)
|
||||
assert.Equal("br-ffhb", config.Respondd.Interfaces[0].InterfaceName)
|
||||
assert.Equal(time.Minute, config.Respondd.CollectInterval.Duration)
|
||||
assert.Equal(time.Hour*24*7, config.Nodes.PruneAfter.Duration)
|
||||
assert.Equal(time.Hour*24*7, config.Database.DeleteAfter.Duration)
|
||||
|
41
cmd/query.go
41
cmd/query.go
@ -4,6 +4,7 @@ import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/FreifunkBremen/yanic/respond"
|
||||
@ -11,36 +12,54 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var wait int
|
||||
var (
|
||||
wait int
|
||||
port int
|
||||
ipAddress string
|
||||
)
|
||||
|
||||
// queryCmd represents the query command
|
||||
var queryCmd = &cobra.Command{
|
||||
Use: "query <interface> <destination>",
|
||||
Use: "query <interfaces> <destination>",
|
||||
Short: "Sends a query on the interface to the destination and waits for a response",
|
||||
Example: `yanic query wlan0 "fe80::eade:27ff:dead:beef"`,
|
||||
Example: `yanic query "eth0,wlan0" "fe80::eade:27ff:dead:beef"`,
|
||||
Args: cobra.ExactArgs(2),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
iface := args[0]
|
||||
ifaces := strings.Split(args[0], ",")
|
||||
dstAddress := net.ParseIP(args[1])
|
||||
|
||||
log.Printf("Sending request address=%s iface=%s", dstAddress, iface)
|
||||
log.Printf("Sending request address=%s ifaces=%s", dstAddress, ifaces)
|
||||
|
||||
var ifacesConfigs []respond.InterfaceConfig
|
||||
for _, iface := range ifaces {
|
||||
ifaceConfig := respond.InterfaceConfig{
|
||||
InterfaceName: iface,
|
||||
Port: port,
|
||||
IPAddress: ipAddress,
|
||||
}
|
||||
ifacesConfigs = append(ifacesConfigs, ifaceConfig)
|
||||
}
|
||||
|
||||
nodes := runtime.NewNodes(&runtime.NodesConfig{})
|
||||
|
||||
sitesDomains := make(map[string][]string)
|
||||
|
||||
collector := respond.NewCollector(nil, nodes, sitesDomains, []string{iface}, 0)
|
||||
collector := respond.NewCollector(nil, nodes, sitesDomains, ifacesConfigs)
|
||||
defer collector.Close()
|
||||
collector.SendPacket(dstAddress)
|
||||
|
||||
time.Sleep(time.Second * time.Duration(wait))
|
||||
|
||||
for id, data := range nodes.List {
|
||||
bytes, err := json.Marshal(data)
|
||||
jq, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
log.Printf("%s: %+v", id, err)
|
||||
log.Printf("%s: %+v", id, data)
|
||||
} else {
|
||||
log.Printf("%s: %+v", id, string(bytes))
|
||||
jqNeighbours, err := json.Marshal(data.Neighbours)
|
||||
if err != nil {
|
||||
log.Printf("%s: %s neighbours: %+v", id, string(jq), data.Neighbours)
|
||||
} else {
|
||||
log.Printf("%s: %s neighbours: %s", id, string(jq), string(jqNeighbours))
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -49,4 +68,6 @@ var queryCmd = &cobra.Command{
|
||||
func init() {
|
||||
RootCmd.AddCommand(queryCmd)
|
||||
queryCmd.Flags().IntVar(&wait, "wait", 1, "Seconds to wait for a response")
|
||||
queryCmd.Flags().IntVar(&port, "port", 0, "define a port to listen (if not set or set to 0 the kernel will use a random free port at its own)")
|
||||
queryCmd.Flags().StringVar(&ipAddress, "ip", "", "ip address which is used for sending (optional - without definition used the link-local address)")
|
||||
}
|
||||
|
@ -54,7 +54,7 @@ var serveCmd = &cobra.Command{
|
||||
time.Sleep(delay)
|
||||
}
|
||||
|
||||
collector = respond.NewCollector(allDatabase.Conn, nodes, config.Respondd.SitesDomains(), config.Respondd.Interfaces, config.Respondd.Port)
|
||||
collector = respond.NewCollector(allDatabase.Conn, nodes, config.Respondd.SitesDomains(), config.Respondd.Interfaces)
|
||||
collector.Start(config.Respondd.CollectInterval.Duration)
|
||||
defer collector.Close()
|
||||
}
|
||||
|
@ -9,11 +9,6 @@ enable = true
|
||||
synchronize = "1m"
|
||||
# how often request per multicast
|
||||
collect_interval = "1m"
|
||||
# interface that has an IP in your mesh network
|
||||
interfaces = ["br-ffhb"]
|
||||
# define a port to listen
|
||||
# if not set or set to 0 the kernel will use a random free port at its own
|
||||
#port = 10001
|
||||
|
||||
# table of a site to save stats for (not exists for global only)
|
||||
#[respondd.sites.example]
|
||||
@ -23,6 +18,20 @@ interfaces = ["br-ffhb"]
|
||||
[respondd.sites.ffhb]
|
||||
domains = ["city"]
|
||||
|
||||
# interface that has an IP in your mesh network
|
||||
[[respondd.interfaces]]
|
||||
# name of interface on which this collector is running
|
||||
ifname = "br-ffhb"
|
||||
# ip address which is used for sending
|
||||
# (optional - without definition used a address of ifname)
|
||||
ip_address = "fd2f:5119:f2d::5"
|
||||
# multicast address to destination of respondd
|
||||
# (optional - without definition used batman default ff02::2:1001)
|
||||
multicast_address = "ff05::2:1001"
|
||||
# define a port to listen
|
||||
# if not set or set to 0 the kernel will use a random free port at its own
|
||||
#port = 10001
|
||||
|
||||
# A little build-in webserver, which statically serves a directory.
|
||||
# This is useful for testing purposes or for a little standalone installation.
|
||||
[webserver]
|
||||
|
@ -3,6 +3,7 @@ package data
|
||||
// Neighbours struct
|
||||
type Neighbours struct {
|
||||
Batadv map[string]BatadvNeighbours `json:"batadv"`
|
||||
Babel map[string]BabelNeighbours `json:"babel"`
|
||||
LLDP map[string]LLDPNeighbours `json:"lldp"`
|
||||
//WifiNeighbours map[string]WifiNeighbours `json:"wifi"`
|
||||
NodeID string `json:"node_id"`
|
||||
@ -27,11 +28,27 @@ type LLDPLink struct {
|
||||
Description string `json:"descr"`
|
||||
}
|
||||
|
||||
// BabelLink struct
|
||||
type BabelLink struct {
|
||||
// How need this:
|
||||
RXCost int `json:"rxcost"`
|
||||
TXCost int `json:"txcost"`
|
||||
Cost int `json:"cost"`
|
||||
Reachability int `json:"reachability"`
|
||||
}
|
||||
|
||||
// BatadvNeighbours struct
|
||||
type BatadvNeighbours struct {
|
||||
Neighbours map[string]BatmanLink `json:"neighbours"`
|
||||
}
|
||||
|
||||
// BabelNeighbours struct
|
||||
type BabelNeighbours struct {
|
||||
Protocol string `json:"protocol"`
|
||||
LinkLocalAddress string `json:"ll-addr"`
|
||||
Neighbours map[string]BabelLink `json:"neighbours"`
|
||||
}
|
||||
|
||||
// WifiNeighbours struct
|
||||
type WifiNeighbours struct {
|
||||
Neighbours map[string]WifiLink `json:"neighbours"`
|
||||
|
@ -14,8 +14,8 @@ type NodeInfo struct {
|
||||
Wireless *Wireless `json:"wireless,omitempty"`
|
||||
}
|
||||
|
||||
// BatInterface struct
|
||||
type BatInterface struct {
|
||||
// NetworkInterface struct
|
||||
type NetworkInterface struct {
|
||||
Interfaces struct {
|
||||
Wireless []string `json:"wireless,omitempty"`
|
||||
Other []string `json:"other,omitempty"`
|
||||
@ -24,16 +24,17 @@ type BatInterface struct {
|
||||
}
|
||||
|
||||
// Addresses returns a flat list of all MAC addresses
|
||||
func (iface *BatInterface) Addresses() []string {
|
||||
func (iface *NetworkInterface) Addresses() []string {
|
||||
return append(append(iface.Interfaces.Other, iface.Interfaces.Tunnel...), iface.Interfaces.Wireless...)
|
||||
}
|
||||
|
||||
// Network struct
|
||||
type Network struct {
|
||||
Mac string `json:"mac"`
|
||||
Addresses []string `json:"addresses"`
|
||||
Mesh map[string]*BatInterface `json:"mesh"`
|
||||
MeshInterfaces []string `json:"mesh_interfaces"`
|
||||
Mac string `json:"mac"`
|
||||
Addresses []string `json:"addresses"`
|
||||
Mesh map[string]*NetworkInterface `json:"mesh"`
|
||||
// still used in gluon?
|
||||
MeshInterfaces []string `json:"mesh_interfaces"`
|
||||
}
|
||||
|
||||
// Owner struct
|
||||
@ -64,6 +65,9 @@ type Software struct {
|
||||
Version string `json:"version,omitempty"`
|
||||
Compat int `json:"compat,omitempty"`
|
||||
} `json:"batman-adv,omitempty"`
|
||||
Babeld struct {
|
||||
Version string `json:"version,omitempty"`
|
||||
} `json:"babeld,omitempty"`
|
||||
Fastd struct {
|
||||
Enabled bool `json:"enabled,omitempty"`
|
||||
Version string `json:"version,omitempty"`
|
||||
|
@ -8,7 +8,7 @@ import (
|
||||
|
||||
func TestNodeinfoBatAddresses(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
batIface := &BatInterface{
|
||||
iface := &NetworkInterface{
|
||||
Interfaces: struct {
|
||||
Wireless []string `json:"wireless,omitempty"`
|
||||
Other []string `json:"other,omitempty"`
|
||||
@ -20,7 +20,7 @@ func TestNodeinfoBatAddresses(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
addr := batIface.Addresses()
|
||||
addr := iface.Addresses()
|
||||
assert.NotNil(addr)
|
||||
assert.Equal([]string{"aa:aa:aa:aa:aa", "aa:aa:aa:aa:ab"}, addr)
|
||||
}
|
||||
|
@ -11,9 +11,9 @@ import (
|
||||
func (conn *Connection) InsertLink(link *runtime.Link, t time.Time) {
|
||||
tags := models.Tags{}
|
||||
tags.SetString("source.id", link.SourceID)
|
||||
tags.SetString("source.mac", link.SourceMAC)
|
||||
tags.SetString("source.addr", link.SourceAddress)
|
||||
tags.SetString("target.id", link.TargetID)
|
||||
tags.SetString("target.mac", link.TargetMAC)
|
||||
tags.SetString("target.addr", link.TargetAddress)
|
||||
|
||||
conn.addPoint(MeasurementLink, tags, models.Fields{"tq": float32(link.TQ) / 2.55}, t)
|
||||
conn.addPoint(MeasurementLink, tags, models.Fields{"tq": link.TQ * 100}, t)
|
||||
}
|
||||
|
@ -158,10 +158,10 @@ func TestToInflux(t *testing.T) {
|
||||
fields, _ = nPoint.Fields()
|
||||
assert.EqualValues("link", nPoint.Name())
|
||||
assert.EqualValues(map[string]string{
|
||||
"source.id": "deadbeef",
|
||||
"source.mac": "a-interface",
|
||||
"target.id": "foobar",
|
||||
"target.mac": "BAFF1E5",
|
||||
"source.id": "deadbeef",
|
||||
"source.addr": "a-interface",
|
||||
"target.id": "foobar",
|
||||
"target.addr": "BAFF1E5",
|
||||
}, tags)
|
||||
assert.EqualValues(80, fields["tq"])
|
||||
|
||||
|
@ -14,10 +14,15 @@ Group for configuration of respondd request.
|
||||
enable = true
|
||||
# synchronize = "1m"
|
||||
collect_interval = "1m"
|
||||
interfaces = ["br-ffhb"]
|
||||
#port = 10001
|
||||
|
||||
#[respondd.sites.example]
|
||||
#domains = ["city"]
|
||||
|
||||
[[respondd.interfaces]]
|
||||
ifname = "br-ffhb"
|
||||
#ip_address = "fe80::..."
|
||||
#multicast_address = "ff02::2:1001"
|
||||
#port = 10001
|
||||
```
|
||||
{% endmethod %}
|
||||
|
||||
@ -46,7 +51,7 @@ synchronize = "1m"
|
||||
{% method %}
|
||||
How often send request per respondd.
|
||||
|
||||
It will send UDP packets with multicast group `ff02::2:1001` and port `1001`.
|
||||
It will send UDP packets with multicast address `ff02::2:1001` and port `1001`.
|
||||
If a node does not answer after the half time, it will request with the last know address under the port `1001`.
|
||||
{% sample lang="toml" %}
|
||||
```toml
|
||||
@ -55,26 +60,16 @@ collect_interval = "1m"
|
||||
{% endmethod %}
|
||||
|
||||
|
||||
### interfaces
|
||||
### sites
|
||||
{% method %}
|
||||
Interface that has an IP in your mesh network
|
||||
List of sites to save stats for (empty for global only)
|
||||
{% sample lang="toml" %}
|
||||
```toml
|
||||
interfaces = ["br-ffhb"]
|
||||
sites = ["ffhb"]
|
||||
```
|
||||
{% endmethod %}
|
||||
|
||||
|
||||
### port
|
||||
{% method %}
|
||||
Define a port to listen and send the respondd packages.
|
||||
If not set or set to 0 the kernel will use a random free port at its own.
|
||||
{% sample lang="toml" %}
|
||||
```toml
|
||||
port = 10001
|
||||
```
|
||||
{% endmethod %}
|
||||
|
||||
### [respondd.sites.example]
|
||||
{% method %}
|
||||
Tables of sites to save stats for (not exists for global only).
|
||||
@ -85,6 +80,7 @@ Here is the site _ffhb_.
|
||||
domains = ["city"]
|
||||
```
|
||||
{% endmethod %}
|
||||
|
||||
#### domains
|
||||
{% method %}
|
||||
list of domains on this site to save stats for (empty for global only)
|
||||
@ -95,6 +91,60 @@ domains = ["city"]
|
||||
{% endmethod %}
|
||||
|
||||
|
||||
### [[respondd.interfaces]]
|
||||
{% method %}
|
||||
Interface that has an ip address in your mesh network.
|
||||
It is possible to have multiple interfaces, just add this group again with new parameters (see toml [[array of table]]).
|
||||
{% sample lang="toml" %}
|
||||
```toml
|
||||
[[respondd.interfaces]]
|
||||
ifname = "br-ffhb"
|
||||
#ip_address = "fe80::..."
|
||||
#multicast_address = "ff02::2:1001"
|
||||
#port = 10001
|
||||
```
|
||||
{% endmethod %}
|
||||
|
||||
### ifname
|
||||
{% method %}
|
||||
name of interface on which this collector is running.
|
||||
{% sample lang="toml" %}
|
||||
```toml
|
||||
ifname = "br-ffhb"
|
||||
```
|
||||
{% endmethod %}
|
||||
|
||||
### ip_address
|
||||
{% method %}
|
||||
ip address is the own address which is used for sending.
|
||||
If not set or set with empty string it will take an address of ifname.
|
||||
{% sample lang="toml" %}
|
||||
```toml
|
||||
ip_address = "fe80::..."
|
||||
```
|
||||
{% endmethod %}
|
||||
|
||||
### multicast_address
|
||||
{% method %}
|
||||
Multicast address to destination of respondd.
|
||||
If not set or set with empty string it will take the batman default multicast address `ff02::2:1001`
|
||||
(Needed in babel for a mesh-network wide routeable multicast addreess `ff05::2:1001`)
|
||||
{% sample lang="toml" %}
|
||||
```toml
|
||||
multicast_address = "ff02::2:1001"
|
||||
```
|
||||
{% endmethod %}
|
||||
|
||||
### port
|
||||
{% method %}
|
||||
Define a port to listen and send the respondd packages.
|
||||
If not set or set to 0 the kernel will use a random free port at its own.
|
||||
{% sample lang="toml" %}
|
||||
```toml
|
||||
port = 10001
|
||||
```
|
||||
{% endmethod %}
|
||||
|
||||
|
||||
## [webserver]
|
||||
{% method %}
|
||||
|
@ -5,7 +5,7 @@ cp /opt/go/src/github.com/FreifunkBremen/yanic/config_example.toml /etc/yanic.co
|
||||
```
|
||||
|
||||
# Quick configuration
|
||||
For an easy startup you only need to edit the `interfaces` in section
|
||||
For an easy startup you only need to edit the `[[respondd.interfaces]]` in section
|
||||
`[respondd]` in file `/etc/yanic.conf`.
|
||||
|
||||
Then create the following files and folders:
|
||||
|
@ -7,7 +7,7 @@ A little overview of yanic in connection with other software:
|
||||
|
||||
It sends the `gluon-neighbour-info` request and collects the answers.
|
||||
|
||||
It will send UDP packets with multicast group `ff02:0:0:0:0:0:2:1001` and port `1001`.
|
||||
It will send UDP packets with multicast address `ff02:0:0:0:0:0:2:1001` and port `1001`.
|
||||
|
||||
If a node does not answer, it will request with the last know address under the port `1001`.
|
||||
|
||||
|
@ -9,6 +9,12 @@ import (
|
||||
"github.com/FreifunkBremen/yanic/runtime"
|
||||
)
|
||||
|
||||
const (
|
||||
LINK_TYPE_WIRELESS = "wifi"
|
||||
LINK_TYPE_TUNNEL = "vpn"
|
||||
LINK_TYPE_FALLBACK = "other"
|
||||
)
|
||||
|
||||
func transform(nodes *runtime.Nodes) *Meshviewer {
|
||||
|
||||
meshviewer := &Meshviewer{
|
||||
@ -34,11 +40,11 @@ func transform(nodes *runtime.Nodes) *Meshviewer {
|
||||
if nodeinfo := nodeOrigin.Nodeinfo; nodeinfo != nil {
|
||||
if meshes := nodeinfo.Network.Mesh; meshes != nil {
|
||||
for _, mesh := range meshes {
|
||||
for _, mac := range mesh.Interfaces.Wireless {
|
||||
typeList[mac] = "wifi"
|
||||
for _, addr := range mesh.Interfaces.Wireless {
|
||||
typeList[addr] = LINK_TYPE_WIRELESS
|
||||
}
|
||||
for _, mac := range mesh.Interfaces.Tunnel {
|
||||
typeList[mac] = "vpn"
|
||||
for _, addr := range mesh.Interfaces.Tunnel {
|
||||
typeList[addr] = LINK_TYPE_TUNNEL
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -47,52 +53,70 @@ func transform(nodes *runtime.Nodes) *Meshviewer {
|
||||
for _, linkOrigin := range nodes.NodeLinks(nodeOrigin) {
|
||||
var key string
|
||||
// keep source and target in the same order
|
||||
switchSourceTarget := strings.Compare(linkOrigin.SourceMAC, linkOrigin.TargetMAC) > 0
|
||||
switchSourceTarget := strings.Compare(linkOrigin.SourceAddress, linkOrigin.TargetAddress) > 0
|
||||
if switchSourceTarget {
|
||||
key = fmt.Sprintf("%s-%s", linkOrigin.SourceMAC, linkOrigin.TargetMAC)
|
||||
key = fmt.Sprintf("%s-%s", linkOrigin.SourceAddress, linkOrigin.TargetAddress)
|
||||
} else {
|
||||
key = fmt.Sprintf("%s-%s", linkOrigin.TargetMAC, linkOrigin.SourceMAC)
|
||||
key = fmt.Sprintf("%s-%s", linkOrigin.TargetAddress, linkOrigin.SourceAddress)
|
||||
}
|
||||
|
||||
if link := links[key]; link != nil {
|
||||
linkType, linkTypeFound := typeList[linkOrigin.SourceAddress]
|
||||
if !linkTypeFound {
|
||||
linkType, linkTypeFound = typeList[linkOrigin.TargetAddress]
|
||||
}
|
||||
|
||||
if switchSourceTarget {
|
||||
link.TargetTQ = float32(linkOrigin.TQ) / 255.0
|
||||
if link.Type == "other" {
|
||||
link.Type = typeList[linkOrigin.TargetMAC]
|
||||
} else if link.Type != typeList[linkOrigin.TargetMAC] {
|
||||
log.Printf("different linktypes %s:%s current: %s source: %s target: %s", linkOrigin.SourceMAC, linkOrigin.TargetMAC, link.Type, typeList[linkOrigin.SourceMAC], typeList[linkOrigin.TargetMAC])
|
||||
link.TargetTQ = linkOrigin.TQ
|
||||
|
||||
linkType, linkTypeFound = typeList[linkOrigin.TargetAddress]
|
||||
if !linkTypeFound {
|
||||
linkType, linkTypeFound = typeList[linkOrigin.SourceAddress]
|
||||
}
|
||||
} else {
|
||||
link.SourceTQ = float32(linkOrigin.TQ) / 255.0
|
||||
if link.Type == "other" {
|
||||
link.Type = typeList[linkOrigin.SourceMAC]
|
||||
} else if link.Type != typeList[linkOrigin.SourceMAC] {
|
||||
log.Printf("different linktypes %s:%s current: %s source: %s target: %s", linkOrigin.SourceMAC, linkOrigin.TargetMAC, link.Type, typeList[linkOrigin.SourceMAC], typeList[linkOrigin.TargetMAC])
|
||||
link.SourceTQ = linkOrigin.TQ
|
||||
}
|
||||
|
||||
if linkTypeFound && linkType != link.Type {
|
||||
if link.Type == LINK_TYPE_FALLBACK {
|
||||
link.Type = linkType
|
||||
} else {
|
||||
log.Printf("different linktypes for '%s' - '%s' prev: '%s' new: '%s' source: '%s' target: '%s'", linkOrigin.SourceAddress, linkOrigin.TargetAddress, link.Type, linkType, typeList[linkOrigin.SourceAddress], typeList[linkOrigin.TargetAddress])
|
||||
}
|
||||
}
|
||||
if link.Type == "" {
|
||||
link.Type = "other"
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
tq := float32(linkOrigin.TQ) / 255.0
|
||||
link := &Link{
|
||||
Type: typeList[linkOrigin.SourceMAC],
|
||||
Source: linkOrigin.SourceID,
|
||||
SourceMAC: linkOrigin.SourceMAC,
|
||||
Target: linkOrigin.TargetID,
|
||||
TargetMAC: linkOrigin.TargetMAC,
|
||||
SourceTQ: tq,
|
||||
TargetTQ: tq,
|
||||
Source: linkOrigin.SourceID,
|
||||
SourceAddress: linkOrigin.SourceAddress,
|
||||
Target: linkOrigin.TargetID,
|
||||
TargetAddress: linkOrigin.TargetAddress,
|
||||
SourceTQ: linkOrigin.TQ,
|
||||
TargetTQ: linkOrigin.TQ,
|
||||
}
|
||||
|
||||
linkType, linkTypeFound := typeList[linkOrigin.SourceAddress]
|
||||
if !linkTypeFound {
|
||||
linkType, linkTypeFound = typeList[linkOrigin.TargetAddress]
|
||||
}
|
||||
|
||||
if switchSourceTarget {
|
||||
link.Type = typeList[linkOrigin.TargetMAC]
|
||||
link.Source = linkOrigin.TargetID
|
||||
link.SourceMAC = linkOrigin.TargetMAC
|
||||
link.SourceAddress = linkOrigin.TargetAddress
|
||||
link.Target = linkOrigin.SourceID
|
||||
link.TargetMAC = linkOrigin.SourceMAC
|
||||
link.TargetAddress = linkOrigin.SourceAddress
|
||||
|
||||
linkType, linkTypeFound = typeList[linkOrigin.TargetAddress]
|
||||
if !linkTypeFound {
|
||||
linkType, linkTypeFound = typeList[linkOrigin.SourceAddress]
|
||||
}
|
||||
}
|
||||
if link.Type == "" {
|
||||
link.Type = "other"
|
||||
|
||||
if linkTypeFound {
|
||||
link.Type = linkType
|
||||
} else {
|
||||
link.Type = LINK_TYPE_FALLBACK
|
||||
}
|
||||
links[key] = link
|
||||
meshviewer.Links = append(meshviewer.Links, link)
|
||||
|
@ -18,7 +18,7 @@ func TestTransform(t *testing.T) {
|
||||
NodeID: "node_a",
|
||||
Network: data.Network{
|
||||
Mac: "node:a:mac",
|
||||
Mesh: map[string]*data.BatInterface{
|
||||
Mesh: map[string]*data.NetworkInterface{
|
||||
"bat0": {
|
||||
Interfaces: struct {
|
||||
Wireless []string `json:"wireless,omitempty"`
|
||||
@ -55,7 +55,7 @@ func TestTransform(t *testing.T) {
|
||||
NodeID: "node_c",
|
||||
Network: data.Network{
|
||||
Mac: "node:c:mac",
|
||||
Mesh: map[string]*data.BatInterface{
|
||||
Mesh: map[string]*data.NetworkInterface{
|
||||
"bat0": {
|
||||
Interfaces: struct {
|
||||
Wireless []string `json:"wireless,omitempty"`
|
||||
@ -85,7 +85,7 @@ func TestTransform(t *testing.T) {
|
||||
NodeID: "node_b",
|
||||
Network: data.Network{
|
||||
Mac: "node:b:mac",
|
||||
Mesh: map[string]*data.BatInterface{
|
||||
Mesh: map[string]*data.NetworkInterface{
|
||||
"bat0": {
|
||||
Interfaces: struct {
|
||||
Wireless []string `json:"wireless,omitempty"`
|
||||
@ -121,7 +121,7 @@ func TestTransform(t *testing.T) {
|
||||
NodeID: "node_d",
|
||||
Network: data.Network{
|
||||
Mac: "node:d:mac",
|
||||
Mesh: map[string]*data.BatInterface{
|
||||
Mesh: map[string]*data.NetworkInterface{
|
||||
"bat0": {
|
||||
Interfaces: struct {
|
||||
Wireless []string `json:"wireless,omitempty"`
|
||||
@ -159,27 +159,27 @@ func TestTransform(t *testing.T) {
|
||||
assert.Len(links, 3)
|
||||
|
||||
for _, link := range links {
|
||||
switch link.SourceMAC {
|
||||
switch link.SourceAddress {
|
||||
case "node:a:mac:lan":
|
||||
assert.Equal("other", link.Type)
|
||||
assert.Equal("node:b:mac:lan", link.TargetMAC)
|
||||
assert.Equal("node:b:mac:lan", link.TargetAddress)
|
||||
assert.Equal(float32(0.2), link.SourceTQ)
|
||||
assert.Equal(float32(0.2), link.TargetTQ)
|
||||
break
|
||||
|
||||
case "node:a:mac:wifi":
|
||||
assert.Equal("wifi", link.Type)
|
||||
assert.Equal("node:b:mac:wifi", link.TargetMAC)
|
||||
assert.Equal("node:b:mac:wifi", link.TargetAddress)
|
||||
assert.Equal(float32(0.6), link.SourceTQ)
|
||||
assert.Equal(float32(0.8), link.TargetTQ)
|
||||
case "node:b:mac:lan":
|
||||
assert.Equal("other", link.Type)
|
||||
assert.Equal("node:c:mac:lan", link.TargetMAC)
|
||||
assert.Equal("node:c:mac:lan", link.TargetAddress)
|
||||
assert.Equal(float32(0.8), link.SourceTQ)
|
||||
assert.Equal(float32(0.4), link.TargetTQ)
|
||||
break
|
||||
default:
|
||||
assert.False(true, "invalid link.SourceMAC found")
|
||||
assert.False(true, "invalid link.SourceAddress found")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -64,13 +64,13 @@ type Location struct {
|
||||
|
||||
// Link
|
||||
type Link struct {
|
||||
Type string `json:"type"`
|
||||
Source string `json:"source"`
|
||||
Target string `json:"target"`
|
||||
SourceTQ float32 `json:"source_tq"`
|
||||
TargetTQ float32 `json:"target_tq"`
|
||||
SourceMAC string `json:"source_mac"`
|
||||
TargetMAC string `json:"target_mac"`
|
||||
Type string `json:"type"`
|
||||
Source string `json:"source"`
|
||||
Target string `json:"target"`
|
||||
SourceTQ float32 `json:"source_tq"`
|
||||
TargetTQ float32 `json:"target_tq"`
|
||||
SourceAddress string `json:"source_addr"`
|
||||
TargetAddress string `json:"target_addr"`
|
||||
}
|
||||
|
||||
func NewNode(nodes *runtime.Nodes, n *runtime.Node) *Node {
|
||||
@ -134,15 +134,15 @@ func NewNode(nodes *runtime.Nodes, n *runtime.Node) *Node {
|
||||
}
|
||||
|
||||
node.Uptime = jsontime.Now().Add(time.Duration(statistic.Uptime) * -time.Second)
|
||||
node.GatewayNexthop = nodes.GetNodeIDbyMAC(statistic.GatewayNexthop)
|
||||
node.GatewayNexthop = nodes.GetNodeIDbyAddress(statistic.GatewayNexthop)
|
||||
if node.GatewayNexthop == "" {
|
||||
node.GatewayNexthop = statistic.GatewayNexthop
|
||||
}
|
||||
node.GatewayIPv4 = nodes.GetNodeIDbyMAC(statistic.GatewayIPv4)
|
||||
node.GatewayIPv4 = nodes.GetNodeIDbyAddress(statistic.GatewayIPv4)
|
||||
if node.GatewayIPv4 == "" {
|
||||
node.GatewayIPv4 = statistic.GatewayIPv4
|
||||
}
|
||||
node.GatewayIPv6 = nodes.GetNodeIDbyMAC(statistic.GatewayIPv6)
|
||||
node.GatewayIPv6 = nodes.GetNodeIDbyAddress(statistic.GatewayIPv6)
|
||||
if node.GatewayIPv6 == "" {
|
||||
node.GatewayIPv6 = statistic.GatewayIPv6
|
||||
}
|
||||
|
@ -17,8 +17,7 @@ import (
|
||||
|
||||
// Collector for a specificle respond messages
|
||||
type Collector struct {
|
||||
connections []*net.UDPConn // UDP sockets
|
||||
ifaceToConn map[string]*net.UDPConn // map from interface name to UDP socket
|
||||
connections []multicastConn // UDP sockets
|
||||
port int
|
||||
|
||||
queue chan *Response // received responses
|
||||
@ -29,17 +28,20 @@ type Collector struct {
|
||||
stop chan interface{}
|
||||
}
|
||||
|
||||
type multicastConn struct {
|
||||
Conn *net.UDPConn
|
||||
MulticastAddress net.IP
|
||||
}
|
||||
|
||||
// NewCollector creates a Collector struct
|
||||
func NewCollector(db database.Connection, nodes *runtime.Nodes, sitesDomains map[string][]string, ifaces []string, port int) *Collector {
|
||||
func NewCollector(db database.Connection, nodes *runtime.Nodes, sitesDomains map[string][]string, ifaces []InterfaceConfig) *Collector {
|
||||
|
||||
coll := &Collector{
|
||||
db: db,
|
||||
nodes: nodes,
|
||||
sitesDomains: sitesDomains,
|
||||
port: port,
|
||||
queue: make(chan *Response, 400),
|
||||
stop: make(chan interface{}),
|
||||
ifaceToConn: make(map[string]*net.UDPConn),
|
||||
}
|
||||
|
||||
for _, iface := range ifaces {
|
||||
@ -55,35 +57,47 @@ func NewCollector(db database.Connection, nodes *runtime.Nodes, sitesDomains map
|
||||
return coll
|
||||
}
|
||||
|
||||
func (coll *Collector) listenUDP(iface string) {
|
||||
if _, found := coll.ifaceToConn[iface]; found {
|
||||
log.Panicf("can not listen twice on %s", iface)
|
||||
func (coll *Collector) listenUDP(iface InterfaceConfig) {
|
||||
|
||||
var addr net.IP
|
||||
|
||||
var err error
|
||||
if iface.IPAddress != "" {
|
||||
addr = net.ParseIP(iface.IPAddress)
|
||||
} else {
|
||||
addr, err = getUnicastAddr(iface.InterfaceName)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
}
|
||||
linkLocalAddr, err := getLinkLocalAddr(iface)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
|
||||
multicastAddress := multicastAddressDefault
|
||||
if iface.MulticastAddress != "" {
|
||||
multicastAddress = iface.MulticastAddress
|
||||
}
|
||||
|
||||
// Open socket
|
||||
conn, err := net.ListenUDP("udp", &net.UDPAddr{
|
||||
IP: linkLocalAddr,
|
||||
Port: coll.port,
|
||||
Zone: iface,
|
||||
IP: addr,
|
||||
Port: iface.Port,
|
||||
Zone: iface.InterfaceName,
|
||||
})
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
conn.SetReadBuffer(maxDataGramSize)
|
||||
|
||||
coll.ifaceToConn[iface] = conn
|
||||
coll.connections = append(coll.connections, conn)
|
||||
coll.connections = append(coll.connections, multicastConn{
|
||||
Conn: conn,
|
||||
MulticastAddress: net.ParseIP(multicastAddress),
|
||||
})
|
||||
|
||||
// Start receiver
|
||||
go coll.receiver(conn)
|
||||
}
|
||||
|
||||
// Returns the first link local unicast address for the given interface name
|
||||
func getLinkLocalAddr(ifname string) (net.IP, error) {
|
||||
// Returns a unicast address of given interface (prefer global unicast address over link local address)
|
||||
func getUnicastAddr(ifname string) (net.IP, error) {
|
||||
iface, err := net.InterfaceByName(ifname)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -93,13 +107,23 @@ func getLinkLocalAddr(ifname string) (net.IP, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var ip net.IP
|
||||
|
||||
for _, addr := range addresses {
|
||||
if ipnet := addr.(*net.IPNet); ipnet.IP.IsLinkLocalUnicast() {
|
||||
return ipnet.IP, nil
|
||||
ipnet, ok := addr.(*net.IPNet)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if ipnet.IP.IsGlobalUnicast() {
|
||||
ip = ipnet.IP
|
||||
} else if ipnet.IP.IsLinkLocalUnicast() && ip == nil {
|
||||
ip = ipnet.IP
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("unable to find link local unicast address for %s", ifname)
|
||||
if ip != nil {
|
||||
return ip, nil
|
||||
}
|
||||
return nil, fmt.Errorf("unable to find a unicast address for %s", ifname)
|
||||
}
|
||||
|
||||
// Start Collector
|
||||
@ -122,7 +146,7 @@ func (coll *Collector) Start(interval time.Duration) {
|
||||
func (coll *Collector) Close() {
|
||||
close(coll.stop)
|
||||
for _, conn := range coll.connections {
|
||||
conn.Close()
|
||||
conn.Conn.Close()
|
||||
}
|
||||
close(coll.queue)
|
||||
}
|
||||
@ -139,7 +163,7 @@ func (coll *Collector) sendOnce() {
|
||||
func (coll *Collector) sendMulticast() {
|
||||
log.Println("sending multicasts")
|
||||
for _, conn := range coll.connections {
|
||||
coll.sendPacket(conn, multiCastGroup)
|
||||
coll.sendPacket(conn.Conn, conn.MulticastAddress)
|
||||
}
|
||||
}
|
||||
|
||||
@ -153,21 +177,29 @@ func (coll *Collector) sendUnicasts(seenBefore jsontime.Time) {
|
||||
})
|
||||
|
||||
// Send unicast packets
|
||||
log.Printf("sending unicast to %d nodes", len(nodes))
|
||||
count := 0
|
||||
for _, node := range nodes {
|
||||
conn := coll.ifaceToConn[node.Address.Zone]
|
||||
if conn == nil {
|
||||
log.Printf("unable to find connection for %s", node.Address.Zone)
|
||||
continue
|
||||
send := 0
|
||||
for _, conn := range coll.connections {
|
||||
if node.Address.Zone != "" && conn.Conn.LocalAddr().(*net.UDPAddr).Zone != node.Address.Zone {
|
||||
continue
|
||||
}
|
||||
coll.sendPacket(conn.Conn, node.Address.IP)
|
||||
send++
|
||||
}
|
||||
if send == 0 {
|
||||
log.Printf("unable to find connection for %s", node.Address.Zone)
|
||||
} else {
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
count += send
|
||||
}
|
||||
coll.sendPacket(conn, node.Address.IP)
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
log.Printf("sending %d unicast pkg for %d nodes", count, len(nodes))
|
||||
}
|
||||
|
||||
// SendPacket sends a UDP request to the given unicast or multicast address on the first UDP socket
|
||||
func (coll *Collector) SendPacket(destination net.IP) {
|
||||
coll.sendPacket(coll.connections[0], destination)
|
||||
coll.sendPacket(coll.connections[0].Conn, destination)
|
||||
}
|
||||
|
||||
// sendPacket sends a UDP request to the given unicast or multicast address on the given UDP socket
|
||||
@ -201,7 +233,7 @@ func (coll *Collector) sender() {
|
||||
func (coll *Collector) parser() {
|
||||
for obj := range coll.queue {
|
||||
if data, err := obj.parse(); err != nil {
|
||||
log.Println("unable to decode response from", obj.Address.String(), err, "\n", string(obj.Raw))
|
||||
log.Println("unable to decode response from", obj.Address.String(), err)
|
||||
} else {
|
||||
coll.saveResponse(obj.Address, data)
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ const (
|
||||
func TestCollector(t *testing.T) {
|
||||
nodes := runtime.NewNodes(&runtime.NodesConfig{})
|
||||
|
||||
collector := NewCollector(nil, nodes, map[string][]string{SITE_TEST: {DOMAIN_TEST}}, []string{}, 10001)
|
||||
collector := NewCollector(nil, nodes, map[string][]string{SITE_TEST: {DOMAIN_TEST}}, []InterfaceConfig{})
|
||||
collector.Start(time.Millisecond)
|
||||
time.Sleep(time.Millisecond * 10)
|
||||
collector.Close()
|
||||
|
@ -5,9 +5,8 @@ import "github.com/FreifunkBremen/yanic/lib/duration"
|
||||
type Config struct {
|
||||
Enable bool `toml:"enable"`
|
||||
Synchronize duration.Duration `toml:"synchronize"`
|
||||
Interfaces []string `toml:"interfaces"`
|
||||
Interfaces []InterfaceConfig `toml:"interfaces"`
|
||||
Sites map[string]SiteConfig `toml:"sites"`
|
||||
Port int `toml:"port"`
|
||||
CollectInterval duration.Duration `toml:"collect_interval"`
|
||||
}
|
||||
|
||||
@ -22,3 +21,10 @@ func (c *Config) SitesDomains() (result map[string][]string) {
|
||||
type SiteConfig struct {
|
||||
Domains []string `toml:"domains"`
|
||||
}
|
||||
|
||||
type InterfaceConfig struct {
|
||||
InterfaceName string `toml:"ifname"`
|
||||
IPAddress string `toml:"ip_address"`
|
||||
MulticastAddress string `toml:"multicast_address"`
|
||||
Port int `toml:"port"`
|
||||
}
|
||||
|
@ -4,10 +4,10 @@ import (
|
||||
"net"
|
||||
)
|
||||
|
||||
// default multicast group used by announced
|
||||
var multiCastGroup = net.ParseIP("ff02:0:0:0:0:0:2:1001")
|
||||
|
||||
const (
|
||||
// default multicast group used by announced
|
||||
multicastAddressDefault = "ff02:0:0:0:0:0:2:1001"
|
||||
|
||||
// default udp port used by announced
|
||||
port = 1001
|
||||
|
||||
|
@ -20,11 +20,11 @@ type Node struct {
|
||||
|
||||
// Link represents a link between two nodes
|
||||
type Link struct {
|
||||
SourceID string
|
||||
SourceMAC string
|
||||
TargetID string
|
||||
TargetMAC string
|
||||
TQ int
|
||||
SourceID string
|
||||
SourceAddress string
|
||||
TargetID string
|
||||
TargetAddress string
|
||||
TQ float32
|
||||
}
|
||||
|
||||
// IsGateway returns whether the node is a gateway
|
||||
|
@ -100,8 +100,8 @@ func (nodes *Nodes) Select(f func(*Node) bool) []*Node {
|
||||
return result
|
||||
}
|
||||
|
||||
func (nodes *Nodes) GetNodeIDbyMAC(mac string) string {
|
||||
return nodes.ifaceToNodeID[mac]
|
||||
func (nodes *Nodes) GetNodeIDbyAddress(addr string) string {
|
||||
return nodes.ifaceToNodeID[addr]
|
||||
}
|
||||
|
||||
// NodeLinks returns a list of links to known neighbours
|
||||
@ -116,11 +116,24 @@ func (nodes *Nodes) NodeLinks(node *Node) (result []Link) {
|
||||
for neighbourMAC, link := range batadv.Neighbours {
|
||||
if neighbourID := nodes.ifaceToNodeID[neighbourMAC]; neighbourID != "" {
|
||||
result = append(result, Link{
|
||||
SourceID: neighbours.NodeID,
|
||||
SourceMAC: sourceMAC,
|
||||
TargetID: neighbourID,
|
||||
TargetMAC: neighbourMAC,
|
||||
TQ: link.Tq,
|
||||
SourceID: neighbours.NodeID,
|
||||
SourceAddress: sourceMAC,
|
||||
TargetID: neighbourID,
|
||||
TargetAddress: neighbourMAC,
|
||||
TQ: float32(link.Tq) / 255.0,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, iface := range neighbours.Babel {
|
||||
for neighbourIP, link := range iface.Neighbours {
|
||||
if neighbourID := nodes.ifaceToNodeID[neighbourIP]; neighbourID != "" {
|
||||
result = append(result, Link{
|
||||
SourceID: neighbours.NodeID,
|
||||
SourceAddress: iface.LinkLocalAddress,
|
||||
TargetID: neighbourID,
|
||||
TargetAddress: neighbourIP,
|
||||
TQ: 1.0 - (float32(link.Cost) / 65535.0),
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -179,19 +192,19 @@ func (nodes *Nodes) readIfaces(nodeinfo *data.NodeInfo) {
|
||||
|
||||
addresses := []string{network.Mac}
|
||||
|
||||
for _, batinterface := range network.Mesh {
|
||||
addresses = append(addresses, batinterface.Addresses()...)
|
||||
for _, iface := range network.Mesh {
|
||||
addresses = append(addresses, iface.Addresses()...)
|
||||
}
|
||||
|
||||
for _, mac := range addresses {
|
||||
if mac == "" {
|
||||
for _, addr := range addresses {
|
||||
if addr == "" {
|
||||
continue
|
||||
}
|
||||
if oldNodeID, _ := nodes.ifaceToNodeID[mac]; oldNodeID != nodeID {
|
||||
if oldNodeID, _ := nodes.ifaceToNodeID[addr]; oldNodeID != nodeID {
|
||||
if oldNodeID != "" {
|
||||
log.Printf("override nodeID from %s to %s on MAC address %s", oldNodeID, nodeID, mac)
|
||||
log.Printf("override nodeID from %s to %s on MAC address %s", oldNodeID, nodeID, addr)
|
||||
}
|
||||
nodes.ifaceToNodeID[mac] = nodeID
|
||||
nodes.ifaceToNodeID[addr] = nodeID
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -179,7 +179,7 @@ func TestLinksNodes(t *testing.T) {
|
||||
"f4:f2:6d:d7:a3:0b": {
|
||||
Neighbours: map[string]data.BatmanLink{
|
||||
"f4:f2:6d:d7:a3:0a": {
|
||||
Tq: 200, Lastseen: 0.42,
|
||||
Tq: 204, Lastseen: 0.42,
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -198,11 +198,11 @@ func TestLinksNodes(t *testing.T) {
|
||||
assert.Len(links, 1)
|
||||
link := links[0]
|
||||
assert.Equal(link.SourceID, "f4f26dd7a30b")
|
||||
assert.Equal(link.SourceMAC, "f4:f2:6d:d7:a3:0b")
|
||||
assert.Equal(link.SourceAddress, "f4:f2:6d:d7:a3:0b")
|
||||
assert.Equal(link.TargetID, "f4f26dd7a30a")
|
||||
assert.Equal(link.TargetMAC, "f4:f2:6d:d7:a3:0a")
|
||||
assert.Equal(link.TQ, 200)
|
||||
assert.Equal(link.TargetAddress, "f4:f2:6d:d7:a3:0a")
|
||||
assert.Equal(link.TQ, float32(0.8))
|
||||
|
||||
nodeid := nodes.GetNodeIDbyMAC("f4:f2:6d:d7:a3:0a")
|
||||
nodeid := nodes.GetNodeIDbyAddress("f4:f2:6d:d7:a3:0a")
|
||||
assert.Equal("f4f26dd7a30a", nodeid)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user