From f944fa4d250692d58aeb77864ff9fda70941c515 Mon Sep 17 00:00:00 2001 From: Gabor Lekeny Date: Sun, 16 Oct 2022 18:29:05 +0200 Subject: [PATCH 01/10] feat: add multicast UDP listener support --- internal/socket/sockopts_posix.go | 103 ++++++++++++++++++++++++++++++ listener.go | 6 ++ options.go | 10 +++ pkg/errors/errors.go | 2 + 4 files changed, 121 insertions(+) diff --git a/internal/socket/sockopts_posix.go b/internal/socket/sockopts_posix.go index e8aa2a028..862b70ac2 100644 --- a/internal/socket/sockopts_posix.go +++ b/internal/socket/sockopts_posix.go @@ -18,9 +18,11 @@ package socket import ( + "net" "os" "syscall" + "github.com/panjf2000/gnet/v2/pkg/errors" "golang.org/x/sys/unix" ) @@ -83,3 +85,104 @@ func SetLinger(fd, sec int) error { } return unix.SetsockoptLinger(fd, syscall.SOL_SOCKET, syscall.SO_LINGER, &l) } + +// SetMulticastMembership returns with a socket option function based on the IP +// version. Returns nil when multicast membership cannot be applied. +func SetMulticastMembership(proto, addr string) func(int, int) error { + var udpVersion string + udpAddr, err := net.ResolveUDPAddr(proto, addr) + if err != nil || !udpAddr.IP.IsMulticast() { + return nil + } + + udpVersion, err = determineUDPProto(proto, udpAddr) + if err != nil { + return nil + } + + switch udpVersion { + case "udp4": + return func(fd int, ifIndex int) error { + return SetIPv4MulticastMembership(fd, udpAddr.IP, ifIndex) + } + case "udp6": + return func(fd int, ifIndex int) error { + return SetIPv6MulticastMembership(fd, udpAddr.IP, ifIndex) + } + default: + return nil + } +} + +// SetIPv4MulticastMemership joins fd to the specified multicast IPv4 address. +// ifIndex is the index of the interface where the multicast datagrams will be +// received. If ifIndex is 0 then the operating system will choose the default, +// it is usually needed when the host has multiple network interfaces configured. +func SetIPv4MulticastMembership(fd int, mcast net.IP, ifIndex int) error { + // Multicast interfaces are selected by IP address on IPv4 (and by index on IPv6) + ip, err := interfaceFirstIPv4Addr(ifIndex) + if err != nil { + return err + } + + mreq := &unix.IPMreq{} + copy(mreq.Multiaddr[:], mcast.To4()) + copy(mreq.Interface[:], ip.To4()) + + if ifIndex > 0 { + if err := os.NewSyscallError("setsockopt", unix.SetsockoptInet4Addr(fd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF, mreq.Interface)); err != nil { + return err + } + } + + if err := os.NewSyscallError("setsockopt", unix.SetsockoptByte(fd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP, 0)); err != nil { + return err + } + return os.NewSyscallError("setsockopt", unix.SetsockoptIPMreq(fd, syscall.IPPROTO_IP, syscall.IP_ADD_MEMBERSHIP, mreq)) +} + +// SetIPv6MulticastMemership joins fd to the specified multicast IPv6 address. +// ifIndex is the index of the interface where the multicast datagrams will be +// received. If ifIndex is 0 then the operating system will choose the default, +// it is usually needed when the host has multiple network interfaces configured. +func SetIPv6MulticastMembership(fd int, mcast net.IP, ifIndex int) error { + mreq := &unix.IPv6Mreq{} + mreq.Interface = uint32(ifIndex) + copy(mreq.Multiaddr[:], mcast.To16()) + + if ifIndex > 0 { + if err := os.NewSyscallError("setsockopt", unix.SetsockoptInt(fd, syscall.IPPROTO_IPV6, syscall.IPV6_MULTICAST_IF, ifIndex)); err != nil { + return err + } + } + + if err := os.NewSyscallError("setsockopt", unix.SetsockoptInt(fd, syscall.IPPROTO_IPV6, syscall.IPV6_MULTICAST_LOOP, 0)); err != nil { + return err + } + return os.NewSyscallError("setsockopt", unix.SetsockoptIPv6Mreq(fd, syscall.IPPROTO_IPV6, syscall.IPV6_JOIN_GROUP, mreq)) +} + +// interfaceFirstIPv4Addr returns the first IPv4 address of the interface. +func interfaceFirstIPv4Addr(ifIndex int) (net.IP, error) { + if ifIndex == 0 { + return net.IP([]byte{0, 0, 0, 0}), nil + } + iface, err := net.InterfaceByIndex(ifIndex) + if err != nil { + return nil, err + } + addrs, err := iface.Addrs() + if err != nil { + return nil, err + } + for _, addr := range addrs { + ip, _, err := net.ParseCIDR(addr.String()) + if err != nil { + return nil, err + } + if ip.To4() != nil { + return ip, nil + } + } + return nil, errors.ErrNoIPv4AddressOnInterface +} diff --git a/listener.go b/listener.go index 63033a94a..c1199fe27 100644 --- a/listener.go +++ b/listener.go @@ -100,6 +100,12 @@ func initListener(network, addr string, options *Options) (l *listener, err erro sockOpt := socket.Option{SetSockOpt: socket.SetSendBuffer, Opt: options.SocketSendBuffer} sockOpts = append(sockOpts, sockOpt) } + if strings.HasPrefix(network, "udp") { + if sockoptFn := socket.SetMulticastMembership(network, addr); sockoptFn != nil { + sockOpt := socket.Option{SetSockOpt: sockoptFn, Opt: options.MulticastInterfaceIndex} + sockOpts = append(sockOpts, sockOpt) + } + } l = &listener{network: network, address: addr, sockOpts: sockOpts} err = l.normalize() return diff --git a/options.go b/options.go index 931385b9f..d941b23bb 100644 --- a/options.go +++ b/options.go @@ -63,6 +63,9 @@ type Options struct { // ReusePort indicates whether to set up the SO_REUSEPORT socket option. ReusePort bool + // MulticastInterfaceIndex is the index of the interface name where the multicast UDP addresses will be bound to. + MulticastInterfaceIndex int + // ============================= Options for both server-side and client-side ============================= // ReadBufferCap is the maximum number of bytes that can be read from the peer when the readable event comes. @@ -239,3 +242,10 @@ func WithLogger(logger logging.Logger) Option { opts.Logger = logger } } + +// WithMulticastInterfaceIndex sets the interface name where UDP multicast sockets will be bound to. +func WithMulticastInterfaceIndex(idx int) Option { + return func(opts *Options) { + opts.MulticastInterfaceIndex = idx + } +} diff --git a/pkg/errors/errors.go b/pkg/errors/errors.go index 4343f7d4d..0bd67fc0b 100644 --- a/pkg/errors/errors.go +++ b/pkg/errors/errors.go @@ -39,4 +39,6 @@ var ( ErrUnsupportedOp = errors.New("unsupported operation") // ErrNegativeSize occurs when trying to pass a negative size to a buffer. ErrNegativeSize = errors.New("negative size is invalid") + // ErrNoIPv4AddressOnInterface occurs when an IPv4 multicast address is set on an interface but IPv4 is not configured. + ErrNoIPv4AddressOnInterface = errors.New("no IPv4 address on interface") ) From 8632a307b443ea658112352c3cab05a7f04ceee2 Mon Sep 17 00:00:00 2001 From: Gabor Lekeny Date: Thu, 3 Nov 2022 10:07:17 +0100 Subject: [PATCH 02/10] add multicast tests --- gnet_test.go | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) diff --git a/gnet_test.go b/gnet_test.go index 751eaa495..9b56c6f13 100644 --- a/gnet_test.go +++ b/gnet_test.go @@ -25,6 +25,7 @@ import ( "math/rand" "net" "runtime" + "sync" "sync/atomic" "testing" "time" @@ -411,6 +412,101 @@ func startClient(t *testing.T, network, addr string, multicore, async bool) { } } +// NOTE: TestServeMulticast can fail with "write: no buffer space available" on wifi interface +func TestServeMulticast(t *testing.T) { + // 224.0.0.169 is an unassigned address from the Local Network Control Block + // https://www.iana.org/assignments/multicast-addresses/multicast-addresses.xhtml#multicast-addresses-1 + t.Run("udp-multicast", func(t *testing.T) { + testMulticast(t, "224.0.0.169:9991", false, false, 10) + }) + t.Run("udp-multicast-reuseport", func(t *testing.T) { + testMulticast(t, "224.0.0.169:9991", true, false, 10) + }) + t.Run("udp-multicast-reuseaddr", func(t *testing.T) { + testMulticast(t, "224.0.0.169:9991", false, true, 10) + }) +} + +func testMulticast(t *testing.T, addr string, reuseport, reuseaddr bool, nclients int) { + ts := &testMcastServer{ + t: t, + addr: addr, + nclients: nclients, + } + err := Run(ts, "udp://"+addr, + WithReuseAddr(reuseaddr), + WithReusePort(reuseport), + WithSocketRecvBuffer(2*nclients*1024), // enough space to receive messages from nclients to eliminate dropped packets + WithTicker(true)) + assert.NoError(t, err) +} + +type testMcastServer struct { + *BuiltinEventEngine + t *testing.T + mcast sync.Map + addr string + nclients int + started int32 + active int32 +} + +func (s *testMcastServer) startMcastClient() { + rand.Seed(time.Now().UnixNano()) + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + c, err := net.Dial("udp", s.addr) + require.NoError(s.t, err) + defer c.Close() + ch := make(chan []byte, 10000) + s.mcast.Store(c.LocalAddr().String(), ch) + duration := time.Duration((rand.Float64()*2+1)*float64(time.Second)) / 2 + s.t.Logf("test duration: %dms", duration/time.Millisecond) + start := time.Now() + for time.Since(start) < duration { + reqData := make([]byte, 1024) + _, err = rand.Read(reqData) + require.NoError(s.t, err) + _, err = c.Write(reqData) + require.NoError(s.t, err) + select { + case respData := <-ch: + require.Equalf(s.t, reqData, respData, "response mismatch, length of bytes: %d vs %d", len(reqData), len(respData)) + case <-ctx.Done(): + require.Fail(s.t, "timeout receiving message") + return + } + } +} + +func (s *testMcastServer) OnTraffic(c Conn) (action Action) { + buf, _ := c.Next(-1) + b := make([]byte, len(buf)) + copy(b, buf) + ch, ok := s.mcast.Load(c.RemoteAddr().String()) + require.True(s.t, ok) + ch.(chan []byte) <- b + return +} + +func (s *testMcastServer) OnTick() (delay time.Duration, action Action) { + if atomic.CompareAndSwapInt32(&s.started, 0, 1) { + for i := 0; i < s.nclients; i++ { + atomic.AddInt32(&s.active, 1) + go func() { + s.startMcastClient() + atomic.AddInt32(&s.active, -1) + }() + } + } + if atomic.LoadInt32(&s.active) == 0 { + action = Shutdown + return + } + delay = time.Second / 5 + return +} + func TestDefaultGnetServer(t *testing.T) { svr := BuiltinEventEngine{} svr.OnBoot(Engine{}) From f35d864fe114b5310b3f929706c996ebcdb655b5 Mon Sep 17 00:00:00 2001 From: Gabor Lekeny Date: Fri, 4 Nov 2022 17:33:57 +0100 Subject: [PATCH 03/10] Fix golangci-lint error --- gnet_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gnet_test.go b/gnet_test.go index 9b56c6f13..0a0c41300 100644 --- a/gnet_test.go +++ b/gnet_test.go @@ -412,7 +412,7 @@ func startClient(t *testing.T, network, addr string, multicore, async bool) { } } -// NOTE: TestServeMulticast can fail with "write: no buffer space available" on wifi interface +// NOTE: TestServeMulticast can fail with "write: no buffer space available" on wifi interface. func TestServeMulticast(t *testing.T) { // 224.0.0.169 is an unassigned address from the Local Network Control Block // https://www.iana.org/assignments/multicast-addresses/multicast-addresses.xhtml#multicast-addresses-1 From bd7f81d55a4eb64765965fbff611ae1cede92873 Mon Sep 17 00:00:00 2001 From: Gabor Lekeny Date: Fri, 4 Nov 2022 18:44:41 +0100 Subject: [PATCH 04/10] Add IPv6 test on loopback interface --- gnet_test.go | 91 ++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 78 insertions(+), 13 deletions(-) diff --git a/gnet_test.go b/gnet_test.go index 0a0c41300..1629d9756 100644 --- a/gnet_test.go +++ b/gnet_test.go @@ -21,6 +21,7 @@ import ( "context" "encoding/binary" "errors" + "fmt" "io" "math/rand" "net" @@ -414,30 +415,65 @@ func startClient(t *testing.T, network, addr string, multicore, async bool) { // NOTE: TestServeMulticast can fail with "write: no buffer space available" on wifi interface. func TestServeMulticast(t *testing.T) { - // 224.0.0.169 is an unassigned address from the Local Network Control Block - // https://www.iana.org/assignments/multicast-addresses/multicast-addresses.xhtml#multicast-addresses-1 - t.Run("udp-multicast", func(t *testing.T) { - testMulticast(t, "224.0.0.169:9991", false, false, 10) - }) - t.Run("udp-multicast-reuseport", func(t *testing.T) { - testMulticast(t, "224.0.0.169:9991", true, false, 10) + t.Run("IPv4", func(t *testing.T) { + // 224.0.0.169 is an unassigned address from the Local Network Control Block + // https://www.iana.org/assignments/multicast-addresses/multicast-addresses.xhtml#multicast-addresses-1 + t.Run("udp-multicast", func(t *testing.T) { + testMulticast(t, "224.0.0.169:9991", false, false, -1, 10) + }) + t.Run("udp-multicast-reuseport", func(t *testing.T) { + testMulticast(t, "224.0.0.169:9991", true, false, -1, 10) + }) + t.Run("udp-multicast-reuseaddr", func(t *testing.T) { + testMulticast(t, "224.0.0.169:9991", false, true, -1, 10) + }) }) - t.Run("udp-multicast-reuseaddr", func(t *testing.T) { - testMulticast(t, "224.0.0.169:9991", false, true, 10) + t.Run("IPv6", func(t *testing.T) { + iface, err := findLoopbackInterface() + require.NoError(t, err) + // ff02::3 is an unassigned address from Link-Local Scope Multicast Addressess + // https://www.iana.org/assignments/ipv6-multicast-addresses/ipv6-multicast-addresses.xhtml#link-local + t.Run("udp-multicast", func(t *testing.T) { + testMulticast(t, fmt.Sprintf("[ff02::3%%%s]:9991", iface.Name), false, false, iface.Index, 10) + }) + t.Run("udp-multicast-reuseport", func(t *testing.T) { + testMulticast(t, fmt.Sprintf("[ff02::3%%%s]:9991", iface.Name), true, false, iface.Index, 10) + }) + t.Run("udp-multicast-reuseaddr", func(t *testing.T) { + testMulticast(t, fmt.Sprintf("[ff02::3%%%s]:9991", iface.Name), false, true, iface.Index, 10) + }) }) } -func testMulticast(t *testing.T, addr string, reuseport, reuseaddr bool, nclients int) { +func findLoopbackInterface() (*net.Interface, error) { + ifaces, err := net.Interfaces() + if err != nil { + return nil, err + } + for _, iface := range ifaces { + if iface.Flags&(net.FlagLoopback|net.FlagMulticast) == net.FlagLoopback|net.FlagMulticast { + return &iface, nil + } + } + return nil, errors.New("no loopback interface that supports multicast") +} + +func testMulticast(t *testing.T, addr string, reuseport, reuseaddr bool, index, nclients int) { ts := &testMcastServer{ t: t, addr: addr, nclients: nclients, } - err := Run(ts, "udp://"+addr, + options := []Option{ WithReuseAddr(reuseaddr), WithReusePort(reuseport), - WithSocketRecvBuffer(2*nclients*1024), // enough space to receive messages from nclients to eliminate dropped packets - WithTicker(true)) + WithSocketRecvBuffer(2 * nclients * 1024), // enough space to receive messages from nclients to eliminate dropped packets + WithTicker(true), + } + if index != -1 { + options = append(options, WithMulticastInterfaceIndex(index)) + } + err := Run(ts, "udp://"+addr, options...) assert.NoError(t, err) } @@ -507,6 +543,35 @@ func (s *testMcastServer) OnTick() (delay time.Duration, action Action) { return } +type testMulticastBindServer struct { + *BuiltinEventEngine +} + +func (t *testMulticastBindServer) OnTick() (delay time.Duration, action Action) { + action = Shutdown + return +} + +func TestMulticastBindIPv4(t *testing.T) { + ts := &testMulticastBindServer{} + iface, err := findLoopbackInterface() + require.NoError(t, err) + err = Run(ts, "udp://224.0.0.169:9991", + WithMulticastInterfaceIndex(iface.Index), + WithTicker(true)) + assert.NoError(t, err) +} + +func TestMulticastBindIPv6(t *testing.T) { + ts := &testMulticastBindServer{} + iface, err := findLoopbackInterface() + require.NoError(t, err) + err = Run(ts, fmt.Sprintf("udp://[ff02::3%%%s]:9991", iface.Name), + WithMulticastInterfaceIndex(iface.Index), + WithTicker(true)) + assert.NoError(t, err) +} + func TestDefaultGnetServer(t *testing.T) { svr := BuiltinEventEngine{} svr.OnBoot(Engine{}) From 59603440d737297dc7d9c50987626a2cc6cb33d0 Mon Sep 17 00:00:00 2001 From: Gabor Lekeny Date: Fri, 4 Nov 2022 18:46:18 +0100 Subject: [PATCH 05/10] Fix golangci-lint error --- gnet_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gnet_test.go b/gnet_test.go index 1629d9756..55a84d8c8 100644 --- a/gnet_test.go +++ b/gnet_test.go @@ -431,7 +431,7 @@ func TestServeMulticast(t *testing.T) { t.Run("IPv6", func(t *testing.T) { iface, err := findLoopbackInterface() require.NoError(t, err) - // ff02::3 is an unassigned address from Link-Local Scope Multicast Addressess + // ff02::3 is an unassigned address from Link-Local Scope Multicast Addresses // https://www.iana.org/assignments/ipv6-multicast-addresses/ipv6-multicast-addresses.xhtml#link-local t.Run("udp-multicast", func(t *testing.T) { testMulticast(t, fmt.Sprintf("[ff02::3%%%s]:9991", iface.Name), false, false, iface.Index, 10) From 2bc64db57bdd9b98cfc12536efab497b3589ba5f Mon Sep 17 00:00:00 2001 From: Gabor Lekeny Date: Mon, 7 Nov 2022 08:20:08 +0100 Subject: [PATCH 06/10] Skip multicast IPv6 tests on Linux --- gnet_test.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/gnet_test.go b/gnet_test.go index 55a84d8c8..4786e7bea 100644 --- a/gnet_test.go +++ b/gnet_test.go @@ -431,6 +431,9 @@ func TestServeMulticast(t *testing.T) { t.Run("IPv6", func(t *testing.T) { iface, err := findLoopbackInterface() require.NoError(t, err) + if iface.Flags&net.FlagMulticast != net.FlagMulticast { + t.Skip("multicast is not supported on loopback interface") + } // ff02::3 is an unassigned address from Link-Local Scope Multicast Addresses // https://www.iana.org/assignments/ipv6-multicast-addresses/ipv6-multicast-addresses.xhtml#link-local t.Run("udp-multicast", func(t *testing.T) { @@ -451,11 +454,11 @@ func findLoopbackInterface() (*net.Interface, error) { return nil, err } for _, iface := range ifaces { - if iface.Flags&(net.FlagLoopback|net.FlagMulticast) == net.FlagLoopback|net.FlagMulticast { + if iface.Flags&net.FlagLoopback == net.FlagLoopback { return &iface, nil } } - return nil, errors.New("no loopback interface that supports multicast") + return nil, errors.New("no loopback interface") } func testMulticast(t *testing.T, addr string, reuseport, reuseaddr bool, index, nclients int) { From eeadd086baf7704bbcbcba5ea179513433d97489 Mon Sep 17 00:00:00 2001 From: Gabor Lekeny Date: Mon, 7 Nov 2022 09:05:38 +0100 Subject: [PATCH 07/10] Add MacOS workaround --- gnet_test.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/gnet_test.go b/gnet_test.go index 4786e7bea..bbdd28093 100644 --- a/gnet_test.go +++ b/gnet_test.go @@ -508,6 +508,9 @@ func (s *testMcastServer) startMcastClient() { require.NoError(s.t, err) _, err = c.Write(reqData) require.NoError(s.t, err) + // Workaround for MacOS "write: no buffer space available" error messages + // https://developer.apple.com/forums/thread/42334 + time.Sleep(time.Millisecond) select { case respData := <-ch: require.Equalf(s.t, reqData, respData, "response mismatch, length of bytes: %d vs %d", len(reqData), len(respData)) From aca68c9692a4b25c6bdf8b6ed83a288b38150b73 Mon Sep 17 00:00:00 2001 From: Gabor Lekeny Date: Mon, 7 Nov 2022 09:25:42 +0100 Subject: [PATCH 08/10] Only call SetMulticastMembership when it is necessary --- internal/socket/sockopts_posix.go | 10 ++-------- listener.go | 9 ++++++--- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/internal/socket/sockopts_posix.go b/internal/socket/sockopts_posix.go index 862b70ac2..e95d9cf30 100644 --- a/internal/socket/sockopts_posix.go +++ b/internal/socket/sockopts_posix.go @@ -88,14 +88,8 @@ func SetLinger(fd, sec int) error { // SetMulticastMembership returns with a socket option function based on the IP // version. Returns nil when multicast membership cannot be applied. -func SetMulticastMembership(proto, addr string) func(int, int) error { - var udpVersion string - udpAddr, err := net.ResolveUDPAddr(proto, addr) - if err != nil || !udpAddr.IP.IsMulticast() { - return nil - } - - udpVersion, err = determineUDPProto(proto, udpAddr) +func SetMulticastMembership(proto string, udpAddr *net.UDPAddr) func(int, int) error { + udpVersion, err := determineUDPProto(proto, udpAddr) if err != nil { return nil } diff --git a/listener.go b/listener.go index c1199fe27..de4815637 100644 --- a/listener.go +++ b/listener.go @@ -101,9 +101,12 @@ func initListener(network, addr string, options *Options) (l *listener, err erro sockOpts = append(sockOpts, sockOpt) } if strings.HasPrefix(network, "udp") { - if sockoptFn := socket.SetMulticastMembership(network, addr); sockoptFn != nil { - sockOpt := socket.Option{SetSockOpt: sockoptFn, Opt: options.MulticastInterfaceIndex} - sockOpts = append(sockOpts, sockOpt) + udpAddr, err := net.ResolveUDPAddr(network, addr) + if err == nil && udpAddr.IP.IsMulticast() { + if sockoptFn := socket.SetMulticastMembership(network, udpAddr); sockoptFn != nil { + sockOpt := socket.Option{SetSockOpt: sockoptFn, Opt: options.MulticastInterfaceIndex} + sockOpts = append(sockOpts, sockOpt) + } } } l = &listener{network: network, address: addr, sockOpts: sockOpts} From 53a889e1ff586baf757c2573c564d681c7e38904 Mon Sep 17 00:00:00 2001 From: Gabor Lekeny Date: Mon, 7 Nov 2022 09:44:29 +0100 Subject: [PATCH 09/10] Run IPv6 checks on Linux --- gnet_test.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/gnet_test.go b/gnet_test.go index bbdd28093..2b2641cdd 100644 --- a/gnet_test.go +++ b/gnet_test.go @@ -429,11 +429,8 @@ func TestServeMulticast(t *testing.T) { }) }) t.Run("IPv6", func(t *testing.T) { - iface, err := findLoopbackInterface() + iface, err := findMulticastInterface() require.NoError(t, err) - if iface.Flags&net.FlagMulticast != net.FlagMulticast { - t.Skip("multicast is not supported on loopback interface") - } // ff02::3 is an unassigned address from Link-Local Scope Multicast Addresses // https://www.iana.org/assignments/ipv6-multicast-addresses/ipv6-multicast-addresses.xhtml#link-local t.Run("udp-multicast", func(t *testing.T) { @@ -448,13 +445,16 @@ func TestServeMulticast(t *testing.T) { }) } -func findLoopbackInterface() (*net.Interface, error) { +func findMulticastInterface() (*net.Interface, error) { ifaces, err := net.Interfaces() if err != nil { return nil, err } for _, iface := range ifaces { if iface.Flags&net.FlagLoopback == net.FlagLoopback { + continue + } + if iface.Flags&net.FlagUp&net.FlagMulticast == net.FlagUp&net.FlagMulticast { return &iface, nil } } @@ -560,7 +560,7 @@ func (t *testMulticastBindServer) OnTick() (delay time.Duration, action Action) func TestMulticastBindIPv4(t *testing.T) { ts := &testMulticastBindServer{} - iface, err := findLoopbackInterface() + iface, err := findMulticastInterface() require.NoError(t, err) err = Run(ts, "udp://224.0.0.169:9991", WithMulticastInterfaceIndex(iface.Index), @@ -570,7 +570,7 @@ func TestMulticastBindIPv4(t *testing.T) { func TestMulticastBindIPv6(t *testing.T) { ts := &testMulticastBindServer{} - iface, err := findLoopbackInterface() + iface, err := findMulticastInterface() require.NoError(t, err) err = Run(ts, fmt.Sprintf("udp://[ff02::3%%%s]:9991", iface.Name), WithMulticastInterfaceIndex(iface.Index), From 91f25e7568db2d1a967f692f9905ffda7b3bcf5f Mon Sep 17 00:00:00 2001 From: Gabor Lekeny Date: Mon, 7 Nov 2022 09:49:06 +0100 Subject: [PATCH 10/10] Revert "Run IPv6 checks on Linux" This reverts commit 53a889e1ff586baf757c2573c564d681c7e38904. --- gnet_test.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/gnet_test.go b/gnet_test.go index 2b2641cdd..bbdd28093 100644 --- a/gnet_test.go +++ b/gnet_test.go @@ -429,8 +429,11 @@ func TestServeMulticast(t *testing.T) { }) }) t.Run("IPv6", func(t *testing.T) { - iface, err := findMulticastInterface() + iface, err := findLoopbackInterface() require.NoError(t, err) + if iface.Flags&net.FlagMulticast != net.FlagMulticast { + t.Skip("multicast is not supported on loopback interface") + } // ff02::3 is an unassigned address from Link-Local Scope Multicast Addresses // https://www.iana.org/assignments/ipv6-multicast-addresses/ipv6-multicast-addresses.xhtml#link-local t.Run("udp-multicast", func(t *testing.T) { @@ -445,16 +448,13 @@ func TestServeMulticast(t *testing.T) { }) } -func findMulticastInterface() (*net.Interface, error) { +func findLoopbackInterface() (*net.Interface, error) { ifaces, err := net.Interfaces() if err != nil { return nil, err } for _, iface := range ifaces { if iface.Flags&net.FlagLoopback == net.FlagLoopback { - continue - } - if iface.Flags&net.FlagUp&net.FlagMulticast == net.FlagUp&net.FlagMulticast { return &iface, nil } } @@ -560,7 +560,7 @@ func (t *testMulticastBindServer) OnTick() (delay time.Duration, action Action) func TestMulticastBindIPv4(t *testing.T) { ts := &testMulticastBindServer{} - iface, err := findMulticastInterface() + iface, err := findLoopbackInterface() require.NoError(t, err) err = Run(ts, "udp://224.0.0.169:9991", WithMulticastInterfaceIndex(iface.Index), @@ -570,7 +570,7 @@ func TestMulticastBindIPv4(t *testing.T) { func TestMulticastBindIPv6(t *testing.T) { ts := &testMulticastBindServer{} - iface, err := findMulticastInterface() + iface, err := findLoopbackInterface() require.NoError(t, err) err = Run(ts, fmt.Sprintf("udp://[ff02::3%%%s]:9991", iface.Name), WithMulticastInterfaceIndex(iface.Index),