From 3bd1c99157abf82fb1e705d7d728ce091844c166 Mon Sep 17 00:00:00 2001 From: Varun Chawla Date: Sun, 22 Feb 2026 18:30:03 -0800 Subject: [PATCH 1/3] grpclb, channelz: replace net.IP with netip.Addr Replace legacy net.IP usage with the modern net/netip package: - grpclb: use netip.AddrFromSlice instead of net.IP cast for parsing server list IP addresses, with proper error handling for invalid IPs. - channelz: use netip.AddrFromSlice and net.TCPAddr.AddrPort() for converting addresses in the channelz protoconv layer. Contributes to #8884. --- balancer/grpclb/grpclb_remote_balancer.go | 10 +++++++++- channelz/internal/protoconv/socket.go | 14 ++++++++++---- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/balancer/grpclb/grpclb_remote_balancer.go b/balancer/grpclb/grpclb_remote_balancer.go index 002052120570..0ead6a6468ca 100644 --- a/balancer/grpclb/grpclb_remote_balancer.go +++ b/balancer/grpclb/grpclb_remote_balancer.go @@ -23,6 +23,7 @@ import ( "fmt" "io" "net" + "net/netip" "sync" "time" @@ -82,7 +83,14 @@ func (lb *lbBalancer) processServerList(l *lbpb.ServerList) { } md := metadata.Pairs(lbTokenKey, s.LoadBalanceToken) - ipStr := net.IP(s.IpAddress).String() + ip, ok := netip.AddrFromSlice(s.IpAddress) + if !ok { + if lb.logger.V(2) { + lb.logger.Infof("Server list entry:|%d|, failed to parse IP address: %x", i, s.IpAddress) + } + continue + } + ipStr := ip.String() addr := imetadata.Set(resolver.Address{Addr: net.JoinHostPort(ipStr, fmt.Sprintf("%d", s.Port))}, md) if lb.logger.V(2) { lb.logger.Infof("Server list entry:|%d|, ipStr:|%s|, port:|%d|, load balancer token:|%v|", i, ipStr, s.Port, s.LoadBalanceToken) diff --git a/channelz/internal/protoconv/socket.go b/channelz/internal/protoconv/socket.go index 0998f0a86e6e..f7cb6ab2678b 100644 --- a/channelz/internal/protoconv/socket.go +++ b/channelz/internal/protoconv/socket.go @@ -20,6 +20,7 @@ package protoconv import ( "net" + "net/netip" "time" "google.golang.org/grpc/codes" @@ -62,13 +63,18 @@ func addrToProto(a net.Addr) *channelzpb.Address { // TODO: Address_OtherAddress{}. Need proto def for Value. case "ip": // Note zone info is discarded through the conversion. - return &channelzpb.Address{Address: &channelzpb.Address_TcpipAddress{TcpipAddress: &channelzpb.Address_TcpIpAddress{IpAddress: a.(*net.IPAddr).IP}}} + if addr, ok := netip.AddrFromSlice(a.(*net.IPAddr).IP); ok { + return &channelzpb.Address{Address: &channelzpb.Address_TcpipAddress{TcpipAddress: &channelzpb.Address_TcpIpAddress{IpAddress: addr.AsSlice()}}} + } case "ip+net": // Note mask info is discarded through the conversion. - return &channelzpb.Address{Address: &channelzpb.Address_TcpipAddress{TcpipAddress: &channelzpb.Address_TcpIpAddress{IpAddress: a.(*net.IPNet).IP}}} + if addr, ok := netip.AddrFromSlice(a.(*net.IPNet).IP); ok { + return &channelzpb.Address{Address: &channelzpb.Address_TcpipAddress{TcpipAddress: &channelzpb.Address_TcpIpAddress{IpAddress: addr.AsSlice()}}} + } case "tcp": - // Note zone info is discarded through the conversion. - return &channelzpb.Address{Address: &channelzpb.Address_TcpipAddress{TcpipAddress: &channelzpb.Address_TcpIpAddress{IpAddress: a.(*net.TCPAddr).IP, Port: int32(a.(*net.TCPAddr).Port)}}} + tcpAddr := a.(*net.TCPAddr) + addrPort := tcpAddr.AddrPort() + return &channelzpb.Address{Address: &channelzpb.Address_TcpipAddress{TcpipAddress: &channelzpb.Address_TcpIpAddress{IpAddress: addrPort.Addr().AsSlice(), Port: int32(addrPort.Port())}}} case "unix", "unixgram", "unixpacket": return &channelzpb.Address{Address: &channelzpb.Address_UdsAddress_{UdsAddress: &channelzpb.Address_UdsAddress{Filename: a.String()}}} default: From 3ec05cf2be8f15e2f6342d99ee9328c241ba6549 Mon Sep 17 00:00:00 2001 From: Varun Chawla Date: Fri, 27 Feb 2026 13:36:46 -0800 Subject: [PATCH 2/3] grpclb, channelz: address review comments - channelz: remove unnecessary net.IP -> netip.Addr -> []byte round-trip in the "ip" and "ip+net" cases; use the IP byte slice directly - channelz: collapse tcpAddr intermediate variable per nit - grpclb: restore original fallback behaviour for malformed IP addresses by using fmt.Sprintf("? %x", ...) instead of silently skipping entries --- balancer/grpclb/grpclb_remote_balancer.go | 12 +++++------- channelz/internal/protoconv/socket.go | 12 +++--------- 2 files changed, 8 insertions(+), 16 deletions(-) diff --git a/balancer/grpclb/grpclb_remote_balancer.go b/balancer/grpclb/grpclb_remote_balancer.go index 0ead6a6468ca..baf9bc692f4f 100644 --- a/balancer/grpclb/grpclb_remote_balancer.go +++ b/balancer/grpclb/grpclb_remote_balancer.go @@ -83,14 +83,12 @@ func (lb *lbBalancer) processServerList(l *lbpb.ServerList) { } md := metadata.Pairs(lbTokenKey, s.LoadBalanceToken) - ip, ok := netip.AddrFromSlice(s.IpAddress) - if !ok { - if lb.logger.V(2) { - lb.logger.Infof("Server list entry:|%d|, failed to parse IP address: %x", i, s.IpAddress) - } - continue + var ipStr string + if ip, ok := netip.AddrFromSlice(s.IpAddress); ok { + ipStr = ip.String() + } else { + ipStr = fmt.Sprintf("? %x", s.IpAddress) } - ipStr := ip.String() addr := imetadata.Set(resolver.Address{Addr: net.JoinHostPort(ipStr, fmt.Sprintf("%d", s.Port))}, md) if lb.logger.V(2) { lb.logger.Infof("Server list entry:|%d|, ipStr:|%s|, port:|%d|, load balancer token:|%v|", i, ipStr, s.Port, s.LoadBalanceToken) diff --git a/channelz/internal/protoconv/socket.go b/channelz/internal/protoconv/socket.go index f7cb6ab2678b..a6c5756a3e6a 100644 --- a/channelz/internal/protoconv/socket.go +++ b/channelz/internal/protoconv/socket.go @@ -20,7 +20,6 @@ package protoconv import ( "net" - "net/netip" "time" "google.golang.org/grpc/codes" @@ -63,17 +62,12 @@ func addrToProto(a net.Addr) *channelzpb.Address { // TODO: Address_OtherAddress{}. Need proto def for Value. case "ip": // Note zone info is discarded through the conversion. - if addr, ok := netip.AddrFromSlice(a.(*net.IPAddr).IP); ok { - return &channelzpb.Address{Address: &channelzpb.Address_TcpipAddress{TcpipAddress: &channelzpb.Address_TcpIpAddress{IpAddress: addr.AsSlice()}}} - } + return &channelzpb.Address{Address: &channelzpb.Address_TcpipAddress{TcpipAddress: &channelzpb.Address_TcpIpAddress{IpAddress: a.(*net.IPAddr).IP}}} case "ip+net": // Note mask info is discarded through the conversion. - if addr, ok := netip.AddrFromSlice(a.(*net.IPNet).IP); ok { - return &channelzpb.Address{Address: &channelzpb.Address_TcpipAddress{TcpipAddress: &channelzpb.Address_TcpIpAddress{IpAddress: addr.AsSlice()}}} - } + return &channelzpb.Address{Address: &channelzpb.Address_TcpipAddress{TcpipAddress: &channelzpb.Address_TcpIpAddress{IpAddress: a.(*net.IPNet).IP}}} case "tcp": - tcpAddr := a.(*net.TCPAddr) - addrPort := tcpAddr.AddrPort() + addrPort := a.(*net.TCPAddr).AddrPort() return &channelzpb.Address{Address: &channelzpb.Address_TcpipAddress{TcpipAddress: &channelzpb.Address_TcpIpAddress{IpAddress: addrPort.Addr().AsSlice(), Port: int32(addrPort.Port())}}} case "unix", "unixgram", "unixpacket": return &channelzpb.Address{Address: &channelzpb.Address_UdsAddress_{UdsAddress: &channelzpb.Address_UdsAddress{Filename: a.String()}}} From 48210b8fa50e0a308706280f2eff1cc768c11d12 Mon Sep 17 00:00:00 2001 From: Varun Chawla Date: Wed, 11 Mar 2026 21:33:37 -0700 Subject: [PATCH 3/3] simplify conversion per review - remove unnecessary round-trip --- channelz/internal/protoconv/socket.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/channelz/internal/protoconv/socket.go b/channelz/internal/protoconv/socket.go index a6c5756a3e6a..0998f0a86e6e 100644 --- a/channelz/internal/protoconv/socket.go +++ b/channelz/internal/protoconv/socket.go @@ -67,8 +67,8 @@ func addrToProto(a net.Addr) *channelzpb.Address { // Note mask info is discarded through the conversion. return &channelzpb.Address{Address: &channelzpb.Address_TcpipAddress{TcpipAddress: &channelzpb.Address_TcpIpAddress{IpAddress: a.(*net.IPNet).IP}}} case "tcp": - addrPort := a.(*net.TCPAddr).AddrPort() - return &channelzpb.Address{Address: &channelzpb.Address_TcpipAddress{TcpipAddress: &channelzpb.Address_TcpIpAddress{IpAddress: addrPort.Addr().AsSlice(), Port: int32(addrPort.Port())}}} + // Note zone info is discarded through the conversion. + return &channelzpb.Address{Address: &channelzpb.Address_TcpipAddress{TcpipAddress: &channelzpb.Address_TcpIpAddress{IpAddress: a.(*net.TCPAddr).IP, Port: int32(a.(*net.TCPAddr).Port)}}} case "unix", "unixgram", "unixpacket": return &channelzpb.Address{Address: &channelzpb.Address_UdsAddress_{UdsAddress: &channelzpb.Address_UdsAddress{Filename: a.String()}}} default: