178 lines
3.5 KiB
Go
178 lines
3.5 KiB
Go
package tendrils
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
"time"
|
|
)
|
|
|
|
type MulticastGroup struct {
|
|
IP net.IP
|
|
}
|
|
|
|
type MulticastMembership struct {
|
|
Node *Node
|
|
LastSeen time.Time
|
|
}
|
|
|
|
type MulticastGroupMembers struct {
|
|
Group *MulticastGroup
|
|
Members map[string]*MulticastMembership // source IP -> membership
|
|
}
|
|
|
|
func (g *MulticastGroup) Name() string {
|
|
ip := g.IP.To4()
|
|
if ip == nil {
|
|
return g.IP.String()
|
|
}
|
|
|
|
switch g.IP.String() {
|
|
case "224.0.0.251":
|
|
return "mdns"
|
|
case "224.0.1.129":
|
|
return "ptp"
|
|
case "224.0.1.130":
|
|
return "ptp-announce"
|
|
case "224.0.1.131":
|
|
return "ptp-sync"
|
|
case "224.0.1.132":
|
|
return "ptp-delay"
|
|
case "224.2.127.254":
|
|
return "sap"
|
|
case "239.255.254.253":
|
|
return "shure-slp"
|
|
case "239.255.255.250":
|
|
return "ssdp"
|
|
case "239.255.255.253":
|
|
return "slp"
|
|
case "239.255.255.255":
|
|
return "admin-scoped-broadcast"
|
|
}
|
|
|
|
// sACN (239.255.x.x, universes 1-63999)
|
|
if ip[0] == 239 && ip[1] == 255 {
|
|
universe := int(ip[2])*256 + int(ip[3])
|
|
if universe >= 1 && universe <= 63999 {
|
|
return fmt.Sprintf("sacn:%d", universe)
|
|
}
|
|
}
|
|
|
|
// Dante audio multicast (239.69-71.x.x)
|
|
if ip[0] == 239 && ip[1] >= 69 && ip[1] <= 71 {
|
|
flowID := (int(ip[1]-69) << 16) | (int(ip[2]) << 8) | int(ip[3])
|
|
return fmt.Sprintf("dante-mcast:%d", flowID)
|
|
}
|
|
|
|
// Dante AV multicast (239.253.x.x)
|
|
if ip[0] == 239 && ip[1] == 253 {
|
|
flowID := (int(ip[2]) << 8) | int(ip[3])
|
|
return fmt.Sprintf("dante-av:%d", flowID)
|
|
}
|
|
|
|
return g.IP.String()
|
|
}
|
|
|
|
func (g *MulticastGroup) IsDante() bool {
|
|
ip := g.IP.To4()
|
|
if ip == nil {
|
|
return false
|
|
}
|
|
if ip[0] == 239 && ip[1] >= 69 && ip[1] <= 71 {
|
|
return true
|
|
}
|
|
if ip[0] == 239 && ip[1] == 253 {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (n *Nodes) UpdateMulticastMembership(sourceIP, groupIP net.IP) {
|
|
n.mu.Lock()
|
|
defer n.mu.Unlock()
|
|
|
|
node := n.getNodeByIPLocked(sourceIP)
|
|
|
|
groupKey := groupIP.String()
|
|
sourceKey := sourceIP.String()
|
|
|
|
gm := n.multicastGroups[groupKey]
|
|
if gm == nil {
|
|
gm = &MulticastGroupMembers{
|
|
Group: &MulticastGroup{IP: groupIP},
|
|
Members: map[string]*MulticastMembership{},
|
|
}
|
|
n.multicastGroups[groupKey] = gm
|
|
}
|
|
|
|
gm.Members[sourceKey] = &MulticastMembership{
|
|
Node: node,
|
|
LastSeen: time.Now(),
|
|
}
|
|
}
|
|
|
|
func (n *Nodes) RemoveMulticastMembership(sourceIP, groupIP net.IP) {
|
|
n.mu.Lock()
|
|
defer n.mu.Unlock()
|
|
|
|
groupKey := groupIP.String()
|
|
sourceKey := sourceIP.String()
|
|
|
|
if gm := n.multicastGroups[groupKey]; gm != nil {
|
|
delete(gm.Members, sourceKey)
|
|
if len(gm.Members) == 0 {
|
|
delete(n.multicastGroups, groupKey)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (n *Nodes) GetDanteMulticastGroups(deviceIP net.IP) []net.IP {
|
|
n.mu.RLock()
|
|
defer n.mu.RUnlock()
|
|
|
|
deviceKey := deviceIP.String()
|
|
var groups []net.IP
|
|
|
|
for _, gm := range n.multicastGroups {
|
|
if !gm.Group.IsDante() {
|
|
continue
|
|
}
|
|
if _, exists := gm.Members[deviceKey]; exists {
|
|
groups = append(groups, gm.Group.IP)
|
|
}
|
|
}
|
|
return groups
|
|
}
|
|
|
|
func (n *Nodes) GetMulticastGroupMembers(groupIP net.IP) []*Node {
|
|
n.mu.RLock()
|
|
defer n.mu.RUnlock()
|
|
|
|
groupKey := groupIP.String()
|
|
gm := n.multicastGroups[groupKey]
|
|
if gm == nil {
|
|
return nil
|
|
}
|
|
|
|
var members []*Node
|
|
for _, membership := range gm.Members {
|
|
if membership.Node != nil {
|
|
members = append(members, membership.Node)
|
|
}
|
|
}
|
|
return members
|
|
}
|
|
|
|
func (n *Nodes) expireMulticastMemberships() {
|
|
expireTime := time.Now().Add(-5 * time.Minute)
|
|
for groupKey, gm := range n.multicastGroups {
|
|
for sourceKey, membership := range gm.Members {
|
|
if membership.LastSeen.Before(expireTime) {
|
|
delete(gm.Members, sourceKey)
|
|
}
|
|
}
|
|
if len(gm.Members) == 0 {
|
|
delete(n.multicastGroups, groupKey)
|
|
}
|
|
}
|
|
}
|