Skip to content

Commit d3c7573

Browse files
authored
Merge pull request kubernetes#131427 from princepereira/automated-cherry-pick-of-#131138-upstream-release-1.33
Automated cherry pick of kubernetes#131138: Fix for HNS local endpoint was being deleted instead of the remote endpoint.
2 parents 5dc8b8d + 950bb3b commit d3c7573

File tree

3 files changed

+110
-34
lines changed

3 files changed

+110
-34
lines changed

pkg/proxy/winkernel/hns.go

Lines changed: 34 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -133,40 +133,43 @@ func (hns hns) getAllEndpointsByNetwork(networkName string) (map[string]*(endpoi
133133
continue
134134
}
135135

136-
// Add to map with key endpoint ID or IP address
137-
// Storing this is expensive in terms of memory, however there is a bug in Windows Server 2019 that can cause two endpoints to be created with the same IP address.
138-
// TODO: Store by IP only and remove any lookups by endpoint ID.
139-
endpointInfos[ep.Id] = &endpointInfo{
140-
ip: ep.IpConfigurations[0].IpAddress,
141-
isLocal: uint32(ep.Flags&hcn.EndpointFlagsRemoteEndpoint) == 0,
142-
macAddress: ep.MacAddress,
143-
hnsID: ep.Id,
144-
hns: hns,
145-
// only ready and not terminating endpoints were added to HNS
146-
ready: true,
147-
serving: true,
148-
terminating: false,
149-
}
150-
endpointInfos[ep.IpConfigurations[0].IpAddress] = endpointInfos[ep.Id]
136+
for index, ipConfig := range ep.IpConfigurations {
137+
138+
if index > 1 {
139+
// Expecting only ipv4 and ipv6 ipaddresses
140+
// This is highly unlikely to happen, but if it does, we should log a warning
141+
// and break out of the loop
142+
klog.Warning("Endpoint ipconfiguration holds more than 2 IP addresses.", "hnsID", ep.Id, "IP", ipConfig.IpAddress, "ipConfigCount", len(ep.IpConfigurations))
143+
break
144+
}
151145

152-
if len(ep.IpConfigurations) == 1 {
153-
continue
154-
}
146+
isLocal := uint32(ep.Flags&hcn.EndpointFlagsRemoteEndpoint) == 0
155147

156-
// If ipFamilyPolicy is RequireDualStack or PreferDualStack, then there will be 2 IPS (iPV4 and IPV6)
157-
// in the endpoint list
158-
endpointDualstack := &endpointInfo{
159-
ip: ep.IpConfigurations[1].IpAddress,
160-
isLocal: uint32(ep.Flags&hcn.EndpointFlagsRemoteEndpoint) == 0,
161-
macAddress: ep.MacAddress,
162-
hnsID: ep.Id,
163-
hns: hns,
164-
// only ready and not terminating endpoints were added to HNS
165-
ready: true,
166-
serving: true,
167-
terminating: false,
148+
if existingEp, ok := endpointInfos[ipConfig.IpAddress]; ok && isLocal {
149+
// If the endpoint is already part of the queried endpoints map and is local,
150+
// then we should not add it again to the map
151+
// This is to avoid overwriting the remote endpoint info with a local endpoint.
152+
klog.V(3).InfoS("Endpoint already exists in queried endpoints map; skipping.", "newLocalEndpoint", ep, "ipConfig", ipConfig, "existingEndpoint", existingEp)
153+
continue
154+
}
155+
156+
// Add to map with key endpoint ID or IP address
157+
// Storing this is expensive in terms of memory, however there is a bug in Windows Server 2019 and 2022 that can cause two endpoints (local and remote) to be created with the same IP address.
158+
// TODO: Store by IP only and remove any lookups by endpoint ID.
159+
epInfo := &endpointInfo{
160+
ip: ipConfig.IpAddress,
161+
isLocal: isLocal,
162+
macAddress: ep.MacAddress,
163+
hnsID: ep.Id,
164+
hns: hns,
165+
// only ready and not terminating endpoints were added to HNS
166+
ready: true,
167+
serving: true,
168+
terminating: false,
169+
}
170+
endpointInfos[ep.Id] = epInfo
171+
endpointInfos[ipConfig.IpAddress] = epInfo
168172
}
169-
endpointInfos[ep.IpConfigurations[1].IpAddress] = endpointDualstack
170173
}
171174
klog.V(3).InfoS("Queried endpoints from network", "network", networkName, "count", len(endpointInfos))
172175
klog.V(5).InfoS("Queried endpoints details", "network", networkName, "endpointInfos", endpointInfos)

pkg/proxy/winkernel/hns_test.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,70 @@ func TestGetAllEndpointsByNetwork(t *testing.T) {
112112
}
113113
}
114114

115+
func TestGetAllEndpointsByNetworkWithDupEP(t *testing.T) {
116+
hcnMock := getHcnMock("L2Bridge")
117+
hns := hns{hcn: hcnMock}
118+
119+
ipv4Config := &hcn.IpConfig{
120+
IpAddress: epIpAddress,
121+
}
122+
ipv6Config := &hcn.IpConfig{
123+
IpAddress: epIpv6Address,
124+
}
125+
remoteEndpoint := &hcn.HostComputeEndpoint{
126+
IpConfigurations: []hcn.IpConfig{*ipv4Config, *ipv6Config},
127+
MacAddress: epMacAddress,
128+
SchemaVersion: hcn.SchemaVersion{
129+
Major: 2,
130+
Minor: 0,
131+
},
132+
Flags: hcn.EndpointFlagsRemoteEndpoint,
133+
}
134+
Network, _ := hcnMock.GetNetworkByName(testNetwork)
135+
remoteEndpoint, err := hns.hcn.CreateEndpoint(Network, remoteEndpoint)
136+
if err != nil {
137+
t.Error(err)
138+
}
139+
140+
// Create a duplicate local endpoint with the same IP address
141+
dupLocalEndpoint := &hcn.HostComputeEndpoint{
142+
IpConfigurations: []hcn.IpConfig{*ipv4Config, *ipv6Config},
143+
MacAddress: epMacAddress,
144+
SchemaVersion: hcn.SchemaVersion{
145+
Major: 2,
146+
Minor: 0,
147+
},
148+
}
149+
150+
dupLocalEndpoint, err = hns.hcn.CreateEndpoint(Network, dupLocalEndpoint)
151+
if err != nil {
152+
t.Error(err)
153+
}
154+
155+
mapEndpointsInfo, err := hns.getAllEndpointsByNetwork(Network.Name)
156+
if err != nil {
157+
t.Error(err)
158+
}
159+
endpointIpv4, ipv4EpPresent := mapEndpointsInfo[ipv4Config.IpAddress]
160+
assert.True(t, ipv4EpPresent, "IPV4 endpoint is missing in Dualstack mode")
161+
assert.Equal(t, endpointIpv4.ip, epIpAddress, "IPV4 IP is missing in Dualstack mode")
162+
assert.Equal(t, endpointIpv4.hnsID, remoteEndpoint.Id, "HNS ID is not matching with remote endpoint")
163+
164+
endpointIpv6, ipv6EpPresent := mapEndpointsInfo[ipv6Config.IpAddress]
165+
assert.True(t, ipv6EpPresent, "IPV6 endpoint is missing in Dualstack mode")
166+
assert.Equal(t, endpointIpv6.ip, epIpv6Address, "IPV6 IP is missing in Dualstack mode")
167+
assert.Equal(t, endpointIpv6.hnsID, remoteEndpoint.Id, "HNS ID is not matching with remote endpoint")
168+
169+
err = hns.hcn.DeleteEndpoint(remoteEndpoint)
170+
if err != nil {
171+
t.Error(err)
172+
}
173+
err = hns.hcn.DeleteEndpoint(dupLocalEndpoint)
174+
if err != nil {
175+
t.Error(err)
176+
}
177+
}
178+
115179
func TestGetEndpointByID(t *testing.T) {
116180
// TODO: remove skip once the test has been fixed.
117181
t.Skip("Skipping failing test on Windows.")

pkg/proxy/winkernel/proxier.go

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -535,6 +535,11 @@ func (ep *endpointInfo) DecrementRefCount() {
535535
if !ep.IsLocal() && ep.refCount != nil && *ep.refCount > 0 {
536536
*ep.refCount--
537537
}
538+
refCount := 0
539+
if ep.refCount != nil {
540+
refCount = int(*ep.refCount)
541+
}
542+
klog.V(5).InfoS("Endpoint RefCount after decrement.", "endpointInfo", ep, "refCount", refCount)
538543
}
539544

540545
func (ep *endpointInfo) Cleanup() {
@@ -1770,10 +1775,14 @@ func (proxier *Proxier) syncProxyRules() {
17701775

17711776
// remove stale endpoint refcount entries
17721777
for epIP := range proxier.terminatedEndpoints {
1773-
if epToDelete := queriedEndpoints[epIP]; epToDelete != nil && epToDelete.hnsID != "" {
1778+
klog.V(5).InfoS("Terminated endpoints ready for deletion", "epIP", epIP)
1779+
if epToDelete := queriedEndpoints[epIP]; epToDelete != nil && epToDelete.hnsID != "" && !epToDelete.IsLocal() {
17741780
if refCount := proxier.endPointsRefCount.getRefCount(epToDelete.hnsID); refCount == nil || *refCount == 0 {
1775-
klog.V(3).InfoS("Deleting unreferenced remote endpoint", "hnsID", epToDelete.hnsID)
1776-
proxier.hns.deleteEndpoint(epToDelete.hnsID)
1781+
klog.V(3).InfoS("Deleting unreferenced remote endpoint", "hnsID", epToDelete.hnsID, "IP", epToDelete.ip)
1782+
err := proxier.hns.deleteEndpoint(epToDelete.hnsID)
1783+
if err != nil {
1784+
klog.ErrorS(err, "Deleting unreferenced remote endpoint failed", "hnsID", epToDelete.hnsID)
1785+
}
17771786
}
17781787
}
17791788
}

0 commit comments

Comments
 (0)