From f429a35a7b6b512915afe32b921252427a021d69 Mon Sep 17 00:00:00 2001 From: Eric Daniels Date: Tue, 26 Mar 2024 11:55:22 -0400 Subject: [PATCH] Prefer IPv6 over IPv4-in-IPv6 --- conn.go | 31 ++++++++++++++++++++++++++----- conn_test.go | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 5 deletions(-) diff --git a/conn.go b/conn.go index c736814..d1f72fd 100644 --- a/conn.go +++ b/conn.go @@ -954,7 +954,7 @@ func (c *Conn) readLoop(name string, pktConn ipPacketConn, inboundBufferSize int c.log.Warnf("[%s] no interface for %d", c.name, ifIndex) return } - var selectedAddr *netip.Addr + var selectedAddrs []netip.Addr for _, addr := range ifc.ipAddrs { addrCopy := addr @@ -977,13 +977,34 @@ func (c *Conn) readLoop(name string, pktConn ipPacketConn, inboundBufferSize int } } - selectedAddr = &addrCopy - break + selectedAddrs = append(selectedAddrs, addrCopy) } - if selectedAddr == nil { + if len(selectedAddrs) == 0 { c.log.Debugf("[%s] failed to find suitable IP for interface %d; deriving address from source address c.name,instead", ifIndex) } else { - localAddress = selectedAddr + // choose the best match + var choice *netip.Addr + for _, option := range selectedAddrs { + optCopy := option + if option.Is4() { + // select first + choice = &optCopy + break + } + // we're okay with 4In6 for now but ideally we get a an actual IPv6. + // Maybe in the future we never want this but it does look like Docker + // can route IPv4 over IPv6. + if choice == nil { + choice = &optCopy + } else if !optCopy.Is4In6() { + choice = &optCopy + } + if !optCopy.Is4In6() { + break + } + // otherwise keep searching for an actual IPv6 + } + localAddress = choice } } if ifIndex == -1 || localAddress == nil { diff --git a/conn_test.go b/conn_test.go index a282be6..2ae4e18 100644 --- a/conn_test.go +++ b/conn_test.go @@ -451,6 +451,52 @@ func TestValidCommunicationIPv46MixedLocalAddress(t *testing.T) { } } +func TestValidCommunicationIPv66Mixed(t *testing.T) { + if runtime.GOARCH == "386" { + t.Skip("IPv6 not supported on 386 for some reason") + } + + lim := test.TimeOut(time.Second * 10) + defer lim.Stop() + + report := test.CheckRoutines(t) + defer report() + + aSock6 := createListener6(t) + bSock6 := createListener6(t) + + aServer, err := Server(nil, ipv6.NewPacketConn(aSock6), &Config{ + LocalNames: []string{"pion-mdns-1.local"}, + }) + check(err, t) + + bServer, err := Server(nil, ipv6.NewPacketConn(bSock6), &Config{}) + check(err, t) + + header, addr, err := bServer.QueryAddr(context.TODO(), "pion-mdns-1.local") + check(err, t) + if header.Type != dnsmessage.TypeAAAA { + t.Fatalf("expected AAAA but got %s", header.Type) + } + if addr.String() == localAddress { + t.Fatalf("unexpected local address: %v", addr) + } + if addr.Is4In6() { + t.Fatalf("expected address to not be ipv4-to-ipv6 mapped: %v", addr) + } + checkIPv6(addr, t) + + check(aServer.Close(), t) + check(bServer.Close(), t) + + if len(aServer.queries) > 0 { + t.Fatalf("Queries not cleaned up after aServer close") + } + if len(bServer.queries) > 0 { + t.Fatalf("Queries not cleaned up after bServer close") + } +} + func TestValidCommunicationIPv66MixedLocalAddress(t *testing.T) { if runtime.GOARCH == "386" { t.Skip("IPv6 not supported on 386 for some reason")