Skip to content

Commit c2e9871

Browse files
Fix listener addr duplication for dualStack svc with IPv6 as primary (istio#56151) (istio#56181)
* Fix listener addr duplication for dualStack svc with IPv6 as primary When a dual-stack service is configured with ipFamilies: [IPv6, IPv4], the listener ends up using the same IPv4 address for both address and additionalAddress (ignoring the IPv6 address). This happens because GetAddressForProxy() (via filterAddresses method) prioritizes IPv4 over IPv6, and GetExtraAddressesForProxy() also returns the same IPv4 address from the service. This PR modifies the filterAddresses to return the addresses based on the firstAddrFamily. * Add validation when addresses is empty * Add unit tests for GetAddressForProxy method --------- (cherry picked from commit 1bc024b) Signed-off-by: Sridhar Gaddam <sgaddam@redhat.com>
1 parent 1521432 commit c2e9871

File tree

2 files changed

+130
-0
lines changed

2 files changed

+130
-0
lines changed

pilot/pkg/model/service.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1693,6 +1693,10 @@ func (s *Service) getAllAddressesForProxy(node *Proxy) []string {
16931693
}
16941694

16951695
func filterAddresses(addresses []string, supportsV4, supportsV6 bool) []string {
1696+
if len(addresses) == 0 {
1697+
return nil
1698+
}
1699+
16961700
var ipv4Addresses []string
16971701
var ipv6Addresses []string
16981702
for _, addr := range addresses {
@@ -1717,6 +1721,34 @@ func filterAddresses(addresses []string, supportsV4, supportsV6 bool) []string {
17171721
}
17181722
}
17191723
}
1724+
1725+
if supportsV4 && supportsV6 {
1726+
firstAddrFamily := ""
1727+
if strings.Contains(addresses[0], "/") {
1728+
if prefix, err := netip.ParsePrefix(addresses[0]); err == nil {
1729+
if prefix.Addr().Is4() {
1730+
firstAddrFamily = "v4"
1731+
} else if prefix.Addr().Is6() {
1732+
firstAddrFamily = "v6"
1733+
}
1734+
}
1735+
} else {
1736+
if ipAddr, err := netip.ParseAddr(addresses[0]); err == nil {
1737+
if ipAddr.Is4() {
1738+
firstAddrFamily = "v4"
1739+
} else if ipAddr.Is6() {
1740+
firstAddrFamily = "v6"
1741+
}
1742+
}
1743+
}
1744+
1745+
if firstAddrFamily == "v4" {
1746+
return ipv4Addresses
1747+
} else if firstAddrFamily == "v6" {
1748+
return ipv6Addresses
1749+
}
1750+
}
1751+
17201752
if len(ipv4Addresses) > 0 {
17211753
return ipv4Addresses
17221754
}

pilot/pkg/model/service_test.go

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -731,6 +731,104 @@ func TestGetAllAddresses(t *testing.T) {
731731
}
732732
}
733733

734+
func TestGetAddressForProxy(t *testing.T) {
735+
tests := []struct {
736+
name string
737+
service *Service
738+
proxy *Proxy
739+
expectedAddress string
740+
}{
741+
{
742+
name: "IPv4 mode with IPv4 addresses, expected to return the first IPv4 address",
743+
service: &Service{
744+
ClusterVIPs: AddressMap{
745+
Addresses: map[cluster.ID][]string{
746+
"cl1": {"10.0.0.1", "10.0.0.2"},
747+
},
748+
},
749+
},
750+
proxy: &Proxy{
751+
Metadata: &NodeMetadata{
752+
ClusterID: "cl1",
753+
},
754+
ipMode: IPv4,
755+
},
756+
expectedAddress: "10.0.0.1",
757+
},
758+
{
759+
name: "IPv6 mode with IPv6 addresses, expected to return the first IPv6 address",
760+
service: &Service{
761+
ClusterVIPs: AddressMap{
762+
Addresses: map[cluster.ID][]string{
763+
"cl1": {"2001:db8:abcd::1", "2001:db8:abcd::2"},
764+
},
765+
},
766+
},
767+
proxy: &Proxy{
768+
Metadata: &NodeMetadata{
769+
ClusterID: "cl1",
770+
},
771+
ipMode: IPv6,
772+
},
773+
expectedAddress: "2001:db8:abcd::1",
774+
},
775+
{
776+
name: "Dual mode with both IPv6 and IPv4 addresses, expected to return the IPv6 address",
777+
service: &Service{
778+
ClusterVIPs: AddressMap{
779+
Addresses: map[cluster.ID][]string{
780+
"cl1": {"2001:db8:abcd::1", "10.0.0.1"},
781+
},
782+
},
783+
},
784+
proxy: &Proxy{
785+
Metadata: &NodeMetadata{
786+
ClusterID: "cl1",
787+
},
788+
ipMode: Dual,
789+
},
790+
expectedAddress: "2001:db8:abcd::1",
791+
},
792+
{
793+
name: "IPv4 mode with Auto-allocated IPv4 address",
794+
service: &Service{
795+
DefaultAddress: constants.UnspecifiedIP,
796+
AutoAllocatedIPv4Address: "240.240.0.1",
797+
},
798+
proxy: &Proxy{
799+
Metadata: &NodeMetadata{
800+
DNSAutoAllocate: true,
801+
DNSCapture: true,
802+
},
803+
ipMode: IPv4,
804+
},
805+
expectedAddress: "240.240.0.1",
806+
},
807+
{
808+
name: "IPv6 mode with Auto-allocated IPv6 address",
809+
service: &Service{
810+
DefaultAddress: constants.UnspecifiedIP,
811+
AutoAllocatedIPv6Address: "2001:2::f0f0:e351",
812+
},
813+
proxy: &Proxy{
814+
Metadata: &NodeMetadata{
815+
DNSAutoAllocate: true,
816+
DNSCapture: true,
817+
},
818+
ipMode: IPv6,
819+
},
820+
expectedAddress: "2001:2::f0f0:e351",
821+
},
822+
}
823+
824+
for _, tt := range tests {
825+
t.Run(tt.name, func(t *testing.T) {
826+
result := tt.service.GetAddressForProxy(tt.proxy)
827+
assert.Equal(t, result, tt.expectedAddress)
828+
})
829+
}
830+
}
831+
734832
func BenchmarkEndpointDeepCopy(b *testing.B) {
735833
ep := &IstioEndpoint{
736834
Labels: labels.Instance{"label-foo": "aaa", "label-bar": "bbb"},

0 commit comments

Comments
 (0)