diff --git a/src/multicast/interfaces_linux.go b/src/multicast/interfaces_linux.go new file mode 100644 index 00000000..dc08f122 --- /dev/null +++ b/src/multicast/interfaces_linux.go @@ -0,0 +1,7 @@ +// +build linux + +package multicast + +func (m *Multicast) _updateInterfaces() { + return +} diff --git a/src/multicast/interfaces_other.go b/src/multicast/interfaces_other.go new file mode 100644 index 00000000..5896a096 --- /dev/null +++ b/src/multicast/interfaces_other.go @@ -0,0 +1,65 @@ +// +build !linux + +package multicast + +import ( + "net" + "regexp" +) + +func (m *Multicast) _updateInterfaces() { + interfaces := make(map[string]interfaceInfo) + intfs := m.getAllowedInterfaces() + for _, intf := range intfs { + addrs, err := intf.Addrs() + if err != nil { + m.log.Warnf("Failed up get addresses for interface %s: %s", intf.Name, err) + continue + } + interfaces[intf.Name] = interfaceInfo{ + iface: intf, + addrs: addrs, + } + } + m._interfaces = interfaces +} + +// getAllowedInterfaces returns the currently known/enabled multicast interfaces. +func (m *Multicast) getAllowedInterfaces() map[string]net.Interface { + interfaces := make(map[string]net.Interface) + // Get interface expressions from config + current := m.config.GetCurrent() + exprs := current.MulticastInterfaces + // Ask the system for network interfaces + allifaces, err := net.Interfaces() + if err != nil { + panic(err) + } + // Work out which interfaces to announce on + for _, iface := range allifaces { + if iface.Flags&net.FlagUp == 0 { + // Ignore interfaces that are down + continue + } + if iface.Flags&net.FlagMulticast == 0 { + // Ignore non-multicast interfaces + continue + } + if iface.Flags&net.FlagPointToPoint != 0 { + // Ignore point-to-point interfaces + continue + } + for _, expr := range exprs { + // Compile each regular expression + e, err := regexp.Compile(expr) + if err != nil { + panic(err) + } + // Does the interface match the regular expression? Store it if so + if e.MatchString(iface.Name) { + interfaces[iface.Name] = iface + } + } + } + return interfaces +} diff --git a/src/multicast/multicast.go b/src/multicast/multicast.go index 7de3cfab..945e9a2e 100644 --- a/src/multicast/multicast.go +++ b/src/multicast/multicast.go @@ -4,7 +4,6 @@ import ( "context" "fmt" "net" - "regexp" "time" "github.com/Arceliar/phony" @@ -155,23 +154,6 @@ func (m *Multicast) _updateConfig(config *config.NodeConfig) { m.log.Debugln("Reloaded multicast configuration successfully") } -func (m *Multicast) _updateInterfaces() { - interfaces := make(map[string]interfaceInfo) - intfs := m.getAllowedInterfaces() - for _, intf := range intfs { - addrs, err := intf.Addrs() - if err != nil { - m.log.Warnf("Failed up get addresses for interface %s: %s", intf.Name, err) - continue - } - interfaces[intf.Name] = interfaceInfo{ - iface: intf, - addrs: addrs, - } - } - m._interfaces = interfaces -} - func (m *Multicast) Interfaces() map[string]net.Interface { interfaces := make(map[string]net.Interface) phony.Block(m, func() { @@ -182,46 +164,6 @@ func (m *Multicast) Interfaces() map[string]net.Interface { return interfaces } -// getAllowedInterfaces returns the currently known/enabled multicast interfaces. -func (m *Multicast) getAllowedInterfaces() map[string]net.Interface { - interfaces := make(map[string]net.Interface) - // Get interface expressions from config - current := m.config.GetCurrent() - exprs := current.MulticastInterfaces - // Ask the system for network interfaces - allifaces, err := net.Interfaces() - if err != nil { - panic(err) - } - // Work out which interfaces to announce on - for _, iface := range allifaces { - if iface.Flags&net.FlagUp == 0 { - // Ignore interfaces that are down - continue - } - if iface.Flags&net.FlagMulticast == 0 { - // Ignore non-multicast interfaces - continue - } - if iface.Flags&net.FlagPointToPoint != 0 { - // Ignore point-to-point interfaces - continue - } - for _, expr := range exprs { - // Compile each regular expression - e, err := regexp.Compile(expr) - if err != nil { - panic(err) - } - // Does the interface match the regular expression? Store it if so - if e.MatchString(iface.Name) { - interfaces[iface.Name] = iface - } - } - } - return interfaces -} - func (m *Multicast) _announce() { if !m.isOpen { return diff --git a/src/multicast/multicast_linux.go b/src/multicast/multicast_linux.go new file mode 100644 index 00000000..76c614da --- /dev/null +++ b/src/multicast/multicast_linux.go @@ -0,0 +1,99 @@ +// +build linux + +package multicast + +import ( + "fmt" + "net" + "regexp" + "syscall" + + "github.com/vishvananda/netlink" + "golang.org/x/sys/unix" +) + +func (m *Multicast) _multicastStarted() { + linkChanges := make(chan netlink.LinkUpdate) + addrChanges := make(chan netlink.AddrUpdate) + + linkClose := make(chan struct{}) + addrClose := make(chan struct{}) + + if err := netlink.LinkSubscribe(linkChanges, linkClose); err != nil { + panic(err) + } + + if err := netlink.AddrSubscribe(addrChanges, addrClose); err != nil { + panic(err) + } + + fmt.Println("Listening for netlink changes") + + go func() { + defer fmt.Println("No longer listening for netlink changes") + for { + select { + case change := <-linkChanges: + attrs := change.Attrs() + add := true + add = add && attrs.Flags&net.FlagUp == 1 + add = add && attrs.Flags&net.FlagMulticast == 1 + add = add && attrs.Flags&net.FlagPointToPoint == 0 + + match := false + for _, expr := range exprs { + e, err := regexp.Compile(expr) + if err != nil { + panic(err) + } + if e.MatchString(attrs.Name) { + match = true + break + } + } + add = add && match + + if add { + m.Act(nil, func() { + fmt.Println("Link added:", attrs.Name) + if intf, err := net.InterfaceByName(attrs.Name); err == nil { + m._interfaces[attrs.Name] = intf + } + }) + } else { + m.Act(nil, func() { + fmt.Println("Link removed:", attrs.Name) + delete(m._interfaces, attrs.Name) + }) + } + + case change := <-addrChanges: + m.Act(nil, func() { + fmt.Println("Addr changed:", change) + }) + + case <-linkClose: + return + + case <-addrClose: + return + } + } + }() +} + +func (m *Multicast) multicastReuse(network string, address string, c syscall.RawConn) error { + var control error + var reuseport error + + control = c.Control(func(fd uintptr) { + reuseport = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEPORT, 1) + }) + + switch { + case reuseport != nil: + return reuseport + default: + return control + } +} diff --git a/src/multicast/multicast_unix.go b/src/multicast/multicast_unix.go index 1ff48b17..cfcd04a6 100644 --- a/src/multicast/multicast_unix.go +++ b/src/multicast/multicast_unix.go @@ -1,9 +1,12 @@ -// +build linux netbsd freebsd openbsd dragonflybsd +// +build netbsd freebsd openbsd dragonflybsd package multicast -import "syscall" -import "golang.org/x/sys/unix" +import ( + "syscall" + + "golang.org/x/sys/unix" +) func (m *Multicast) _multicastStarted() {