From 50349f400f17c5ed2fe875a46d97995f5f623320 Mon Sep 17 00:00:00 2001 From: Nicolai Ommer Date: Sun, 24 Mar 2024 13:48:32 +0100 Subject: [PATCH] Use golang.org/x/net/ipv4 to receive multicast messages in ssl-multicast-sources With the previous approach, message from different interfaces were received by all interfaces. With the higher level net/ipv4, suggested by the GO documentation, this can be done in a more sane way. --- cmd/ssl-multicast-sources/main.go | 70 ++++++++++++++++++------------- go.mod | 5 +++ go.sum | 4 ++ 3 files changed, 51 insertions(+), 28 deletions(-) diff --git a/cmd/ssl-multicast-sources/main.go b/cmd/ssl-multicast-sources/main.go index 1eed308..eb9caa8 100644 --- a/cmd/ssl-multicast-sources/main.go +++ b/cmd/ssl-multicast-sources/main.go @@ -2,10 +2,12 @@ package main import ( "flag" + "golang.org/x/net/ipv4" "log" "net" "os" "os/signal" + "strings" "sync" "syscall" ) @@ -13,9 +15,8 @@ import ( const maxDatagramSize = 8192 type Source struct { - nif string - ip string - port int + cm string + src string } var detectedRemotes map[string][]Source @@ -30,11 +31,8 @@ func main() { mcAddresses = []string{"224.5.23.1:10003", "224.5.23.2:10006", "224.5.23.2:10010", "224.5.23.2:10012"} } - ifiList := interfaces() for _, address := range mcAddresses { - for _, ifi := range ifiList { - go receiveOnInterface(address, ifi) - } + go receiveOnInterfaceIpv4(address) } signals := make(chan os.Signal, 1) @@ -54,38 +52,54 @@ func interfaces() (interfaces []net.Interface) { return } -func receiveOnInterface(address string, ifi net.Interface) { - addr, err := net.ResolveUDPAddr("udp", address) +func receiveOnInterfaceIpv4(address string) { + group := net.ParseIP(strings.Split(address, ":")[0]) + c, err := net.ListenPacket("udp4", address) if err != nil { - log.Printf("Could resolve multicast address %v: %v", address, err) + log.Printf("Could not start listening on %v: %v", address, err) return } + defer func(c net.PacketConn) { + if err := c.Close(); err != nil { + log.Println("Failed to close connection:", err) + } + }(c) - conn, err := net.ListenMulticastUDP("udp", &ifi, addr) - if err != nil { - log.Printf("Could not listen at %v: %v", address, err) - return - } + log.Printf("Listening on %s", address) + + p := ipv4.NewPacketConn(c) - if err := conn.SetReadBuffer(maxDatagramSize); err != nil { - log.Println("Could not set read buffer: ", err) + if err := p.SetControlMessage(ipv4.FlagDst, true); err != nil { + log.Println("Failed to set control message flag 'FlagDst':", err) } - log.Printf("Listening on %s (%s)", address, ifi.Name) + ifiList := interfaces() + for _, ifi := range ifiList { + if err := p.JoinGroup(&ifi, &net.UDPAddr{IP: group}); err != nil { + log.Printf("Failed to join multicast group %v: %v", group, err) + } else { + log.Printf("Joined multicast group %v on interface %+v", group, ifi) + } + } data := make([]byte, maxDatagramSize) for { - _, remoteAddr, err := conn.ReadFromUDP(data) + _, cm, src, err := p.ReadFrom(data) if err != nil { - log.Println("ReadFromUDP failed:", err) - return + log.Printf("Could not read from %v: %v", address, err) + continue + } + if cm.Dst.IsMulticast() { + if cm.Dst.Equal(group) { + addRemote(address, Source{ + cm: cm.String(), + src: src.String(), + }) + } else { + // unknown group, discard + continue + } } - - addRemote(address, Source{ - nif: ifi.Name, - ip: remoteAddr.IP.String(), - port: remoteAddr.Port, - }) } } @@ -104,5 +118,5 @@ func addRemote(address string, source Source) { } detectedRemotes[address] = append(detectedRemotes[address], source) - log.Printf("New source on %v: %+v\n", address, source) + log.Printf("New source on %v: %+v", address, source) } diff --git a/go.mod b/go.mod index 5c5c26d..81c0cc8 100644 --- a/go.mod +++ b/go.mod @@ -6,3 +6,8 @@ require ( github.com/pkg/errors v0.9.1 google.golang.org/protobuf v1.33.0 ) + +require ( + golang.org/x/net v0.22.0 // indirect + golang.org/x/sys v0.18.0 // indirect +) diff --git a/go.sum b/go.sum index 7f7e0ac..5ec8775 100644 --- a/go.sum +++ b/go.sum @@ -3,6 +3,10 @@ github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= +golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=