Skip to content

Commit 00272e8

Browse files
authored
dns: Support link local IPv6 addresses (#7889)
1 parent 17d08f7 commit 00272e8

File tree

12 files changed

+82
-49
lines changed

12 files changed

+82
-49
lines changed

balancer/pickfirst/pickfirstleaf/pickfirstleaf.go

+7-3
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import (
3030
"errors"
3131
"fmt"
3232
"net"
33+
"net/netip"
3334
"sync"
3435
"time"
3536

@@ -417,11 +418,14 @@ func addressFamily(address string) ipAddrFamily {
417418
if err != nil {
418419
return ipAddrFamilyUnknown
419420
}
420-
ip := net.ParseIP(host)
421+
ip, err := netip.ParseAddr(host)
422+
if err != nil {
423+
return ipAddrFamilyUnknown
424+
}
421425
switch {
422-
case ip.To4() != nil:
426+
case ip.Is4() || ip.Is4In6():
423427
return ipAddrFamilyV4
424-
case ip.To16() != nil:
428+
case ip.Is6():
425429
return ipAddrFamilyV6
426430
default:
427431
return ipAddrFamilyUnknown

balancer/pickfirst/pickfirstleaf/pickfirstleaf_ext_test.go

+6-6
Original file line numberDiff line numberDiff line change
@@ -1091,7 +1091,7 @@ func (s) TestPickFirstLeaf_InterleavingIPV4Preffered(t *testing.T) {
10911091
{Addresses: []resolver.Address{{Addr: "[::FFFF:192.168.0.1]:2222"}}},
10921092
{Addresses: []resolver.Address{{Addr: "[0001:0001:0001:0001:0001:0001:0001:0001]:8080"}}},
10931093
{Addresses: []resolver.Address{{Addr: "[0002:0002:0002:0002:0002:0002:0002:0002]:8080"}}},
1094-
{Addresses: []resolver.Address{{Addr: "[0003:0003:0003:0003:0003:0003:0003:0003]:3333"}}},
1094+
{Addresses: []resolver.Address{{Addr: "[fe80::1%eth0]:3333"}}},
10951095
{Addresses: []resolver.Address{{Addr: "grpc.io:80"}}}, // not an IP.
10961096
},
10971097
},
@@ -1107,7 +1107,7 @@ func (s) TestPickFirstLeaf_InterleavingIPV4Preffered(t *testing.T) {
11071107
{Addr: "2.2.2.2:2"},
11081108
{Addr: "[0002:0002:0002:0002:0002:0002:0002:0002]:8080"},
11091109
{Addr: "3.3.3.3:3"},
1110-
{Addr: "[0003:0003:0003:0003:0003:0003:0003:0003]:3333"},
1110+
{Addr: "[fe80::1%eth0]:3333"},
11111111
{Addr: "[::FFFF:192.168.0.1]:2222"},
11121112
}
11131113

@@ -1135,7 +1135,7 @@ func (s) TestPickFirstLeaf_InterleavingIPv6Preffered(t *testing.T) {
11351135
{Addresses: []resolver.Address{{Addr: "3.3.3.3:3"}}},
11361136
{Addresses: []resolver.Address{{Addr: "[::FFFF:192.168.0.1]:2222"}}},
11371137
{Addresses: []resolver.Address{{Addr: "[0002:0002:0002:0002:0002:0002:0002:0002]:2222"}}},
1138-
{Addresses: []resolver.Address{{Addr: "[0003:0003:0003:0003:0003:0003:0003:0003]:3333"}}},
1138+
{Addresses: []resolver.Address{{Addr: "[fe80::1%eth0]:3333"}}},
11391139
{Addresses: []resolver.Address{{Addr: "grpc.io:80"}}}, // not an IP.
11401140
},
11411141
},
@@ -1150,7 +1150,7 @@ func (s) TestPickFirstLeaf_InterleavingIPv6Preffered(t *testing.T) {
11501150
{Addr: "grpc.io:80"},
11511151
{Addr: "[0002:0002:0002:0002:0002:0002:0002:0002]:2222"},
11521152
{Addr: "2.2.2.2:2"},
1153-
{Addr: "[0003:0003:0003:0003:0003:0003:0003:0003]:3333"},
1153+
{Addr: "[fe80::1%eth0]:3333"},
11541154
{Addr: "3.3.3.3:3"},
11551155
{Addr: "[::FFFF:192.168.0.1]:2222"},
11561156
}
@@ -1180,7 +1180,7 @@ func (s) TestPickFirstLeaf_InterleavingUnknownPreffered(t *testing.T) {
11801180
{Addresses: []resolver.Address{{Addr: "[::FFFF:192.168.0.1]:2222"}}},
11811181
{Addresses: []resolver.Address{{Addr: "[0001:0001:0001:0001:0001:0001:0001:0001]:8080"}}},
11821182
{Addresses: []resolver.Address{{Addr: "[0002:0002:0002:0002:0002:0002:0002:0002]:8080"}}},
1183-
{Addresses: []resolver.Address{{Addr: "[0003:0003:0003:0003:0003:0003:0003:0003]:3333"}}},
1183+
{Addresses: []resolver.Address{{Addr: "[fe80::1%eth0]:3333"}}},
11841184
{Addresses: []resolver.Address{{Addr: "example.com:80"}}}, // not an IP.
11851185
},
11861186
},
@@ -1197,7 +1197,7 @@ func (s) TestPickFirstLeaf_InterleavingUnknownPreffered(t *testing.T) {
11971197
{Addr: "2.2.2.2:2"},
11981198
{Addr: "[0002:0002:0002:0002:0002:0002:0002:0002]:8080"},
11991199
{Addr: "3.3.3.3:3"},
1200-
{Addr: "[0003:0003:0003:0003:0003:0003:0003:0003]:3333"},
1200+
{Addr: "[fe80::1%eth0]:3333"},
12011201
{Addr: "[::FFFF:192.168.0.1]:2222"},
12021202
}
12031203

channelz/service/service_test.go

+6-5
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"context"
2323
"fmt"
2424
"net"
25+
"net/netip"
2526
"strconv"
2627
"strings"
2728
"testing"
@@ -605,8 +606,8 @@ func (s) TestGetSocket(t *testing.T) {
605606
lastMessageReceivedTimestamp: time.Unix(3, 0),
606607
localFlowControlWindow: 65536,
607608
remoteFlowControlWindow: 1024,
608-
localAddr: &net.TCPAddr{IP: net.ParseIP("1.0.0.1"), Port: 10001},
609-
remoteAddr: &net.TCPAddr{IP: net.ParseIP("12.0.0.1"), Port: 10002},
609+
localAddr: &net.TCPAddr{IP: netip.MustParseAddr("1.0.0.1").AsSlice(), Port: 10001},
610+
remoteAddr: &net.TCPAddr{IP: netip.MustParseAddr("12.0.0.1").AsSlice(), Port: 10002},
610611
remoteName: "remote.remote",
611612
}), newSocket(czSocket{
612613
streamsStarted: 10,
@@ -637,11 +638,11 @@ func (s) TestGetSocket(t *testing.T) {
637638
lastMessageReceivedTimestamp: time.Unix(0, 0),
638639
localFlowControlWindow: 65536,
639640
remoteFlowControlWindow: 10240,
640-
localAddr: &net.IPAddr{IP: net.ParseIP("1.0.0.1")},
641-
remoteAddr: &net.IPAddr{IP: net.ParseIP("9.0.0.1")},
641+
localAddr: &net.IPAddr{IP: netip.MustParseAddr("1.0.0.1").AsSlice()},
642+
remoteAddr: &net.IPAddr{IP: netip.MustParseAddr("9.0.0.1").AsSlice()},
642643
remoteName: "",
643644
}), newSocket(czSocket{
644-
localAddr: &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 10001},
645+
localAddr: &net.TCPAddr{IP: netip.MustParseAddr("127.0.0.1").AsSlice(), Port: 10001},
645646
}), newSocket(czSocket{
646647
security: &credentials.TLSChannelzSecurityValue{
647648
StandardName: "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",

internal/credentials/xds/handshake_info_test.go

+11-4
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ package xds
2121
import (
2222
"crypto/x509"
2323
"net"
24+
"net/netip"
2425
"net/url"
2526
"regexp"
2627
"testing"
@@ -142,8 +143,11 @@ func TestMatchingSANExists_FailureCases(t *testing.T) {
142143
inputCert := &x509.Certificate{
143144
DNSNames: []string{"foo.bar.example.com", "bar.baz.test.com", "*.example.com"},
144145
EmailAddresses: []string{"[email protected]", "[email protected]"},
145-
IPAddresses: []net.IP{net.ParseIP("192.0.0.1"), net.ParseIP("2001:db8::68")},
146-
URIs: []*url.URL{url1, url2},
146+
IPAddresses: []net.IP{
147+
netip.MustParseAddr("192.0.0.1").AsSlice(),
148+
netip.MustParseAddr("2001:db8::68").AsSlice(),
149+
},
150+
URIs: []*url.URL{url1, url2},
147151
}
148152

149153
tests := []struct {
@@ -214,8 +218,11 @@ func TestMatchingSANExists_Success(t *testing.T) {
214218
inputCert := &x509.Certificate{
215219
DNSNames: []string{"baz.test.com", "*.example.com"},
216220
EmailAddresses: []string{"[email protected]", "[email protected]"},
217-
IPAddresses: []net.IP{net.ParseIP("192.0.0.1"), net.ParseIP("2001:db8::68")},
218-
URIs: []*url.URL{url1, url2},
221+
IPAddresses: []net.IP{
222+
netip.MustParseAddr("192.0.0.1").AsSlice(),
223+
netip.MustParseAddr("2001:db8::68").AsSlice(),
224+
},
225+
URIs: []*url.URL{url1, url2},
219226
}
220227

221228
tests := []struct {

internal/resolver/dns/dns_resolver.go

+19-18
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626
"fmt"
2727
rand "math/rand/v2"
2828
"net"
29+
"net/netip"
2930
"os"
3031
"strconv"
3132
"strings"
@@ -122,7 +123,7 @@ func (b *dnsBuilder) Build(target resolver.Target, cc resolver.ClientConn, opts
122123
}
123124

124125
// IP address.
125-
if ipAddr, ok := formatIP(host); ok {
126+
if ipAddr, err := formatIP(host); err == nil {
126127
addr := []resolver.Address{{Addr: ipAddr + ":" + port}}
127128
cc.UpdateState(resolver.State{Addresses: addr})
128129
return deadResolver{}, nil
@@ -260,9 +261,9 @@ func (d *dnsResolver) lookupSRV(ctx context.Context) ([]resolver.Address, error)
260261
return nil, err
261262
}
262263
for _, a := range lbAddrs {
263-
ip, ok := formatIP(a)
264-
if !ok {
265-
return nil, fmt.Errorf("dns: error parsing A record IP address %v", a)
264+
ip, err := formatIP(a)
265+
if err != nil {
266+
return nil, fmt.Errorf("dns: error parsing A record IP address %v: %v", a, err)
266267
}
267268
addr := ip + ":" + strconv.Itoa(int(s.Port))
268269
newAddrs = append(newAddrs, resolver.Address{Addr: addr, ServerName: s.Target})
@@ -322,9 +323,9 @@ func (d *dnsResolver) lookupHost(ctx context.Context) ([]resolver.Address, error
322323
}
323324
newAddrs := make([]resolver.Address, 0, len(addrs))
324325
for _, a := range addrs {
325-
ip, ok := formatIP(a)
326-
if !ok {
327-
return nil, fmt.Errorf("dns: error parsing A record IP address %v", a)
326+
ip, err := formatIP(a)
327+
if err != nil {
328+
return nil, fmt.Errorf("dns: error parsing A record IP address %v: %v", a, err)
328329
}
329330
addr := ip + ":" + d.port
330331
newAddrs = append(newAddrs, resolver.Address{Addr: addr})
@@ -351,19 +352,19 @@ func (d *dnsResolver) lookup() (*resolver.State, error) {
351352
return &state, nil
352353
}
353354

354-
// formatIP returns ok = false if addr is not a valid textual representation of
355-
// an IP address. If addr is an IPv4 address, return the addr and ok = true.
355+
// formatIP returns an error if addr is not a valid textual representation of
356+
// an IP address. If addr is an IPv4 address, return the addr and error = nil.
356357
// If addr is an IPv6 address, return the addr enclosed in square brackets and
357-
// ok = true.
358-
func formatIP(addr string) (addrIP string, ok bool) {
359-
ip := net.ParseIP(addr)
360-
if ip == nil {
361-
return "", false
358+
// error = nil.
359+
func formatIP(addr string) (string, error) {
360+
ip, err := netip.ParseAddr(addr)
361+
if err != nil {
362+
return "", err
362363
}
363-
if ip.To4() != nil {
364-
return addr, true
364+
if ip.Is4() {
365+
return addr, nil
365366
}
366-
return "[" + addr + "]", true
367+
return "[" + addr + "]", nil
367368
}
368369

369370
// parseTarget takes the user input target string and default port, returns
@@ -379,7 +380,7 @@ func parseTarget(target, defaultPort string) (host, port string, err error) {
379380
if target == "" {
380381
return "", "", internal.ErrMissingAddr
381382
}
382-
if ip := net.ParseIP(target); ip != nil {
383+
if _, err := netip.ParseAddr(target); err == nil {
383384
// target is an IPv4 or IPv6(without brackets) address
384385
return target, defaultPort, nil
385386
}

internal/resolver/dns/dns_resolver_test.go

+10-1
Original file line numberDiff line numberDiff line change
@@ -755,7 +755,16 @@ func (s) TestIPResolver(t *testing.T) {
755755
target: "[2001:db8::1]:http",
756756
wantAddr: []resolver.Address{{Addr: "[2001:db8::1]:http"}},
757757
},
758-
// TODO(yuxuanli): zone support?
758+
{
759+
name: "ipv6 with zone and port",
760+
target: "[fe80::1%25eth0]:1234",
761+
wantAddr: []resolver.Address{{Addr: "[fe80::1%eth0]:1234"}},
762+
},
763+
{
764+
name: "ipv6 with zone and default port",
765+
target: "fe80::1%25eth0",
766+
wantAddr: []resolver.Address{{Addr: "[fe80::1%eth0]:443"}},
767+
},
759768
}
760769

761770
for _, test := range tests {

internal/testutils/fakegrpclb/server.go

+5-4
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"fmt"
2525
"io"
2626
"net"
27+
"net/netip"
2728
"strconv"
2829
"sync"
2930
"time"
@@ -85,17 +86,17 @@ func NewServer(params ServerParams) (*Server, error) {
8586
if err != nil {
8687
return nil, fmt.Errorf("failed to parse list of backend address %q: %v", addr, err)
8788
}
88-
ip := net.ParseIP(ipStr)
89-
if ip == nil {
90-
return nil, fmt.Errorf("failed to parse ip: %q", ipStr)
89+
ip, err := netip.ParseAddr(ipStr)
90+
if err != nil {
91+
return nil, fmt.Errorf("failed to parse ip %q: %v", ipStr, err)
9192
}
9293
port, err := strconv.Atoi(portStr)
9394
if err != nil {
9495
return nil, fmt.Errorf("failed to convert port %q to int", portStr)
9596
}
9697
logger.Infof("Adding backend ip: %q, port: %d to server list", ip.String(), port)
9798
servers = append(servers, &lbpb.Server{
98-
IpAddress: ip,
99+
IpAddress: ip.AsSlice(),
99100
Port: int32(port),
100101
})
101102
}

internal/xds/rbac/matchers.go

+5-2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"errors"
2121
"fmt"
2222
"net"
23+
"net/netip"
2324
"regexp"
2425

2526
v3corepb "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
@@ -344,7 +345,8 @@ func newRemoteIPMatcher(cidrRange *v3corepb.CidrRange) (*remoteIPMatcher, error)
344345
}
345346

346347
func (sim *remoteIPMatcher) match(data *rpcData) bool {
347-
return sim.ipNet.Contains(net.IP(net.ParseIP(data.peerInfo.Addr.String())))
348+
ip, _ := netip.ParseAddr(data.peerInfo.Addr.String())
349+
return sim.ipNet.Contains(net.IP(ip.AsSlice()))
348350
}
349351

350352
type localIPMatcher struct {
@@ -361,7 +363,8 @@ func newLocalIPMatcher(cidrRange *v3corepb.CidrRange) (*localIPMatcher, error) {
361363
}
362364

363365
func (dim *localIPMatcher) match(data *rpcData) bool {
364-
return dim.ipNet.Contains(net.IP(net.ParseIP(data.localAddr.String())))
366+
ip, _ := netip.ParseAddr(data.localAddr.String())
367+
return dim.ipNet.Contains(net.IP(ip.AsSlice()))
365368
}
366369

367370
// portMatcher matches on whether the destination port of the RPC matches the

scripts/vet.sh

+4
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,10 @@ git grep '"github.com/envoyproxy/go-control-plane/envoy' -- '*.go' ':(exclude)*.
8282
git grep -e 'context.Background()' --or -e 'context.TODO()' -- "*_test.go" | grep -v "benchmark/primitives/context_test.go" | grep -v "credential
8383
s/google" | grep -v "internal/transport/" | grep -v "xds/internal/" | grep -v "security/advancedtls" | grep -v 'context.WithTimeout(' | not grep -v 'context.WithCancel('
8484

85+
# Disallow usage of net.ParseIP in favour of netip.ParseAddr as the former
86+
# can't parse link local IPv6 addresses.
87+
not git grep 'net.ParseIP' -- '*.go'
88+
8589
misspell -error .
8690

8791
# - gofmt, goimports, go vet, go mod tidy.

security/advancedtls/crl_test.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import (
3030
"fmt"
3131
"math/big"
3232
"net"
33+
"net/netip"
3334
"os"
3435
"path"
3536
"strings"
@@ -463,7 +464,7 @@ func setupTLSConn(t *testing.T) (net.Listener, *x509.Certificate, *ecdsa.Private
463464
Subject: pkix.Name{CommonName: "test-cert"},
464465
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
465466
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
466-
IPAddresses: []net.IP{net.ParseIP("::1")},
467+
IPAddresses: []net.IP{netip.MustParseAddr("::1").AsSlice()},
467468
CRLDistributionPoints: []string{"http://static.corp.google.com/crl/campus-sln/borg"},
468469
}
469470

test/local_creds_test.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"context"
2323
"fmt"
2424
"net"
25+
"net/netip"
2526
"strings"
2627
"testing"
2728
"time"
@@ -181,11 +182,11 @@ func testLocalCredsE2EFail(dopts []grpc.DialOption) error {
181182

182183
var fakeClientAddr, fakeServerAddr net.Addr
183184
fakeClientAddr = &net.IPAddr{
184-
IP: net.ParseIP("10.8.9.10"),
185+
IP: netip.MustParseAddr("10.8.9.10").AsSlice(),
185186
Zone: "",
186187
}
187188
fakeServerAddr = &net.IPAddr{
188-
IP: net.ParseIP("10.8.9.11"),
189+
IP: netip.MustParseAddr("10.8.9.11").AsSlice(),
189190
Zone: "",
190191
}
191192

xds/internal/xdsclient/xdsresource/filter_chain_test.go

+4-3
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"errors"
2323
"fmt"
2424
"net"
25+
"net/netip"
2526
"strings"
2627
"testing"
2728

@@ -2438,7 +2439,7 @@ func (s) TestLookup_Successes(t *testing.T) {
24382439
lis: lisWithoutDefaultChain,
24392440
params: FilterChainLookupParams{
24402441
IsUnspecifiedListener: true,
2441-
DestAddr: net.ParseIP("2001:68::db8"),
2442+
DestAddr: netip.MustParseAddr("2001:68::db8").AsSlice(),
24422443
SourceAddr: net.IPv4(10, 1, 1, 1),
24432444
SourcePort: 1,
24442445
},
@@ -2468,8 +2469,8 @@ func (s) TestLookup_Successes(t *testing.T) {
24682469
lis: lisWithoutDefaultChain,
24692470
params: FilterChainLookupParams{
24702471
IsUnspecifiedListener: true,
2471-
DestAddr: net.ParseIP("2001:68::1"),
2472-
SourceAddr: net.ParseIP("2001:68::2"),
2472+
DestAddr: netip.MustParseAddr("2001:68::1").AsSlice(),
2473+
SourceAddr: netip.MustParseAddr("2001:68::2").AsSlice(),
24732474
SourcePort: 1,
24742475
},
24752476
wantFC: &FilterChain{

0 commit comments

Comments
 (0)