k8s.io/kubernetes@v1.29.3/pkg/proxy/nftables/proxier_test.go (about)

     1  /*
     2  Copyright 2015 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package nftables
    18  
    19  import (
    20  	"fmt"
    21  	"net"
    22  	"reflect"
    23  	"sort"
    24  	"strings"
    25  	"testing"
    26  	"time"
    27  
    28  	"github.com/danwinship/knftables"
    29  	"github.com/lithammer/dedent"
    30  	v1 "k8s.io/api/core/v1"
    31  	discovery "k8s.io/api/discovery/v1"
    32  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    33  	"k8s.io/apimachinery/pkg/types"
    34  	"k8s.io/apimachinery/pkg/util/intstr"
    35  	utilfeature "k8s.io/apiserver/pkg/util/feature"
    36  	featuregatetesting "k8s.io/component-base/featuregate/testing"
    37  	"k8s.io/component-base/metrics/testutil"
    38  	"k8s.io/klog/v2"
    39  	"k8s.io/kubernetes/pkg/features"
    40  	"k8s.io/kubernetes/pkg/proxy"
    41  	"k8s.io/kubernetes/pkg/proxy/conntrack"
    42  	"k8s.io/kubernetes/pkg/proxy/metrics"
    43  
    44  	"k8s.io/kubernetes/pkg/proxy/healthcheck"
    45  	proxyutil "k8s.io/kubernetes/pkg/proxy/util"
    46  	proxyutiliptables "k8s.io/kubernetes/pkg/proxy/util/iptables"
    47  	proxyutiltest "k8s.io/kubernetes/pkg/proxy/util/testing"
    48  	"k8s.io/kubernetes/pkg/util/async"
    49  	"k8s.io/utils/exec"
    50  	fakeexec "k8s.io/utils/exec/testing"
    51  	netutils "k8s.io/utils/net"
    52  	"k8s.io/utils/ptr"
    53  )
    54  
    55  func TestDeleteEndpointConnections(t *testing.T) {
    56  	const (
    57  		UDP  = v1.ProtocolUDP
    58  		TCP  = v1.ProtocolTCP
    59  		SCTP = v1.ProtocolSCTP
    60  	)
    61  
    62  	testCases := []struct {
    63  		description  string
    64  		svcName      string
    65  		svcIP        string
    66  		svcPort      int32
    67  		protocol     v1.Protocol
    68  		endpoint     string // IP:port endpoint
    69  		simulatedErr string
    70  	}{
    71  		{
    72  			description: "V4 UDP",
    73  			svcName:     "v4-udp",
    74  			svcIP:       "172.30.1.1",
    75  			svcPort:     80,
    76  			protocol:    UDP,
    77  			endpoint:    "10.240.0.3:80",
    78  		},
    79  		{
    80  			description: "V4 TCP",
    81  			svcName:     "v4-tcp",
    82  			svcIP:       "172.30.2.2",
    83  			svcPort:     80,
    84  			protocol:    TCP,
    85  			endpoint:    "10.240.0.4:80",
    86  		},
    87  		{
    88  			description: "V4 SCTP",
    89  			svcName:     "v4-sctp",
    90  			svcIP:       "172.30.3.3",
    91  			svcPort:     80,
    92  			protocol:    SCTP,
    93  			endpoint:    "10.240.0.5:80",
    94  		},
    95  		{
    96  			description:  "V4 UDP, nothing to delete, benign error",
    97  			svcName:      "v4-udp-nothing-to-delete",
    98  			svcIP:        "172.30.4.4",
    99  			svcPort:      80,
   100  			protocol:     UDP,
   101  			endpoint:     "10.240.0.6:80",
   102  			simulatedErr: conntrack.NoConnectionToDelete,
   103  		},
   104  		{
   105  			description:  "V4 UDP, unexpected error, should be glogged",
   106  			svcName:      "v4-udp-simulated-error",
   107  			svcIP:        "172.30.5.5",
   108  			svcPort:      80,
   109  			protocol:     UDP,
   110  			endpoint:     "10.240.0.7:80",
   111  			simulatedErr: "simulated error",
   112  		},
   113  		{
   114  			description: "V6 UDP",
   115  			svcName:     "v6-udp",
   116  			svcIP:       "fd00:1234::20",
   117  			svcPort:     80,
   118  			protocol:    UDP,
   119  			endpoint:    "[2001:db8::2]:80",
   120  		},
   121  		{
   122  			description: "V6 TCP",
   123  			svcName:     "v6-tcp",
   124  			svcIP:       "fd00:1234::30",
   125  			svcPort:     80,
   126  			protocol:    TCP,
   127  			endpoint:    "[2001:db8::3]:80",
   128  		},
   129  		{
   130  			description: "V6 SCTP",
   131  			svcName:     "v6-sctp",
   132  			svcIP:       "fd00:1234::40",
   133  			svcPort:     80,
   134  			protocol:    SCTP,
   135  			endpoint:    "[2001:db8::4]:80",
   136  		},
   137  	}
   138  
   139  	for _, tc := range testCases {
   140  		t.Run(tc.description, func(t *testing.T) {
   141  			priorGlogErrs := klog.Stats.Error.Lines()
   142  
   143  			// Create a fake executor for the conntrack utility.
   144  			fcmd := fakeexec.FakeCmd{}
   145  			fexec := &fakeexec.FakeExec{
   146  				LookPathFunc: func(cmd string) (string, error) { return cmd, nil },
   147  			}
   148  			execFunc := func(cmd string, args ...string) exec.Cmd {
   149  				return fakeexec.InitFakeCmd(&fcmd, cmd, args...)
   150  			}
   151  
   152  			if tc.protocol == UDP {
   153  				cmdOutput := "1 flow entries have been deleted"
   154  				var simErr error
   155  
   156  				// First call outputs cmdOutput and succeeds
   157  				fcmd.CombinedOutputScript = append(fcmd.CombinedOutputScript,
   158  					func() ([]byte, []byte, error) { return []byte(cmdOutput), nil, nil },
   159  				)
   160  				fexec.CommandScript = append(fexec.CommandScript, execFunc)
   161  
   162  				// Second call may succeed or fail
   163  				if tc.simulatedErr != "" {
   164  					cmdOutput = ""
   165  					simErr = fmt.Errorf(tc.simulatedErr)
   166  				}
   167  				fcmd.CombinedOutputScript = append(fcmd.CombinedOutputScript,
   168  					func() ([]byte, []byte, error) { return []byte(cmdOutput), nil, simErr },
   169  				)
   170  				fexec.CommandScript = append(fexec.CommandScript, execFunc)
   171  			}
   172  
   173  			endpointIP := proxyutil.IPPart(tc.endpoint)
   174  			_, fp := NewFakeProxier(proxyutil.GetIPFamilyFromIP(endpointIP))
   175  			fp.exec = fexec
   176  
   177  			makeServiceMap(fp,
   178  				makeTestService("ns1", tc.svcName, func(svc *v1.Service) {
   179  					svc.Spec.ClusterIP = tc.svcIP
   180  					svc.Spec.Ports = []v1.ServicePort{{
   181  						Name:     "p80",
   182  						Port:     tc.svcPort,
   183  						Protocol: tc.protocol,
   184  					}}
   185  					svc.Spec.ExternalTrafficPolicy = v1.ServiceExternalTrafficPolicyLocal
   186  				}),
   187  			)
   188  			fp.svcPortMap.Update(fp.serviceChanges)
   189  
   190  			slice := makeTestEndpointSlice("ns1", tc.svcName, 1, func(eps *discovery.EndpointSlice) {
   191  				if fp.ipFamily == v1.IPv6Protocol {
   192  					eps.AddressType = discovery.AddressTypeIPv6
   193  				} else {
   194  					eps.AddressType = discovery.AddressTypeIPv4
   195  				}
   196  				eps.Endpoints = []discovery.Endpoint{{
   197  					Addresses: []string{endpointIP},
   198  				}}
   199  				eps.Ports = []discovery.EndpointPort{{
   200  					Name:     ptr.To("p80"),
   201  					Port:     ptr.To[int32](80),
   202  					Protocol: ptr.To(tc.protocol),
   203  				}}
   204  			})
   205  
   206  			// Add and then remove the endpoint slice
   207  			fp.OnEndpointSliceAdd(slice)
   208  			fp.syncProxyRules()
   209  			fp.OnEndpointSliceDelete(slice)
   210  			fp.syncProxyRules()
   211  
   212  			// Check the executed conntrack command
   213  			if tc.protocol == UDP {
   214  				if fexec.CommandCalls != 2 {
   215  					t.Fatalf("Expected conntrack to be executed 2 times, but got %d", fexec.CommandCalls)
   216  				}
   217  
   218  				// First clear conntrack entries for the clusterIP when the
   219  				// endpoint is first added.
   220  				expectCommand := fmt.Sprintf("conntrack -D --orig-dst %s -p udp", tc.svcIP)
   221  				if fp.ipFamily == v1.IPv6Protocol {
   222  					expectCommand += " -f ipv6"
   223  				}
   224  				actualCommand := strings.Join(fcmd.CombinedOutputLog[0], " ")
   225  				if actualCommand != expectCommand {
   226  					t.Errorf("Expected command: %s, but executed %s", expectCommand, actualCommand)
   227  				}
   228  
   229  				// Then clear conntrack entries for the endpoint when it is
   230  				// deleted.
   231  				expectCommand = fmt.Sprintf("conntrack -D --orig-dst %s --dst-nat %s -p udp", tc.svcIP, endpointIP)
   232  				if fp.ipFamily == v1.IPv6Protocol {
   233  					expectCommand += " -f ipv6"
   234  				}
   235  				actualCommand = strings.Join(fcmd.CombinedOutputLog[1], " ")
   236  				if actualCommand != expectCommand {
   237  					t.Errorf("Expected command: %s, but executed %s", expectCommand, actualCommand)
   238  				}
   239  			} else if fexec.CommandCalls != 0 {
   240  				t.Fatalf("Expected conntrack to be executed 0 times, but got %d", fexec.CommandCalls)
   241  			}
   242  
   243  			// Check the number of new glog errors
   244  			var expGlogErrs int64
   245  			if tc.simulatedErr != "" && tc.simulatedErr != conntrack.NoConnectionToDelete {
   246  				expGlogErrs = 1
   247  			}
   248  			glogErrs := klog.Stats.Error.Lines() - priorGlogErrs
   249  			if glogErrs != expGlogErrs {
   250  				t.Errorf("Expected %d glogged errors, but got %d", expGlogErrs, glogErrs)
   251  			}
   252  		})
   253  	}
   254  }
   255  
   256  // Conventions for tests using NewFakeProxier:
   257  //
   258  // Pod IPs:             10.0.0.0/8
   259  // Service ClusterIPs:  172.30.0.0/16
   260  // Node IPs:            192.168.0.0/24
   261  // Local Node IP:       192.168.0.2
   262  // Service ExternalIPs: 192.168.99.0/24
   263  // LoadBalancer IPs:    1.2.3.4, 5.6.7.8, 9.10.11.12
   264  // Non-cluster IPs:     203.0.113.0/24
   265  // LB Source Range:     203.0.113.0/25
   266  
   267  const testHostname = "test-hostname"
   268  const testNodeIP = "192.168.0.2"
   269  const testNodeIPAlt = "192.168.1.2"
   270  const testExternalIP = "192.168.99.11"
   271  const testNodeIPv6 = "2001:db8::1"
   272  const testNodeIPv6Alt = "2001:db8:1::2"
   273  const testExternalClient = "203.0.113.2"
   274  const testExternalClientBlocked = "203.0.113.130"
   275  
   276  var testNodeIPs = []string{testNodeIP, testNodeIPAlt, testExternalIP, testNodeIPv6, testNodeIPv6Alt}
   277  
   278  func NewFakeProxier(ipFamily v1.IPFamily) (*knftables.Fake, *Proxier) {
   279  	// TODO: Call NewProxier after refactoring out the goroutine
   280  	// invocation into a Run() method.
   281  	nftablesFamily := knftables.IPv4Family
   282  	podCIDR := "10.0.0.0/8"
   283  	if ipFamily == v1.IPv6Protocol {
   284  		nftablesFamily = knftables.IPv6Family
   285  		podCIDR = "fd00:10::/64"
   286  	}
   287  	detectLocal, _ := proxyutiliptables.NewDetectLocalByCIDR(podCIDR)
   288  
   289  	networkInterfacer := proxyutiltest.NewFakeNetwork()
   290  	itf := net.Interface{Index: 0, MTU: 0, Name: "lo", HardwareAddr: nil, Flags: 0}
   291  	addrs := []net.Addr{
   292  		&net.IPNet{IP: netutils.ParseIPSloppy("127.0.0.1"), Mask: net.CIDRMask(8, 32)},
   293  		&net.IPNet{IP: netutils.ParseIPSloppy("::1/128"), Mask: net.CIDRMask(128, 128)},
   294  	}
   295  	networkInterfacer.AddInterfaceAddr(&itf, addrs)
   296  	itf1 := net.Interface{Index: 1, MTU: 0, Name: "eth0", HardwareAddr: nil, Flags: 0}
   297  	addrs1 := []net.Addr{
   298  		&net.IPNet{IP: netutils.ParseIPSloppy(testNodeIP), Mask: net.CIDRMask(24, 32)},
   299  		&net.IPNet{IP: netutils.ParseIPSloppy(testNodeIPAlt), Mask: net.CIDRMask(24, 32)},
   300  		&net.IPNet{IP: netutils.ParseIPSloppy(testExternalIP), Mask: net.CIDRMask(24, 32)},
   301  		&net.IPNet{IP: netutils.ParseIPSloppy(testNodeIPv6), Mask: net.CIDRMask(64, 128)},
   302  		&net.IPNet{IP: netutils.ParseIPSloppy(testNodeIPv6Alt), Mask: net.CIDRMask(64, 128)},
   303  	}
   304  	networkInterfacer.AddInterfaceAddr(&itf1, addrs1)
   305  
   306  	nft := knftables.NewFake(nftablesFamily, kubeProxyTable)
   307  
   308  	p := &Proxier{
   309  		ipFamily:            ipFamily,
   310  		exec:                &fakeexec.FakeExec{},
   311  		svcPortMap:          make(proxy.ServicePortMap),
   312  		serviceChanges:      proxy.NewServiceChangeTracker(newServiceInfo, ipFamily, nil, nil),
   313  		endpointsMap:        make(proxy.EndpointsMap),
   314  		endpointsChanges:    proxy.NewEndpointsChangeTracker(testHostname, newEndpointInfo, ipFamily, nil, nil),
   315  		nftables:            nft,
   316  		masqueradeMark:      "0x4000",
   317  		localDetector:       detectLocal,
   318  		hostname:            testHostname,
   319  		serviceHealthServer: healthcheck.NewFakeServiceHealthServer(),
   320  		nodeIP:              netutils.ParseIPSloppy(testNodeIP),
   321  		nodePortAddresses:   proxyutil.NewNodePortAddresses(ipFamily, nil),
   322  		networkInterfacer:   networkInterfacer,
   323  		staleChains:         make(map[string]time.Time),
   324  	}
   325  	p.setInitialized(true)
   326  	p.syncRunner = async.NewBoundedFrequencyRunner("test-sync-runner", p.syncProxyRules, 0, time.Minute, 1)
   327  
   328  	return nft, p
   329  }
   330  
   331  // TestOverallNFTablesRules creates a variety of services and verifies that the generated
   332  // rules are exactly as expected.
   333  func TestOverallNFTablesRules(t *testing.T) {
   334  	nft, fp := NewFakeProxier(v1.IPv4Protocol)
   335  	metrics.RegisterMetrics()
   336  
   337  	makeServiceMap(fp,
   338  		// create ClusterIP service
   339  		makeTestService("ns1", "svc1", func(svc *v1.Service) {
   340  			svc.Spec.ClusterIP = "172.30.0.41"
   341  			svc.Spec.Ports = []v1.ServicePort{{
   342  				Name:     "p80",
   343  				Port:     80,
   344  				Protocol: v1.ProtocolTCP,
   345  			}}
   346  		}),
   347  		// create LoadBalancer service with Local traffic policy
   348  		makeTestService("ns2", "svc2", func(svc *v1.Service) {
   349  			svc.Spec.Type = "LoadBalancer"
   350  			svc.Spec.ExternalTrafficPolicy = v1.ServiceExternalTrafficPolicyLocal
   351  			svc.Spec.ClusterIP = "172.30.0.42"
   352  			svc.Spec.Ports = []v1.ServicePort{{
   353  				Name:     "p80",
   354  				Port:     80,
   355  				Protocol: v1.ProtocolTCP,
   356  				NodePort: 3001,
   357  			}}
   358  			svc.Status.LoadBalancer.Ingress = []v1.LoadBalancerIngress{{
   359  				IP: "1.2.3.4",
   360  			}}
   361  			svc.Spec.ExternalIPs = []string{"192.168.99.22"}
   362  			svc.Spec.HealthCheckNodePort = 30000
   363  		}),
   364  		// create NodePort service
   365  		makeTestService("ns3", "svc3", func(svc *v1.Service) {
   366  			svc.Spec.Type = "NodePort"
   367  			svc.Spec.ClusterIP = "172.30.0.43"
   368  			svc.Spec.Ports = []v1.ServicePort{{
   369  				Name:     "p80",
   370  				Port:     80,
   371  				Protocol: v1.ProtocolTCP,
   372  				NodePort: 3003,
   373  			}}
   374  		}),
   375  		// create ExternalIP service
   376  		makeTestService("ns4", "svc4", func(svc *v1.Service) {
   377  			svc.Spec.Type = "NodePort"
   378  			svc.Spec.ClusterIP = "172.30.0.44"
   379  			svc.Spec.ExternalIPs = []string{"192.168.99.33"}
   380  			svc.Spec.Ports = []v1.ServicePort{{
   381  				Name:       "p80",
   382  				Port:       80,
   383  				Protocol:   v1.ProtocolTCP,
   384  				TargetPort: intstr.FromInt32(80),
   385  			}}
   386  		}),
   387  		// create LoadBalancer service with Cluster traffic policy, source ranges,
   388  		// and session affinity
   389  		makeTestService("ns5", "svc5", func(svc *v1.Service) {
   390  			svc.Spec.Type = "LoadBalancer"
   391  			svc.Spec.ExternalTrafficPolicy = v1.ServiceExternalTrafficPolicyCluster
   392  			svc.Spec.ClusterIP = "172.30.0.45"
   393  			svc.Spec.Ports = []v1.ServicePort{{
   394  				Name:     "p80",
   395  				Port:     80,
   396  				Protocol: v1.ProtocolTCP,
   397  				NodePort: 3002,
   398  			}}
   399  			svc.Status.LoadBalancer.Ingress = []v1.LoadBalancerIngress{{
   400  				IP: "5.6.7.8",
   401  			}}
   402  			svc.Spec.HealthCheckNodePort = 30000
   403  			// Extra whitespace to ensure that invalid value will not result
   404  			// in a crash, for backward compatibility.
   405  			svc.Spec.LoadBalancerSourceRanges = []string{" 203.0.113.0/25"}
   406  
   407  			svc.Spec.SessionAffinity = v1.ServiceAffinityClientIP
   408  			svc.Spec.SessionAffinityConfig = &v1.SessionAffinityConfig{
   409  				ClientIP: &v1.ClientIPConfig{
   410  					TimeoutSeconds: ptr.To[int32](10800),
   411  				},
   412  			}
   413  		}),
   414  		// create ClusterIP service with no endpoints
   415  		makeTestService("ns6", "svc6", func(svc *v1.Service) {
   416  			svc.Spec.Type = "ClusterIP"
   417  			svc.Spec.ClusterIP = "172.30.0.46"
   418  			svc.Spec.Ports = []v1.ServicePort{{
   419  				Name:       "p80",
   420  				Port:       80,
   421  				Protocol:   v1.ProtocolTCP,
   422  				TargetPort: intstr.FromInt32(80),
   423  			}}
   424  		}),
   425  	)
   426  	populateEndpointSlices(fp,
   427  		// create ClusterIP service endpoints
   428  		makeTestEndpointSlice("ns1", "svc1", 1, func(eps *discovery.EndpointSlice) {
   429  			eps.AddressType = discovery.AddressTypeIPv4
   430  			eps.Endpoints = []discovery.Endpoint{{
   431  				Addresses: []string{"10.180.0.1"},
   432  			}}
   433  			eps.Ports = []discovery.EndpointPort{{
   434  				Name:     ptr.To("p80"),
   435  				Port:     ptr.To[int32](80),
   436  				Protocol: ptr.To(v1.ProtocolTCP),
   437  			}}
   438  		}),
   439  		// create Local LoadBalancer endpoints. Note that since we aren't setting
   440  		// its NodeName, this endpoint will be considered non-local and ignored.
   441  		makeTestEndpointSlice("ns2", "svc2", 1, func(eps *discovery.EndpointSlice) {
   442  			eps.AddressType = discovery.AddressTypeIPv4
   443  			eps.Endpoints = []discovery.Endpoint{{
   444  				Addresses: []string{"10.180.0.2"},
   445  			}}
   446  			eps.Ports = []discovery.EndpointPort{{
   447  				Name:     ptr.To("p80"),
   448  				Port:     ptr.To[int32](80),
   449  				Protocol: ptr.To(v1.ProtocolTCP),
   450  			}}
   451  		}),
   452  		// create NodePort service endpoints
   453  		makeTestEndpointSlice("ns3", "svc3", 1, func(eps *discovery.EndpointSlice) {
   454  			eps.AddressType = discovery.AddressTypeIPv4
   455  			eps.Endpoints = []discovery.Endpoint{{
   456  				Addresses: []string{"10.180.0.3"},
   457  			}}
   458  			eps.Ports = []discovery.EndpointPort{{
   459  				Name:     ptr.To("p80"),
   460  				Port:     ptr.To[int32](80),
   461  				Protocol: ptr.To(v1.ProtocolTCP),
   462  			}}
   463  		}),
   464  		// create ExternalIP service endpoints
   465  		makeTestEndpointSlice("ns4", "svc4", 1, func(eps *discovery.EndpointSlice) {
   466  			eps.AddressType = discovery.AddressTypeIPv4
   467  			eps.Endpoints = []discovery.Endpoint{{
   468  				Addresses: []string{"10.180.0.4"},
   469  			}, {
   470  				Addresses: []string{"10.180.0.5"},
   471  				NodeName:  ptr.To(testHostname),
   472  			}}
   473  			eps.Ports = []discovery.EndpointPort{{
   474  				Name:     ptr.To("p80"),
   475  				Port:     ptr.To[int32](80),
   476  				Protocol: ptr.To(v1.ProtocolTCP),
   477  			}}
   478  		}),
   479  		// create Cluster LoadBalancer endpoints
   480  		makeTestEndpointSlice("ns5", "svc5", 1, func(eps *discovery.EndpointSlice) {
   481  			eps.AddressType = discovery.AddressTypeIPv4
   482  			eps.Endpoints = []discovery.Endpoint{{
   483  				Addresses: []string{"10.180.0.3"},
   484  			}}
   485  			eps.Ports = []discovery.EndpointPort{{
   486  				Name:     ptr.To("p80"),
   487  				Port:     ptr.To[int32](80),
   488  				Protocol: ptr.To(v1.ProtocolTCP),
   489  			}}
   490  		}),
   491  	)
   492  
   493  	fp.syncProxyRules()
   494  
   495  	expected := dedent.Dedent(`
   496  		add table ip kube-proxy { comment "rules for kube-proxy" ; }
   497  
   498  		add chain ip kube-proxy forward
   499  		add rule ip kube-proxy forward ct state invalid drop
   500  		add chain ip kube-proxy mark-for-masquerade
   501  		add rule ip kube-proxy mark-for-masquerade mark set mark or 0x4000
   502  		add chain ip kube-proxy masquerading
   503  		add rule ip kube-proxy masquerading mark and 0x4000 == 0 return
   504  		add rule ip kube-proxy masquerading mark set mark xor 0x4000
   505  		add rule ip kube-proxy masquerading masquerade fully-random
   506  		add chain ip kube-proxy services
   507  		add chain ip kube-proxy filter-forward { type filter hook forward priority -101 ; }
   508  		add rule ip kube-proxy filter-forward ct state new jump endpoints-check
   509  		add rule ip kube-proxy filter-forward jump forward
   510  		add rule ip kube-proxy filter-forward ct state new jump firewall-check
   511  		add chain ip kube-proxy filter-input { type filter hook input priority -101 ; }
   512  		add rule ip kube-proxy filter-input ct state new jump endpoints-check
   513  		add rule ip kube-proxy filter-input ct state new jump firewall-check
   514  		add chain ip kube-proxy filter-output { type filter hook output priority -101 ; }
   515  		add rule ip kube-proxy filter-output ct state new jump endpoints-check
   516  		add rule ip kube-proxy filter-output ct state new jump firewall-check
   517  		add chain ip kube-proxy nat-output { type nat hook output priority -100 ; }
   518  		add rule ip kube-proxy nat-output jump services
   519  		add chain ip kube-proxy nat-postrouting { type nat hook postrouting priority 100 ; }
   520  		add rule ip kube-proxy nat-postrouting jump masquerading
   521  		add chain ip kube-proxy nat-prerouting { type nat hook prerouting priority -100 ; }
   522  		add rule ip kube-proxy nat-prerouting jump services
   523  
   524  		add set ip kube-proxy firewall { type ipv4_addr . inet_proto . inet_service ; comment "destinations that are subject to LoadBalancerSourceRanges" ; }
   525  		add set ip kube-proxy firewall-allow { type ipv4_addr . inet_proto . inet_service . ipv4_addr ; flags interval ; comment "destinations+sources that are allowed by LoadBalancerSourceRanges" ; }
   526  		add chain ip kube-proxy firewall-check
   527  		add chain ip kube-proxy firewall-allow-check
   528  		add rule ip kube-proxy firewall-allow-check ip daddr . meta l4proto . th dport . ip saddr @firewall-allow return
   529  		add rule ip kube-proxy firewall-allow-check drop
   530  		add rule ip kube-proxy firewall-check ip daddr . meta l4proto . th dport @firewall jump firewall-allow-check
   531  
   532  		add chain ip kube-proxy reject-chain { comment "helper for @no-endpoint-services / @no-endpoint-nodeports" ; }
   533  		add rule ip kube-proxy reject-chain reject
   534  
   535  		add map ip kube-proxy no-endpoint-services { type ipv4_addr . inet_proto . inet_service : verdict ; comment "vmap to drop or reject packets to services with no endpoints" ; }
   536  		add map ip kube-proxy no-endpoint-nodeports { type inet_proto . inet_service : verdict ; comment "vmap to drop or reject packets to service nodeports with no endpoints" ; }
   537  
   538  		add chain ip kube-proxy endpoints-check
   539  		add rule ip kube-proxy endpoints-check ip daddr . meta l4proto . th dport vmap @no-endpoint-services
   540  		add rule ip kube-proxy endpoints-check fib daddr type local ip daddr != 127.0.0.0/8 meta l4proto . th dport vmap @no-endpoint-nodeports
   541  
   542  		add map ip kube-proxy service-ips { type ipv4_addr . inet_proto . inet_service : verdict ; comment "ClusterIP, ExternalIP and LoadBalancer IP traffic" ; }
   543  		add map ip kube-proxy service-nodeports { type inet_proto . inet_service : verdict ; comment "NodePort traffic" ; }
   544  		add rule ip kube-proxy services ip daddr . meta l4proto . th dport vmap @service-ips
   545  		add rule ip kube-proxy services fib daddr type local ip daddr != 127.0.0.0/8 meta l4proto . th dport vmap @service-nodeports
   546  
   547  		# svc1
   548  		add chain ip kube-proxy service-ULMVA6XW-ns1/svc1/tcp/p80
   549  		add rule ip kube-proxy service-ULMVA6XW-ns1/svc1/tcp/p80 ip daddr 172.30.0.41 tcp dport 80 ip saddr != 10.0.0.0/8 jump mark-for-masquerade
   550  		add rule ip kube-proxy service-ULMVA6XW-ns1/svc1/tcp/p80 numgen random mod 1 vmap { 0 : goto endpoint-5OJB2KTY-ns1/svc1/tcp/p80__10.180.0.1/80 }
   551  
   552  		add chain ip kube-proxy endpoint-5OJB2KTY-ns1/svc1/tcp/p80__10.180.0.1/80
   553  		add rule ip kube-proxy endpoint-5OJB2KTY-ns1/svc1/tcp/p80__10.180.0.1/80 ip saddr 10.180.0.1 jump mark-for-masquerade
   554  		add rule ip kube-proxy endpoint-5OJB2KTY-ns1/svc1/tcp/p80__10.180.0.1/80 meta l4proto tcp dnat to 10.180.0.1:80
   555  
   556  		add element ip kube-proxy service-ips { 172.30.0.41 . tcp . 80 : goto service-ULMVA6XW-ns1/svc1/tcp/p80 }
   557  
   558  		# svc2
   559  		add chain ip kube-proxy service-42NFTM6N-ns2/svc2/tcp/p80
   560  		add rule ip kube-proxy service-42NFTM6N-ns2/svc2/tcp/p80 ip daddr 172.30.0.42 tcp dport 80 ip saddr != 10.0.0.0/8 jump mark-for-masquerade
   561  		add rule ip kube-proxy service-42NFTM6N-ns2/svc2/tcp/p80 numgen random mod 1 vmap { 0 : goto endpoint-SGOXE6O3-ns2/svc2/tcp/p80__10.180.0.2/80 }
   562  		add chain ip kube-proxy external-42NFTM6N-ns2/svc2/tcp/p80
   563  		add rule ip kube-proxy external-42NFTM6N-ns2/svc2/tcp/p80 ip saddr 10.0.0.0/8 goto service-42NFTM6N-ns2/svc2/tcp/p80 comment "short-circuit pod traffic"
   564  		add rule ip kube-proxy external-42NFTM6N-ns2/svc2/tcp/p80 fib saddr type local jump mark-for-masquerade comment "masquerade local traffic"
   565  		add rule ip kube-proxy external-42NFTM6N-ns2/svc2/tcp/p80 fib saddr type local goto service-42NFTM6N-ns2/svc2/tcp/p80 comment "short-circuit local traffic"
   566  		add chain ip kube-proxy endpoint-SGOXE6O3-ns2/svc2/tcp/p80__10.180.0.2/80
   567  		add rule ip kube-proxy endpoint-SGOXE6O3-ns2/svc2/tcp/p80__10.180.0.2/80 ip saddr 10.180.0.2 jump mark-for-masquerade
   568  		add rule ip kube-proxy endpoint-SGOXE6O3-ns2/svc2/tcp/p80__10.180.0.2/80 meta l4proto tcp dnat to 10.180.0.2:80
   569  
   570  		add element ip kube-proxy service-ips { 172.30.0.42 . tcp . 80 : goto service-42NFTM6N-ns2/svc2/tcp/p80 }
   571  		add element ip kube-proxy service-ips { 192.168.99.22 . tcp . 80 : goto external-42NFTM6N-ns2/svc2/tcp/p80 }
   572  		add element ip kube-proxy service-ips { 1.2.3.4 . tcp . 80 : goto external-42NFTM6N-ns2/svc2/tcp/p80 }
   573  		add element ip kube-proxy service-nodeports { tcp . 3001 : goto external-42NFTM6N-ns2/svc2/tcp/p80 }
   574  
   575  		add element ip kube-proxy no-endpoint-nodeports { tcp . 3001 comment "ns2/svc2:p80" : drop }
   576  		add element ip kube-proxy no-endpoint-services { 1.2.3.4 . tcp . 80 comment "ns2/svc2:p80" : drop }
   577  		add element ip kube-proxy no-endpoint-services { 192.168.99.22 . tcp . 80 comment "ns2/svc2:p80" : drop }
   578  
   579  		# svc3
   580  		add chain ip kube-proxy service-4AT6LBPK-ns3/svc3/tcp/p80
   581  		add rule ip kube-proxy service-4AT6LBPK-ns3/svc3/tcp/p80 ip daddr 172.30.0.43 tcp dport 80 ip saddr != 10.0.0.0/8 jump mark-for-masquerade
   582  		add rule ip kube-proxy service-4AT6LBPK-ns3/svc3/tcp/p80 numgen random mod 1 vmap { 0 : goto endpoint-UEIP74TE-ns3/svc3/tcp/p80__10.180.0.3/80 }
   583  		add chain ip kube-proxy external-4AT6LBPK-ns3/svc3/tcp/p80
   584  		add rule ip kube-proxy external-4AT6LBPK-ns3/svc3/tcp/p80 jump mark-for-masquerade
   585  		add rule ip kube-proxy external-4AT6LBPK-ns3/svc3/tcp/p80 goto service-4AT6LBPK-ns3/svc3/tcp/p80
   586  		add chain ip kube-proxy endpoint-UEIP74TE-ns3/svc3/tcp/p80__10.180.0.3/80
   587  		add rule ip kube-proxy endpoint-UEIP74TE-ns3/svc3/tcp/p80__10.180.0.3/80 ip saddr 10.180.0.3 jump mark-for-masquerade
   588  		add rule ip kube-proxy endpoint-UEIP74TE-ns3/svc3/tcp/p80__10.180.0.3/80 meta l4proto tcp dnat to 10.180.0.3:80
   589  
   590  		add element ip kube-proxy service-ips { 172.30.0.43 . tcp . 80 : goto service-4AT6LBPK-ns3/svc3/tcp/p80 }
   591  		add element ip kube-proxy service-nodeports { tcp . 3003 : goto external-4AT6LBPK-ns3/svc3/tcp/p80 }
   592  
   593  		# svc4
   594  		add chain ip kube-proxy service-LAUZTJTB-ns4/svc4/tcp/p80
   595  		add rule ip kube-proxy service-LAUZTJTB-ns4/svc4/tcp/p80 ip daddr 172.30.0.44 tcp dport 80 ip saddr != 10.0.0.0/8 jump mark-for-masquerade
   596  		add rule ip kube-proxy service-LAUZTJTB-ns4/svc4/tcp/p80 numgen random mod 2 vmap { 0 : goto endpoint-UNZV3OEC-ns4/svc4/tcp/p80__10.180.0.4/80 , 1 : goto endpoint-5RFCDDV7-ns4/svc4/tcp/p80__10.180.0.5/80 }
   597  		add chain ip kube-proxy external-LAUZTJTB-ns4/svc4/tcp/p80
   598  		add rule ip kube-proxy external-LAUZTJTB-ns4/svc4/tcp/p80 jump mark-for-masquerade
   599  		add rule ip kube-proxy external-LAUZTJTB-ns4/svc4/tcp/p80 goto service-LAUZTJTB-ns4/svc4/tcp/p80
   600  		add chain ip kube-proxy endpoint-5RFCDDV7-ns4/svc4/tcp/p80__10.180.0.5/80
   601  		add rule ip kube-proxy endpoint-5RFCDDV7-ns4/svc4/tcp/p80__10.180.0.5/80 ip saddr 10.180.0.5 jump mark-for-masquerade
   602  		add rule ip kube-proxy endpoint-5RFCDDV7-ns4/svc4/tcp/p80__10.180.0.5/80 meta l4proto tcp dnat to 10.180.0.5:80
   603  		add chain ip kube-proxy endpoint-UNZV3OEC-ns4/svc4/tcp/p80__10.180.0.4/80
   604  		add rule ip kube-proxy endpoint-UNZV3OEC-ns4/svc4/tcp/p80__10.180.0.4/80 ip saddr 10.180.0.4 jump mark-for-masquerade
   605  		add rule ip kube-proxy endpoint-UNZV3OEC-ns4/svc4/tcp/p80__10.180.0.4/80 meta l4proto tcp dnat to 10.180.0.4:80
   606  
   607  		add element ip kube-proxy service-ips { 172.30.0.44 . tcp . 80 : goto service-LAUZTJTB-ns4/svc4/tcp/p80 }
   608  		add element ip kube-proxy service-ips { 192.168.99.33 . tcp . 80 : goto external-LAUZTJTB-ns4/svc4/tcp/p80 }
   609  
   610  		# svc5
   611  		add set ip kube-proxy affinity-GTK6MW7G-ns5/svc5/tcp/p80__10.180.0.3/80 { type ipv4_addr ; flags dynamic,timeout ; timeout 10800s ; }
   612  		add chain ip kube-proxy service-HVFWP5L3-ns5/svc5/tcp/p80
   613  		add rule ip kube-proxy service-HVFWP5L3-ns5/svc5/tcp/p80 ip daddr 172.30.0.45 tcp dport 80 ip saddr != 10.0.0.0/8 jump mark-for-masquerade
   614  		add rule ip kube-proxy service-HVFWP5L3-ns5/svc5/tcp/p80 ip saddr @affinity-GTK6MW7G-ns5/svc5/tcp/p80__10.180.0.3/80 goto endpoint-GTK6MW7G-ns5/svc5/tcp/p80__10.180.0.3/80
   615  		add rule ip kube-proxy service-HVFWP5L3-ns5/svc5/tcp/p80 numgen random mod 1 vmap { 0 : goto endpoint-GTK6MW7G-ns5/svc5/tcp/p80__10.180.0.3/80 }
   616  		add chain ip kube-proxy external-HVFWP5L3-ns5/svc5/tcp/p80
   617  		add rule ip kube-proxy external-HVFWP5L3-ns5/svc5/tcp/p80 jump mark-for-masquerade
   618  		add rule ip kube-proxy external-HVFWP5L3-ns5/svc5/tcp/p80 goto service-HVFWP5L3-ns5/svc5/tcp/p80
   619  
   620  		add chain ip kube-proxy endpoint-GTK6MW7G-ns5/svc5/tcp/p80__10.180.0.3/80
   621  		add rule ip kube-proxy endpoint-GTK6MW7G-ns5/svc5/tcp/p80__10.180.0.3/80 ip saddr 10.180.0.3 jump mark-for-masquerade
   622  		add rule ip kube-proxy endpoint-GTK6MW7G-ns5/svc5/tcp/p80__10.180.0.3/80 update @affinity-GTK6MW7G-ns5/svc5/tcp/p80__10.180.0.3/80 { ip saddr }
   623  		add rule ip kube-proxy endpoint-GTK6MW7G-ns5/svc5/tcp/p80__10.180.0.3/80 meta l4proto tcp dnat to 10.180.0.3:80
   624  
   625  		add element ip kube-proxy service-ips { 172.30.0.45 . tcp . 80 : goto service-HVFWP5L3-ns5/svc5/tcp/p80 }
   626  		add element ip kube-proxy service-ips { 5.6.7.8 . tcp . 80 : goto external-HVFWP5L3-ns5/svc5/tcp/p80 }
   627  		add element ip kube-proxy service-nodeports { tcp . 3002 : goto external-HVFWP5L3-ns5/svc5/tcp/p80 }
   628  		add element ip kube-proxy firewall { 5.6.7.8 . tcp . 80 comment "ns5/svc5:p80" }
   629  		add element ip kube-proxy firewall-allow { 5.6.7.8 . tcp . 80 . 203.0.113.0/25 comment "ns5/svc5:p80" }
   630  
   631  		# svc6
   632  		add element ip kube-proxy no-endpoint-services { 172.30.0.46 . tcp . 80 comment "ns6/svc6:p80" : goto reject-chain }
   633  		`)
   634  
   635  	assertNFTablesTransactionEqual(t, getLine(), expected, nft.Dump())
   636  }
   637  
   638  // TestNoEndpointsReject tests that a service with no endpoints rejects connections to
   639  // its ClusterIP, ExternalIPs, NodePort, and LoadBalancer IP.
   640  func TestNoEndpointsReject(t *testing.T) {
   641  	nft, fp := NewFakeProxier(v1.IPv4Protocol)
   642  	svcIP := "172.30.0.41"
   643  	svcPort := 80
   644  	svcNodePort := 3001
   645  	svcExternalIPs := "192.168.99.11"
   646  	svcLBIP := "1.2.3.4"
   647  	svcPortName := proxy.ServicePortName{
   648  		NamespacedName: makeNSN("ns1", "svc1"),
   649  		Port:           "p80",
   650  	}
   651  
   652  	makeServiceMap(fp,
   653  		makeTestService(svcPortName.Namespace, svcPortName.Name, func(svc *v1.Service) {
   654  			svc.Spec.Type = v1.ServiceTypeLoadBalancer
   655  			svc.Spec.ClusterIP = svcIP
   656  			svc.Spec.ExternalIPs = []string{svcExternalIPs}
   657  			svc.Spec.Ports = []v1.ServicePort{{
   658  				Name:     svcPortName.Port,
   659  				Protocol: v1.ProtocolTCP,
   660  				Port:     int32(svcPort),
   661  				NodePort: int32(svcNodePort),
   662  			}}
   663  			svc.Status.LoadBalancer.Ingress = []v1.LoadBalancerIngress{{
   664  				IP: svcLBIP,
   665  			}}
   666  		}),
   667  	)
   668  	fp.syncProxyRules()
   669  
   670  	runPacketFlowTests(t, getLine(), nft, testNodeIPs, []packetFlowTest{
   671  		{
   672  			name:     "pod to cluster IP with no endpoints",
   673  			sourceIP: "10.0.0.2",
   674  			destIP:   svcIP,
   675  			destPort: svcPort,
   676  			output:   "REJECT",
   677  		},
   678  		{
   679  			name:     "external to external IP with no endpoints",
   680  			sourceIP: testExternalClient,
   681  			destIP:   svcExternalIPs,
   682  			destPort: svcPort,
   683  			output:   "REJECT",
   684  		},
   685  		{
   686  			name:     "pod to NodePort with no endpoints",
   687  			sourceIP: "10.0.0.2",
   688  			destIP:   testNodeIP,
   689  			destPort: svcNodePort,
   690  			output:   "REJECT",
   691  		},
   692  		{
   693  			name:     "external to NodePort with no endpoints",
   694  			sourceIP: testExternalClient,
   695  			destIP:   testNodeIP,
   696  			destPort: svcNodePort,
   697  			output:   "REJECT",
   698  		},
   699  		{
   700  			name:     "pod to LoadBalancer IP with no endpoints",
   701  			sourceIP: "10.0.0.2",
   702  			destIP:   svcLBIP,
   703  			destPort: svcPort,
   704  			output:   "REJECT",
   705  		},
   706  		{
   707  			name:     "external to LoadBalancer IP with no endpoints",
   708  			sourceIP: testExternalClient,
   709  			destIP:   svcLBIP,
   710  			destPort: svcPort,
   711  			output:   "REJECT",
   712  		},
   713  	})
   714  }
   715  
   716  // TestClusterIPGeneral tests various basic features of a ClusterIP service
   717  func TestClusterIPGeneral(t *testing.T) {
   718  	nft, fp := NewFakeProxier(v1.IPv4Protocol)
   719  
   720  	makeServiceMap(fp,
   721  		makeTestService("ns1", "svc1", func(svc *v1.Service) {
   722  			svc.Spec.ClusterIP = "172.30.0.41"
   723  			svc.Spec.Ports = []v1.ServicePort{{
   724  				Name:     "http",
   725  				Port:     80,
   726  				Protocol: v1.ProtocolTCP,
   727  			}}
   728  		}),
   729  		makeTestService("ns2", "svc2", func(svc *v1.Service) {
   730  			svc.Spec.ClusterIP = "172.30.0.42"
   731  			svc.Spec.Ports = []v1.ServicePort{
   732  				{
   733  					Name:     "http",
   734  					Port:     80,
   735  					Protocol: v1.ProtocolTCP,
   736  				},
   737  				{
   738  					Name:       "https",
   739  					Port:       443,
   740  					Protocol:   v1.ProtocolTCP,
   741  					TargetPort: intstr.FromInt32(8443),
   742  				},
   743  				{
   744  					// Of course this should really be UDP, but if we
   745  					// create a service with UDP ports, the Proxier will
   746  					// try to do conntrack cleanup and we'd have to set
   747  					// the FakeExec up to be able to deal with that...
   748  					Name:     "dns-sctp",
   749  					Port:     53,
   750  					Protocol: v1.ProtocolSCTP,
   751  				},
   752  				{
   753  					Name:     "dns-tcp",
   754  					Port:     53,
   755  					Protocol: v1.ProtocolTCP,
   756  					// We use TargetPort on TCP but not SCTP to help
   757  					// disambiguate the output.
   758  					TargetPort: intstr.FromInt32(5353),
   759  				},
   760  			}
   761  		}),
   762  	)
   763  
   764  	populateEndpointSlices(fp,
   765  		makeTestEndpointSlice("ns1", "svc1", 1, func(eps *discovery.EndpointSlice) {
   766  			eps.AddressType = discovery.AddressTypeIPv4
   767  			eps.Endpoints = []discovery.Endpoint{{
   768  				Addresses: []string{"10.180.0.1"},
   769  				NodeName:  ptr.To(testHostname),
   770  			}}
   771  			eps.Ports = []discovery.EndpointPort{{
   772  				Name:     ptr.To("http"),
   773  				Port:     ptr.To[int32](80),
   774  				Protocol: ptr.To(v1.ProtocolTCP),
   775  			}}
   776  		}),
   777  		makeTestEndpointSlice("ns2", "svc2", 1, func(eps *discovery.EndpointSlice) {
   778  			eps.AddressType = discovery.AddressTypeIPv4
   779  			eps.Endpoints = []discovery.Endpoint{
   780  				{
   781  					Addresses: []string{"10.180.0.1"},
   782  					NodeName:  ptr.To(testHostname),
   783  				},
   784  				{
   785  					Addresses: []string{"10.180.2.1"},
   786  					NodeName:  ptr.To("host2"),
   787  				},
   788  			}
   789  			eps.Ports = []discovery.EndpointPort{
   790  				{
   791  					Name:     ptr.To("http"),
   792  					Port:     ptr.To[int32](80),
   793  					Protocol: ptr.To(v1.ProtocolTCP),
   794  				},
   795  				{
   796  					Name:     ptr.To("https"),
   797  					Port:     ptr.To[int32](8443),
   798  					Protocol: ptr.To(v1.ProtocolTCP),
   799  				},
   800  				{
   801  					Name:     ptr.To("dns-sctp"),
   802  					Port:     ptr.To[int32](53),
   803  					Protocol: ptr.To(v1.ProtocolSCTP),
   804  				},
   805  				{
   806  					Name:     ptr.To("dns-tcp"),
   807  					Port:     ptr.To[int32](5353),
   808  					Protocol: ptr.To(v1.ProtocolTCP),
   809  				},
   810  			}
   811  		}),
   812  	)
   813  
   814  	fp.syncProxyRules()
   815  
   816  	runPacketFlowTests(t, getLine(), nft, testNodeIPs, []packetFlowTest{
   817  		{
   818  			name:     "simple clusterIP",
   819  			sourceIP: "10.180.0.2",
   820  			destIP:   "172.30.0.41",
   821  			destPort: 80,
   822  			output:   "10.180.0.1:80",
   823  			masq:     false,
   824  		},
   825  		{
   826  			name:     "hairpin to cluster IP",
   827  			sourceIP: "10.180.0.1",
   828  			destIP:   "172.30.0.41",
   829  			destPort: 80,
   830  			output:   "10.180.0.1:80",
   831  			masq:     true,
   832  		},
   833  		{
   834  			name:     "clusterIP with multiple endpoints",
   835  			sourceIP: "10.180.0.2",
   836  			destIP:   "172.30.0.42",
   837  			destPort: 80,
   838  			output:   "10.180.0.1:80, 10.180.2.1:80",
   839  			masq:     false,
   840  		},
   841  		{
   842  			name:     "clusterIP with TargetPort",
   843  			sourceIP: "10.180.0.2",
   844  			destIP:   "172.30.0.42",
   845  			destPort: 443,
   846  			output:   "10.180.0.1:8443, 10.180.2.1:8443",
   847  			masq:     false,
   848  		},
   849  		{
   850  			name:     "clusterIP with TCP and SCTP on same port (TCP)",
   851  			sourceIP: "10.180.0.2",
   852  			protocol: v1.ProtocolTCP,
   853  			destIP:   "172.30.0.42",
   854  			destPort: 53,
   855  			output:   "10.180.0.1:5353, 10.180.2.1:5353",
   856  			masq:     false,
   857  		},
   858  		{
   859  			name:     "clusterIP with TCP and SCTP on same port (SCTP)",
   860  			sourceIP: "10.180.0.2",
   861  			protocol: v1.ProtocolSCTP,
   862  			destIP:   "172.30.0.42",
   863  			destPort: 53,
   864  			output:   "10.180.0.1:53, 10.180.2.1:53",
   865  			masq:     false,
   866  		},
   867  		{
   868  			name:     "TCP-only port does not match UDP traffic",
   869  			sourceIP: "10.180.0.2",
   870  			protocol: v1.ProtocolUDP,
   871  			destIP:   "172.30.0.42",
   872  			destPort: 80,
   873  			output:   "",
   874  		},
   875  		{
   876  			name:     "svc1 does not accept svc2's ports",
   877  			sourceIP: "10.180.0.2",
   878  			destIP:   "172.30.0.41",
   879  			destPort: 443,
   880  			output:   "",
   881  		},
   882  	})
   883  }
   884  
   885  func TestLoadBalancer(t *testing.T) {
   886  	nft, fp := NewFakeProxier(v1.IPv4Protocol)
   887  	svcIP := "172.30.0.41"
   888  	svcPort := 80
   889  	svcNodePort := 3001
   890  	svcLBIP1 := "1.2.3.4"
   891  	svcLBIP2 := "5.6.7.8"
   892  	svcPortName := proxy.ServicePortName{
   893  		NamespacedName: makeNSN("ns1", "svc1"),
   894  		Port:           "p80",
   895  		Protocol:       v1.ProtocolTCP,
   896  	}
   897  
   898  	makeServiceMap(fp,
   899  		makeTestService(svcPortName.Namespace, svcPortName.Name, func(svc *v1.Service) {
   900  			svc.Spec.Type = "LoadBalancer"
   901  			svc.Spec.ClusterIP = svcIP
   902  			svc.Spec.Ports = []v1.ServicePort{{
   903  				Name:     svcPortName.Port,
   904  				Port:     int32(svcPort),
   905  				Protocol: v1.ProtocolTCP,
   906  				NodePort: int32(svcNodePort),
   907  			}}
   908  			svc.Status.LoadBalancer.Ingress = []v1.LoadBalancerIngress{
   909  				{IP: svcLBIP1},
   910  				{IP: svcLBIP2},
   911  			}
   912  			svc.Spec.LoadBalancerSourceRanges = []string{
   913  				"192.168.0.0/24",
   914  
   915  				// Regression test that excess whitespace gets ignored
   916  				" 203.0.113.0/25",
   917  			}
   918  		}),
   919  	)
   920  
   921  	epIP := "10.180.0.1"
   922  	populateEndpointSlices(fp,
   923  		makeTestEndpointSlice(svcPortName.Namespace, svcPortName.Name, 1, func(eps *discovery.EndpointSlice) {
   924  			eps.AddressType = discovery.AddressTypeIPv4
   925  			eps.Endpoints = []discovery.Endpoint{{
   926  				Addresses: []string{epIP},
   927  			}}
   928  			eps.Ports = []discovery.EndpointPort{{
   929  				Name:     ptr.To(svcPortName.Port),
   930  				Port:     ptr.To(int32(svcPort)),
   931  				Protocol: ptr.To(v1.ProtocolTCP),
   932  			}}
   933  		}),
   934  	)
   935  
   936  	fp.syncProxyRules()
   937  
   938  	runPacketFlowTests(t, getLine(), nft, testNodeIPs, []packetFlowTest{
   939  		{
   940  			name:     "pod to cluster IP",
   941  			sourceIP: "10.0.0.2",
   942  			destIP:   svcIP,
   943  			destPort: svcPort,
   944  			output:   fmt.Sprintf("%s:%d", epIP, svcPort),
   945  			masq:     false,
   946  		},
   947  		{
   948  			name:     "external to nodePort",
   949  			sourceIP: testExternalClient,
   950  			destIP:   testNodeIP,
   951  			destPort: svcNodePort,
   952  			output:   fmt.Sprintf("%s:%d", epIP, svcPort),
   953  			masq:     true,
   954  		},
   955  		{
   956  			name:     "nodePort bypasses LoadBalancerSourceRanges",
   957  			sourceIP: testExternalClientBlocked,
   958  			destIP:   testNodeIP,
   959  			destPort: svcNodePort,
   960  			output:   fmt.Sprintf("%s:%d", epIP, svcPort),
   961  			masq:     true,
   962  		},
   963  		{
   964  			name:     "accepted external to LB1",
   965  			sourceIP: testExternalClient,
   966  			destIP:   svcLBIP1,
   967  			destPort: svcPort,
   968  			output:   fmt.Sprintf("%s:%d", epIP, svcPort),
   969  			masq:     true,
   970  		},
   971  		{
   972  			name:     "accepted external to LB2",
   973  			sourceIP: testExternalClient,
   974  			destIP:   svcLBIP2,
   975  			destPort: svcPort,
   976  			output:   fmt.Sprintf("%s:%d", epIP, svcPort),
   977  			masq:     true,
   978  		},
   979  		{
   980  			name:     "blocked external to LB1",
   981  			sourceIP: testExternalClientBlocked,
   982  			destIP:   svcLBIP1,
   983  			destPort: svcPort,
   984  			output:   "DROP",
   985  		},
   986  		{
   987  			name:     "blocked external to LB2",
   988  			sourceIP: testExternalClientBlocked,
   989  			destIP:   svcLBIP2,
   990  			destPort: svcPort,
   991  			output:   "DROP",
   992  		},
   993  		{
   994  			name:     "pod to LB1 (blocked by LoadBalancerSourceRanges)",
   995  			sourceIP: "10.0.0.2",
   996  			destIP:   svcLBIP1,
   997  			destPort: svcPort,
   998  			output:   "DROP",
   999  		},
  1000  		{
  1001  			name:     "pod to LB2 (blocked by LoadBalancerSourceRanges)",
  1002  			sourceIP: "10.0.0.2",
  1003  			destIP:   svcLBIP2,
  1004  			destPort: svcPort,
  1005  			output:   "DROP",
  1006  		},
  1007  		{
  1008  			name:     "node to LB1 (allowed by LoadBalancerSourceRanges)",
  1009  			sourceIP: testNodeIP,
  1010  			destIP:   svcLBIP1,
  1011  			destPort: svcPort,
  1012  			output:   fmt.Sprintf("%s:%d", epIP, svcPort),
  1013  			masq:     true,
  1014  		},
  1015  		{
  1016  			name:     "node to LB2 (allowed by LoadBalancerSourceRanges)",
  1017  			sourceIP: testNodeIP,
  1018  			destIP:   svcLBIP2,
  1019  			destPort: svcPort,
  1020  			output:   fmt.Sprintf("%s:%d", epIP, svcPort),
  1021  			masq:     true,
  1022  		},
  1023  
  1024  		// The LB rules assume that when you connect from a node to a LB IP, that
  1025  		// something external to kube-proxy will cause the connection to be
  1026  		// SNATted to the LB IP, so if the LoadBalancerSourceRanges include the
  1027  		// node IP, then we add a rule allowing traffic from the LB IP as well...
  1028  		{
  1029  			name:     "same node to LB1, SNATted to LB1 (implicitly allowed)",
  1030  			sourceIP: svcLBIP1,
  1031  			destIP:   svcLBIP1,
  1032  			destPort: svcPort,
  1033  			output:   fmt.Sprintf("%s:%d", epIP, svcPort),
  1034  			masq:     true,
  1035  		},
  1036  		{
  1037  			name:     "same node to LB2, SNATted to LB2 (implicitly allowed)",
  1038  			sourceIP: svcLBIP2,
  1039  			destIP:   svcLBIP2,
  1040  			destPort: svcPort,
  1041  			output:   fmt.Sprintf("%s:%d", epIP, svcPort),
  1042  			masq:     true,
  1043  		},
  1044  	})
  1045  }
  1046  
  1047  // TestNodePorts tests NodePort services under various combinations of the
  1048  // --nodeport-addresses and --localhost-nodeports flags.
  1049  func TestNodePorts(t *testing.T) {
  1050  	testCases := []struct {
  1051  		name string
  1052  
  1053  		family            v1.IPFamily
  1054  		nodePortAddresses []string
  1055  
  1056  		// allowAltNodeIP is true if we expect NodePort traffic on the alternate
  1057  		// node IP to be accepted
  1058  		allowAltNodeIP bool
  1059  
  1060  		// expectFirewall is true if we expect firewall to be filled in with
  1061  		// an anti-martian-packet rule
  1062  		expectFirewall bool
  1063  	}{
  1064  		{
  1065  			name: "ipv4",
  1066  
  1067  			family:            v1.IPv4Protocol,
  1068  			nodePortAddresses: nil,
  1069  
  1070  			allowAltNodeIP: true,
  1071  			expectFirewall: true,
  1072  		},
  1073  		{
  1074  			name: "ipv4, multiple nodeport-addresses",
  1075  
  1076  			family:            v1.IPv4Protocol,
  1077  			nodePortAddresses: []string{"192.168.0.0/24", "192.168.1.0/24", "2001:db8::/64"},
  1078  
  1079  			allowAltNodeIP: true,
  1080  			expectFirewall: false,
  1081  		},
  1082  		{
  1083  			name: "ipv6",
  1084  
  1085  			family:            v1.IPv6Protocol,
  1086  			nodePortAddresses: nil,
  1087  
  1088  			allowAltNodeIP: true,
  1089  			expectFirewall: false,
  1090  		},
  1091  		{
  1092  			name: "ipv6, multiple nodeport-addresses",
  1093  
  1094  			family:            v1.IPv6Protocol,
  1095  			nodePortAddresses: []string{"192.168.0.0/24", "192.168.1.0/24", "2001:db8::/64"},
  1096  
  1097  			allowAltNodeIP: false,
  1098  			expectFirewall: false,
  1099  		},
  1100  	}
  1101  
  1102  	for _, tc := range testCases {
  1103  		t.Run(tc.name, func(t *testing.T) {
  1104  			nft, fp := NewFakeProxier(tc.family)
  1105  
  1106  			var svcIP, epIP1, epIP2 string
  1107  			if tc.family == v1.IPv4Protocol {
  1108  				svcIP = "172.30.0.41"
  1109  				epIP1 = "10.180.0.1"
  1110  				epIP2 = "10.180.2.1"
  1111  			} else {
  1112  				svcIP = "fd00:172:30::41"
  1113  				epIP1 = "fd00:10:180::1"
  1114  				epIP2 = "fd00:10:180::2:1"
  1115  			}
  1116  			if tc.nodePortAddresses != nil {
  1117  				fp.nodePortAddresses = proxyutil.NewNodePortAddresses(tc.family, tc.nodePortAddresses)
  1118  			}
  1119  
  1120  			makeServiceMap(fp,
  1121  				makeTestService("ns1", "svc1", func(svc *v1.Service) {
  1122  					svc.Spec.Type = v1.ServiceTypeNodePort
  1123  					svc.Spec.ClusterIP = svcIP
  1124  					svc.Spec.Ports = []v1.ServicePort{{
  1125  						Name:     "p80",
  1126  						Port:     80,
  1127  						Protocol: v1.ProtocolTCP,
  1128  						NodePort: 3001,
  1129  					}}
  1130  				}),
  1131  			)
  1132  
  1133  			populateEndpointSlices(fp,
  1134  				makeTestEndpointSlice("ns1", "svc1", 1, func(eps *discovery.EndpointSlice) {
  1135  					if tc.family == v1.IPv4Protocol {
  1136  						eps.AddressType = discovery.AddressTypeIPv4
  1137  					} else {
  1138  						eps.AddressType = discovery.AddressTypeIPv6
  1139  					}
  1140  					eps.Endpoints = []discovery.Endpoint{{
  1141  						Addresses: []string{epIP1},
  1142  						NodeName:  nil,
  1143  					}, {
  1144  						Addresses: []string{epIP2},
  1145  						NodeName:  ptr.To(testHostname),
  1146  					}}
  1147  					eps.Ports = []discovery.EndpointPort{{
  1148  						Name:     ptr.To("p80"),
  1149  						Port:     ptr.To[int32](80),
  1150  						Protocol: ptr.To(v1.ProtocolTCP),
  1151  					}}
  1152  				}),
  1153  			)
  1154  
  1155  			fp.syncProxyRules()
  1156  
  1157  			var podIP, externalClientIP, nodeIP, altNodeIP string
  1158  			if tc.family == v1.IPv4Protocol {
  1159  				podIP = "10.0.0.2"
  1160  				externalClientIP = testExternalClient
  1161  				nodeIP = testNodeIP
  1162  				altNodeIP = testNodeIPAlt
  1163  			} else {
  1164  				podIP = "fd00:10::2"
  1165  				externalClientIP = "2600:5200::1"
  1166  				nodeIP = testNodeIPv6
  1167  				altNodeIP = testNodeIPv6Alt
  1168  			}
  1169  			output := net.JoinHostPort(epIP1, "80") + ", " + net.JoinHostPort(epIP2, "80")
  1170  
  1171  			// Basic tests are the same for all cases
  1172  			runPacketFlowTests(t, getLine(), nft, testNodeIPs, []packetFlowTest{
  1173  				{
  1174  					name:     "pod to cluster IP",
  1175  					sourceIP: podIP,
  1176  					destIP:   svcIP,
  1177  					destPort: 80,
  1178  					output:   output,
  1179  					masq:     false,
  1180  				},
  1181  				{
  1182  					name:     "external to nodePort",
  1183  					sourceIP: externalClientIP,
  1184  					destIP:   nodeIP,
  1185  					destPort: 3001,
  1186  					output:   output,
  1187  					masq:     true,
  1188  				},
  1189  				{
  1190  					name:     "node to nodePort",
  1191  					sourceIP: nodeIP,
  1192  					destIP:   nodeIP,
  1193  					destPort: 3001,
  1194  					output:   output,
  1195  					masq:     true,
  1196  				},
  1197  			})
  1198  
  1199  			// NodePort on altNodeIP should be allowed, unless
  1200  			// nodePortAddressess excludes altNodeIP
  1201  			if tc.allowAltNodeIP {
  1202  				runPacketFlowTests(t, getLine(), nft, testNodeIPs, []packetFlowTest{
  1203  					{
  1204  						name:     "external to nodePort on secondary IP",
  1205  						sourceIP: externalClientIP,
  1206  						destIP:   altNodeIP,
  1207  						destPort: 3001,
  1208  						output:   output,
  1209  						masq:     true,
  1210  					},
  1211  				})
  1212  			} else {
  1213  				runPacketFlowTests(t, getLine(), nft, testNodeIPs, []packetFlowTest{
  1214  					{
  1215  						name:     "secondary nodeIP ignores NodePorts",
  1216  						sourceIP: externalClientIP,
  1217  						destIP:   altNodeIP,
  1218  						destPort: 3001,
  1219  						output:   "",
  1220  					},
  1221  				})
  1222  			}
  1223  		})
  1224  	}
  1225  }
  1226  
  1227  func TestDropInvalidRule(t *testing.T) {
  1228  	for _, tcpLiberal := range []bool{false, true} {
  1229  		t.Run(fmt.Sprintf("tcpLiberal %t", tcpLiberal), func(t *testing.T) {
  1230  			nft, fp := NewFakeProxier(v1.IPv4Protocol)
  1231  			fp.conntrackTCPLiberal = tcpLiberal
  1232  			fp.syncProxyRules()
  1233  
  1234  			var expected string
  1235  			if !tcpLiberal {
  1236  				expected = "ct state invalid drop"
  1237  			}
  1238  
  1239  			assertNFTablesChainEqual(t, getLine(), nft, kubeForwardChain, expected)
  1240  		})
  1241  	}
  1242  }
  1243  
  1244  // TestExternalTrafficPolicyLocal tests that traffic to externally-facing IPs does not get
  1245  // masqueraded when using Local traffic policy. For traffic from external sources, that
  1246  // means it can also only be routed to local endpoints, but for traffic from internal
  1247  // sources, it gets routed to all endpoints.
  1248  func TestExternalTrafficPolicyLocal(t *testing.T) {
  1249  	nft, fp := NewFakeProxier(v1.IPv4Protocol)
  1250  
  1251  	svcIP := "172.30.0.41"
  1252  	svcPort := 80
  1253  	svcNodePort := 3001
  1254  	svcHealthCheckNodePort := 30000
  1255  	svcExternalIPs := "192.168.99.11"
  1256  	svcLBIP := "1.2.3.4"
  1257  	svcPortName := proxy.ServicePortName{
  1258  		NamespacedName: makeNSN("ns1", "svc1"),
  1259  		Port:           "p80",
  1260  	}
  1261  
  1262  	makeServiceMap(fp,
  1263  		makeTestService(svcPortName.Namespace, svcPortName.Name, func(svc *v1.Service) {
  1264  			svc.Spec.Type = v1.ServiceTypeLoadBalancer
  1265  			svc.Spec.ExternalTrafficPolicy = v1.ServiceExternalTrafficPolicyLocal
  1266  			svc.Spec.ClusterIP = svcIP
  1267  			svc.Spec.ExternalIPs = []string{svcExternalIPs}
  1268  			svc.Spec.Ports = []v1.ServicePort{{
  1269  				Name:       svcPortName.Port,
  1270  				Port:       int32(svcPort),
  1271  				Protocol:   v1.ProtocolTCP,
  1272  				NodePort:   int32(svcNodePort),
  1273  				TargetPort: intstr.FromInt32(int32(svcPort)),
  1274  			}}
  1275  			svc.Spec.HealthCheckNodePort = int32(svcHealthCheckNodePort)
  1276  			svc.Status.LoadBalancer.Ingress = []v1.LoadBalancerIngress{{
  1277  				IP: svcLBIP,
  1278  			}}
  1279  		}),
  1280  	)
  1281  
  1282  	epIP1 := "10.180.0.1"
  1283  	epIP2 := "10.180.2.1"
  1284  	populateEndpointSlices(fp,
  1285  		makeTestEndpointSlice(svcPortName.Namespace, svcPortName.Name, 1, func(eps *discovery.EndpointSlice) {
  1286  			eps.AddressType = discovery.AddressTypeIPv4
  1287  			eps.Endpoints = []discovery.Endpoint{{
  1288  				Addresses: []string{epIP1},
  1289  			}, {
  1290  				Addresses: []string{epIP2},
  1291  				NodeName:  ptr.To(testHostname),
  1292  			}}
  1293  			eps.Ports = []discovery.EndpointPort{{
  1294  				Name:     ptr.To(svcPortName.Port),
  1295  				Port:     ptr.To(int32(svcPort)),
  1296  				Protocol: ptr.To(v1.ProtocolTCP),
  1297  			}}
  1298  		}),
  1299  	)
  1300  
  1301  	fp.syncProxyRules()
  1302  
  1303  	runPacketFlowTests(t, getLine(), nft, testNodeIPs, []packetFlowTest{
  1304  		{
  1305  			name:     "pod to cluster IP hits both endpoints, unmasqueraded",
  1306  			sourceIP: "10.0.0.2",
  1307  			destIP:   svcIP,
  1308  			destPort: svcPort,
  1309  			output:   fmt.Sprintf("%s:%d, %s:%d", epIP1, svcPort, epIP2, svcPort),
  1310  			masq:     false,
  1311  		},
  1312  		{
  1313  			name:     "pod to external IP hits both endpoints, unmasqueraded",
  1314  			sourceIP: "10.0.0.2",
  1315  			destIP:   svcExternalIPs,
  1316  			destPort: svcPort,
  1317  			output:   fmt.Sprintf("%s:%d, %s:%d", epIP1, svcPort, epIP2, svcPort),
  1318  			masq:     false,
  1319  		},
  1320  		{
  1321  			name:     "external to external IP hits only local endpoint, unmasqueraded",
  1322  			sourceIP: testExternalClient,
  1323  			destIP:   svcExternalIPs,
  1324  			destPort: svcPort,
  1325  			output:   fmt.Sprintf("%s:%d", epIP2, svcPort),
  1326  			masq:     false,
  1327  		},
  1328  		{
  1329  			name:     "pod to LB IP hits only both endpoints, unmasqueraded",
  1330  			sourceIP: "10.0.0.2",
  1331  			destIP:   svcLBIP,
  1332  			destPort: svcPort,
  1333  			output:   fmt.Sprintf("%s:%d, %s:%d", epIP1, svcPort, epIP2, svcPort),
  1334  			masq:     false,
  1335  		},
  1336  		{
  1337  			name:     "external to LB IP hits only local endpoint, unmasqueraded",
  1338  			sourceIP: testExternalClient,
  1339  			destIP:   svcLBIP,
  1340  			destPort: svcPort,
  1341  			output:   fmt.Sprintf("%s:%d", epIP2, svcPort),
  1342  			masq:     false,
  1343  		},
  1344  		{
  1345  			name:     "pod to NodePort hits both endpoints, unmasqueraded",
  1346  			sourceIP: "10.0.0.2",
  1347  			destIP:   testNodeIP,
  1348  			destPort: svcNodePort,
  1349  			output:   fmt.Sprintf("%s:%d, %s:%d", epIP1, svcPort, epIP2, svcPort),
  1350  			masq:     false,
  1351  		},
  1352  		{
  1353  			name:     "external to NodePort hits only local endpoint, unmasqueraded",
  1354  			sourceIP: testExternalClient,
  1355  			destIP:   testNodeIP,
  1356  			destPort: svcNodePort,
  1357  			output:   fmt.Sprintf("%s:%d", epIP2, svcPort),
  1358  			masq:     false,
  1359  		},
  1360  	})
  1361  }
  1362  
  1363  // TestExternalTrafficPolicyCluster tests that traffic to an externally-facing IP gets
  1364  // masqueraded when using Cluster traffic policy.
  1365  func TestExternalTrafficPolicyCluster(t *testing.T) {
  1366  	nft, fp := NewFakeProxier(v1.IPv4Protocol)
  1367  
  1368  	svcIP := "172.30.0.41"
  1369  	svcPort := 80
  1370  	svcNodePort := 3001
  1371  	svcExternalIPs := "192.168.99.11"
  1372  	svcLBIP := "1.2.3.4"
  1373  	svcPortName := proxy.ServicePortName{
  1374  		NamespacedName: makeNSN("ns1", "svc1"),
  1375  		Port:           "p80",
  1376  	}
  1377  
  1378  	makeServiceMap(fp,
  1379  		makeTestService(svcPortName.Namespace, svcPortName.Name, func(svc *v1.Service) {
  1380  			svc.Spec.Type = v1.ServiceTypeLoadBalancer
  1381  			svc.Spec.ClusterIP = svcIP
  1382  			svc.Spec.ExternalIPs = []string{svcExternalIPs}
  1383  			svc.Spec.Ports = []v1.ServicePort{{
  1384  				Name:       svcPortName.Port,
  1385  				Port:       int32(svcPort),
  1386  				Protocol:   v1.ProtocolTCP,
  1387  				NodePort:   int32(svcNodePort),
  1388  				TargetPort: intstr.FromInt32(int32(svcPort)),
  1389  			}}
  1390  			svc.Status.LoadBalancer.Ingress = []v1.LoadBalancerIngress{{
  1391  				IP: svcLBIP,
  1392  			}}
  1393  			svc.Spec.ExternalTrafficPolicy = v1.ServiceExternalTrafficPolicyCluster
  1394  		}),
  1395  	)
  1396  
  1397  	epIP1 := "10.180.0.1"
  1398  	epIP2 := "10.180.2.1"
  1399  	populateEndpointSlices(fp,
  1400  		makeTestEndpointSlice(svcPortName.Namespace, svcPortName.Name, 1, func(eps *discovery.EndpointSlice) {
  1401  			eps.AddressType = discovery.AddressTypeIPv4
  1402  			eps.Endpoints = []discovery.Endpoint{{
  1403  				Addresses: []string{epIP1},
  1404  				NodeName:  nil,
  1405  			}, {
  1406  				Addresses: []string{epIP2},
  1407  				NodeName:  ptr.To(testHostname),
  1408  			}}
  1409  			eps.Ports = []discovery.EndpointPort{{
  1410  				Name:     ptr.To(svcPortName.Port),
  1411  				Port:     ptr.To(int32(svcPort)),
  1412  				Protocol: ptr.To(v1.ProtocolTCP),
  1413  			}}
  1414  		}),
  1415  	)
  1416  
  1417  	fp.syncProxyRules()
  1418  
  1419  	runPacketFlowTests(t, getLine(), nft, testNodeIPs, []packetFlowTest{
  1420  		{
  1421  			name:     "pod to cluster IP hits both endpoints, unmasqueraded",
  1422  			sourceIP: "10.0.0.2",
  1423  			destIP:   svcIP,
  1424  			destPort: svcPort,
  1425  			output:   fmt.Sprintf("%s:%d, %s:%d", epIP1, svcPort, epIP2, svcPort),
  1426  			masq:     false,
  1427  		},
  1428  		{
  1429  			name:     "pod to external IP hits both endpoints, masqueraded",
  1430  			sourceIP: "10.0.0.2",
  1431  			destIP:   svcExternalIPs,
  1432  			destPort: svcPort,
  1433  			output:   fmt.Sprintf("%s:%d, %s:%d", epIP1, svcPort, epIP2, svcPort),
  1434  			masq:     true,
  1435  		},
  1436  		{
  1437  			name:     "external to external IP hits both endpoints, masqueraded",
  1438  			sourceIP: testExternalClient,
  1439  			destIP:   svcExternalIPs,
  1440  			destPort: svcPort,
  1441  			output:   fmt.Sprintf("%s:%d, %s:%d", epIP1, svcPort, epIP2, svcPort),
  1442  			masq:     true,
  1443  		},
  1444  		{
  1445  			name:     "pod to LB IP hits both endpoints, masqueraded",
  1446  			sourceIP: "10.0.0.2",
  1447  			destIP:   svcLBIP,
  1448  			destPort: svcPort,
  1449  			output:   fmt.Sprintf("%s:%d, %s:%d", epIP1, svcPort, epIP2, svcPort),
  1450  			masq:     true,
  1451  		},
  1452  		{
  1453  			name:     "external to LB IP hits both endpoints, masqueraded",
  1454  			sourceIP: testExternalClient,
  1455  			destIP:   svcLBIP,
  1456  			destPort: svcPort,
  1457  			output:   fmt.Sprintf("%s:%d, %s:%d", epIP1, svcPort, epIP2, svcPort),
  1458  			masq:     true,
  1459  		},
  1460  		{
  1461  			name:     "pod to NodePort hits both endpoints, masqueraded",
  1462  			sourceIP: "10.0.0.2",
  1463  			destIP:   testNodeIP,
  1464  			destPort: svcNodePort,
  1465  			output:   fmt.Sprintf("%s:%d, %s:%d", epIP1, svcPort, epIP2, svcPort),
  1466  			masq:     true,
  1467  		},
  1468  		{
  1469  			name:     "external to NodePort hits both endpoints, masqueraded",
  1470  			sourceIP: testExternalClient,
  1471  			destIP:   testNodeIP,
  1472  			destPort: svcNodePort,
  1473  			output:   fmt.Sprintf("%s:%d, %s:%d", epIP1, svcPort, epIP2, svcPort),
  1474  			masq:     true,
  1475  		},
  1476  	})
  1477  }
  1478  
  1479  func makeTestService(namespace, name string, svcFunc func(*v1.Service)) *v1.Service {
  1480  	svc := &v1.Service{
  1481  		ObjectMeta: metav1.ObjectMeta{
  1482  			Name:        name,
  1483  			Namespace:   namespace,
  1484  			Annotations: map[string]string{},
  1485  		},
  1486  		Spec:   v1.ServiceSpec{},
  1487  		Status: v1.ServiceStatus{},
  1488  	}
  1489  	svcFunc(svc)
  1490  	return svc
  1491  }
  1492  
  1493  func addTestPort(array []v1.ServicePort, name string, protocol v1.Protocol, port, nodeport int32, targetPort int) []v1.ServicePort {
  1494  	svcPort := v1.ServicePort{
  1495  		Name:       name,
  1496  		Protocol:   protocol,
  1497  		Port:       port,
  1498  		NodePort:   nodeport,
  1499  		TargetPort: intstr.FromInt32(int32(targetPort)),
  1500  	}
  1501  	return append(array, svcPort)
  1502  }
  1503  
  1504  func TestBuildServiceMapAddRemove(t *testing.T) {
  1505  	_, fp := NewFakeProxier(v1.IPv4Protocol)
  1506  
  1507  	services := []*v1.Service{
  1508  		makeTestService("somewhere-else", "cluster-ip", func(svc *v1.Service) {
  1509  			svc.Spec.Type = v1.ServiceTypeClusterIP
  1510  			svc.Spec.ClusterIP = "172.30.55.4"
  1511  			svc.Spec.Ports = addTestPort(svc.Spec.Ports, "something", "UDP", 1234, 4321, 0)
  1512  			svc.Spec.Ports = addTestPort(svc.Spec.Ports, "somethingelse", "UDP", 1235, 5321, 0)
  1513  			svc.Spec.Ports = addTestPort(svc.Spec.Ports, "sctpport", "SCTP", 1236, 6321, 0)
  1514  		}),
  1515  		makeTestService("somewhere-else", "node-port", func(svc *v1.Service) {
  1516  			svc.Spec.Type = v1.ServiceTypeNodePort
  1517  			svc.Spec.ClusterIP = "172.30.55.10"
  1518  			svc.Spec.Ports = addTestPort(svc.Spec.Ports, "blahblah", "UDP", 345, 678, 0)
  1519  			svc.Spec.Ports = addTestPort(svc.Spec.Ports, "moreblahblah", "TCP", 344, 677, 0)
  1520  			svc.Spec.Ports = addTestPort(svc.Spec.Ports, "muchmoreblah", "SCTP", 343, 676, 0)
  1521  		}),
  1522  		makeTestService("somewhere", "load-balancer", func(svc *v1.Service) {
  1523  			svc.Spec.Type = v1.ServiceTypeLoadBalancer
  1524  			svc.Spec.ClusterIP = "172.30.55.11"
  1525  			svc.Spec.LoadBalancerIP = "1.2.3.4"
  1526  			svc.Spec.Ports = addTestPort(svc.Spec.Ports, "foobar", "UDP", 8675, 30061, 7000)
  1527  			svc.Spec.Ports = addTestPort(svc.Spec.Ports, "baz", "UDP", 8676, 30062, 7001)
  1528  			svc.Status.LoadBalancer = v1.LoadBalancerStatus{
  1529  				Ingress: []v1.LoadBalancerIngress{
  1530  					{IP: "1.2.3.4"},
  1531  				},
  1532  			}
  1533  		}),
  1534  		makeTestService("somewhere", "only-local-load-balancer", func(svc *v1.Service) {
  1535  			svc.Spec.Type = v1.ServiceTypeLoadBalancer
  1536  			svc.Spec.ClusterIP = "172.30.55.12"
  1537  			svc.Spec.LoadBalancerIP = "5.6.7.8"
  1538  			svc.Spec.Ports = addTestPort(svc.Spec.Ports, "foobar2", "UDP", 8677, 30063, 7002)
  1539  			svc.Spec.Ports = addTestPort(svc.Spec.Ports, "baz", "UDP", 8678, 30064, 7003)
  1540  			svc.Status.LoadBalancer = v1.LoadBalancerStatus{
  1541  				Ingress: []v1.LoadBalancerIngress{
  1542  					{IP: "5.6.7.8"},
  1543  				},
  1544  			}
  1545  			svc.Spec.ExternalTrafficPolicy = v1.ServiceExternalTrafficPolicyLocal
  1546  			svc.Spec.HealthCheckNodePort = 345
  1547  		}),
  1548  	}
  1549  
  1550  	for i := range services {
  1551  		fp.OnServiceAdd(services[i])
  1552  	}
  1553  	result := fp.svcPortMap.Update(fp.serviceChanges)
  1554  	if len(fp.svcPortMap) != 10 {
  1555  		t.Errorf("expected service map length 10, got %v", fp.svcPortMap)
  1556  	}
  1557  
  1558  	if len(result.DeletedUDPClusterIPs) != 0 {
  1559  		// Services only added, so nothing stale yet
  1560  		t.Errorf("expected stale UDP services length 0, got %d", len(result.DeletedUDPClusterIPs))
  1561  	}
  1562  
  1563  	// The only-local-loadbalancer ones get added
  1564  	healthCheckNodePorts := fp.svcPortMap.HealthCheckNodePorts()
  1565  	if len(healthCheckNodePorts) != 1 {
  1566  		t.Errorf("expected 1 healthcheck port, got %v", healthCheckNodePorts)
  1567  	} else {
  1568  		nsn := makeNSN("somewhere", "only-local-load-balancer")
  1569  		if port, found := healthCheckNodePorts[nsn]; !found || port != 345 {
  1570  			t.Errorf("expected healthcheck port [%q]=345: got %v", nsn, healthCheckNodePorts)
  1571  		}
  1572  	}
  1573  
  1574  	// Remove some stuff
  1575  	// oneService is a modification of services[0] with removed first port.
  1576  	oneService := makeTestService("somewhere-else", "cluster-ip", func(svc *v1.Service) {
  1577  		svc.Spec.Type = v1.ServiceTypeClusterIP
  1578  		svc.Spec.ClusterIP = "172.30.55.4"
  1579  		svc.Spec.Ports = addTestPort(svc.Spec.Ports, "somethingelse", "UDP", 1235, 5321, 0)
  1580  	})
  1581  
  1582  	fp.OnServiceUpdate(services[0], oneService)
  1583  	fp.OnServiceDelete(services[1])
  1584  	fp.OnServiceDelete(services[2])
  1585  	fp.OnServiceDelete(services[3])
  1586  
  1587  	result = fp.svcPortMap.Update(fp.serviceChanges)
  1588  	if len(fp.svcPortMap) != 1 {
  1589  		t.Errorf("expected service map length 1, got %v", fp.svcPortMap)
  1590  	}
  1591  
  1592  	// All services but one were deleted. While you'd expect only the ClusterIPs
  1593  	// from the three deleted services here, we still have the ClusterIP for
  1594  	// the not-deleted service, because one of it's ServicePorts was deleted.
  1595  	expectedStaleUDPServices := []string{"172.30.55.10", "172.30.55.4", "172.30.55.11", "172.30.55.12"}
  1596  	if len(result.DeletedUDPClusterIPs) != len(expectedStaleUDPServices) {
  1597  		t.Errorf("expected stale UDP services length %d, got %v", len(expectedStaleUDPServices), result.DeletedUDPClusterIPs.UnsortedList())
  1598  	}
  1599  	for _, ip := range expectedStaleUDPServices {
  1600  		if !result.DeletedUDPClusterIPs.Has(ip) {
  1601  			t.Errorf("expected stale UDP service service %s", ip)
  1602  		}
  1603  	}
  1604  
  1605  	healthCheckNodePorts = fp.svcPortMap.HealthCheckNodePorts()
  1606  	if len(healthCheckNodePorts) != 0 {
  1607  		t.Errorf("expected 0 healthcheck ports, got %v", healthCheckNodePorts)
  1608  	}
  1609  }
  1610  
  1611  func TestBuildServiceMapServiceHeadless(t *testing.T) {
  1612  	_, fp := NewFakeProxier(v1.IPv4Protocol)
  1613  
  1614  	makeServiceMap(fp,
  1615  		makeTestService("somewhere-else", "headless", func(svc *v1.Service) {
  1616  			svc.Spec.Type = v1.ServiceTypeClusterIP
  1617  			svc.Spec.ClusterIP = v1.ClusterIPNone
  1618  			svc.Spec.Ports = addTestPort(svc.Spec.Ports, "rpc", "UDP", 1234, 0, 0)
  1619  		}),
  1620  		makeTestService("somewhere-else", "headless-without-port", func(svc *v1.Service) {
  1621  			svc.Spec.Type = v1.ServiceTypeClusterIP
  1622  			svc.Spec.ClusterIP = v1.ClusterIPNone
  1623  		}),
  1624  	)
  1625  
  1626  	// Headless service should be ignored
  1627  	result := fp.svcPortMap.Update(fp.serviceChanges)
  1628  	if len(fp.svcPortMap) != 0 {
  1629  		t.Errorf("expected service map length 0, got %d", len(fp.svcPortMap))
  1630  	}
  1631  
  1632  	if len(result.DeletedUDPClusterIPs) != 0 {
  1633  		t.Errorf("expected stale UDP services length 0, got %d", len(result.DeletedUDPClusterIPs))
  1634  	}
  1635  
  1636  	// No proxied services, so no healthchecks
  1637  	healthCheckNodePorts := fp.svcPortMap.HealthCheckNodePorts()
  1638  	if len(healthCheckNodePorts) != 0 {
  1639  		t.Errorf("expected healthcheck ports length 0, got %d", len(healthCheckNodePorts))
  1640  	}
  1641  }
  1642  
  1643  func TestBuildServiceMapServiceTypeExternalName(t *testing.T) {
  1644  	_, fp := NewFakeProxier(v1.IPv4Protocol)
  1645  
  1646  	makeServiceMap(fp,
  1647  		makeTestService("somewhere-else", "external-name", func(svc *v1.Service) {
  1648  			svc.Spec.Type = v1.ServiceTypeExternalName
  1649  			svc.Spec.ClusterIP = "172.30.55.4" // Should be ignored
  1650  			svc.Spec.ExternalName = "foo2.bar.com"
  1651  			svc.Spec.Ports = addTestPort(svc.Spec.Ports, "blah", "UDP", 1235, 5321, 0)
  1652  		}),
  1653  	)
  1654  
  1655  	result := fp.svcPortMap.Update(fp.serviceChanges)
  1656  	if len(fp.svcPortMap) != 0 {
  1657  		t.Errorf("expected service map length 0, got %v", fp.svcPortMap)
  1658  	}
  1659  	if len(result.DeletedUDPClusterIPs) != 0 {
  1660  		t.Errorf("expected stale UDP services length 0, got %v", result.DeletedUDPClusterIPs)
  1661  	}
  1662  	// No proxied services, so no healthchecks
  1663  	healthCheckNodePorts := fp.svcPortMap.HealthCheckNodePorts()
  1664  	if len(healthCheckNodePorts) != 0 {
  1665  		t.Errorf("expected healthcheck ports length 0, got %v", healthCheckNodePorts)
  1666  	}
  1667  }
  1668  
  1669  func TestBuildServiceMapServiceUpdate(t *testing.T) {
  1670  	_, fp := NewFakeProxier(v1.IPv4Protocol)
  1671  
  1672  	servicev1 := makeTestService("somewhere", "some-service", func(svc *v1.Service) {
  1673  		svc.Spec.Type = v1.ServiceTypeClusterIP
  1674  		svc.Spec.ClusterIP = "172.30.55.4"
  1675  		svc.Spec.Ports = addTestPort(svc.Spec.Ports, "something", "UDP", 1234, 4321, 0)
  1676  		svc.Spec.Ports = addTestPort(svc.Spec.Ports, "somethingelse", "TCP", 1235, 5321, 0)
  1677  	})
  1678  	servicev2 := makeTestService("somewhere", "some-service", func(svc *v1.Service) {
  1679  		svc.Spec.Type = v1.ServiceTypeLoadBalancer
  1680  		svc.Spec.ClusterIP = "172.30.55.4"
  1681  		svc.Spec.LoadBalancerIP = "1.2.3.4"
  1682  		svc.Spec.Ports = addTestPort(svc.Spec.Ports, "something", "UDP", 1234, 4321, 7002)
  1683  		svc.Spec.Ports = addTestPort(svc.Spec.Ports, "somethingelse", "TCP", 1235, 5321, 7003)
  1684  		svc.Status.LoadBalancer = v1.LoadBalancerStatus{
  1685  			Ingress: []v1.LoadBalancerIngress{
  1686  				{IP: "1.2.3.4"},
  1687  			},
  1688  		}
  1689  		svc.Spec.ExternalTrafficPolicy = v1.ServiceExternalTrafficPolicyLocal
  1690  		svc.Spec.HealthCheckNodePort = 345
  1691  	})
  1692  
  1693  	fp.OnServiceAdd(servicev1)
  1694  
  1695  	result := fp.svcPortMap.Update(fp.serviceChanges)
  1696  	if len(fp.svcPortMap) != 2 {
  1697  		t.Errorf("expected service map length 2, got %v", fp.svcPortMap)
  1698  	}
  1699  	if len(result.DeletedUDPClusterIPs) != 0 {
  1700  		// Services only added, so nothing stale yet
  1701  		t.Errorf("expected stale UDP services length 0, got %d", len(result.DeletedUDPClusterIPs))
  1702  	}
  1703  	healthCheckNodePorts := fp.svcPortMap.HealthCheckNodePorts()
  1704  	if len(healthCheckNodePorts) != 0 {
  1705  		t.Errorf("expected healthcheck ports length 0, got %v", healthCheckNodePorts)
  1706  	}
  1707  
  1708  	// Change service to load-balancer
  1709  	fp.OnServiceUpdate(servicev1, servicev2)
  1710  	result = fp.svcPortMap.Update(fp.serviceChanges)
  1711  	if len(fp.svcPortMap) != 2 {
  1712  		t.Errorf("expected service map length 2, got %v", fp.svcPortMap)
  1713  	}
  1714  	if len(result.DeletedUDPClusterIPs) != 0 {
  1715  		t.Errorf("expected stale UDP services length 0, got %v", result.DeletedUDPClusterIPs.UnsortedList())
  1716  	}
  1717  	healthCheckNodePorts = fp.svcPortMap.HealthCheckNodePorts()
  1718  	if len(healthCheckNodePorts) != 1 {
  1719  		t.Errorf("expected healthcheck ports length 1, got %v", healthCheckNodePorts)
  1720  	}
  1721  
  1722  	// No change; make sure the service map stays the same and there are
  1723  	// no health-check changes
  1724  	fp.OnServiceUpdate(servicev2, servicev2)
  1725  	result = fp.svcPortMap.Update(fp.serviceChanges)
  1726  	if len(fp.svcPortMap) != 2 {
  1727  		t.Errorf("expected service map length 2, got %v", fp.svcPortMap)
  1728  	}
  1729  	if len(result.DeletedUDPClusterIPs) != 0 {
  1730  		t.Errorf("expected stale UDP services length 0, got %v", result.DeletedUDPClusterIPs.UnsortedList())
  1731  	}
  1732  	healthCheckNodePorts = fp.svcPortMap.HealthCheckNodePorts()
  1733  	if len(healthCheckNodePorts) != 1 {
  1734  		t.Errorf("expected healthcheck ports length 1, got %v", healthCheckNodePorts)
  1735  	}
  1736  
  1737  	// And back to ClusterIP
  1738  	fp.OnServiceUpdate(servicev2, servicev1)
  1739  	result = fp.svcPortMap.Update(fp.serviceChanges)
  1740  	if len(fp.svcPortMap) != 2 {
  1741  		t.Errorf("expected service map length 2, got %v", fp.svcPortMap)
  1742  	}
  1743  	if len(result.DeletedUDPClusterIPs) != 0 {
  1744  		// Services only added, so nothing stale yet
  1745  		t.Errorf("expected stale UDP services length 0, got %d", len(result.DeletedUDPClusterIPs))
  1746  	}
  1747  	healthCheckNodePorts = fp.svcPortMap.HealthCheckNodePorts()
  1748  	if len(healthCheckNodePorts) != 0 {
  1749  		t.Errorf("expected healthcheck ports length 0, got %v", healthCheckNodePorts)
  1750  	}
  1751  }
  1752  
  1753  func populateEndpointSlices(proxier *Proxier, allEndpointSlices ...*discovery.EndpointSlice) {
  1754  	for i := range allEndpointSlices {
  1755  		proxier.OnEndpointSliceAdd(allEndpointSlices[i])
  1756  	}
  1757  }
  1758  
  1759  func makeTestEndpointSlice(namespace, name string, sliceNum int, epsFunc func(*discovery.EndpointSlice)) *discovery.EndpointSlice {
  1760  	eps := &discovery.EndpointSlice{
  1761  		ObjectMeta: metav1.ObjectMeta{
  1762  			Name:      fmt.Sprintf("%s-%d", name, sliceNum),
  1763  			Namespace: namespace,
  1764  			Labels:    map[string]string{discovery.LabelServiceName: name},
  1765  		},
  1766  	}
  1767  	epsFunc(eps)
  1768  	return eps
  1769  }
  1770  
  1771  func makeNSN(namespace, name string) types.NamespacedName {
  1772  	return types.NamespacedName{Namespace: namespace, Name: name}
  1773  }
  1774  
  1775  func makeServicePortName(ns, name, port string, protocol v1.Protocol) proxy.ServicePortName {
  1776  	return proxy.ServicePortName{
  1777  		NamespacedName: makeNSN(ns, name),
  1778  		Port:           port,
  1779  		Protocol:       protocol,
  1780  	}
  1781  }
  1782  
  1783  func makeServiceMap(proxier *Proxier, allServices ...*v1.Service) {
  1784  	for i := range allServices {
  1785  		proxier.OnServiceAdd(allServices[i])
  1786  	}
  1787  
  1788  	proxier.mu.Lock()
  1789  	defer proxier.mu.Unlock()
  1790  	proxier.servicesSynced = true
  1791  }
  1792  
  1793  type endpointExpectation struct {
  1794  	endpoint string
  1795  	isLocal  bool
  1796  }
  1797  
  1798  func checkEndpointExpectations(t *testing.T, tci int, newMap proxy.EndpointsMap, expected map[proxy.ServicePortName][]endpointExpectation) {
  1799  	if len(newMap) != len(expected) {
  1800  		t.Errorf("[%d] expected %d results, got %d: %v", tci, len(expected), len(newMap), newMap)
  1801  	}
  1802  	for x := range expected {
  1803  		if len(newMap[x]) != len(expected[x]) {
  1804  			t.Errorf("[%d] expected %d endpoints for %v, got %d", tci, len(expected[x]), x, len(newMap[x]))
  1805  		} else {
  1806  			for i := range expected[x] {
  1807  				newEp := newMap[x][i]
  1808  				if newEp.String() != expected[x][i].endpoint ||
  1809  					newEp.IsLocal() != expected[x][i].isLocal {
  1810  					t.Errorf("[%d] expected new[%v][%d] to be %v, got %v", tci, x, i, expected[x][i], newEp)
  1811  				}
  1812  			}
  1813  		}
  1814  	}
  1815  }
  1816  
  1817  func TestUpdateEndpointsMap(t *testing.T) {
  1818  	emptyEndpointSlices := []*discovery.EndpointSlice{
  1819  		makeTestEndpointSlice("ns1", "ep1", 1, func(*discovery.EndpointSlice) {}),
  1820  	}
  1821  	subset1 := func(eps *discovery.EndpointSlice) {
  1822  		eps.AddressType = discovery.AddressTypeIPv4
  1823  		eps.Endpoints = []discovery.Endpoint{{
  1824  			Addresses: []string{"10.1.1.1"},
  1825  		}}
  1826  		eps.Ports = []discovery.EndpointPort{{
  1827  			Name:     ptr.To("p11"),
  1828  			Port:     ptr.To[int32](11),
  1829  			Protocol: ptr.To(v1.ProtocolUDP),
  1830  		}}
  1831  	}
  1832  	subset2 := func(eps *discovery.EndpointSlice) {
  1833  		eps.AddressType = discovery.AddressTypeIPv4
  1834  		eps.Endpoints = []discovery.Endpoint{{
  1835  			Addresses: []string{"10.1.1.2"},
  1836  		}}
  1837  		eps.Ports = []discovery.EndpointPort{{
  1838  			Name:     ptr.To("p12"),
  1839  			Port:     ptr.To[int32](12),
  1840  			Protocol: ptr.To(v1.ProtocolUDP),
  1841  		}}
  1842  	}
  1843  	namedPortLocal := []*discovery.EndpointSlice{
  1844  		makeTestEndpointSlice("ns1", "ep1", 1,
  1845  			func(eps *discovery.EndpointSlice) {
  1846  				eps.AddressType = discovery.AddressTypeIPv4
  1847  				eps.Endpoints = []discovery.Endpoint{{
  1848  					Addresses: []string{"10.1.1.1"},
  1849  					NodeName:  ptr.To(testHostname),
  1850  				}}
  1851  				eps.Ports = []discovery.EndpointPort{{
  1852  					Name:     ptr.To("p11"),
  1853  					Port:     ptr.To[int32](11),
  1854  					Protocol: ptr.To(v1.ProtocolUDP),
  1855  				}}
  1856  			}),
  1857  	}
  1858  	namedPort := []*discovery.EndpointSlice{
  1859  		makeTestEndpointSlice("ns1", "ep1", 1, subset1),
  1860  	}
  1861  	namedPortRenamed := []*discovery.EndpointSlice{
  1862  		makeTestEndpointSlice("ns1", "ep1", 1,
  1863  			func(eps *discovery.EndpointSlice) {
  1864  				eps.AddressType = discovery.AddressTypeIPv4
  1865  				eps.Endpoints = []discovery.Endpoint{{
  1866  					Addresses: []string{"10.1.1.1"},
  1867  				}}
  1868  				eps.Ports = []discovery.EndpointPort{{
  1869  					Name:     ptr.To("p11-2"),
  1870  					Port:     ptr.To[int32](11),
  1871  					Protocol: ptr.To(v1.ProtocolUDP),
  1872  				}}
  1873  			}),
  1874  	}
  1875  	namedPortRenumbered := []*discovery.EndpointSlice{
  1876  		makeTestEndpointSlice("ns1", "ep1", 1,
  1877  			func(eps *discovery.EndpointSlice) {
  1878  				eps.AddressType = discovery.AddressTypeIPv4
  1879  				eps.Endpoints = []discovery.Endpoint{{
  1880  					Addresses: []string{"10.1.1.1"},
  1881  				}}
  1882  				eps.Ports = []discovery.EndpointPort{{
  1883  					Name:     ptr.To("p11"),
  1884  					Port:     ptr.To[int32](22),
  1885  					Protocol: ptr.To(v1.ProtocolUDP),
  1886  				}}
  1887  			}),
  1888  	}
  1889  	namedPortsLocalNoLocal := []*discovery.EndpointSlice{
  1890  		makeTestEndpointSlice("ns1", "ep1", 1,
  1891  			func(eps *discovery.EndpointSlice) {
  1892  				eps.AddressType = discovery.AddressTypeIPv4
  1893  				eps.Endpoints = []discovery.Endpoint{{
  1894  					Addresses: []string{"10.1.1.1"},
  1895  				}, {
  1896  					Addresses: []string{"10.1.1.2"},
  1897  					NodeName:  ptr.To(testHostname),
  1898  				}}
  1899  				eps.Ports = []discovery.EndpointPort{{
  1900  					Name:     ptr.To("p11"),
  1901  					Port:     ptr.To[int32](11),
  1902  					Protocol: ptr.To(v1.ProtocolUDP),
  1903  				}, {
  1904  					Name:     ptr.To("p12"),
  1905  					Port:     ptr.To[int32](12),
  1906  					Protocol: ptr.To(v1.ProtocolUDP),
  1907  				}}
  1908  			}),
  1909  	}
  1910  	multipleSubsets := []*discovery.EndpointSlice{
  1911  		makeTestEndpointSlice("ns1", "ep1", 1, subset1),
  1912  		makeTestEndpointSlice("ns1", "ep1", 2, subset2),
  1913  	}
  1914  	subsetLocal := func(eps *discovery.EndpointSlice) {
  1915  		eps.AddressType = discovery.AddressTypeIPv4
  1916  		eps.Endpoints = []discovery.Endpoint{{
  1917  			Addresses: []string{"10.1.1.2"},
  1918  			NodeName:  ptr.To(testHostname),
  1919  		}}
  1920  		eps.Ports = []discovery.EndpointPort{{
  1921  			Name:     ptr.To("p12"),
  1922  			Port:     ptr.To[int32](12),
  1923  			Protocol: ptr.To(v1.ProtocolUDP),
  1924  		}}
  1925  	}
  1926  	multipleSubsetsWithLocal := []*discovery.EndpointSlice{
  1927  		makeTestEndpointSlice("ns1", "ep1", 1, subset1),
  1928  		makeTestEndpointSlice("ns1", "ep1", 2, subsetLocal),
  1929  	}
  1930  	subsetMultiplePortsLocal := func(eps *discovery.EndpointSlice) {
  1931  		eps.AddressType = discovery.AddressTypeIPv4
  1932  		eps.Endpoints = []discovery.Endpoint{{
  1933  			Addresses: []string{"10.1.1.1"},
  1934  			NodeName:  ptr.To(testHostname),
  1935  		}}
  1936  		eps.Ports = []discovery.EndpointPort{{
  1937  			Name:     ptr.To("p11"),
  1938  			Port:     ptr.To[int32](11),
  1939  			Protocol: ptr.To(v1.ProtocolUDP),
  1940  		}, {
  1941  			Name:     ptr.To("p12"),
  1942  			Port:     ptr.To[int32](12),
  1943  			Protocol: ptr.To(v1.ProtocolUDP),
  1944  		}}
  1945  	}
  1946  	subset3 := func(eps *discovery.EndpointSlice) {
  1947  		eps.AddressType = discovery.AddressTypeIPv4
  1948  		eps.Endpoints = []discovery.Endpoint{{
  1949  			Addresses: []string{"10.1.1.3"},
  1950  		}}
  1951  		eps.Ports = []discovery.EndpointPort{{
  1952  			Name:     ptr.To("p13"),
  1953  			Port:     ptr.To[int32](13),
  1954  			Protocol: ptr.To(v1.ProtocolUDP),
  1955  		}}
  1956  	}
  1957  	multipleSubsetsMultiplePortsLocal := []*discovery.EndpointSlice{
  1958  		makeTestEndpointSlice("ns1", "ep1", 1, subsetMultiplePortsLocal),
  1959  		makeTestEndpointSlice("ns1", "ep1", 2, subset3),
  1960  	}
  1961  	subsetMultipleIPsPorts1 := func(eps *discovery.EndpointSlice) {
  1962  		eps.AddressType = discovery.AddressTypeIPv4
  1963  		eps.Endpoints = []discovery.Endpoint{{
  1964  			Addresses: []string{"10.1.1.1"},
  1965  		}, {
  1966  			Addresses: []string{"10.1.1.2"},
  1967  			NodeName:  ptr.To(testHostname),
  1968  		}}
  1969  		eps.Ports = []discovery.EndpointPort{{
  1970  			Name:     ptr.To("p11"),
  1971  			Port:     ptr.To[int32](11),
  1972  			Protocol: ptr.To(v1.ProtocolUDP),
  1973  		}, {
  1974  			Name:     ptr.To("p12"),
  1975  			Port:     ptr.To[int32](12),
  1976  			Protocol: ptr.To(v1.ProtocolUDP),
  1977  		}}
  1978  	}
  1979  	subsetMultipleIPsPorts2 := func(eps *discovery.EndpointSlice) {
  1980  		eps.AddressType = discovery.AddressTypeIPv4
  1981  		eps.Endpoints = []discovery.Endpoint{{
  1982  			Addresses: []string{"10.1.1.3"},
  1983  		}, {
  1984  			Addresses: []string{"10.1.1.4"},
  1985  			NodeName:  ptr.To(testHostname),
  1986  		}}
  1987  		eps.Ports = []discovery.EndpointPort{{
  1988  			Name:     ptr.To("p13"),
  1989  			Port:     ptr.To[int32](13),
  1990  			Protocol: ptr.To(v1.ProtocolUDP),
  1991  		}, {
  1992  			Name:     ptr.To("p14"),
  1993  			Port:     ptr.To[int32](14),
  1994  			Protocol: ptr.To(v1.ProtocolUDP),
  1995  		}}
  1996  	}
  1997  	subsetMultipleIPsPorts3 := func(eps *discovery.EndpointSlice) {
  1998  		eps.AddressType = discovery.AddressTypeIPv4
  1999  		eps.Endpoints = []discovery.Endpoint{{
  2000  			Addresses: []string{"10.2.2.1"},
  2001  		}, {
  2002  			Addresses: []string{"10.2.2.2"},
  2003  			NodeName:  ptr.To(testHostname),
  2004  		}}
  2005  		eps.Ports = []discovery.EndpointPort{{
  2006  			Name:     ptr.To("p21"),
  2007  			Port:     ptr.To[int32](21),
  2008  			Protocol: ptr.To(v1.ProtocolUDP),
  2009  		}, {
  2010  			Name:     ptr.To("p22"),
  2011  			Port:     ptr.To[int32](22),
  2012  			Protocol: ptr.To(v1.ProtocolUDP),
  2013  		}}
  2014  	}
  2015  	multipleSubsetsIPsPorts := []*discovery.EndpointSlice{
  2016  		makeTestEndpointSlice("ns1", "ep1", 1, subsetMultipleIPsPorts1),
  2017  		makeTestEndpointSlice("ns1", "ep1", 2, subsetMultipleIPsPorts2),
  2018  		makeTestEndpointSlice("ns2", "ep2", 1, subsetMultipleIPsPorts3),
  2019  	}
  2020  	complexSubset1 := func(eps *discovery.EndpointSlice) {
  2021  		eps.AddressType = discovery.AddressTypeIPv4
  2022  		eps.Endpoints = []discovery.Endpoint{{
  2023  			Addresses: []string{"10.2.2.2"},
  2024  			NodeName:  ptr.To(testHostname),
  2025  		}, {
  2026  			Addresses: []string{"10.2.2.22"},
  2027  			NodeName:  ptr.To(testHostname),
  2028  		}}
  2029  		eps.Ports = []discovery.EndpointPort{{
  2030  			Name:     ptr.To("p22"),
  2031  			Port:     ptr.To[int32](22),
  2032  			Protocol: ptr.To(v1.ProtocolUDP),
  2033  		}}
  2034  	}
  2035  	complexSubset2 := func(eps *discovery.EndpointSlice) {
  2036  		eps.AddressType = discovery.AddressTypeIPv4
  2037  		eps.Endpoints = []discovery.Endpoint{{
  2038  			Addresses: []string{"10.2.2.3"},
  2039  			NodeName:  ptr.To(testHostname),
  2040  		}}
  2041  		eps.Ports = []discovery.EndpointPort{{
  2042  			Name:     ptr.To("p23"),
  2043  			Port:     ptr.To[int32](23),
  2044  			Protocol: ptr.To(v1.ProtocolUDP),
  2045  		}}
  2046  	}
  2047  	complexSubset3 := func(eps *discovery.EndpointSlice) {
  2048  		eps.AddressType = discovery.AddressTypeIPv4
  2049  		eps.Endpoints = []discovery.Endpoint{{
  2050  			Addresses: []string{"10.4.4.4"},
  2051  			NodeName:  ptr.To(testHostname),
  2052  		}, {
  2053  			Addresses: []string{"10.4.4.5"},
  2054  			NodeName:  ptr.To(testHostname),
  2055  		}}
  2056  		eps.Ports = []discovery.EndpointPort{{
  2057  			Name:     ptr.To("p44"),
  2058  			Port:     ptr.To[int32](44),
  2059  			Protocol: ptr.To(v1.ProtocolUDP),
  2060  		}}
  2061  	}
  2062  	complexSubset4 := func(eps *discovery.EndpointSlice) {
  2063  		eps.AddressType = discovery.AddressTypeIPv4
  2064  		eps.Endpoints = []discovery.Endpoint{{
  2065  			Addresses: []string{"10.4.4.6"},
  2066  			NodeName:  ptr.To(testHostname),
  2067  		}}
  2068  		eps.Ports = []discovery.EndpointPort{{
  2069  			Name:     ptr.To("p45"),
  2070  			Port:     ptr.To[int32](45),
  2071  			Protocol: ptr.To(v1.ProtocolUDP),
  2072  		}}
  2073  	}
  2074  	complexSubset5 := func(eps *discovery.EndpointSlice) {
  2075  		eps.AddressType = discovery.AddressTypeIPv4
  2076  		eps.Endpoints = []discovery.Endpoint{{
  2077  			Addresses: []string{"10.1.1.1"},
  2078  		}, {
  2079  			Addresses: []string{"10.1.1.11"},
  2080  		}}
  2081  		eps.Ports = []discovery.EndpointPort{{
  2082  			Name:     ptr.To("p11"),
  2083  			Port:     ptr.To[int32](11),
  2084  			Protocol: ptr.To(v1.ProtocolUDP),
  2085  		}}
  2086  	}
  2087  	complexSubset6 := func(eps *discovery.EndpointSlice) {
  2088  		eps.AddressType = discovery.AddressTypeIPv4
  2089  		eps.Endpoints = []discovery.Endpoint{{
  2090  			Addresses: []string{"10.1.1.2"},
  2091  		}}
  2092  		eps.Ports = []discovery.EndpointPort{{
  2093  			Name:     ptr.To("p12"),
  2094  			Port:     ptr.To[int32](12),
  2095  			Protocol: ptr.To(v1.ProtocolUDP),
  2096  		}, {
  2097  			Name:     ptr.To("p122"),
  2098  			Port:     ptr.To[int32](122),
  2099  			Protocol: ptr.To(v1.ProtocolUDP),
  2100  		}}
  2101  	}
  2102  	complexSubset7 := func(eps *discovery.EndpointSlice) {
  2103  		eps.AddressType = discovery.AddressTypeIPv4
  2104  		eps.Endpoints = []discovery.Endpoint{{
  2105  			Addresses: []string{"10.3.3.3"},
  2106  		}}
  2107  		eps.Ports = []discovery.EndpointPort{{
  2108  			Name:     ptr.To("p33"),
  2109  			Port:     ptr.To[int32](33),
  2110  			Protocol: ptr.To(v1.ProtocolUDP),
  2111  		}}
  2112  	}
  2113  	complexSubset8 := func(eps *discovery.EndpointSlice) {
  2114  		eps.AddressType = discovery.AddressTypeIPv4
  2115  		eps.Endpoints = []discovery.Endpoint{{
  2116  			Addresses: []string{"10.4.4.4"},
  2117  			NodeName:  ptr.To(testHostname),
  2118  		}}
  2119  		eps.Ports = []discovery.EndpointPort{{
  2120  			Name:     ptr.To("p44"),
  2121  			Port:     ptr.To[int32](44),
  2122  			Protocol: ptr.To(v1.ProtocolUDP),
  2123  		}}
  2124  	}
  2125  	complexBefore := []*discovery.EndpointSlice{
  2126  		makeTestEndpointSlice("ns1", "ep1", 1, subset1),
  2127  		nil,
  2128  		makeTestEndpointSlice("ns2", "ep2", 1, complexSubset1),
  2129  		makeTestEndpointSlice("ns2", "ep2", 2, complexSubset2),
  2130  		nil,
  2131  		makeTestEndpointSlice("ns4", "ep4", 1, complexSubset3),
  2132  		makeTestEndpointSlice("ns4", "ep4", 2, complexSubset4),
  2133  	}
  2134  	complexAfter := []*discovery.EndpointSlice{
  2135  		makeTestEndpointSlice("ns1", "ep1", 1, complexSubset5),
  2136  		makeTestEndpointSlice("ns1", "ep1", 2, complexSubset6),
  2137  		nil,
  2138  		nil,
  2139  		makeTestEndpointSlice("ns3", "ep3", 1, complexSubset7),
  2140  		makeTestEndpointSlice("ns4", "ep4", 1, complexSubset8),
  2141  		nil,
  2142  	}
  2143  
  2144  	testCases := []struct {
  2145  		// previousEndpoints and currentEndpoints are used to call appropriate
  2146  		// handlers OnEndpoints* (based on whether corresponding values are nil
  2147  		// or non-nil) and must be of equal length.
  2148  		name                           string
  2149  		previousEndpoints              []*discovery.EndpointSlice
  2150  		currentEndpoints               []*discovery.EndpointSlice
  2151  		oldEndpoints                   map[proxy.ServicePortName][]endpointExpectation
  2152  		expectedResult                 map[proxy.ServicePortName][]endpointExpectation
  2153  		expectedDeletedUDPEndpoints    []proxy.ServiceEndpoint
  2154  		expectedNewlyActiveUDPServices map[proxy.ServicePortName]bool
  2155  		expectedLocalEndpoints         map[types.NamespacedName]int
  2156  	}{{
  2157  		// Case[0]: nothing
  2158  		name:                           "nothing",
  2159  		oldEndpoints:                   map[proxy.ServicePortName][]endpointExpectation{},
  2160  		expectedResult:                 map[proxy.ServicePortName][]endpointExpectation{},
  2161  		expectedDeletedUDPEndpoints:    []proxy.ServiceEndpoint{},
  2162  		expectedNewlyActiveUDPServices: map[proxy.ServicePortName]bool{},
  2163  		expectedLocalEndpoints:         map[types.NamespacedName]int{},
  2164  	}, {
  2165  		// Case[1]: no change, named port, local
  2166  		name:              "no change, named port, local",
  2167  		previousEndpoints: namedPortLocal,
  2168  		currentEndpoints:  namedPortLocal,
  2169  		oldEndpoints: map[proxy.ServicePortName][]endpointExpectation{
  2170  			makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): {
  2171  				{endpoint: "10.1.1.1:11", isLocal: true},
  2172  			},
  2173  		},
  2174  		expectedResult: map[proxy.ServicePortName][]endpointExpectation{
  2175  			makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): {
  2176  				{endpoint: "10.1.1.1:11", isLocal: true},
  2177  			},
  2178  		},
  2179  		expectedDeletedUDPEndpoints:    []proxy.ServiceEndpoint{},
  2180  		expectedNewlyActiveUDPServices: map[proxy.ServicePortName]bool{},
  2181  		expectedLocalEndpoints: map[types.NamespacedName]int{
  2182  			makeNSN("ns1", "ep1"): 1,
  2183  		},
  2184  	}, {
  2185  		// Case[2]: no change, multiple subsets
  2186  		name:              "no change, multiple subsets",
  2187  		previousEndpoints: multipleSubsets,
  2188  		currentEndpoints:  multipleSubsets,
  2189  		oldEndpoints: map[proxy.ServicePortName][]endpointExpectation{
  2190  			makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): {
  2191  				{endpoint: "10.1.1.1:11", isLocal: false},
  2192  			},
  2193  			makeServicePortName("ns1", "ep1", "p12", v1.ProtocolUDP): {
  2194  				{endpoint: "10.1.1.2:12", isLocal: false},
  2195  			},
  2196  		},
  2197  		expectedResult: map[proxy.ServicePortName][]endpointExpectation{
  2198  			makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): {
  2199  				{endpoint: "10.1.1.1:11", isLocal: false},
  2200  			},
  2201  			makeServicePortName("ns1", "ep1", "p12", v1.ProtocolUDP): {
  2202  				{endpoint: "10.1.1.2:12", isLocal: false},
  2203  			},
  2204  		},
  2205  		expectedDeletedUDPEndpoints:    []proxy.ServiceEndpoint{},
  2206  		expectedNewlyActiveUDPServices: map[proxy.ServicePortName]bool{},
  2207  		expectedLocalEndpoints:         map[types.NamespacedName]int{},
  2208  	}, {
  2209  		// Case[3]: no change, multiple subsets, multiple ports, local
  2210  		name:              "no change, multiple subsets, multiple ports, local",
  2211  		previousEndpoints: multipleSubsetsMultiplePortsLocal,
  2212  		currentEndpoints:  multipleSubsetsMultiplePortsLocal,
  2213  		oldEndpoints: map[proxy.ServicePortName][]endpointExpectation{
  2214  			makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): {
  2215  				{endpoint: "10.1.1.1:11", isLocal: true},
  2216  			},
  2217  			makeServicePortName("ns1", "ep1", "p12", v1.ProtocolUDP): {
  2218  				{endpoint: "10.1.1.1:12", isLocal: true},
  2219  			},
  2220  			makeServicePortName("ns1", "ep1", "p13", v1.ProtocolUDP): {
  2221  				{endpoint: "10.1.1.3:13", isLocal: false},
  2222  			},
  2223  		},
  2224  		expectedResult: map[proxy.ServicePortName][]endpointExpectation{
  2225  			makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): {
  2226  				{endpoint: "10.1.1.1:11", isLocal: true},
  2227  			},
  2228  			makeServicePortName("ns1", "ep1", "p12", v1.ProtocolUDP): {
  2229  				{endpoint: "10.1.1.1:12", isLocal: true},
  2230  			},
  2231  			makeServicePortName("ns1", "ep1", "p13", v1.ProtocolUDP): {
  2232  				{endpoint: "10.1.1.3:13", isLocal: false},
  2233  			},
  2234  		},
  2235  		expectedDeletedUDPEndpoints:    []proxy.ServiceEndpoint{},
  2236  		expectedNewlyActiveUDPServices: map[proxy.ServicePortName]bool{},
  2237  		expectedLocalEndpoints: map[types.NamespacedName]int{
  2238  			makeNSN("ns1", "ep1"): 1,
  2239  		},
  2240  	}, {
  2241  		// Case[4]: no change, multiple endpoints, subsets, IPs, and ports
  2242  		name:              "no change, multiple endpoints, subsets, IPs, and ports",
  2243  		previousEndpoints: multipleSubsetsIPsPorts,
  2244  		currentEndpoints:  multipleSubsetsIPsPorts,
  2245  		oldEndpoints: map[proxy.ServicePortName][]endpointExpectation{
  2246  			makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): {
  2247  				{endpoint: "10.1.1.1:11", isLocal: false},
  2248  				{endpoint: "10.1.1.2:11", isLocal: true},
  2249  			},
  2250  			makeServicePortName("ns1", "ep1", "p12", v1.ProtocolUDP): {
  2251  				{endpoint: "10.1.1.1:12", isLocal: false},
  2252  				{endpoint: "10.1.1.2:12", isLocal: true},
  2253  			},
  2254  			makeServicePortName("ns1", "ep1", "p13", v1.ProtocolUDP): {
  2255  				{endpoint: "10.1.1.3:13", isLocal: false},
  2256  				{endpoint: "10.1.1.4:13", isLocal: true},
  2257  			},
  2258  			makeServicePortName("ns1", "ep1", "p14", v1.ProtocolUDP): {
  2259  				{endpoint: "10.1.1.3:14", isLocal: false},
  2260  				{endpoint: "10.1.1.4:14", isLocal: true},
  2261  			},
  2262  			makeServicePortName("ns2", "ep2", "p21", v1.ProtocolUDP): {
  2263  				{endpoint: "10.2.2.1:21", isLocal: false},
  2264  				{endpoint: "10.2.2.2:21", isLocal: true},
  2265  			},
  2266  			makeServicePortName("ns2", "ep2", "p22", v1.ProtocolUDP): {
  2267  				{endpoint: "10.2.2.1:22", isLocal: false},
  2268  				{endpoint: "10.2.2.2:22", isLocal: true},
  2269  			},
  2270  		},
  2271  		expectedResult: map[proxy.ServicePortName][]endpointExpectation{
  2272  			makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): {
  2273  				{endpoint: "10.1.1.1:11", isLocal: false},
  2274  				{endpoint: "10.1.1.2:11", isLocal: true},
  2275  			},
  2276  			makeServicePortName("ns1", "ep1", "p12", v1.ProtocolUDP): {
  2277  				{endpoint: "10.1.1.1:12", isLocal: false},
  2278  				{endpoint: "10.1.1.2:12", isLocal: true},
  2279  			},
  2280  			makeServicePortName("ns1", "ep1", "p13", v1.ProtocolUDP): {
  2281  				{endpoint: "10.1.1.3:13", isLocal: false},
  2282  				{endpoint: "10.1.1.4:13", isLocal: true},
  2283  			},
  2284  			makeServicePortName("ns1", "ep1", "p14", v1.ProtocolUDP): {
  2285  				{endpoint: "10.1.1.3:14", isLocal: false},
  2286  				{endpoint: "10.1.1.4:14", isLocal: true},
  2287  			},
  2288  			makeServicePortName("ns2", "ep2", "p21", v1.ProtocolUDP): {
  2289  				{endpoint: "10.2.2.1:21", isLocal: false},
  2290  				{endpoint: "10.2.2.2:21", isLocal: true},
  2291  			},
  2292  			makeServicePortName("ns2", "ep2", "p22", v1.ProtocolUDP): {
  2293  				{endpoint: "10.2.2.1:22", isLocal: false},
  2294  				{endpoint: "10.2.2.2:22", isLocal: true},
  2295  			},
  2296  		},
  2297  		expectedDeletedUDPEndpoints:    []proxy.ServiceEndpoint{},
  2298  		expectedNewlyActiveUDPServices: map[proxy.ServicePortName]bool{},
  2299  		expectedLocalEndpoints: map[types.NamespacedName]int{
  2300  			makeNSN("ns1", "ep1"): 2,
  2301  			makeNSN("ns2", "ep2"): 1,
  2302  		},
  2303  	}, {
  2304  		// Case[5]: add an Endpoints
  2305  		name:              "add an Endpoints",
  2306  		previousEndpoints: []*discovery.EndpointSlice{nil},
  2307  		currentEndpoints:  namedPortLocal,
  2308  		oldEndpoints:      map[proxy.ServicePortName][]endpointExpectation{},
  2309  		expectedResult: map[proxy.ServicePortName][]endpointExpectation{
  2310  			makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): {
  2311  				{endpoint: "10.1.1.1:11", isLocal: true},
  2312  			},
  2313  		},
  2314  		expectedDeletedUDPEndpoints: []proxy.ServiceEndpoint{},
  2315  		expectedNewlyActiveUDPServices: map[proxy.ServicePortName]bool{
  2316  			makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): true,
  2317  		},
  2318  		expectedLocalEndpoints: map[types.NamespacedName]int{
  2319  			makeNSN("ns1", "ep1"): 1,
  2320  		},
  2321  	}, {
  2322  		// Case[6]: remove an Endpoints
  2323  		name:              "remove an Endpoints",
  2324  		previousEndpoints: namedPortLocal,
  2325  		currentEndpoints:  []*discovery.EndpointSlice{nil},
  2326  		oldEndpoints: map[proxy.ServicePortName][]endpointExpectation{
  2327  			makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): {
  2328  				{endpoint: "10.1.1.1:11", isLocal: true},
  2329  			},
  2330  		},
  2331  		expectedResult: map[proxy.ServicePortName][]endpointExpectation{},
  2332  		expectedDeletedUDPEndpoints: []proxy.ServiceEndpoint{{
  2333  			Endpoint:        "10.1.1.1:11",
  2334  			ServicePortName: makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP),
  2335  		}},
  2336  		expectedNewlyActiveUDPServices: map[proxy.ServicePortName]bool{},
  2337  		expectedLocalEndpoints:         map[types.NamespacedName]int{},
  2338  	}, {
  2339  		// Case[7]: add an IP and port
  2340  		name:              "add an IP and port",
  2341  		previousEndpoints: namedPort,
  2342  		currentEndpoints:  namedPortsLocalNoLocal,
  2343  		oldEndpoints: map[proxy.ServicePortName][]endpointExpectation{
  2344  			makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): {
  2345  				{endpoint: "10.1.1.1:11", isLocal: false},
  2346  			},
  2347  		},
  2348  		expectedResult: map[proxy.ServicePortName][]endpointExpectation{
  2349  			makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): {
  2350  				{endpoint: "10.1.1.1:11", isLocal: false},
  2351  				{endpoint: "10.1.1.2:11", isLocal: true},
  2352  			},
  2353  			makeServicePortName("ns1", "ep1", "p12", v1.ProtocolUDP): {
  2354  				{endpoint: "10.1.1.1:12", isLocal: false},
  2355  				{endpoint: "10.1.1.2:12", isLocal: true},
  2356  			},
  2357  		},
  2358  		expectedDeletedUDPEndpoints: []proxy.ServiceEndpoint{},
  2359  		expectedNewlyActiveUDPServices: map[proxy.ServicePortName]bool{
  2360  			makeServicePortName("ns1", "ep1", "p12", v1.ProtocolUDP): true,
  2361  		},
  2362  		expectedLocalEndpoints: map[types.NamespacedName]int{
  2363  			makeNSN("ns1", "ep1"): 1,
  2364  		},
  2365  	}, {
  2366  		// Case[8]: remove an IP and port
  2367  		name:              "remove an IP and port",
  2368  		previousEndpoints: namedPortsLocalNoLocal,
  2369  		currentEndpoints:  namedPort,
  2370  		oldEndpoints: map[proxy.ServicePortName][]endpointExpectation{
  2371  			makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): {
  2372  				{endpoint: "10.1.1.1:11", isLocal: false},
  2373  				{endpoint: "10.1.1.2:11", isLocal: true},
  2374  			},
  2375  			makeServicePortName("ns1", "ep1", "p12", v1.ProtocolUDP): {
  2376  				{endpoint: "10.1.1.1:12", isLocal: false},
  2377  				{endpoint: "10.1.1.2:12", isLocal: true},
  2378  			},
  2379  		},
  2380  		expectedResult: map[proxy.ServicePortName][]endpointExpectation{
  2381  			makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): {
  2382  				{endpoint: "10.1.1.1:11", isLocal: false},
  2383  			},
  2384  		},
  2385  		expectedDeletedUDPEndpoints: []proxy.ServiceEndpoint{{
  2386  			Endpoint:        "10.1.1.2:11",
  2387  			ServicePortName: makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP),
  2388  		}, {
  2389  			Endpoint:        "10.1.1.1:12",
  2390  			ServicePortName: makeServicePortName("ns1", "ep1", "p12", v1.ProtocolUDP),
  2391  		}, {
  2392  			Endpoint:        "10.1.1.2:12",
  2393  			ServicePortName: makeServicePortName("ns1", "ep1", "p12", v1.ProtocolUDP),
  2394  		}},
  2395  		expectedNewlyActiveUDPServices: map[proxy.ServicePortName]bool{},
  2396  		expectedLocalEndpoints:         map[types.NamespacedName]int{},
  2397  	}, {
  2398  		// Case[9]: add a subset
  2399  		name:              "add a subset",
  2400  		previousEndpoints: []*discovery.EndpointSlice{namedPort[0], nil},
  2401  		currentEndpoints:  multipleSubsetsWithLocal,
  2402  		oldEndpoints: map[proxy.ServicePortName][]endpointExpectation{
  2403  			makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): {
  2404  				{endpoint: "10.1.1.1:11", isLocal: false},
  2405  			},
  2406  		},
  2407  		expectedResult: map[proxy.ServicePortName][]endpointExpectation{
  2408  			makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): {
  2409  				{endpoint: "10.1.1.1:11", isLocal: false},
  2410  			},
  2411  			makeServicePortName("ns1", "ep1", "p12", v1.ProtocolUDP): {
  2412  				{endpoint: "10.1.1.2:12", isLocal: true},
  2413  			},
  2414  		},
  2415  		expectedDeletedUDPEndpoints: []proxy.ServiceEndpoint{},
  2416  		expectedNewlyActiveUDPServices: map[proxy.ServicePortName]bool{
  2417  			makeServicePortName("ns1", "ep1", "p12", v1.ProtocolUDP): true,
  2418  		},
  2419  		expectedLocalEndpoints: map[types.NamespacedName]int{
  2420  			makeNSN("ns1", "ep1"): 1,
  2421  		},
  2422  	}, {
  2423  		// Case[10]: remove a subset
  2424  		name:              "remove a subset",
  2425  		previousEndpoints: multipleSubsets,
  2426  		currentEndpoints:  []*discovery.EndpointSlice{namedPort[0], nil},
  2427  		oldEndpoints: map[proxy.ServicePortName][]endpointExpectation{
  2428  			makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): {
  2429  				{endpoint: "10.1.1.1:11", isLocal: false},
  2430  			},
  2431  			makeServicePortName("ns1", "ep1", "p12", v1.ProtocolUDP): {
  2432  				{endpoint: "10.1.1.2:12", isLocal: false},
  2433  			},
  2434  		},
  2435  		expectedResult: map[proxy.ServicePortName][]endpointExpectation{
  2436  			makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): {
  2437  				{endpoint: "10.1.1.1:11", isLocal: false},
  2438  			},
  2439  		},
  2440  		expectedDeletedUDPEndpoints: []proxy.ServiceEndpoint{{
  2441  			Endpoint:        "10.1.1.2:12",
  2442  			ServicePortName: makeServicePortName("ns1", "ep1", "p12", v1.ProtocolUDP),
  2443  		}},
  2444  		expectedNewlyActiveUDPServices: map[proxy.ServicePortName]bool{},
  2445  		expectedLocalEndpoints:         map[types.NamespacedName]int{},
  2446  	}, {
  2447  		// Case[11]: rename a port
  2448  		name:              "rename a port",
  2449  		previousEndpoints: namedPort,
  2450  		currentEndpoints:  namedPortRenamed,
  2451  		oldEndpoints: map[proxy.ServicePortName][]endpointExpectation{
  2452  			makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): {
  2453  				{endpoint: "10.1.1.1:11", isLocal: false},
  2454  			},
  2455  		},
  2456  		expectedResult: map[proxy.ServicePortName][]endpointExpectation{
  2457  			makeServicePortName("ns1", "ep1", "p11-2", v1.ProtocolUDP): {
  2458  				{endpoint: "10.1.1.1:11", isLocal: false},
  2459  			},
  2460  		},
  2461  		expectedDeletedUDPEndpoints: []proxy.ServiceEndpoint{{
  2462  			Endpoint:        "10.1.1.1:11",
  2463  			ServicePortName: makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP),
  2464  		}},
  2465  		expectedNewlyActiveUDPServices: map[proxy.ServicePortName]bool{
  2466  			makeServicePortName("ns1", "ep1", "p11-2", v1.ProtocolUDP): true,
  2467  		},
  2468  		expectedLocalEndpoints: map[types.NamespacedName]int{},
  2469  	}, {
  2470  		// Case[12]: renumber a port
  2471  		name:              "renumber a port",
  2472  		previousEndpoints: namedPort,
  2473  		currentEndpoints:  namedPortRenumbered,
  2474  		oldEndpoints: map[proxy.ServicePortName][]endpointExpectation{
  2475  			makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): {
  2476  				{endpoint: "10.1.1.1:11", isLocal: false},
  2477  			},
  2478  		},
  2479  		expectedResult: map[proxy.ServicePortName][]endpointExpectation{
  2480  			makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): {
  2481  				{endpoint: "10.1.1.1:22", isLocal: false},
  2482  			},
  2483  		},
  2484  		expectedDeletedUDPEndpoints: []proxy.ServiceEndpoint{{
  2485  			Endpoint:        "10.1.1.1:11",
  2486  			ServicePortName: makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP),
  2487  		}},
  2488  		expectedNewlyActiveUDPServices: map[proxy.ServicePortName]bool{},
  2489  		expectedLocalEndpoints:         map[types.NamespacedName]int{},
  2490  	}, {
  2491  		// Case[13]: complex add and remove
  2492  		name:              "complex add and remove",
  2493  		previousEndpoints: complexBefore,
  2494  		currentEndpoints:  complexAfter,
  2495  		oldEndpoints: map[proxy.ServicePortName][]endpointExpectation{
  2496  			makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): {
  2497  				{endpoint: "10.1.1.1:11", isLocal: false},
  2498  			},
  2499  			makeServicePortName("ns2", "ep2", "p22", v1.ProtocolUDP): {
  2500  				{endpoint: "10.2.2.22:22", isLocal: true},
  2501  				{endpoint: "10.2.2.2:22", isLocal: true},
  2502  			},
  2503  			makeServicePortName("ns2", "ep2", "p23", v1.ProtocolUDP): {
  2504  				{endpoint: "10.2.2.3:23", isLocal: true},
  2505  			},
  2506  			makeServicePortName("ns4", "ep4", "p44", v1.ProtocolUDP): {
  2507  				{endpoint: "10.4.4.4:44", isLocal: true},
  2508  				{endpoint: "10.4.4.5:44", isLocal: true},
  2509  			},
  2510  			makeServicePortName("ns4", "ep4", "p45", v1.ProtocolUDP): {
  2511  				{endpoint: "10.4.4.6:45", isLocal: true},
  2512  			},
  2513  		},
  2514  		expectedResult: map[proxy.ServicePortName][]endpointExpectation{
  2515  			makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): {
  2516  				{endpoint: "10.1.1.11:11", isLocal: false},
  2517  				{endpoint: "10.1.1.1:11", isLocal: false},
  2518  			},
  2519  			makeServicePortName("ns1", "ep1", "p12", v1.ProtocolUDP): {
  2520  				{endpoint: "10.1.1.2:12", isLocal: false},
  2521  			},
  2522  			makeServicePortName("ns1", "ep1", "p122", v1.ProtocolUDP): {
  2523  				{endpoint: "10.1.1.2:122", isLocal: false},
  2524  			},
  2525  			makeServicePortName("ns3", "ep3", "p33", v1.ProtocolUDP): {
  2526  				{endpoint: "10.3.3.3:33", isLocal: false},
  2527  			},
  2528  			makeServicePortName("ns4", "ep4", "p44", v1.ProtocolUDP): {
  2529  				{endpoint: "10.4.4.4:44", isLocal: true},
  2530  			},
  2531  		},
  2532  		expectedDeletedUDPEndpoints: []proxy.ServiceEndpoint{{
  2533  			Endpoint:        "10.2.2.2:22",
  2534  			ServicePortName: makeServicePortName("ns2", "ep2", "p22", v1.ProtocolUDP),
  2535  		}, {
  2536  			Endpoint:        "10.2.2.22:22",
  2537  			ServicePortName: makeServicePortName("ns2", "ep2", "p22", v1.ProtocolUDP),
  2538  		}, {
  2539  			Endpoint:        "10.2.2.3:23",
  2540  			ServicePortName: makeServicePortName("ns2", "ep2", "p23", v1.ProtocolUDP),
  2541  		}, {
  2542  			Endpoint:        "10.4.4.5:44",
  2543  			ServicePortName: makeServicePortName("ns4", "ep4", "p44", v1.ProtocolUDP),
  2544  		}, {
  2545  			Endpoint:        "10.4.4.6:45",
  2546  			ServicePortName: makeServicePortName("ns4", "ep4", "p45", v1.ProtocolUDP),
  2547  		}},
  2548  		expectedNewlyActiveUDPServices: map[proxy.ServicePortName]bool{
  2549  			makeServicePortName("ns1", "ep1", "p12", v1.ProtocolUDP):  true,
  2550  			makeServicePortName("ns1", "ep1", "p122", v1.ProtocolUDP): true,
  2551  			makeServicePortName("ns3", "ep3", "p33", v1.ProtocolUDP):  true,
  2552  		},
  2553  		expectedLocalEndpoints: map[types.NamespacedName]int{
  2554  			makeNSN("ns4", "ep4"): 1,
  2555  		},
  2556  	}, {
  2557  		// Case[14]: change from 0 endpoint address to 1 unnamed port
  2558  		name:              "change from 0 endpoint address to 1 unnamed port",
  2559  		previousEndpoints: emptyEndpointSlices,
  2560  		currentEndpoints:  namedPort,
  2561  		oldEndpoints:      map[proxy.ServicePortName][]endpointExpectation{},
  2562  		expectedResult: map[proxy.ServicePortName][]endpointExpectation{
  2563  			makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): {
  2564  				{endpoint: "10.1.1.1:11", isLocal: false},
  2565  			},
  2566  		},
  2567  		expectedDeletedUDPEndpoints: []proxy.ServiceEndpoint{},
  2568  		expectedNewlyActiveUDPServices: map[proxy.ServicePortName]bool{
  2569  			makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): true,
  2570  		},
  2571  		expectedLocalEndpoints: map[types.NamespacedName]int{},
  2572  	},
  2573  	}
  2574  
  2575  	for tci, tc := range testCases {
  2576  		t.Run(tc.name, func(t *testing.T) {
  2577  			_, fp := NewFakeProxier(v1.IPv4Protocol)
  2578  			fp.hostname = testHostname
  2579  
  2580  			// First check that after adding all previous versions of endpoints,
  2581  			// the fp.oldEndpoints is as we expect.
  2582  			for i := range tc.previousEndpoints {
  2583  				if tc.previousEndpoints[i] != nil {
  2584  					fp.OnEndpointSliceAdd(tc.previousEndpoints[i])
  2585  				}
  2586  			}
  2587  			fp.endpointsMap.Update(fp.endpointsChanges)
  2588  			checkEndpointExpectations(t, tci, fp.endpointsMap, tc.oldEndpoints)
  2589  
  2590  			// Now let's call appropriate handlers to get to state we want to be.
  2591  			if len(tc.previousEndpoints) != len(tc.currentEndpoints) {
  2592  				t.Fatalf("[%d] different lengths of previous and current endpoints", tci)
  2593  			}
  2594  
  2595  			for i := range tc.previousEndpoints {
  2596  				prev, curr := tc.previousEndpoints[i], tc.currentEndpoints[i]
  2597  				switch {
  2598  				case prev == nil:
  2599  					fp.OnEndpointSliceAdd(curr)
  2600  				case curr == nil:
  2601  					fp.OnEndpointSliceDelete(prev)
  2602  				default:
  2603  					fp.OnEndpointSliceUpdate(prev, curr)
  2604  				}
  2605  			}
  2606  			result := fp.endpointsMap.Update(fp.endpointsChanges)
  2607  			newMap := fp.endpointsMap
  2608  			checkEndpointExpectations(t, tci, newMap, tc.expectedResult)
  2609  			if len(result.DeletedUDPEndpoints) != len(tc.expectedDeletedUDPEndpoints) {
  2610  				t.Errorf("[%d] expected %d staleEndpoints, got %d: %v", tci, len(tc.expectedDeletedUDPEndpoints), len(result.DeletedUDPEndpoints), result.DeletedUDPEndpoints)
  2611  			}
  2612  			for _, x := range tc.expectedDeletedUDPEndpoints {
  2613  				found := false
  2614  				for _, stale := range result.DeletedUDPEndpoints {
  2615  					if stale == x {
  2616  						found = true
  2617  						break
  2618  					}
  2619  				}
  2620  				if !found {
  2621  					t.Errorf("[%d] expected staleEndpoints[%v], but didn't find it: %v", tci, x, result.DeletedUDPEndpoints)
  2622  				}
  2623  			}
  2624  			if len(result.NewlyActiveUDPServices) != len(tc.expectedNewlyActiveUDPServices) {
  2625  				t.Errorf("[%d] expected %d staleServiceNames, got %d: %v", tci, len(tc.expectedNewlyActiveUDPServices), len(result.NewlyActiveUDPServices), result.NewlyActiveUDPServices)
  2626  			}
  2627  			for svcName := range tc.expectedNewlyActiveUDPServices {
  2628  				found := false
  2629  				for _, stale := range result.NewlyActiveUDPServices {
  2630  					if stale == svcName {
  2631  						found = true
  2632  					}
  2633  				}
  2634  				if !found {
  2635  					t.Errorf("[%d] expected staleServiceNames[%v], but didn't find it: %v", tci, svcName, result.NewlyActiveUDPServices)
  2636  				}
  2637  			}
  2638  			localReadyEndpoints := fp.endpointsMap.LocalReadyEndpoints()
  2639  			if !reflect.DeepEqual(localReadyEndpoints, tc.expectedLocalEndpoints) {
  2640  				t.Errorf("[%d] expected local endpoints %v, got %v", tci, tc.expectedLocalEndpoints, localReadyEndpoints)
  2641  			}
  2642  		})
  2643  	}
  2644  }
  2645  
  2646  // TestHealthCheckNodePortWhenTerminating tests that health check node ports are not enabled when all local endpoints are terminating
  2647  func TestHealthCheckNodePortWhenTerminating(t *testing.T) {
  2648  	_, fp := NewFakeProxier(v1.IPv4Protocol)
  2649  	fp.OnServiceSynced()
  2650  	fp.OnEndpointSlicesSynced()
  2651  
  2652  	serviceName := "svc1"
  2653  	namespaceName := "ns1"
  2654  
  2655  	fp.OnServiceAdd(&v1.Service{
  2656  		ObjectMeta: metav1.ObjectMeta{Name: serviceName, Namespace: namespaceName},
  2657  		Spec: v1.ServiceSpec{
  2658  			ClusterIP: "172.30.1.1",
  2659  			Selector:  map[string]string{"foo": "bar"},
  2660  			Ports:     []v1.ServicePort{{Name: "", TargetPort: intstr.FromInt32(80), Protocol: v1.ProtocolTCP}},
  2661  		},
  2662  	})
  2663  
  2664  	endpointSlice := &discovery.EndpointSlice{
  2665  		ObjectMeta: metav1.ObjectMeta{
  2666  			Name:      fmt.Sprintf("%s-1", serviceName),
  2667  			Namespace: namespaceName,
  2668  			Labels:    map[string]string{discovery.LabelServiceName: serviceName},
  2669  		},
  2670  		Ports: []discovery.EndpointPort{{
  2671  			Name:     ptr.To(""),
  2672  			Port:     ptr.To[int32](80),
  2673  			Protocol: ptr.To(v1.ProtocolTCP),
  2674  		}},
  2675  		AddressType: discovery.AddressTypeIPv4,
  2676  		Endpoints: []discovery.Endpoint{{
  2677  			Addresses:  []string{"10.0.1.1"},
  2678  			Conditions: discovery.EndpointConditions{Ready: ptr.To(true)},
  2679  			NodeName:   ptr.To(testHostname),
  2680  		}, {
  2681  			Addresses:  []string{"10.0.1.2"},
  2682  			Conditions: discovery.EndpointConditions{Ready: ptr.To(true)},
  2683  			NodeName:   ptr.To(testHostname),
  2684  		}, {
  2685  			Addresses:  []string{"10.0.1.3"},
  2686  			Conditions: discovery.EndpointConditions{Ready: ptr.To(true)},
  2687  			NodeName:   ptr.To(testHostname),
  2688  		}, { // not ready endpoints should be ignored
  2689  			Addresses:  []string{"10.0.1.4"},
  2690  			Conditions: discovery.EndpointConditions{Ready: ptr.To(false)},
  2691  			NodeName:   ptr.To(testHostname),
  2692  		}},
  2693  	}
  2694  
  2695  	fp.OnEndpointSliceAdd(endpointSlice)
  2696  	_ = fp.endpointsMap.Update(fp.endpointsChanges)
  2697  	localReadyEndpoints := fp.endpointsMap.LocalReadyEndpoints()
  2698  	if len(localReadyEndpoints) != 1 {
  2699  		t.Errorf("unexpected number of local ready endpoints, expected 1 but got: %d", len(localReadyEndpoints))
  2700  	}
  2701  
  2702  	// set all endpoints to terminating
  2703  	endpointSliceTerminating := &discovery.EndpointSlice{
  2704  		ObjectMeta: metav1.ObjectMeta{
  2705  			Name:      fmt.Sprintf("%s-1", serviceName),
  2706  			Namespace: namespaceName,
  2707  			Labels:    map[string]string{discovery.LabelServiceName: serviceName},
  2708  		},
  2709  		Ports: []discovery.EndpointPort{{
  2710  			Name:     ptr.To(""),
  2711  			Port:     ptr.To[int32](80),
  2712  			Protocol: ptr.To(v1.ProtocolTCP),
  2713  		}},
  2714  		AddressType: discovery.AddressTypeIPv4,
  2715  		Endpoints: []discovery.Endpoint{{
  2716  			Addresses: []string{"10.0.1.1"},
  2717  			Conditions: discovery.EndpointConditions{
  2718  				Ready:       ptr.To(false),
  2719  				Serving:     ptr.To(true),
  2720  				Terminating: ptr.To(false),
  2721  			},
  2722  			NodeName: ptr.To(testHostname),
  2723  		}, {
  2724  			Addresses: []string{"10.0.1.2"},
  2725  			Conditions: discovery.EndpointConditions{
  2726  				Ready:       ptr.To(false),
  2727  				Serving:     ptr.To(true),
  2728  				Terminating: ptr.To(true),
  2729  			},
  2730  			NodeName: ptr.To(testHostname),
  2731  		}, {
  2732  			Addresses: []string{"10.0.1.3"},
  2733  			Conditions: discovery.EndpointConditions{
  2734  				Ready:       ptr.To(false),
  2735  				Serving:     ptr.To(true),
  2736  				Terminating: ptr.To(true),
  2737  			},
  2738  			NodeName: ptr.To(testHostname),
  2739  		}, { // not ready endpoints should be ignored
  2740  			Addresses: []string{"10.0.1.4"},
  2741  			Conditions: discovery.EndpointConditions{
  2742  				Ready:       ptr.To(false),
  2743  				Serving:     ptr.To(false),
  2744  				Terminating: ptr.To(true),
  2745  			},
  2746  			NodeName: ptr.To(testHostname),
  2747  		}},
  2748  	}
  2749  
  2750  	fp.OnEndpointSliceUpdate(endpointSlice, endpointSliceTerminating)
  2751  	_ = fp.endpointsMap.Update(fp.endpointsChanges)
  2752  	localReadyEndpoints = fp.endpointsMap.LocalReadyEndpoints()
  2753  	if len(localReadyEndpoints) != 0 {
  2754  		t.Errorf("unexpected number of local ready endpoints, expected 0 but got: %d", len(localReadyEndpoints))
  2755  	}
  2756  }
  2757  
  2758  func TestProxierDeleteNodePortStaleUDP(t *testing.T) {
  2759  	fcmd := fakeexec.FakeCmd{}
  2760  	fexec := &fakeexec.FakeExec{
  2761  		LookPathFunc: func(cmd string) (string, error) { return cmd, nil },
  2762  	}
  2763  	execFunc := func(cmd string, args ...string) exec.Cmd {
  2764  		return fakeexec.InitFakeCmd(&fcmd, cmd, args...)
  2765  	}
  2766  	cmdOutput := "1 flow entries have been deleted"
  2767  	cmdFunc := func() ([]byte, []byte, error) { return []byte(cmdOutput), nil, nil }
  2768  
  2769  	// Delete ClusterIP entries
  2770  	fcmd.CombinedOutputScript = append(fcmd.CombinedOutputScript, cmdFunc)
  2771  	fexec.CommandScript = append(fexec.CommandScript, execFunc)
  2772  	// Delete ExternalIP entries
  2773  	fcmd.CombinedOutputScript = append(fcmd.CombinedOutputScript, cmdFunc)
  2774  	fexec.CommandScript = append(fexec.CommandScript, execFunc)
  2775  	// Delete LoadBalancerIP entries
  2776  	fcmd.CombinedOutputScript = append(fcmd.CombinedOutputScript, cmdFunc)
  2777  	fexec.CommandScript = append(fexec.CommandScript, execFunc)
  2778  	// Delete NodePort entries
  2779  	fcmd.CombinedOutputScript = append(fcmd.CombinedOutputScript, cmdFunc)
  2780  	fexec.CommandScript = append(fexec.CommandScript, execFunc)
  2781  
  2782  	_, fp := NewFakeProxier(v1.IPv4Protocol)
  2783  	fp.exec = fexec
  2784  
  2785  	svcIP := "172.30.0.41"
  2786  	extIP := "192.168.99.11"
  2787  	lbIngressIP := "1.2.3.4"
  2788  	svcPort := 80
  2789  	nodePort := 31201
  2790  	svcPortName := proxy.ServicePortName{
  2791  		NamespacedName: makeNSN("ns1", "svc1"),
  2792  		Port:           "p80",
  2793  		Protocol:       v1.ProtocolUDP,
  2794  	}
  2795  
  2796  	makeServiceMap(fp,
  2797  		makeTestService(svcPortName.Namespace, svcPortName.Name, func(svc *v1.Service) {
  2798  			svc.Spec.ClusterIP = svcIP
  2799  			svc.Spec.ExternalIPs = []string{extIP}
  2800  			svc.Spec.Type = "LoadBalancer"
  2801  			svc.Spec.Ports = []v1.ServicePort{{
  2802  				Name:     svcPortName.Port,
  2803  				Port:     int32(svcPort),
  2804  				Protocol: v1.ProtocolUDP,
  2805  				NodePort: int32(nodePort),
  2806  			}}
  2807  			svc.Status.LoadBalancer.Ingress = []v1.LoadBalancerIngress{{
  2808  				IP: lbIngressIP,
  2809  			}}
  2810  		}),
  2811  	)
  2812  
  2813  	fp.syncProxyRules()
  2814  	if fexec.CommandCalls != 0 {
  2815  		t.Fatalf("Created service without endpoints must not clear conntrack entries")
  2816  	}
  2817  
  2818  	epIP := "10.180.0.1"
  2819  	populateEndpointSlices(fp,
  2820  		makeTestEndpointSlice(svcPortName.Namespace, svcPortName.Name, 1, func(eps *discovery.EndpointSlice) {
  2821  			eps.AddressType = discovery.AddressTypeIPv4
  2822  			eps.Endpoints = []discovery.Endpoint{{
  2823  				Addresses: []string{epIP},
  2824  				Conditions: discovery.EndpointConditions{
  2825  					Serving: ptr.To(false),
  2826  				},
  2827  			}}
  2828  			eps.Ports = []discovery.EndpointPort{{
  2829  				Name:     ptr.To(svcPortName.Port),
  2830  				Port:     ptr.To(int32(svcPort)),
  2831  				Protocol: ptr.To(v1.ProtocolUDP),
  2832  			}}
  2833  		}),
  2834  	)
  2835  
  2836  	fp.syncProxyRules()
  2837  
  2838  	if fexec.CommandCalls != 0 {
  2839  		t.Fatalf("Updated UDP service with not ready endpoints must not clear UDP entries")
  2840  	}
  2841  
  2842  	populateEndpointSlices(fp,
  2843  		makeTestEndpointSlice(svcPortName.Namespace, svcPortName.Name, 1, func(eps *discovery.EndpointSlice) {
  2844  			eps.AddressType = discovery.AddressTypeIPv4
  2845  			eps.Endpoints = []discovery.Endpoint{{
  2846  				Addresses: []string{epIP},
  2847  				Conditions: discovery.EndpointConditions{
  2848  					Serving: ptr.To(true),
  2849  				},
  2850  			}}
  2851  			eps.Ports = []discovery.EndpointPort{{
  2852  				Name:     ptr.To(svcPortName.Port),
  2853  				Port:     ptr.To(int32(svcPort)),
  2854  				Protocol: ptr.To(v1.ProtocolUDP),
  2855  			}}
  2856  		}),
  2857  	)
  2858  
  2859  	fp.syncProxyRules()
  2860  
  2861  	if fexec.CommandCalls != 4 {
  2862  		t.Fatalf("Updated UDP service with new endpoints must clear UDP entries 4 times: ClusterIP, NodePort, ExternalIP and LB")
  2863  	}
  2864  
  2865  	// the order is not guaranteed so we have to compare the strings in any order
  2866  	expectedCommands := []string{
  2867  		// Delete ClusterIP Conntrack entries
  2868  		fmt.Sprintf("conntrack -D --orig-dst %s -p %s", svcIP, strings.ToLower(string((v1.ProtocolUDP)))),
  2869  		// Delete ExternalIP Conntrack entries
  2870  		fmt.Sprintf("conntrack -D --orig-dst %s -p %s", extIP, strings.ToLower(string((v1.ProtocolUDP)))),
  2871  		// Delete LoadBalancerIP Conntrack entries
  2872  		fmt.Sprintf("conntrack -D --orig-dst %s -p %s", lbIngressIP, strings.ToLower(string((v1.ProtocolUDP)))),
  2873  		// Delete NodePort Conntrack entrie
  2874  		fmt.Sprintf("conntrack -D -p %s --dport %d", strings.ToLower(string((v1.ProtocolUDP))), nodePort),
  2875  	}
  2876  	actualCommands := []string{
  2877  		strings.Join(fcmd.CombinedOutputLog[0], " "),
  2878  		strings.Join(fcmd.CombinedOutputLog[1], " "),
  2879  		strings.Join(fcmd.CombinedOutputLog[2], " "),
  2880  		strings.Join(fcmd.CombinedOutputLog[3], " "),
  2881  	}
  2882  	sort.Strings(expectedCommands)
  2883  	sort.Strings(actualCommands)
  2884  
  2885  	if !reflect.DeepEqual(expectedCommands, actualCommands) {
  2886  		t.Errorf("Expected commands: %v, but executed %v", expectedCommands, actualCommands)
  2887  	}
  2888  }
  2889  
  2890  // TODO(thockin): add *more* tests for syncProxyRules() or break it down further and test the pieces.
  2891  
  2892  // This test ensures that the iptables proxier supports translating Endpoints to
  2893  // iptables output when internalTrafficPolicy is specified
  2894  func TestInternalTrafficPolicy(t *testing.T) {
  2895  	type endpoint struct {
  2896  		ip       string
  2897  		hostname string
  2898  	}
  2899  
  2900  	testCases := []struct {
  2901  		name                  string
  2902  		line                  string
  2903  		internalTrafficPolicy *v1.ServiceInternalTrafficPolicy
  2904  		endpoints             []endpoint
  2905  		flowTests             []packetFlowTest
  2906  	}{
  2907  		{
  2908  			name:                  "internalTrafficPolicy is cluster",
  2909  			line:                  getLine(),
  2910  			internalTrafficPolicy: ptr.To(v1.ServiceInternalTrafficPolicyCluster),
  2911  			endpoints: []endpoint{
  2912  				{"10.0.1.1", testHostname},
  2913  				{"10.0.1.2", "host1"},
  2914  				{"10.0.1.3", "host2"},
  2915  			},
  2916  			flowTests: []packetFlowTest{
  2917  				{
  2918  					name:     "pod to ClusterIP hits all endpoints",
  2919  					sourceIP: "10.0.0.2",
  2920  					destIP:   "172.30.1.1",
  2921  					destPort: 80,
  2922  					output:   "10.0.1.1:80, 10.0.1.2:80, 10.0.1.3:80",
  2923  					masq:     false,
  2924  				},
  2925  			},
  2926  		},
  2927  		{
  2928  			name:                  "internalTrafficPolicy is local and there is one local endpoint",
  2929  			line:                  getLine(),
  2930  			internalTrafficPolicy: ptr.To(v1.ServiceInternalTrafficPolicyLocal),
  2931  			endpoints: []endpoint{
  2932  				{"10.0.1.1", testHostname},
  2933  				{"10.0.1.2", "host1"},
  2934  				{"10.0.1.3", "host2"},
  2935  			},
  2936  			flowTests: []packetFlowTest{
  2937  				{
  2938  					name:     "pod to ClusterIP hits only local endpoint",
  2939  					sourceIP: "10.0.0.2",
  2940  					destIP:   "172.30.1.1",
  2941  					destPort: 80,
  2942  					output:   "10.0.1.1:80",
  2943  					masq:     false,
  2944  				},
  2945  			},
  2946  		},
  2947  		{
  2948  			name:                  "internalTrafficPolicy is local and there are multiple local endpoints",
  2949  			line:                  getLine(),
  2950  			internalTrafficPolicy: ptr.To(v1.ServiceInternalTrafficPolicyLocal),
  2951  			endpoints: []endpoint{
  2952  				{"10.0.1.1", testHostname},
  2953  				{"10.0.1.2", testHostname},
  2954  				{"10.0.1.3", "host2"},
  2955  			},
  2956  			flowTests: []packetFlowTest{
  2957  				{
  2958  					name:     "pod to ClusterIP hits all local endpoints",
  2959  					sourceIP: "10.0.0.2",
  2960  					destIP:   "172.30.1.1",
  2961  					destPort: 80,
  2962  					output:   "10.0.1.1:80, 10.0.1.2:80",
  2963  					masq:     false,
  2964  				},
  2965  			},
  2966  		},
  2967  		{
  2968  			name:                  "internalTrafficPolicy is local and there are no local endpoints",
  2969  			line:                  getLine(),
  2970  			internalTrafficPolicy: ptr.To(v1.ServiceInternalTrafficPolicyLocal),
  2971  			endpoints: []endpoint{
  2972  				{"10.0.1.1", "host0"},
  2973  				{"10.0.1.2", "host1"},
  2974  				{"10.0.1.3", "host2"},
  2975  			},
  2976  			flowTests: []packetFlowTest{
  2977  				{
  2978  					name:     "no endpoints",
  2979  					sourceIP: "10.0.0.2",
  2980  					destIP:   "172.30.1.1",
  2981  					destPort: 80,
  2982  					output:   "DROP",
  2983  				},
  2984  			},
  2985  		},
  2986  	}
  2987  
  2988  	for _, tc := range testCases {
  2989  		t.Run(tc.name, func(t *testing.T) {
  2990  			nft, fp := NewFakeProxier(v1.IPv4Protocol)
  2991  			fp.OnServiceSynced()
  2992  			fp.OnEndpointSlicesSynced()
  2993  
  2994  			serviceName := "svc1"
  2995  			namespaceName := "ns1"
  2996  
  2997  			svc := &v1.Service{
  2998  				ObjectMeta: metav1.ObjectMeta{Name: serviceName, Namespace: namespaceName},
  2999  				Spec: v1.ServiceSpec{
  3000  					ClusterIP: "172.30.1.1",
  3001  					Selector:  map[string]string{"foo": "bar"},
  3002  					Ports:     []v1.ServicePort{{Name: "", Port: 80, Protocol: v1.ProtocolTCP}},
  3003  				},
  3004  			}
  3005  			if tc.internalTrafficPolicy != nil {
  3006  				svc.Spec.InternalTrafficPolicy = tc.internalTrafficPolicy
  3007  			}
  3008  
  3009  			fp.OnServiceAdd(svc)
  3010  
  3011  			endpointSlice := &discovery.EndpointSlice{
  3012  				ObjectMeta: metav1.ObjectMeta{
  3013  					Name:      fmt.Sprintf("%s-1", serviceName),
  3014  					Namespace: namespaceName,
  3015  					Labels:    map[string]string{discovery.LabelServiceName: serviceName},
  3016  				},
  3017  				Ports: []discovery.EndpointPort{{
  3018  					Name:     ptr.To(""),
  3019  					Port:     ptr.To[int32](80),
  3020  					Protocol: ptr.To(v1.ProtocolTCP),
  3021  				}},
  3022  				AddressType: discovery.AddressTypeIPv4,
  3023  			}
  3024  			for _, ep := range tc.endpoints {
  3025  				endpointSlice.Endpoints = append(endpointSlice.Endpoints, discovery.Endpoint{
  3026  					Addresses:  []string{ep.ip},
  3027  					Conditions: discovery.EndpointConditions{Ready: ptr.To(true)},
  3028  					NodeName:   ptr.To(ep.hostname),
  3029  				})
  3030  			}
  3031  
  3032  			fp.OnEndpointSliceAdd(endpointSlice)
  3033  			fp.syncProxyRules()
  3034  			runPacketFlowTests(t, tc.line, nft, testNodeIPs, tc.flowTests)
  3035  
  3036  			fp.OnEndpointSliceDelete(endpointSlice)
  3037  			fp.syncProxyRules()
  3038  			runPacketFlowTests(t, tc.line, nft, testNodeIPs, []packetFlowTest{
  3039  				{
  3040  					name:     "endpoints deleted",
  3041  					sourceIP: "10.0.0.2",
  3042  					destIP:   "172.30.1.1",
  3043  					destPort: 80,
  3044  					output:   "REJECT",
  3045  				},
  3046  			})
  3047  		})
  3048  	}
  3049  }
  3050  
  3051  // TestTerminatingEndpointsTrafficPolicyLocal tests that when there are local ready and
  3052  // ready + terminating endpoints, only the ready endpoints are used.
  3053  func TestTerminatingEndpointsTrafficPolicyLocal(t *testing.T) {
  3054  	service := &v1.Service{
  3055  		ObjectMeta: metav1.ObjectMeta{Name: "svc1", Namespace: "ns1"},
  3056  		Spec: v1.ServiceSpec{
  3057  			ClusterIP:             "172.30.1.1",
  3058  			Type:                  v1.ServiceTypeLoadBalancer,
  3059  			ExternalTrafficPolicy: v1.ServiceExternalTrafficPolicyLocal,
  3060  			Ports: []v1.ServicePort{
  3061  				{
  3062  					Name:       "",
  3063  					TargetPort: intstr.FromInt32(80),
  3064  					Port:       80,
  3065  					Protocol:   v1.ProtocolTCP,
  3066  				},
  3067  			},
  3068  			HealthCheckNodePort: 30000,
  3069  		},
  3070  		Status: v1.ServiceStatus{
  3071  			LoadBalancer: v1.LoadBalancerStatus{
  3072  				Ingress: []v1.LoadBalancerIngress{
  3073  					{IP: "1.2.3.4"},
  3074  				},
  3075  			},
  3076  		},
  3077  	}
  3078  
  3079  	testcases := []struct {
  3080  		name          string
  3081  		line          string
  3082  		endpointslice *discovery.EndpointSlice
  3083  		flowTests     []packetFlowTest
  3084  	}{
  3085  		{
  3086  			name: "ready endpoints exist",
  3087  			line: getLine(),
  3088  			endpointslice: &discovery.EndpointSlice{
  3089  				ObjectMeta: metav1.ObjectMeta{
  3090  					Name:      fmt.Sprintf("%s-1", "svc1"),
  3091  					Namespace: "ns1",
  3092  					Labels:    map[string]string{discovery.LabelServiceName: "svc1"},
  3093  				},
  3094  				Ports: []discovery.EndpointPort{{
  3095  					Name:     ptr.To(""),
  3096  					Port:     ptr.To[int32](80),
  3097  					Protocol: ptr.To(v1.ProtocolTCP),
  3098  				}},
  3099  				AddressType: discovery.AddressTypeIPv4,
  3100  				Endpoints: []discovery.Endpoint{
  3101  					{
  3102  						Addresses: []string{"10.0.1.1"},
  3103  						Conditions: discovery.EndpointConditions{
  3104  							Ready:       ptr.To(true),
  3105  							Serving:     ptr.To(true),
  3106  							Terminating: ptr.To(false),
  3107  						},
  3108  						NodeName: ptr.To(testHostname),
  3109  					},
  3110  					{
  3111  						Addresses: []string{"10.0.1.2"},
  3112  						Conditions: discovery.EndpointConditions{
  3113  							Ready:       ptr.To(true),
  3114  							Serving:     ptr.To(true),
  3115  							Terminating: ptr.To(false),
  3116  						},
  3117  						NodeName: ptr.To(testHostname),
  3118  					},
  3119  					{
  3120  						// this endpoint should be ignored for external since there are ready non-terminating endpoints
  3121  						Addresses: []string{"10.0.1.3"},
  3122  						Conditions: discovery.EndpointConditions{
  3123  							Ready:       ptr.To(false),
  3124  							Serving:     ptr.To(true),
  3125  							Terminating: ptr.To(true),
  3126  						},
  3127  						NodeName: ptr.To(testHostname),
  3128  					},
  3129  					{
  3130  						// this endpoint should be ignored for external since there are ready non-terminating endpoints
  3131  						Addresses: []string{"10.0.1.4"},
  3132  						Conditions: discovery.EndpointConditions{
  3133  							Ready:       ptr.To(false),
  3134  							Serving:     ptr.To(false),
  3135  							Terminating: ptr.To(true),
  3136  						},
  3137  						NodeName: ptr.To(testHostname),
  3138  					},
  3139  					{
  3140  						// this endpoint should be ignored for external since it's not local
  3141  						Addresses: []string{"10.0.1.5"},
  3142  						Conditions: discovery.EndpointConditions{
  3143  							Ready:       ptr.To(true),
  3144  							Serving:     ptr.To(true),
  3145  							Terminating: ptr.To(false),
  3146  						},
  3147  						NodeName: ptr.To("host-1"),
  3148  					},
  3149  				},
  3150  			},
  3151  			flowTests: []packetFlowTest{
  3152  				{
  3153  					name:     "pod to clusterIP",
  3154  					sourceIP: "10.0.0.2",
  3155  					destIP:   "172.30.1.1",
  3156  					destPort: 80,
  3157  					output:   "10.0.1.1:80, 10.0.1.2:80, 10.0.1.5:80",
  3158  					masq:     false,
  3159  				},
  3160  				{
  3161  					name:     "external to LB",
  3162  					sourceIP: testExternalClient,
  3163  					destIP:   "1.2.3.4",
  3164  					destPort: 80,
  3165  					output:   "10.0.1.1:80, 10.0.1.2:80",
  3166  					masq:     false,
  3167  				},
  3168  			},
  3169  		},
  3170  		{
  3171  			name: "only terminating endpoints exist",
  3172  			line: getLine(),
  3173  			endpointslice: &discovery.EndpointSlice{
  3174  				ObjectMeta: metav1.ObjectMeta{
  3175  					Name:      fmt.Sprintf("%s-1", "svc1"),
  3176  					Namespace: "ns1",
  3177  					Labels:    map[string]string{discovery.LabelServiceName: "svc1"},
  3178  				},
  3179  				Ports: []discovery.EndpointPort{{
  3180  					Name:     ptr.To(""),
  3181  					Port:     ptr.To[int32](80),
  3182  					Protocol: ptr.To(v1.ProtocolTCP),
  3183  				}},
  3184  				AddressType: discovery.AddressTypeIPv4,
  3185  				Endpoints: []discovery.Endpoint{
  3186  					{
  3187  						// this endpoint should be used since there are only ready terminating endpoints
  3188  						Addresses: []string{"10.0.1.2"},
  3189  						Conditions: discovery.EndpointConditions{
  3190  							Ready:       ptr.To(false),
  3191  							Serving:     ptr.To(true),
  3192  							Terminating: ptr.To(true),
  3193  						},
  3194  						NodeName: ptr.To(testHostname),
  3195  					},
  3196  					{
  3197  						// this endpoint should be used since there are only ready terminating endpoints
  3198  						Addresses: []string{"10.0.1.3"},
  3199  						Conditions: discovery.EndpointConditions{
  3200  							Ready:       ptr.To(false),
  3201  							Serving:     ptr.To(true),
  3202  							Terminating: ptr.To(true),
  3203  						},
  3204  						NodeName: ptr.To(testHostname),
  3205  					},
  3206  					{
  3207  						// this endpoint should not be used since it is both terminating and not ready.
  3208  						Addresses: []string{"10.0.1.4"},
  3209  						Conditions: discovery.EndpointConditions{
  3210  							Ready:       ptr.To(false),
  3211  							Serving:     ptr.To(false),
  3212  							Terminating: ptr.To(true),
  3213  						},
  3214  						NodeName: ptr.To(testHostname),
  3215  					},
  3216  					{
  3217  						// this endpoint should be ignored for external since it's not local
  3218  						Addresses: []string{"10.0.1.5"},
  3219  						Conditions: discovery.EndpointConditions{
  3220  							Ready:       ptr.To(true),
  3221  							Serving:     ptr.To(true),
  3222  							Terminating: ptr.To(false),
  3223  						},
  3224  						NodeName: ptr.To("host-1"),
  3225  					},
  3226  				},
  3227  			},
  3228  			flowTests: []packetFlowTest{
  3229  				{
  3230  					name:     "pod to clusterIP",
  3231  					sourceIP: "10.0.0.2",
  3232  					destIP:   "172.30.1.1",
  3233  					destPort: 80,
  3234  					output:   "10.0.1.5:80",
  3235  					masq:     false,
  3236  				},
  3237  				{
  3238  					name:     "external to LB",
  3239  					sourceIP: testExternalClient,
  3240  					destIP:   "1.2.3.4",
  3241  					destPort: 80,
  3242  					output:   "10.0.1.2:80, 10.0.1.3:80",
  3243  					masq:     false,
  3244  				},
  3245  			},
  3246  		},
  3247  		{
  3248  			name: "terminating endpoints on remote node",
  3249  			line: getLine(),
  3250  			endpointslice: &discovery.EndpointSlice{
  3251  				ObjectMeta: metav1.ObjectMeta{
  3252  					Name:      fmt.Sprintf("%s-1", "svc1"),
  3253  					Namespace: "ns1",
  3254  					Labels:    map[string]string{discovery.LabelServiceName: "svc1"},
  3255  				},
  3256  				Ports: []discovery.EndpointPort{{
  3257  					Name:     ptr.To(""),
  3258  					Port:     ptr.To[int32](80),
  3259  					Protocol: ptr.To(v1.ProtocolTCP),
  3260  				}},
  3261  				AddressType: discovery.AddressTypeIPv4,
  3262  				Endpoints: []discovery.Endpoint{
  3263  					{
  3264  						// this endpoint won't be used because it's not local,
  3265  						// but it will prevent a REJECT rule from being created
  3266  						Addresses: []string{"10.0.1.5"},
  3267  						Conditions: discovery.EndpointConditions{
  3268  							Ready:       ptr.To(false),
  3269  							Serving:     ptr.To(true),
  3270  							Terminating: ptr.To(true),
  3271  						},
  3272  						NodeName: ptr.To("host-1"),
  3273  					},
  3274  				},
  3275  			},
  3276  			flowTests: []packetFlowTest{
  3277  				{
  3278  					name:     "pod to clusterIP",
  3279  					sourceIP: "10.0.0.2",
  3280  					destIP:   "172.30.1.1",
  3281  					destPort: 80,
  3282  					output:   "10.0.1.5:80",
  3283  				},
  3284  				{
  3285  					name:     "external to LB, no locally-usable endpoints",
  3286  					sourceIP: testExternalClient,
  3287  					destIP:   "1.2.3.4",
  3288  					destPort: 80,
  3289  					output:   "DROP",
  3290  				},
  3291  			},
  3292  		},
  3293  		{
  3294  			name: "no usable endpoints on any node",
  3295  			line: getLine(),
  3296  			endpointslice: &discovery.EndpointSlice{
  3297  				ObjectMeta: metav1.ObjectMeta{
  3298  					Name:      fmt.Sprintf("%s-1", "svc1"),
  3299  					Namespace: "ns1",
  3300  					Labels:    map[string]string{discovery.LabelServiceName: "svc1"},
  3301  				},
  3302  				Ports: []discovery.EndpointPort{{
  3303  					Name:     ptr.To(""),
  3304  					Port:     ptr.To[int32](80),
  3305  					Protocol: ptr.To(v1.ProtocolTCP),
  3306  				}},
  3307  				AddressType: discovery.AddressTypeIPv4,
  3308  				Endpoints: []discovery.Endpoint{
  3309  					{
  3310  						// Local but not ready or serving
  3311  						Addresses: []string{"10.0.1.5"},
  3312  						Conditions: discovery.EndpointConditions{
  3313  							Ready:       ptr.To(false),
  3314  							Serving:     ptr.To(false),
  3315  							Terminating: ptr.To(true),
  3316  						},
  3317  						NodeName: ptr.To(testHostname),
  3318  					},
  3319  					{
  3320  						// Remote and not ready or serving
  3321  						Addresses: []string{"10.0.1.5"},
  3322  						Conditions: discovery.EndpointConditions{
  3323  							Ready:       ptr.To(false),
  3324  							Serving:     ptr.To(false),
  3325  							Terminating: ptr.To(true),
  3326  						},
  3327  						NodeName: ptr.To("host-1"),
  3328  					},
  3329  				},
  3330  			},
  3331  			flowTests: []packetFlowTest{
  3332  				{
  3333  					name:     "pod to clusterIP, no usable endpoints",
  3334  					sourceIP: "10.0.0.2",
  3335  					destIP:   "172.30.1.1",
  3336  					destPort: 80,
  3337  					output:   "REJECT",
  3338  				},
  3339  				{
  3340  					name:     "external to LB, no usable endpoints",
  3341  					sourceIP: testExternalClient,
  3342  					destIP:   "1.2.3.4",
  3343  					destPort: 80,
  3344  					output:   "REJECT",
  3345  				},
  3346  			},
  3347  		},
  3348  	}
  3349  
  3350  	for _, testcase := range testcases {
  3351  		t.Run(testcase.name, func(t *testing.T) {
  3352  			nft, fp := NewFakeProxier(v1.IPv4Protocol)
  3353  			fp.OnServiceSynced()
  3354  			fp.OnEndpointSlicesSynced()
  3355  
  3356  			fp.OnServiceAdd(service)
  3357  
  3358  			fp.OnEndpointSliceAdd(testcase.endpointslice)
  3359  			fp.syncProxyRules()
  3360  			runPacketFlowTests(t, testcase.line, nft, testNodeIPs, testcase.flowTests)
  3361  
  3362  			fp.OnEndpointSliceDelete(testcase.endpointslice)
  3363  			fp.syncProxyRules()
  3364  			runPacketFlowTests(t, testcase.line, nft, testNodeIPs, []packetFlowTest{
  3365  				{
  3366  					name:     "pod to clusterIP after endpoints deleted",
  3367  					sourceIP: "10.0.0.2",
  3368  					destIP:   "172.30.1.1",
  3369  					destPort: 80,
  3370  					output:   "REJECT",
  3371  				},
  3372  				{
  3373  					name:     "external to LB after endpoints deleted",
  3374  					sourceIP: testExternalClient,
  3375  					destIP:   "1.2.3.4",
  3376  					destPort: 80,
  3377  					output:   "REJECT",
  3378  				},
  3379  			})
  3380  		})
  3381  	}
  3382  }
  3383  
  3384  // TestTerminatingEndpointsTrafficPolicyCluster tests that when there are cluster-wide
  3385  // ready and ready + terminating endpoints, only the ready endpoints are used.
  3386  func TestTerminatingEndpointsTrafficPolicyCluster(t *testing.T) {
  3387  	service := &v1.Service{
  3388  		ObjectMeta: metav1.ObjectMeta{Name: "svc1", Namespace: "ns1"},
  3389  		Spec: v1.ServiceSpec{
  3390  			ClusterIP:             "172.30.1.1",
  3391  			Type:                  v1.ServiceTypeLoadBalancer,
  3392  			ExternalTrafficPolicy: v1.ServiceExternalTrafficPolicyCluster,
  3393  			Ports: []v1.ServicePort{
  3394  				{
  3395  					Name:       "",
  3396  					TargetPort: intstr.FromInt32(80),
  3397  					Port:       80,
  3398  					Protocol:   v1.ProtocolTCP,
  3399  				},
  3400  			},
  3401  			HealthCheckNodePort: 30000,
  3402  		},
  3403  		Status: v1.ServiceStatus{
  3404  			LoadBalancer: v1.LoadBalancerStatus{
  3405  				Ingress: []v1.LoadBalancerIngress{
  3406  					{IP: "1.2.3.4"},
  3407  				},
  3408  			},
  3409  		},
  3410  	}
  3411  
  3412  	testcases := []struct {
  3413  		name          string
  3414  		line          string
  3415  		endpointslice *discovery.EndpointSlice
  3416  		flowTests     []packetFlowTest
  3417  	}{
  3418  		{
  3419  			name: "ready endpoints exist",
  3420  			line: getLine(),
  3421  			endpointslice: &discovery.EndpointSlice{
  3422  				ObjectMeta: metav1.ObjectMeta{
  3423  					Name:      fmt.Sprintf("%s-1", "svc1"),
  3424  					Namespace: "ns1",
  3425  					Labels:    map[string]string{discovery.LabelServiceName: "svc1"},
  3426  				},
  3427  				Ports: []discovery.EndpointPort{{
  3428  					Name:     ptr.To(""),
  3429  					Port:     ptr.To[int32](80),
  3430  					Protocol: ptr.To(v1.ProtocolTCP),
  3431  				}},
  3432  				AddressType: discovery.AddressTypeIPv4,
  3433  				Endpoints: []discovery.Endpoint{
  3434  					{
  3435  						Addresses: []string{"10.0.1.1"},
  3436  						Conditions: discovery.EndpointConditions{
  3437  							Ready:       ptr.To(true),
  3438  							Serving:     ptr.To(true),
  3439  							Terminating: ptr.To(false),
  3440  						},
  3441  						NodeName: ptr.To(testHostname),
  3442  					},
  3443  					{
  3444  						Addresses: []string{"10.0.1.2"},
  3445  						Conditions: discovery.EndpointConditions{
  3446  							Ready:       ptr.To(true),
  3447  							Serving:     ptr.To(true),
  3448  							Terminating: ptr.To(false),
  3449  						},
  3450  						NodeName: ptr.To(testHostname),
  3451  					},
  3452  					{
  3453  						// this endpoint should be ignored since there are ready non-terminating endpoints
  3454  						Addresses: []string{"10.0.1.3"},
  3455  						Conditions: discovery.EndpointConditions{
  3456  							Ready:       ptr.To(false),
  3457  							Serving:     ptr.To(true),
  3458  							Terminating: ptr.To(true),
  3459  						},
  3460  						NodeName: ptr.To("another-host"),
  3461  					},
  3462  					{
  3463  						// this endpoint should be ignored since it is not "serving"
  3464  						Addresses: []string{"10.0.1.4"},
  3465  						Conditions: discovery.EndpointConditions{
  3466  							Ready:       ptr.To(false),
  3467  							Serving:     ptr.To(false),
  3468  							Terminating: ptr.To(true),
  3469  						},
  3470  						NodeName: ptr.To("another-host"),
  3471  					},
  3472  					{
  3473  						Addresses: []string{"10.0.1.5"},
  3474  						Conditions: discovery.EndpointConditions{
  3475  							Ready:       ptr.To(true),
  3476  							Serving:     ptr.To(true),
  3477  							Terminating: ptr.To(false),
  3478  						},
  3479  						NodeName: ptr.To("another-host"),
  3480  					},
  3481  				},
  3482  			},
  3483  			flowTests: []packetFlowTest{
  3484  				{
  3485  					name:     "pod to clusterIP",
  3486  					sourceIP: "10.0.0.2",
  3487  					destIP:   "172.30.1.1",
  3488  					destPort: 80,
  3489  					output:   "10.0.1.1:80, 10.0.1.2:80, 10.0.1.5:80",
  3490  					masq:     false,
  3491  				},
  3492  				{
  3493  					name:     "external to LB",
  3494  					sourceIP: testExternalClient,
  3495  					destIP:   "1.2.3.4",
  3496  					destPort: 80,
  3497  					output:   "10.0.1.1:80, 10.0.1.2:80, 10.0.1.5:80",
  3498  					masq:     true,
  3499  				},
  3500  			},
  3501  		},
  3502  		{
  3503  			name: "only terminating endpoints exist",
  3504  			line: getLine(),
  3505  			endpointslice: &discovery.EndpointSlice{
  3506  				ObjectMeta: metav1.ObjectMeta{
  3507  					Name:      fmt.Sprintf("%s-1", "svc1"),
  3508  					Namespace: "ns1",
  3509  					Labels:    map[string]string{discovery.LabelServiceName: "svc1"},
  3510  				},
  3511  				Ports: []discovery.EndpointPort{{
  3512  					Name:     ptr.To(""),
  3513  					Port:     ptr.To[int32](80),
  3514  					Protocol: ptr.To(v1.ProtocolTCP),
  3515  				}},
  3516  				AddressType: discovery.AddressTypeIPv4,
  3517  				Endpoints: []discovery.Endpoint{
  3518  					{
  3519  						// this endpoint should be used since there are only ready terminating endpoints
  3520  						Addresses: []string{"10.0.1.2"},
  3521  						Conditions: discovery.EndpointConditions{
  3522  							Ready:       ptr.To(false),
  3523  							Serving:     ptr.To(true),
  3524  							Terminating: ptr.To(true),
  3525  						},
  3526  						NodeName: ptr.To(testHostname),
  3527  					},
  3528  					{
  3529  						// this endpoint should be used since there are only ready terminating endpoints
  3530  						Addresses: []string{"10.0.1.3"},
  3531  						Conditions: discovery.EndpointConditions{
  3532  							Ready:       ptr.To(false),
  3533  							Serving:     ptr.To(true),
  3534  							Terminating: ptr.To(true),
  3535  						},
  3536  						NodeName: ptr.To(testHostname),
  3537  					},
  3538  					{
  3539  						// this endpoint should not be used since it is both terminating and not ready.
  3540  						Addresses: []string{"10.0.1.4"},
  3541  						Conditions: discovery.EndpointConditions{
  3542  							Ready:       ptr.To(false),
  3543  							Serving:     ptr.To(false),
  3544  							Terminating: ptr.To(true),
  3545  						},
  3546  						NodeName: ptr.To("another-host"),
  3547  					},
  3548  					{
  3549  						// this endpoint should be used since there are only ready terminating endpoints
  3550  						Addresses: []string{"10.0.1.5"},
  3551  						Conditions: discovery.EndpointConditions{
  3552  							Ready:       ptr.To(false),
  3553  							Serving:     ptr.To(true),
  3554  							Terminating: ptr.To(true),
  3555  						},
  3556  						NodeName: ptr.To("another-host"),
  3557  					},
  3558  				},
  3559  			},
  3560  			flowTests: []packetFlowTest{
  3561  				{
  3562  					name:     "pod to clusterIP",
  3563  					sourceIP: "10.0.0.2",
  3564  					destIP:   "172.30.1.1",
  3565  					destPort: 80,
  3566  					output:   "10.0.1.2:80, 10.0.1.3:80, 10.0.1.5:80",
  3567  					masq:     false,
  3568  				},
  3569  				{
  3570  					name:     "external to LB",
  3571  					sourceIP: testExternalClient,
  3572  					destIP:   "1.2.3.4",
  3573  					destPort: 80,
  3574  					output:   "10.0.1.2:80, 10.0.1.3:80, 10.0.1.5:80",
  3575  					masq:     true,
  3576  				},
  3577  			},
  3578  		},
  3579  		{
  3580  			name: "terminating endpoints on remote node",
  3581  			line: getLine(),
  3582  			endpointslice: &discovery.EndpointSlice{
  3583  				ObjectMeta: metav1.ObjectMeta{
  3584  					Name:      fmt.Sprintf("%s-1", "svc1"),
  3585  					Namespace: "ns1",
  3586  					Labels:    map[string]string{discovery.LabelServiceName: "svc1"},
  3587  				},
  3588  				Ports: []discovery.EndpointPort{{
  3589  					Name:     ptr.To(""),
  3590  					Port:     ptr.To[int32](80),
  3591  					Protocol: ptr.To(v1.ProtocolTCP),
  3592  				}},
  3593  				AddressType: discovery.AddressTypeIPv4,
  3594  				Endpoints: []discovery.Endpoint{
  3595  					{
  3596  						Addresses: []string{"10.0.1.5"},
  3597  						Conditions: discovery.EndpointConditions{
  3598  							Ready:       ptr.To(false),
  3599  							Serving:     ptr.To(true),
  3600  							Terminating: ptr.To(true),
  3601  						},
  3602  						NodeName: ptr.To("host-1"),
  3603  					},
  3604  				},
  3605  			},
  3606  			flowTests: []packetFlowTest{
  3607  				{
  3608  					name:     "pod to clusterIP",
  3609  					sourceIP: "10.0.0.2",
  3610  					destIP:   "172.30.1.1",
  3611  					destPort: 80,
  3612  					output:   "10.0.1.5:80",
  3613  					masq:     false,
  3614  				},
  3615  				{
  3616  					name:     "external to LB",
  3617  					sourceIP: testExternalClient,
  3618  					destIP:   "1.2.3.4",
  3619  					destPort: 80,
  3620  					output:   "10.0.1.5:80",
  3621  					masq:     true,
  3622  				},
  3623  			},
  3624  		},
  3625  		{
  3626  			name: "no usable endpoints on any node",
  3627  			line: getLine(),
  3628  			endpointslice: &discovery.EndpointSlice{
  3629  				ObjectMeta: metav1.ObjectMeta{
  3630  					Name:      fmt.Sprintf("%s-1", "svc1"),
  3631  					Namespace: "ns1",
  3632  					Labels:    map[string]string{discovery.LabelServiceName: "svc1"},
  3633  				},
  3634  				Ports: []discovery.EndpointPort{{
  3635  					Name:     ptr.To(""),
  3636  					Port:     ptr.To[int32](80),
  3637  					Protocol: ptr.To(v1.ProtocolTCP),
  3638  				}},
  3639  				AddressType: discovery.AddressTypeIPv4,
  3640  				Endpoints: []discovery.Endpoint{
  3641  					{
  3642  						// Local, not ready or serving
  3643  						Addresses: []string{"10.0.1.5"},
  3644  						Conditions: discovery.EndpointConditions{
  3645  							Ready:       ptr.To(false),
  3646  							Serving:     ptr.To(false),
  3647  							Terminating: ptr.To(true),
  3648  						},
  3649  						NodeName: ptr.To(testHostname),
  3650  					},
  3651  					{
  3652  						// Remote, not ready or serving
  3653  						Addresses: []string{"10.0.1.5"},
  3654  						Conditions: discovery.EndpointConditions{
  3655  							Ready:       ptr.To(false),
  3656  							Serving:     ptr.To(false),
  3657  							Terminating: ptr.To(true),
  3658  						},
  3659  						NodeName: ptr.To("host-1"),
  3660  					},
  3661  				},
  3662  			},
  3663  			flowTests: []packetFlowTest{
  3664  				{
  3665  					name:     "pod to clusterIP",
  3666  					sourceIP: "10.0.0.2",
  3667  					destIP:   "172.30.1.1",
  3668  					destPort: 80,
  3669  					output:   "REJECT",
  3670  				},
  3671  				{
  3672  					name:     "external to LB",
  3673  					sourceIP: testExternalClient,
  3674  					destIP:   "1.2.3.4",
  3675  					destPort: 80,
  3676  					output:   "REJECT",
  3677  				},
  3678  			},
  3679  		},
  3680  	}
  3681  
  3682  	for _, testcase := range testcases {
  3683  		t.Run(testcase.name, func(t *testing.T) {
  3684  
  3685  			nft, fp := NewFakeProxier(v1.IPv4Protocol)
  3686  			fp.OnServiceSynced()
  3687  			fp.OnEndpointSlicesSynced()
  3688  
  3689  			fp.OnServiceAdd(service)
  3690  
  3691  			fp.OnEndpointSliceAdd(testcase.endpointslice)
  3692  			fp.syncProxyRules()
  3693  			runPacketFlowTests(t, testcase.line, nft, testNodeIPs, testcase.flowTests)
  3694  
  3695  			fp.OnEndpointSliceDelete(testcase.endpointslice)
  3696  			fp.syncProxyRules()
  3697  			runPacketFlowTests(t, testcase.line, nft, testNodeIPs, []packetFlowTest{
  3698  				{
  3699  					name:     "pod to clusterIP after endpoints deleted",
  3700  					sourceIP: "10.0.0.2",
  3701  					destIP:   "172.30.1.1",
  3702  					destPort: 80,
  3703  					output:   "REJECT",
  3704  				},
  3705  				{
  3706  					name:     "external to LB after endpoints deleted",
  3707  					sourceIP: testExternalClient,
  3708  					destIP:   "1.2.3.4",
  3709  					destPort: 80,
  3710  					output:   "REJECT",
  3711  				},
  3712  			})
  3713  		})
  3714  	}
  3715  }
  3716  
  3717  func TestInternalExternalMasquerade(t *testing.T) {
  3718  	// (Put the test setup code in an internal function so we can have it here at the
  3719  	// top, before the test cases that will be run against it.)
  3720  	setupTest := func(fp *Proxier) {
  3721  		makeServiceMap(fp,
  3722  			makeTestService("ns1", "svc1", func(svc *v1.Service) {
  3723  				svc.Spec.Type = "LoadBalancer"
  3724  				svc.Spec.ClusterIP = "172.30.0.41"
  3725  				svc.Spec.Ports = []v1.ServicePort{{
  3726  					Name:     "p80",
  3727  					Port:     80,
  3728  					Protocol: v1.ProtocolTCP,
  3729  					NodePort: int32(3001),
  3730  				}}
  3731  				svc.Spec.HealthCheckNodePort = 30001
  3732  				svc.Status.LoadBalancer.Ingress = []v1.LoadBalancerIngress{{
  3733  					IP: "1.2.3.4",
  3734  				}}
  3735  			}),
  3736  			makeTestService("ns2", "svc2", func(svc *v1.Service) {
  3737  				svc.Spec.Type = "LoadBalancer"
  3738  				svc.Spec.ClusterIP = "172.30.0.42"
  3739  				svc.Spec.Ports = []v1.ServicePort{{
  3740  					Name:     "p80",
  3741  					Port:     80,
  3742  					Protocol: v1.ProtocolTCP,
  3743  					NodePort: int32(3002),
  3744  				}}
  3745  				svc.Spec.HealthCheckNodePort = 30002
  3746  				svc.Spec.ExternalTrafficPolicy = v1.ServiceExternalTrafficPolicyLocal
  3747  				svc.Status.LoadBalancer.Ingress = []v1.LoadBalancerIngress{{
  3748  					IP: "5.6.7.8",
  3749  				}}
  3750  			}),
  3751  			makeTestService("ns3", "svc3", func(svc *v1.Service) {
  3752  				svc.Spec.Type = "LoadBalancer"
  3753  				svc.Spec.ClusterIP = "172.30.0.43"
  3754  				svc.Spec.Ports = []v1.ServicePort{{
  3755  					Name:     "p80",
  3756  					Port:     80,
  3757  					Protocol: v1.ProtocolTCP,
  3758  					NodePort: int32(3003),
  3759  				}}
  3760  				svc.Spec.HealthCheckNodePort = 30003
  3761  				svc.Spec.InternalTrafficPolicy = ptr.To(v1.ServiceInternalTrafficPolicyLocal)
  3762  				svc.Status.LoadBalancer.Ingress = []v1.LoadBalancerIngress{{
  3763  					IP: "9.10.11.12",
  3764  				}}
  3765  			}),
  3766  		)
  3767  
  3768  		populateEndpointSlices(fp,
  3769  			makeTestEndpointSlice("ns1", "svc1", 1, func(eps *discovery.EndpointSlice) {
  3770  				eps.AddressType = discovery.AddressTypeIPv4
  3771  				eps.Endpoints = []discovery.Endpoint{
  3772  					{
  3773  						Addresses: []string{"10.180.0.1"},
  3774  						NodeName:  ptr.To(testHostname),
  3775  					},
  3776  					{
  3777  						Addresses: []string{"10.180.1.1"},
  3778  						NodeName:  ptr.To("remote"),
  3779  					},
  3780  				}
  3781  				eps.Ports = []discovery.EndpointPort{{
  3782  					Name:     ptr.To("p80"),
  3783  					Port:     ptr.To[int32](80),
  3784  					Protocol: ptr.To(v1.ProtocolTCP),
  3785  				}}
  3786  			}),
  3787  			makeTestEndpointSlice("ns2", "svc2", 1, func(eps *discovery.EndpointSlice) {
  3788  				eps.AddressType = discovery.AddressTypeIPv4
  3789  				eps.Endpoints = []discovery.Endpoint{
  3790  					{
  3791  						Addresses: []string{"10.180.0.2"},
  3792  						NodeName:  ptr.To(testHostname),
  3793  					},
  3794  					{
  3795  						Addresses: []string{"10.180.1.2"},
  3796  						NodeName:  ptr.To("remote"),
  3797  					},
  3798  				}
  3799  				eps.Ports = []discovery.EndpointPort{{
  3800  					Name:     ptr.To("p80"),
  3801  					Port:     ptr.To[int32](80),
  3802  					Protocol: ptr.To(v1.ProtocolTCP),
  3803  				}}
  3804  			}),
  3805  			makeTestEndpointSlice("ns3", "svc3", 1, func(eps *discovery.EndpointSlice) {
  3806  				eps.AddressType = discovery.AddressTypeIPv4
  3807  				eps.Endpoints = []discovery.Endpoint{
  3808  					{
  3809  						Addresses: []string{"10.180.0.3"},
  3810  						NodeName:  ptr.To(testHostname),
  3811  					},
  3812  					{
  3813  						Addresses: []string{"10.180.1.3"},
  3814  						NodeName:  ptr.To("remote"),
  3815  					},
  3816  				}
  3817  				eps.Ports = []discovery.EndpointPort{{
  3818  					Name:     ptr.To("p80"),
  3819  					Port:     ptr.To[int32](80),
  3820  					Protocol: ptr.To(v1.ProtocolTCP),
  3821  				}}
  3822  			}),
  3823  		)
  3824  
  3825  		fp.syncProxyRules()
  3826  	}
  3827  
  3828  	// We use the same flowTests for all of the testCases. The "output" and "masq"
  3829  	// values here represent the normal case (working localDetector, no masqueradeAll)
  3830  	flowTests := []packetFlowTest{
  3831  		{
  3832  			name:     "pod to ClusterIP",
  3833  			sourceIP: "10.0.0.2",
  3834  			destIP:   "172.30.0.41",
  3835  			destPort: 80,
  3836  			output:   "10.180.0.1:80, 10.180.1.1:80",
  3837  			masq:     false,
  3838  		},
  3839  		{
  3840  			name:     "pod to NodePort",
  3841  			sourceIP: "10.0.0.2",
  3842  			destIP:   testNodeIP,
  3843  			destPort: 3001,
  3844  			output:   "10.180.0.1:80, 10.180.1.1:80",
  3845  			masq:     true,
  3846  		},
  3847  		{
  3848  			name:     "pod to LB",
  3849  			sourceIP: "10.0.0.2",
  3850  			destIP:   "1.2.3.4",
  3851  			destPort: 80,
  3852  			output:   "10.180.0.1:80, 10.180.1.1:80",
  3853  			masq:     true,
  3854  		},
  3855  		{
  3856  			name:     "node to ClusterIP",
  3857  			sourceIP: testNodeIP,
  3858  			destIP:   "172.30.0.41",
  3859  			destPort: 80,
  3860  			output:   "10.180.0.1:80, 10.180.1.1:80",
  3861  			masq:     true,
  3862  		},
  3863  		{
  3864  			name:     "node to NodePort",
  3865  			sourceIP: testNodeIP,
  3866  			destIP:   testNodeIP,
  3867  			destPort: 3001,
  3868  			output:   "10.180.0.1:80, 10.180.1.1:80",
  3869  			masq:     true,
  3870  		},
  3871  		{
  3872  			name:     "node to LB",
  3873  			sourceIP: testNodeIP,
  3874  			destIP:   "1.2.3.4",
  3875  			destPort: 80,
  3876  			output:   "10.180.0.1:80, 10.180.1.1:80",
  3877  			masq:     true,
  3878  		},
  3879  		{
  3880  			name:     "external to ClusterIP",
  3881  			sourceIP: testExternalClient,
  3882  			destIP:   "172.30.0.41",
  3883  			destPort: 80,
  3884  			output:   "10.180.0.1:80, 10.180.1.1:80",
  3885  			masq:     true,
  3886  		},
  3887  		{
  3888  			name:     "external to NodePort",
  3889  			sourceIP: testExternalClient,
  3890  			destIP:   testNodeIP,
  3891  			destPort: 3001,
  3892  			output:   "10.180.0.1:80, 10.180.1.1:80",
  3893  			masq:     true,
  3894  		},
  3895  		{
  3896  			name:     "external to LB",
  3897  			sourceIP: testExternalClient,
  3898  			destIP:   "1.2.3.4",
  3899  			destPort: 80,
  3900  			output:   "10.180.0.1:80, 10.180.1.1:80",
  3901  			masq:     true,
  3902  		},
  3903  		{
  3904  			name:     "pod to ClusterIP with eTP:Local",
  3905  			sourceIP: "10.0.0.2",
  3906  			destIP:   "172.30.0.42",
  3907  			destPort: 80,
  3908  
  3909  			// externalTrafficPolicy does not apply to ClusterIP traffic, so same
  3910  			// as "Pod to ClusterIP"
  3911  			output: "10.180.0.2:80, 10.180.1.2:80",
  3912  			masq:   false,
  3913  		},
  3914  		{
  3915  			name:     "pod to NodePort with eTP:Local",
  3916  			sourceIP: "10.0.0.2",
  3917  			destIP:   testNodeIP,
  3918  			destPort: 3002,
  3919  
  3920  			// See the comment below in the "pod to LB with eTP:Local" case.
  3921  			// It doesn't actually make sense to short-circuit here, since if
  3922  			// you connect directly to a NodePort from outside the cluster,
  3923  			// you only get the local endpoints. But it's simpler for us and
  3924  			// slightly more convenient for users to have this case get
  3925  			// short-circuited too.
  3926  			output: "10.180.0.2:80, 10.180.1.2:80",
  3927  			masq:   false,
  3928  		},
  3929  		{
  3930  			name:     "pod to LB with eTP:Local",
  3931  			sourceIP: "10.0.0.2",
  3932  			destIP:   "5.6.7.8",
  3933  			destPort: 80,
  3934  
  3935  			// The short-circuit rule is supposed to make this behave the same
  3936  			// way it would if the packet actually went out to the LB and then
  3937  			// came back into the cluster. So it gets routed to all endpoints,
  3938  			// not just local ones. In reality, if the packet actually left
  3939  			// the cluster, it would have to get masqueraded, but since we can
  3940  			// avoid doing that in the short-circuit case, and not masquerading
  3941  			// is more useful, we avoid masquerading.
  3942  			output: "10.180.0.2:80, 10.180.1.2:80",
  3943  			masq:   false,
  3944  		},
  3945  		{
  3946  			name:     "node to ClusterIP with eTP:Local",
  3947  			sourceIP: testNodeIP,
  3948  			destIP:   "172.30.0.42",
  3949  			destPort: 80,
  3950  
  3951  			// externalTrafficPolicy does not apply to ClusterIP traffic, so same
  3952  			// as "node to ClusterIP"
  3953  			output: "10.180.0.2:80, 10.180.1.2:80",
  3954  			masq:   true,
  3955  		},
  3956  		{
  3957  			name:     "node to NodePort with eTP:Local",
  3958  			sourceIP: testNodeIP,
  3959  			destIP:   testNodeIP,
  3960  			destPort: 3001,
  3961  
  3962  			// The traffic gets short-circuited, ignoring externalTrafficPolicy, so
  3963  			// same as "node to NodePort" above.
  3964  			output: "10.180.0.1:80, 10.180.1.1:80",
  3965  			masq:   true,
  3966  		},
  3967  		{
  3968  			name:     "node to LB with eTP:Local",
  3969  			sourceIP: testNodeIP,
  3970  			destIP:   "5.6.7.8",
  3971  			destPort: 80,
  3972  
  3973  			// The traffic gets short-circuited, ignoring externalTrafficPolicy, so
  3974  			// same as "node to LB" above.
  3975  			output: "10.180.0.2:80, 10.180.1.2:80",
  3976  			masq:   true,
  3977  		},
  3978  		{
  3979  			name:     "external to ClusterIP with eTP:Local",
  3980  			sourceIP: testExternalClient,
  3981  			destIP:   "172.30.0.42",
  3982  			destPort: 80,
  3983  
  3984  			// externalTrafficPolicy does not apply to ClusterIP traffic, so same
  3985  			// as "external to ClusterIP" above.
  3986  			output: "10.180.0.2:80, 10.180.1.2:80",
  3987  			masq:   true,
  3988  		},
  3989  		{
  3990  			name:     "external to NodePort with eTP:Local",
  3991  			sourceIP: testExternalClient,
  3992  			destIP:   testNodeIP,
  3993  			destPort: 3002,
  3994  
  3995  			// externalTrafficPolicy applies; only the local endpoint is
  3996  			// selected, and we don't masquerade.
  3997  			output: "10.180.0.2:80",
  3998  			masq:   false,
  3999  		},
  4000  		{
  4001  			name:     "external to LB with eTP:Local",
  4002  			sourceIP: testExternalClient,
  4003  			destIP:   "5.6.7.8",
  4004  			destPort: 80,
  4005  
  4006  			// externalTrafficPolicy applies; only the local endpoint is
  4007  			// selected, and we don't masquerade.
  4008  			output: "10.180.0.2:80",
  4009  			masq:   false,
  4010  		},
  4011  		{
  4012  			name:     "pod to ClusterIP with iTP:Local",
  4013  			sourceIP: "10.0.0.2",
  4014  			destIP:   "172.30.0.43",
  4015  			destPort: 80,
  4016  
  4017  			// internalTrafficPolicy applies; only the local endpoint is
  4018  			// selected.
  4019  			output: "10.180.0.3:80",
  4020  			masq:   false,
  4021  		},
  4022  		{
  4023  			name:     "pod to NodePort with iTP:Local",
  4024  			sourceIP: "10.0.0.2",
  4025  			destIP:   testNodeIP,
  4026  			destPort: 3003,
  4027  
  4028  			// internalTrafficPolicy does not apply to NodePort traffic, so same as
  4029  			// "pod to NodePort" above.
  4030  			output: "10.180.0.3:80, 10.180.1.3:80",
  4031  			masq:   true,
  4032  		},
  4033  		{
  4034  			name:     "pod to LB with iTP:Local",
  4035  			sourceIP: "10.0.0.2",
  4036  			destIP:   "9.10.11.12",
  4037  			destPort: 80,
  4038  
  4039  			// internalTrafficPolicy does not apply to LoadBalancer traffic, so
  4040  			// same as "pod to LB" above.
  4041  			output: "10.180.0.3:80, 10.180.1.3:80",
  4042  			masq:   true,
  4043  		},
  4044  		{
  4045  			name:     "node to ClusterIP with iTP:Local",
  4046  			sourceIP: testNodeIP,
  4047  			destIP:   "172.30.0.43",
  4048  			destPort: 80,
  4049  
  4050  			// internalTrafficPolicy applies; only the local endpoint is selected.
  4051  			// Traffic is masqueraded as in the "node to ClusterIP" case because
  4052  			// internalTrafficPolicy does not affect masquerading.
  4053  			output: "10.180.0.3:80",
  4054  			masq:   true,
  4055  		},
  4056  		{
  4057  			name:     "node to NodePort with iTP:Local",
  4058  			sourceIP: testNodeIP,
  4059  			destIP:   testNodeIP,
  4060  			destPort: 3003,
  4061  
  4062  			// internalTrafficPolicy does not apply to NodePort traffic, so same as
  4063  			// "node to NodePort" above.
  4064  			output: "10.180.0.3:80, 10.180.1.3:80",
  4065  			masq:   true,
  4066  		},
  4067  		{
  4068  			name:     "node to LB with iTP:Local",
  4069  			sourceIP: testNodeIP,
  4070  			destIP:   "9.10.11.12",
  4071  			destPort: 80,
  4072  
  4073  			// internalTrafficPolicy does not apply to LoadBalancer traffic, so
  4074  			// same as "node to LB" above.
  4075  			output: "10.180.0.3:80, 10.180.1.3:80",
  4076  			masq:   true,
  4077  		},
  4078  		{
  4079  			name:     "external to ClusterIP with iTP:Local",
  4080  			sourceIP: testExternalClient,
  4081  			destIP:   "172.30.0.43",
  4082  			destPort: 80,
  4083  
  4084  			// internalTrafficPolicy applies; only the local endpoint is selected.
  4085  			// Traffic is masqueraded as in the "external to ClusterIP" case
  4086  			// because internalTrafficPolicy does not affect masquerading.
  4087  			output: "10.180.0.3:80",
  4088  			masq:   true,
  4089  		},
  4090  		{
  4091  			name:     "external to NodePort with iTP:Local",
  4092  			sourceIP: testExternalClient,
  4093  			destIP:   testNodeIP,
  4094  			destPort: 3003,
  4095  
  4096  			// internalTrafficPolicy does not apply to NodePort traffic, so same as
  4097  			// "external to NodePort" above.
  4098  			output: "10.180.0.3:80, 10.180.1.3:80",
  4099  			masq:   true,
  4100  		},
  4101  		{
  4102  			name:     "external to LB with iTP:Local",
  4103  			sourceIP: testExternalClient,
  4104  			destIP:   "9.10.11.12",
  4105  			destPort: 80,
  4106  
  4107  			// internalTrafficPolicy does not apply to LoadBalancer traffic, so
  4108  			// same as "external to LB" above.
  4109  			output: "10.180.0.3:80, 10.180.1.3:80",
  4110  			masq:   true,
  4111  		},
  4112  	}
  4113  
  4114  	type packetFlowTestOverride struct {
  4115  		output *string
  4116  		masq   *bool
  4117  	}
  4118  
  4119  	testCases := []struct {
  4120  		name          string
  4121  		line          string
  4122  		masqueradeAll bool
  4123  		localDetector bool
  4124  		overrides     map[string]packetFlowTestOverride
  4125  	}{
  4126  		{
  4127  			name:          "base",
  4128  			line:          getLine(),
  4129  			masqueradeAll: false,
  4130  			localDetector: true,
  4131  			overrides:     nil,
  4132  		},
  4133  		{
  4134  			name:          "no LocalTrafficDetector",
  4135  			line:          getLine(),
  4136  			masqueradeAll: false,
  4137  			localDetector: false,
  4138  			overrides: map[string]packetFlowTestOverride{
  4139  				// With no LocalTrafficDetector, all traffic to a
  4140  				// ClusterIP is assumed to be from a pod, and thus to not
  4141  				// require masquerading.
  4142  				"node to ClusterIP": {
  4143  					masq: ptr.To(false),
  4144  				},
  4145  				"node to ClusterIP with eTP:Local": {
  4146  					masq: ptr.To(false),
  4147  				},
  4148  				"node to ClusterIP with iTP:Local": {
  4149  					masq: ptr.To(false),
  4150  				},
  4151  				"external to ClusterIP": {
  4152  					masq: ptr.To(false),
  4153  				},
  4154  				"external to ClusterIP with eTP:Local": {
  4155  					masq: ptr.To(false),
  4156  				},
  4157  				"external to ClusterIP with iTP:Local": {
  4158  					masq: ptr.To(false),
  4159  				},
  4160  
  4161  				// And there's no eTP:Local short-circuit for pod traffic,
  4162  				// so pods get only the local endpoints.
  4163  				"pod to NodePort with eTP:Local": {
  4164  					output: ptr.To("10.180.0.2:80"),
  4165  				},
  4166  				"pod to LB with eTP:Local": {
  4167  					output: ptr.To("10.180.0.2:80"),
  4168  				},
  4169  			},
  4170  		},
  4171  		{
  4172  			name:          "masqueradeAll",
  4173  			line:          getLine(),
  4174  			masqueradeAll: true,
  4175  			localDetector: true,
  4176  			overrides: map[string]packetFlowTestOverride{
  4177  				// All "to ClusterIP" traffic gets masqueraded when using
  4178  				// --masquerade-all.
  4179  				"pod to ClusterIP": {
  4180  					masq: ptr.To(true),
  4181  				},
  4182  				"pod to ClusterIP with eTP:Local": {
  4183  					masq: ptr.To(true),
  4184  				},
  4185  				"pod to ClusterIP with iTP:Local": {
  4186  					masq: ptr.To(true),
  4187  				},
  4188  			},
  4189  		},
  4190  		{
  4191  			name:          "masqueradeAll, no LocalTrafficDetector",
  4192  			line:          getLine(),
  4193  			masqueradeAll: true,
  4194  			localDetector: false,
  4195  			overrides: map[string]packetFlowTestOverride{
  4196  				// As in "masqueradeAll"
  4197  				"pod to ClusterIP": {
  4198  					masq: ptr.To(true),
  4199  				},
  4200  				"pod to ClusterIP with eTP:Local": {
  4201  					masq: ptr.To(true),
  4202  				},
  4203  				"pod to ClusterIP with iTP:Local": {
  4204  					masq: ptr.To(true),
  4205  				},
  4206  
  4207  				// As in "no LocalTrafficDetector"
  4208  				"pod to NodePort with eTP:Local": {
  4209  					output: ptr.To("10.180.0.2:80"),
  4210  				},
  4211  				"pod to LB with eTP:Local": {
  4212  					output: ptr.To("10.180.0.2:80"),
  4213  				},
  4214  			},
  4215  		},
  4216  	}
  4217  
  4218  	for _, tc := range testCases {
  4219  		t.Run(tc.name, func(t *testing.T) {
  4220  			nft, fp := NewFakeProxier(v1.IPv4Protocol)
  4221  			fp.masqueradeAll = tc.masqueradeAll
  4222  			if !tc.localDetector {
  4223  				fp.localDetector = proxyutiliptables.NewNoOpLocalDetector()
  4224  			}
  4225  			setupTest(fp)
  4226  
  4227  			// Merge base flowTests with per-test-case overrides
  4228  			tcFlowTests := make([]packetFlowTest, len(flowTests))
  4229  			overridesApplied := 0
  4230  			for i := range flowTests {
  4231  				tcFlowTests[i] = flowTests[i]
  4232  				if overrides, set := tc.overrides[flowTests[i].name]; set {
  4233  					overridesApplied++
  4234  					if overrides.masq != nil {
  4235  						if tcFlowTests[i].masq == *overrides.masq {
  4236  							t.Errorf("%q override value for masq is same as base value", flowTests[i].name)
  4237  						}
  4238  						tcFlowTests[i].masq = *overrides.masq
  4239  					}
  4240  					if overrides.output != nil {
  4241  						if tcFlowTests[i].output == *overrides.output {
  4242  							t.Errorf("%q override value for output is same as base value", flowTests[i].name)
  4243  						}
  4244  						tcFlowTests[i].output = *overrides.output
  4245  					}
  4246  				}
  4247  			}
  4248  			if overridesApplied != len(tc.overrides) {
  4249  				t.Errorf("%d overrides did not match any test case name!", len(tc.overrides)-overridesApplied)
  4250  			}
  4251  			runPacketFlowTests(t, tc.line, nft, testNodeIPs, tcFlowTests)
  4252  		})
  4253  	}
  4254  }
  4255  
  4256  // Test calling syncProxyRules() multiple times with various changes
  4257  func TestSyncProxyRulesRepeated(t *testing.T) {
  4258  	nft, fp := NewFakeProxier(v1.IPv4Protocol)
  4259  
  4260  	baseRules := dedent.Dedent(`
  4261  		add table ip kube-proxy { comment "rules for kube-proxy" ; }
  4262  
  4263  		add chain ip kube-proxy endpoints-check
  4264  		add chain ip kube-proxy filter-forward { type filter hook forward priority -101 ; }
  4265  		add chain ip kube-proxy filter-input { type filter hook input priority -101 ; }
  4266  		add chain ip kube-proxy filter-output { type filter hook output priority -101 ; }
  4267  		add chain ip kube-proxy firewall-allow-check
  4268  		add chain ip kube-proxy firewall-check
  4269  		add chain ip kube-proxy forward
  4270  		add chain ip kube-proxy mark-for-masquerade
  4271  		add chain ip kube-proxy masquerading
  4272  		add chain ip kube-proxy nat-output { type nat hook output priority -100 ; }
  4273  		add chain ip kube-proxy nat-postrouting { type nat hook postrouting priority 100 ; }
  4274  		add chain ip kube-proxy nat-prerouting { type nat hook prerouting priority -100 ; }
  4275  		add chain ip kube-proxy reject-chain { comment "helper for @no-endpoint-services / @no-endpoint-nodeports" ; }
  4276  		add chain ip kube-proxy services
  4277  
  4278  		add rule ip kube-proxy endpoints-check ip daddr . meta l4proto . th dport vmap @no-endpoint-services
  4279  		add rule ip kube-proxy endpoints-check fib daddr type local ip daddr != 127.0.0.0/8 meta l4proto . th dport vmap @no-endpoint-nodeports
  4280  		add rule ip kube-proxy filter-forward ct state new jump endpoints-check
  4281  		add rule ip kube-proxy filter-forward jump forward
  4282  		add rule ip kube-proxy filter-forward ct state new jump firewall-check
  4283  		add rule ip kube-proxy filter-input ct state new jump endpoints-check
  4284  		add rule ip kube-proxy filter-input ct state new jump firewall-check
  4285  		add rule ip kube-proxy filter-output ct state new jump endpoints-check
  4286  		add rule ip kube-proxy filter-output ct state new jump firewall-check
  4287  		add rule ip kube-proxy firewall-allow-check ip daddr . meta l4proto . th dport . ip saddr @firewall-allow return
  4288  		add rule ip kube-proxy firewall-allow-check drop
  4289  		add rule ip kube-proxy firewall-check ip daddr . meta l4proto . th dport @firewall jump firewall-allow-check
  4290  		add rule ip kube-proxy forward ct state invalid drop
  4291  		add rule ip kube-proxy mark-for-masquerade mark set mark or 0x4000
  4292  		add rule ip kube-proxy masquerading mark and 0x4000 == 0 return
  4293  		add rule ip kube-proxy masquerading mark set mark xor 0x4000
  4294  		add rule ip kube-proxy masquerading masquerade fully-random
  4295  		add rule ip kube-proxy nat-output jump services
  4296  		add rule ip kube-proxy nat-postrouting jump masquerading
  4297  		add rule ip kube-proxy nat-prerouting jump services
  4298  		add rule ip kube-proxy reject-chain reject
  4299  		add rule ip kube-proxy services ip daddr . meta l4proto . th dport vmap @service-ips
  4300  		add rule ip kube-proxy services fib daddr type local ip daddr != 127.0.0.0/8 meta l4proto . th dport vmap @service-nodeports
  4301  
  4302  		add set ip kube-proxy firewall { type ipv4_addr . inet_proto . inet_service ; comment "destinations that are subject to LoadBalancerSourceRanges" ; }
  4303  		add set ip kube-proxy firewall-allow { type ipv4_addr . inet_proto . inet_service . ipv4_addr ; flags interval ; comment "destinations+sources that are allowed by LoadBalancerSourceRanges" ; }
  4304  		add map ip kube-proxy no-endpoint-nodeports { type inet_proto . inet_service : verdict ; comment "vmap to drop or reject packets to service nodeports with no endpoints" ; }
  4305  		add map ip kube-proxy no-endpoint-services { type ipv4_addr . inet_proto . inet_service : verdict ; comment "vmap to drop or reject packets to services with no endpoints" ; }
  4306  		add map ip kube-proxy service-ips { type ipv4_addr . inet_proto . inet_service : verdict ; comment "ClusterIP, ExternalIP and LoadBalancer IP traffic" ; }
  4307  		add map ip kube-proxy service-nodeports { type inet_proto . inet_service : verdict ; comment "NodePort traffic" ; }
  4308  		`)
  4309  
  4310  	// Helper function to make it look like time has passed (from the point of view of
  4311  	// the stale-chain-deletion code).
  4312  	ageStaleChains := func() {
  4313  		for chain, t := range fp.staleChains {
  4314  			fp.staleChains[chain] = t.Add(-2 * time.Second)
  4315  		}
  4316  	}
  4317  
  4318  	// Create initial state
  4319  	var svc2 *v1.Service
  4320  
  4321  	makeServiceMap(fp,
  4322  		makeTestService("ns1", "svc1", func(svc *v1.Service) {
  4323  			svc.Spec.Type = v1.ServiceTypeClusterIP
  4324  			svc.Spec.ClusterIP = "172.30.0.41"
  4325  			svc.Spec.Ports = []v1.ServicePort{{
  4326  				Name:     "p80",
  4327  				Port:     80,
  4328  				Protocol: v1.ProtocolTCP,
  4329  			}}
  4330  		}),
  4331  		makeTestService("ns2", "svc2", func(svc *v1.Service) {
  4332  			svc2 = svc
  4333  			svc.Spec.Type = v1.ServiceTypeClusterIP
  4334  			svc.Spec.ClusterIP = "172.30.0.42"
  4335  			svc.Spec.Ports = []v1.ServicePort{{
  4336  				Name:     "p8080",
  4337  				Port:     8080,
  4338  				Protocol: v1.ProtocolTCP,
  4339  			}}
  4340  		}),
  4341  	)
  4342  
  4343  	populateEndpointSlices(fp,
  4344  		makeTestEndpointSlice("ns1", "svc1", 1, func(eps *discovery.EndpointSlice) {
  4345  			eps.AddressType = discovery.AddressTypeIPv4
  4346  			eps.Endpoints = []discovery.Endpoint{{
  4347  				Addresses: []string{"10.0.1.1"},
  4348  			}}
  4349  			eps.Ports = []discovery.EndpointPort{{
  4350  				Name:     ptr.To("p80"),
  4351  				Port:     ptr.To[int32](80),
  4352  				Protocol: ptr.To(v1.ProtocolTCP),
  4353  			}}
  4354  		}),
  4355  		makeTestEndpointSlice("ns2", "svc2", 1, func(eps *discovery.EndpointSlice) {
  4356  			eps.AddressType = discovery.AddressTypeIPv4
  4357  			eps.Endpoints = []discovery.Endpoint{{
  4358  				Addresses: []string{"10.0.2.1"},
  4359  			}}
  4360  			eps.Ports = []discovery.EndpointPort{{
  4361  				Name:     ptr.To("p8080"),
  4362  				Port:     ptr.To[int32](8080),
  4363  				Protocol: ptr.To(v1.ProtocolTCP),
  4364  			}}
  4365  		}),
  4366  	)
  4367  
  4368  	fp.syncProxyRules()
  4369  
  4370  	expected := baseRules + dedent.Dedent(`
  4371  		add element ip kube-proxy service-ips { 172.30.0.41 . tcp . 80 : goto service-ULMVA6XW-ns1/svc1/tcp/p80 }
  4372  		add element ip kube-proxy service-ips { 172.30.0.42 . tcp . 8080 : goto service-MHHHYRWA-ns2/svc2/tcp/p8080 }
  4373  
  4374  		add chain ip kube-proxy service-ULMVA6XW-ns1/svc1/tcp/p80
  4375  		add rule ip kube-proxy service-ULMVA6XW-ns1/svc1/tcp/p80 ip daddr 172.30.0.41 tcp dport 80 ip saddr != 10.0.0.0/8 jump mark-for-masquerade
  4376  		add rule ip kube-proxy service-ULMVA6XW-ns1/svc1/tcp/p80 numgen random mod 1 vmap { 0 : goto endpoint-5TPGNJF2-ns1/svc1/tcp/p80__10.0.1.1/80 }
  4377  		add chain ip kube-proxy endpoint-5TPGNJF2-ns1/svc1/tcp/p80__10.0.1.1/80
  4378  		add rule ip kube-proxy endpoint-5TPGNJF2-ns1/svc1/tcp/p80__10.0.1.1/80 ip saddr 10.0.1.1 jump mark-for-masquerade
  4379  		add rule ip kube-proxy endpoint-5TPGNJF2-ns1/svc1/tcp/p80__10.0.1.1/80 meta l4proto tcp dnat to 10.0.1.1:80
  4380  
  4381  		add chain ip kube-proxy service-MHHHYRWA-ns2/svc2/tcp/p8080
  4382  		add rule ip kube-proxy service-MHHHYRWA-ns2/svc2/tcp/p8080 ip daddr 172.30.0.42 tcp dport 8080 ip saddr != 10.0.0.0/8 jump mark-for-masquerade
  4383  		add rule ip kube-proxy service-MHHHYRWA-ns2/svc2/tcp/p8080 numgen random mod 1 vmap { 0 : goto endpoint-7RVP4LUQ-ns2/svc2/tcp/p8080__10.0.2.1/8080 }
  4384  		add chain ip kube-proxy endpoint-7RVP4LUQ-ns2/svc2/tcp/p8080__10.0.2.1/8080
  4385  		add rule ip kube-proxy endpoint-7RVP4LUQ-ns2/svc2/tcp/p8080__10.0.2.1/8080 ip saddr 10.0.2.1 jump mark-for-masquerade
  4386  		add rule ip kube-proxy endpoint-7RVP4LUQ-ns2/svc2/tcp/p8080__10.0.2.1/8080 meta l4proto tcp dnat to 10.0.2.1:8080
  4387                  `)
  4388  	assertNFTablesTransactionEqual(t, getLine(), expected, nft.Dump())
  4389  
  4390  	// Add a new service and its endpoints
  4391  	makeServiceMap(fp,
  4392  		makeTestService("ns3", "svc3", func(svc *v1.Service) {
  4393  			svc.Spec.Type = v1.ServiceTypeClusterIP
  4394  			svc.Spec.ClusterIP = "172.30.0.43"
  4395  			svc.Spec.Ports = []v1.ServicePort{{
  4396  				Name:     "p80",
  4397  				Port:     80,
  4398  				Protocol: v1.ProtocolTCP,
  4399  			}}
  4400  		}),
  4401  	)
  4402  	var eps3 *discovery.EndpointSlice
  4403  	populateEndpointSlices(fp,
  4404  		makeTestEndpointSlice("ns3", "svc3", 1, func(eps *discovery.EndpointSlice) {
  4405  			eps3 = eps
  4406  			eps.AddressType = discovery.AddressTypeIPv4
  4407  			eps.Endpoints = []discovery.Endpoint{{
  4408  				Addresses: []string{"10.0.3.1"},
  4409  			}}
  4410  			eps.Ports = []discovery.EndpointPort{{
  4411  				Name:     ptr.To("p80"),
  4412  				Port:     ptr.To[int32](80),
  4413  				Protocol: ptr.To(v1.ProtocolTCP),
  4414  			}}
  4415  		}),
  4416  	)
  4417  	fp.syncProxyRules()
  4418  
  4419  	expected = baseRules + dedent.Dedent(`
  4420  		add element ip kube-proxy service-ips { 172.30.0.41 . tcp . 80 : goto service-ULMVA6XW-ns1/svc1/tcp/p80 }
  4421  		add element ip kube-proxy service-ips { 172.30.0.42 . tcp . 8080 : goto service-MHHHYRWA-ns2/svc2/tcp/p8080 }
  4422  		add element ip kube-proxy service-ips { 172.30.0.43 . tcp . 80 : goto service-4AT6LBPK-ns3/svc3/tcp/p80 }
  4423  
  4424  		add chain ip kube-proxy service-ULMVA6XW-ns1/svc1/tcp/p80
  4425  		add rule ip kube-proxy service-ULMVA6XW-ns1/svc1/tcp/p80 ip daddr 172.30.0.41 tcp dport 80 ip saddr != 10.0.0.0/8 jump mark-for-masquerade
  4426  		add rule ip kube-proxy service-ULMVA6XW-ns1/svc1/tcp/p80 numgen random mod 1 vmap { 0 : goto endpoint-5TPGNJF2-ns1/svc1/tcp/p80__10.0.1.1/80 }
  4427  		add chain ip kube-proxy endpoint-5TPGNJF2-ns1/svc1/tcp/p80__10.0.1.1/80
  4428  		add rule ip kube-proxy endpoint-5TPGNJF2-ns1/svc1/tcp/p80__10.0.1.1/80 ip saddr 10.0.1.1 jump mark-for-masquerade
  4429  		add rule ip kube-proxy endpoint-5TPGNJF2-ns1/svc1/tcp/p80__10.0.1.1/80 meta l4proto tcp dnat to 10.0.1.1:80
  4430  
  4431  		add chain ip kube-proxy service-MHHHYRWA-ns2/svc2/tcp/p8080
  4432  		add rule ip kube-proxy service-MHHHYRWA-ns2/svc2/tcp/p8080 ip daddr 172.30.0.42 tcp dport 8080 ip saddr != 10.0.0.0/8 jump mark-for-masquerade
  4433  		add rule ip kube-proxy service-MHHHYRWA-ns2/svc2/tcp/p8080 numgen random mod 1 vmap { 0 : goto endpoint-7RVP4LUQ-ns2/svc2/tcp/p8080__10.0.2.1/8080 }
  4434  		add chain ip kube-proxy endpoint-7RVP4LUQ-ns2/svc2/tcp/p8080__10.0.2.1/8080
  4435  		add rule ip kube-proxy endpoint-7RVP4LUQ-ns2/svc2/tcp/p8080__10.0.2.1/8080 ip saddr 10.0.2.1 jump mark-for-masquerade
  4436  		add rule ip kube-proxy endpoint-7RVP4LUQ-ns2/svc2/tcp/p8080__10.0.2.1/8080 meta l4proto tcp dnat to 10.0.2.1:8080
  4437  
  4438  		add chain ip kube-proxy service-4AT6LBPK-ns3/svc3/tcp/p80
  4439  		add rule ip kube-proxy service-4AT6LBPK-ns3/svc3/tcp/p80 ip daddr 172.30.0.43 tcp dport 80 ip saddr != 10.0.0.0/8 jump mark-for-masquerade
  4440  		add rule ip kube-proxy service-4AT6LBPK-ns3/svc3/tcp/p80 numgen random mod 1 vmap { 0 : goto endpoint-2OCDJSZQ-ns3/svc3/tcp/p80__10.0.3.1/80 }
  4441  		add chain ip kube-proxy endpoint-2OCDJSZQ-ns3/svc3/tcp/p80__10.0.3.1/80
  4442  		add rule ip kube-proxy endpoint-2OCDJSZQ-ns3/svc3/tcp/p80__10.0.3.1/80 ip saddr 10.0.3.1 jump mark-for-masquerade
  4443  		add rule ip kube-proxy endpoint-2OCDJSZQ-ns3/svc3/tcp/p80__10.0.3.1/80 meta l4proto tcp dnat to 10.0.3.1:80
  4444  		`)
  4445  	assertNFTablesTransactionEqual(t, getLine(), expected, nft.Dump())
  4446  
  4447  	// Delete a service; its chains will be flushed, but not immediately deleted.
  4448  	fp.OnServiceDelete(svc2)
  4449  	fp.syncProxyRules()
  4450  	expected = baseRules + dedent.Dedent(`
  4451  		add element ip kube-proxy service-ips { 172.30.0.41 . tcp . 80 : goto service-ULMVA6XW-ns1/svc1/tcp/p80 }
  4452  		add element ip kube-proxy service-ips { 172.30.0.43 . tcp . 80 : goto service-4AT6LBPK-ns3/svc3/tcp/p80 }
  4453  
  4454  		add chain ip kube-proxy service-ULMVA6XW-ns1/svc1/tcp/p80
  4455  		add rule ip kube-proxy service-ULMVA6XW-ns1/svc1/tcp/p80 ip daddr 172.30.0.41 tcp dport 80 ip saddr != 10.0.0.0/8 jump mark-for-masquerade
  4456  		add rule ip kube-proxy service-ULMVA6XW-ns1/svc1/tcp/p80 numgen random mod 1 vmap { 0 : goto endpoint-5TPGNJF2-ns1/svc1/tcp/p80__10.0.1.1/80 }
  4457  		add chain ip kube-proxy endpoint-5TPGNJF2-ns1/svc1/tcp/p80__10.0.1.1/80
  4458  		add rule ip kube-proxy endpoint-5TPGNJF2-ns1/svc1/tcp/p80__10.0.1.1/80 ip saddr 10.0.1.1 jump mark-for-masquerade
  4459  		add rule ip kube-proxy endpoint-5TPGNJF2-ns1/svc1/tcp/p80__10.0.1.1/80 meta l4proto tcp dnat to 10.0.1.1:80
  4460  
  4461  		add chain ip kube-proxy service-MHHHYRWA-ns2/svc2/tcp/p8080
  4462  		add chain ip kube-proxy endpoint-7RVP4LUQ-ns2/svc2/tcp/p8080__10.0.2.1/8080
  4463  
  4464  		add chain ip kube-proxy service-4AT6LBPK-ns3/svc3/tcp/p80
  4465  		add rule ip kube-proxy service-4AT6LBPK-ns3/svc3/tcp/p80 ip daddr 172.30.0.43 tcp dport 80 ip saddr != 10.0.0.0/8 jump mark-for-masquerade
  4466  		add rule ip kube-proxy service-4AT6LBPK-ns3/svc3/tcp/p80 numgen random mod 1 vmap { 0 : goto endpoint-2OCDJSZQ-ns3/svc3/tcp/p80__10.0.3.1/80 }
  4467  		add chain ip kube-proxy endpoint-2OCDJSZQ-ns3/svc3/tcp/p80__10.0.3.1/80
  4468  		add rule ip kube-proxy endpoint-2OCDJSZQ-ns3/svc3/tcp/p80__10.0.3.1/80 ip saddr 10.0.3.1 jump mark-for-masquerade
  4469  		add rule ip kube-proxy endpoint-2OCDJSZQ-ns3/svc3/tcp/p80__10.0.3.1/80 meta l4proto tcp dnat to 10.0.3.1:80
  4470  		`)
  4471  	assertNFTablesTransactionEqual(t, getLine(), expected, nft.Dump())
  4472  
  4473  	// Fake the passage of time and confirm that the stale chains get deleted.
  4474  	ageStaleChains()
  4475  	fp.syncProxyRules()
  4476  	expected = baseRules + dedent.Dedent(`
  4477  		add element ip kube-proxy service-ips { 172.30.0.41 . tcp . 80 : goto service-ULMVA6XW-ns1/svc1/tcp/p80 }
  4478  		add element ip kube-proxy service-ips { 172.30.0.43 . tcp . 80 : goto service-4AT6LBPK-ns3/svc3/tcp/p80 }
  4479  
  4480  		add chain ip kube-proxy service-ULMVA6XW-ns1/svc1/tcp/p80
  4481  		add rule ip kube-proxy service-ULMVA6XW-ns1/svc1/tcp/p80 ip daddr 172.30.0.41 tcp dport 80 ip saddr != 10.0.0.0/8 jump mark-for-masquerade
  4482  		add rule ip kube-proxy service-ULMVA6XW-ns1/svc1/tcp/p80 numgen random mod 1 vmap { 0 : goto endpoint-5TPGNJF2-ns1/svc1/tcp/p80__10.0.1.1/80 }
  4483  		add chain ip kube-proxy endpoint-5TPGNJF2-ns1/svc1/tcp/p80__10.0.1.1/80
  4484  		add rule ip kube-proxy endpoint-5TPGNJF2-ns1/svc1/tcp/p80__10.0.1.1/80 ip saddr 10.0.1.1 jump mark-for-masquerade
  4485  		add rule ip kube-proxy endpoint-5TPGNJF2-ns1/svc1/tcp/p80__10.0.1.1/80 meta l4proto tcp dnat to 10.0.1.1:80
  4486  
  4487  		add chain ip kube-proxy service-4AT6LBPK-ns3/svc3/tcp/p80
  4488  		add rule ip kube-proxy service-4AT6LBPK-ns3/svc3/tcp/p80 ip daddr 172.30.0.43 tcp dport 80 ip saddr != 10.0.0.0/8 jump mark-for-masquerade
  4489  		add rule ip kube-proxy service-4AT6LBPK-ns3/svc3/tcp/p80 numgen random mod 1 vmap { 0 : goto endpoint-2OCDJSZQ-ns3/svc3/tcp/p80__10.0.3.1/80 }
  4490  		add chain ip kube-proxy endpoint-2OCDJSZQ-ns3/svc3/tcp/p80__10.0.3.1/80
  4491  		add rule ip kube-proxy endpoint-2OCDJSZQ-ns3/svc3/tcp/p80__10.0.3.1/80 ip saddr 10.0.3.1 jump mark-for-masquerade
  4492  		add rule ip kube-proxy endpoint-2OCDJSZQ-ns3/svc3/tcp/p80__10.0.3.1/80 meta l4proto tcp dnat to 10.0.3.1:80
  4493  		`)
  4494  	assertNFTablesTransactionEqual(t, getLine(), expected, nft.Dump())
  4495  
  4496  	// Add a service, sync, then add its endpoints.
  4497  	makeServiceMap(fp,
  4498  		makeTestService("ns4", "svc4", func(svc *v1.Service) {
  4499  			svc.Spec.Type = v1.ServiceTypeClusterIP
  4500  			svc.Spec.ClusterIP = "172.30.0.44"
  4501  			svc.Spec.Ports = []v1.ServicePort{{
  4502  				Name:     "p80",
  4503  				Port:     80,
  4504  				Protocol: v1.ProtocolTCP,
  4505  			}}
  4506  		}),
  4507  	)
  4508  	fp.syncProxyRules()
  4509  	expected = baseRules + dedent.Dedent(`
  4510  		add element ip kube-proxy service-ips { 172.30.0.41 . tcp . 80 : goto service-ULMVA6XW-ns1/svc1/tcp/p80 }
  4511  		add element ip kube-proxy service-ips { 172.30.0.43 . tcp . 80 : goto service-4AT6LBPK-ns3/svc3/tcp/p80 }
  4512  
  4513  		add chain ip kube-proxy service-ULMVA6XW-ns1/svc1/tcp/p80
  4514  		add rule ip kube-proxy service-ULMVA6XW-ns1/svc1/tcp/p80 ip daddr 172.30.0.41 tcp dport 80 ip saddr != 10.0.0.0/8 jump mark-for-masquerade
  4515  		add rule ip kube-proxy service-ULMVA6XW-ns1/svc1/tcp/p80 numgen random mod 1 vmap { 0 : goto endpoint-5TPGNJF2-ns1/svc1/tcp/p80__10.0.1.1/80 }
  4516  		add chain ip kube-proxy endpoint-5TPGNJF2-ns1/svc1/tcp/p80__10.0.1.1/80
  4517  		add rule ip kube-proxy endpoint-5TPGNJF2-ns1/svc1/tcp/p80__10.0.1.1/80 ip saddr 10.0.1.1 jump mark-for-masquerade
  4518  		add rule ip kube-proxy endpoint-5TPGNJF2-ns1/svc1/tcp/p80__10.0.1.1/80 meta l4proto tcp dnat to 10.0.1.1:80
  4519  
  4520  		add chain ip kube-proxy service-4AT6LBPK-ns3/svc3/tcp/p80
  4521  		add rule ip kube-proxy service-4AT6LBPK-ns3/svc3/tcp/p80 ip daddr 172.30.0.43 tcp dport 80 ip saddr != 10.0.0.0/8 jump mark-for-masquerade
  4522  		add rule ip kube-proxy service-4AT6LBPK-ns3/svc3/tcp/p80 numgen random mod 1 vmap { 0 : goto endpoint-2OCDJSZQ-ns3/svc3/tcp/p80__10.0.3.1/80 }
  4523  		add chain ip kube-proxy endpoint-2OCDJSZQ-ns3/svc3/tcp/p80__10.0.3.1/80
  4524  		add rule ip kube-proxy endpoint-2OCDJSZQ-ns3/svc3/tcp/p80__10.0.3.1/80 ip saddr 10.0.3.1 jump mark-for-masquerade
  4525  		add rule ip kube-proxy endpoint-2OCDJSZQ-ns3/svc3/tcp/p80__10.0.3.1/80 meta l4proto tcp dnat to 10.0.3.1:80
  4526  
  4527  		add element ip kube-proxy no-endpoint-services { 172.30.0.44 . tcp . 80 comment "ns4/svc4:p80" : goto reject-chain }
  4528  		`)
  4529  	assertNFTablesTransactionEqual(t, getLine(), expected, nft.Dump())
  4530  
  4531  	populateEndpointSlices(fp,
  4532  		makeTestEndpointSlice("ns4", "svc4", 1, func(eps *discovery.EndpointSlice) {
  4533  			eps.AddressType = discovery.AddressTypeIPv4
  4534  			eps.Endpoints = []discovery.Endpoint{{
  4535  				Addresses: []string{"10.0.4.1"},
  4536  			}}
  4537  			eps.Ports = []discovery.EndpointPort{{
  4538  				Name:     ptr.To("p80"),
  4539  				Port:     ptr.To[int32](80),
  4540  				Protocol: ptr.To(v1.ProtocolTCP),
  4541  			}}
  4542  		}),
  4543  	)
  4544  	fp.syncProxyRules()
  4545  	expected = baseRules + dedent.Dedent(`
  4546  		add element ip kube-proxy service-ips { 172.30.0.41 . tcp . 80 : goto service-ULMVA6XW-ns1/svc1/tcp/p80 }
  4547  		add element ip kube-proxy service-ips { 172.30.0.43 . tcp . 80 : goto service-4AT6LBPK-ns3/svc3/tcp/p80 }
  4548  		add element ip kube-proxy service-ips { 172.30.0.44 . tcp . 80 : goto service-LAUZTJTB-ns4/svc4/tcp/p80 }
  4549  
  4550  		add chain ip kube-proxy service-ULMVA6XW-ns1/svc1/tcp/p80
  4551  		add rule ip kube-proxy service-ULMVA6XW-ns1/svc1/tcp/p80 ip daddr 172.30.0.41 tcp dport 80 ip saddr != 10.0.0.0/8 jump mark-for-masquerade
  4552  		add rule ip kube-proxy service-ULMVA6XW-ns1/svc1/tcp/p80 numgen random mod 1 vmap { 0 : goto endpoint-5TPGNJF2-ns1/svc1/tcp/p80__10.0.1.1/80 }
  4553  		add chain ip kube-proxy endpoint-5TPGNJF2-ns1/svc1/tcp/p80__10.0.1.1/80
  4554  		add rule ip kube-proxy endpoint-5TPGNJF2-ns1/svc1/tcp/p80__10.0.1.1/80 ip saddr 10.0.1.1 jump mark-for-masquerade
  4555  		add rule ip kube-proxy endpoint-5TPGNJF2-ns1/svc1/tcp/p80__10.0.1.1/80 meta l4proto tcp dnat to 10.0.1.1:80
  4556  
  4557  		add chain ip kube-proxy service-4AT6LBPK-ns3/svc3/tcp/p80
  4558  		add rule ip kube-proxy service-4AT6LBPK-ns3/svc3/tcp/p80 ip daddr 172.30.0.43 tcp dport 80 ip saddr != 10.0.0.0/8 jump mark-for-masquerade
  4559  		add rule ip kube-proxy service-4AT6LBPK-ns3/svc3/tcp/p80 numgen random mod 1 vmap { 0 : goto endpoint-2OCDJSZQ-ns3/svc3/tcp/p80__10.0.3.1/80 }
  4560  		add chain ip kube-proxy endpoint-2OCDJSZQ-ns3/svc3/tcp/p80__10.0.3.1/80
  4561  		add rule ip kube-proxy endpoint-2OCDJSZQ-ns3/svc3/tcp/p80__10.0.3.1/80 ip saddr 10.0.3.1 jump mark-for-masquerade
  4562  		add rule ip kube-proxy endpoint-2OCDJSZQ-ns3/svc3/tcp/p80__10.0.3.1/80 meta l4proto tcp dnat to 10.0.3.1:80
  4563  
  4564  		add chain ip kube-proxy service-LAUZTJTB-ns4/svc4/tcp/p80
  4565  		add rule ip kube-proxy service-LAUZTJTB-ns4/svc4/tcp/p80 ip daddr 172.30.0.44 tcp dport 80 ip saddr != 10.0.0.0/8 jump mark-for-masquerade
  4566  		add rule ip kube-proxy service-LAUZTJTB-ns4/svc4/tcp/p80 numgen random mod 1 vmap { 0 : goto endpoint-WAHRBT2B-ns4/svc4/tcp/p80__10.0.4.1/80 }
  4567  		add chain ip kube-proxy endpoint-WAHRBT2B-ns4/svc4/tcp/p80__10.0.4.1/80
  4568  		add rule ip kube-proxy endpoint-WAHRBT2B-ns4/svc4/tcp/p80__10.0.4.1/80 ip saddr 10.0.4.1 jump mark-for-masquerade
  4569  		add rule ip kube-proxy endpoint-WAHRBT2B-ns4/svc4/tcp/p80__10.0.4.1/80 meta l4proto tcp dnat to 10.0.4.1:80
  4570  		`)
  4571  	assertNFTablesTransactionEqual(t, getLine(), expected, nft.Dump())
  4572  
  4573  	// Change an endpoint of an existing service.
  4574  	eps3update := eps3.DeepCopy()
  4575  	eps3update.Endpoints[0].Addresses[0] = "10.0.3.2"
  4576  	fp.OnEndpointSliceUpdate(eps3, eps3update)
  4577  	fp.syncProxyRules()
  4578  
  4579  	// The old endpoint chain (for 10.0.3.1) will not be deleted yet.
  4580  	expected = baseRules + dedent.Dedent(`
  4581  		add element ip kube-proxy service-ips { 172.30.0.41 . tcp . 80 : goto service-ULMVA6XW-ns1/svc1/tcp/p80 }
  4582  		add element ip kube-proxy service-ips { 172.30.0.43 . tcp . 80 : goto service-4AT6LBPK-ns3/svc3/tcp/p80 }
  4583  		add element ip kube-proxy service-ips { 172.30.0.44 . tcp . 80 : goto service-LAUZTJTB-ns4/svc4/tcp/p80 }
  4584  
  4585  		add chain ip kube-proxy service-ULMVA6XW-ns1/svc1/tcp/p80
  4586  		add rule ip kube-proxy service-ULMVA6XW-ns1/svc1/tcp/p80 ip daddr 172.30.0.41 tcp dport 80 ip saddr != 10.0.0.0/8 jump mark-for-masquerade
  4587  		add rule ip kube-proxy service-ULMVA6XW-ns1/svc1/tcp/p80 numgen random mod 1 vmap { 0 : goto endpoint-5TPGNJF2-ns1/svc1/tcp/p80__10.0.1.1/80 }
  4588  		add chain ip kube-proxy endpoint-5TPGNJF2-ns1/svc1/tcp/p80__10.0.1.1/80
  4589  		add rule ip kube-proxy endpoint-5TPGNJF2-ns1/svc1/tcp/p80__10.0.1.1/80 ip saddr 10.0.1.1 jump mark-for-masquerade
  4590  		add rule ip kube-proxy endpoint-5TPGNJF2-ns1/svc1/tcp/p80__10.0.1.1/80 meta l4proto tcp dnat to 10.0.1.1:80
  4591  
  4592  		add chain ip kube-proxy service-4AT6LBPK-ns3/svc3/tcp/p80
  4593  		add rule ip kube-proxy service-4AT6LBPK-ns3/svc3/tcp/p80 ip daddr 172.30.0.43 tcp dport 80 ip saddr != 10.0.0.0/8 jump mark-for-masquerade
  4594  		add rule ip kube-proxy service-4AT6LBPK-ns3/svc3/tcp/p80 numgen random mod 1 vmap { 0 : goto endpoint-SWWHDC7X-ns3/svc3/tcp/p80__10.0.3.2/80 }
  4595  		add chain ip kube-proxy endpoint-2OCDJSZQ-ns3/svc3/tcp/p80__10.0.3.1/80
  4596  		add chain ip kube-proxy endpoint-SWWHDC7X-ns3/svc3/tcp/p80__10.0.3.2/80
  4597  		add rule ip kube-proxy endpoint-SWWHDC7X-ns3/svc3/tcp/p80__10.0.3.2/80 ip saddr 10.0.3.2 jump mark-for-masquerade
  4598  		add rule ip kube-proxy endpoint-SWWHDC7X-ns3/svc3/tcp/p80__10.0.3.2/80 meta l4proto tcp dnat to 10.0.3.2:80
  4599  
  4600  		add chain ip kube-proxy service-LAUZTJTB-ns4/svc4/tcp/p80
  4601  		add rule ip kube-proxy service-LAUZTJTB-ns4/svc4/tcp/p80 ip daddr 172.30.0.44 tcp dport 80 ip saddr != 10.0.0.0/8 jump mark-for-masquerade
  4602  		add rule ip kube-proxy service-LAUZTJTB-ns4/svc4/tcp/p80 numgen random mod 1 vmap { 0 : goto endpoint-WAHRBT2B-ns4/svc4/tcp/p80__10.0.4.1/80 }
  4603  		add chain ip kube-proxy endpoint-WAHRBT2B-ns4/svc4/tcp/p80__10.0.4.1/80
  4604  		add rule ip kube-proxy endpoint-WAHRBT2B-ns4/svc4/tcp/p80__10.0.4.1/80 ip saddr 10.0.4.1 jump mark-for-masquerade
  4605  		add rule ip kube-proxy endpoint-WAHRBT2B-ns4/svc4/tcp/p80__10.0.4.1/80 meta l4proto tcp dnat to 10.0.4.1:80
  4606  		`)
  4607  	assertNFTablesTransactionEqual(t, getLine(), expected, nft.Dump())
  4608  
  4609  	// (Ensure the old svc3 chain gets deleted in the next sync.)
  4610  	ageStaleChains()
  4611  
  4612  	// Add an endpoint to a service.
  4613  	eps3update2 := eps3update.DeepCopy()
  4614  	eps3update2.Endpoints = append(eps3update2.Endpoints, discovery.Endpoint{Addresses: []string{"10.0.3.3"}})
  4615  	fp.OnEndpointSliceUpdate(eps3update, eps3update2)
  4616  	fp.syncProxyRules()
  4617  
  4618  	expected = baseRules + dedent.Dedent(`
  4619  		add element ip kube-proxy service-ips { 172.30.0.41 . tcp . 80 : goto service-ULMVA6XW-ns1/svc1/tcp/p80 }
  4620  		add element ip kube-proxy service-ips { 172.30.0.43 . tcp . 80 : goto service-4AT6LBPK-ns3/svc3/tcp/p80 }
  4621  		add element ip kube-proxy service-ips { 172.30.0.44 . tcp . 80 : goto service-LAUZTJTB-ns4/svc4/tcp/p80 }
  4622  
  4623  		add chain ip kube-proxy service-ULMVA6XW-ns1/svc1/tcp/p80
  4624  		add rule ip kube-proxy service-ULMVA6XW-ns1/svc1/tcp/p80 ip daddr 172.30.0.41 tcp dport 80 ip saddr != 10.0.0.0/8 jump mark-for-masquerade
  4625  		add rule ip kube-proxy service-ULMVA6XW-ns1/svc1/tcp/p80 numgen random mod 1 vmap { 0 : goto endpoint-5TPGNJF2-ns1/svc1/tcp/p80__10.0.1.1/80 }
  4626  		add chain ip kube-proxy endpoint-5TPGNJF2-ns1/svc1/tcp/p80__10.0.1.1/80
  4627  		add rule ip kube-proxy endpoint-5TPGNJF2-ns1/svc1/tcp/p80__10.0.1.1/80 ip saddr 10.0.1.1 jump mark-for-masquerade
  4628  		add rule ip kube-proxy endpoint-5TPGNJF2-ns1/svc1/tcp/p80__10.0.1.1/80 meta l4proto tcp dnat to 10.0.1.1:80
  4629  
  4630  		add chain ip kube-proxy service-4AT6LBPK-ns3/svc3/tcp/p80
  4631  		add rule ip kube-proxy service-4AT6LBPK-ns3/svc3/tcp/p80 ip daddr 172.30.0.43 tcp dport 80 ip saddr != 10.0.0.0/8 jump mark-for-masquerade
  4632  		add rule ip kube-proxy service-4AT6LBPK-ns3/svc3/tcp/p80 numgen random mod 2 vmap { 0 : goto endpoint-SWWHDC7X-ns3/svc3/tcp/p80__10.0.3.2/80 , 1 : goto endpoint-TQ2QKHCZ-ns3/svc3/tcp/p80__10.0.3.3/80 }
  4633  		add chain ip kube-proxy endpoint-SWWHDC7X-ns3/svc3/tcp/p80__10.0.3.2/80
  4634  		add rule ip kube-proxy endpoint-SWWHDC7X-ns3/svc3/tcp/p80__10.0.3.2/80 ip saddr 10.0.3.2 jump mark-for-masquerade
  4635  		add rule ip kube-proxy endpoint-SWWHDC7X-ns3/svc3/tcp/p80__10.0.3.2/80 meta l4proto tcp dnat to 10.0.3.2:80
  4636  		add chain ip kube-proxy endpoint-TQ2QKHCZ-ns3/svc3/tcp/p80__10.0.3.3/80
  4637  		add rule ip kube-proxy endpoint-TQ2QKHCZ-ns3/svc3/tcp/p80__10.0.3.3/80 ip saddr 10.0.3.3 jump mark-for-masquerade
  4638  		add rule ip kube-proxy endpoint-TQ2QKHCZ-ns3/svc3/tcp/p80__10.0.3.3/80 meta l4proto tcp dnat to 10.0.3.3:80
  4639  
  4640  		add chain ip kube-proxy service-LAUZTJTB-ns4/svc4/tcp/p80
  4641  		add rule ip kube-proxy service-LAUZTJTB-ns4/svc4/tcp/p80 ip daddr 172.30.0.44 tcp dport 80 ip saddr != 10.0.0.0/8 jump mark-for-masquerade
  4642  		add rule ip kube-proxy service-LAUZTJTB-ns4/svc4/tcp/p80 numgen random mod 1 vmap { 0 : goto endpoint-WAHRBT2B-ns4/svc4/tcp/p80__10.0.4.1/80 }
  4643  		add chain ip kube-proxy endpoint-WAHRBT2B-ns4/svc4/tcp/p80__10.0.4.1/80
  4644  		add rule ip kube-proxy endpoint-WAHRBT2B-ns4/svc4/tcp/p80__10.0.4.1/80 ip saddr 10.0.4.1 jump mark-for-masquerade
  4645  		add rule ip kube-proxy endpoint-WAHRBT2B-ns4/svc4/tcp/p80__10.0.4.1/80 meta l4proto tcp dnat to 10.0.4.1:80
  4646  		`)
  4647  	assertNFTablesTransactionEqual(t, getLine(), expected, nft.Dump())
  4648  
  4649  	// Sync with no new changes, so same expected rules as last time
  4650  	fp.syncProxyRules()
  4651  	assertNFTablesTransactionEqual(t, getLine(), expected, nft.Dump())
  4652  }
  4653  
  4654  func TestNoEndpointsMetric(t *testing.T) {
  4655  	type endpoint struct {
  4656  		ip       string
  4657  		hostname string
  4658  	}
  4659  
  4660  	metrics.RegisterMetrics()
  4661  	testCases := []struct {
  4662  		name                                                string
  4663  		internalTrafficPolicy                               *v1.ServiceInternalTrafficPolicy
  4664  		externalTrafficPolicy                               v1.ServiceExternalTrafficPolicy
  4665  		endpoints                                           []endpoint
  4666  		expectedSyncProxyRulesNoLocalEndpointsTotalInternal int
  4667  		expectedSyncProxyRulesNoLocalEndpointsTotalExternal int
  4668  	}{
  4669  		{
  4670  			name:                  "internalTrafficPolicy is set and there are local endpoints",
  4671  			internalTrafficPolicy: ptr.To(v1.ServiceInternalTrafficPolicyLocal),
  4672  			endpoints: []endpoint{
  4673  				{"10.0.1.1", testHostname},
  4674  				{"10.0.1.2", "host1"},
  4675  				{"10.0.1.3", "host2"},
  4676  			},
  4677  		},
  4678  		{
  4679  			name:                  "externalTrafficPolicy is set and there are local endpoints",
  4680  			externalTrafficPolicy: v1.ServiceExternalTrafficPolicyLocal,
  4681  			endpoints: []endpoint{
  4682  				{"10.0.1.1", testHostname},
  4683  				{"10.0.1.2", "host1"},
  4684  				{"10.0.1.3", "host2"},
  4685  			},
  4686  		},
  4687  		{
  4688  			name:                  "both policies are set and there are local endpoints",
  4689  			internalTrafficPolicy: ptr.To(v1.ServiceInternalTrafficPolicyLocal),
  4690  			externalTrafficPolicy: v1.ServiceExternalTrafficPolicyLocal,
  4691  			endpoints: []endpoint{
  4692  				{"10.0.1.1", testHostname},
  4693  				{"10.0.1.2", "host1"},
  4694  				{"10.0.1.3", "host2"},
  4695  			},
  4696  		},
  4697  		{
  4698  			name:                  "internalTrafficPolicy is set and there are no local endpoints",
  4699  			internalTrafficPolicy: ptr.To(v1.ServiceInternalTrafficPolicyLocal),
  4700  			endpoints: []endpoint{
  4701  				{"10.0.1.1", "host0"},
  4702  				{"10.0.1.2", "host1"},
  4703  				{"10.0.1.3", "host2"},
  4704  			},
  4705  			expectedSyncProxyRulesNoLocalEndpointsTotalInternal: 1,
  4706  		},
  4707  		{
  4708  			name:                  "externalTrafficPolicy is set and there are no local endpoints",
  4709  			externalTrafficPolicy: v1.ServiceExternalTrafficPolicyLocal,
  4710  			endpoints: []endpoint{
  4711  				{"10.0.1.1", "host0"},
  4712  				{"10.0.1.2", "host1"},
  4713  				{"10.0.1.3", "host2"},
  4714  			},
  4715  			expectedSyncProxyRulesNoLocalEndpointsTotalExternal: 1,
  4716  		},
  4717  		{
  4718  			name:                  "both policies are set and there are no local endpoints",
  4719  			internalTrafficPolicy: ptr.To(v1.ServiceInternalTrafficPolicyLocal),
  4720  			externalTrafficPolicy: v1.ServiceExternalTrafficPolicyLocal,
  4721  			endpoints: []endpoint{
  4722  				{"10.0.1.1", "host0"},
  4723  				{"10.0.1.2", "host1"},
  4724  				{"10.0.1.3", "host2"},
  4725  			},
  4726  			expectedSyncProxyRulesNoLocalEndpointsTotalInternal: 1,
  4727  			expectedSyncProxyRulesNoLocalEndpointsTotalExternal: 1,
  4728  		},
  4729  		{
  4730  			name:                  "both policies are set and there are no endpoints at all",
  4731  			internalTrafficPolicy: ptr.To(v1.ServiceInternalTrafficPolicyLocal),
  4732  			externalTrafficPolicy: v1.ServiceExternalTrafficPolicyLocal,
  4733  			endpoints:             []endpoint{},
  4734  			expectedSyncProxyRulesNoLocalEndpointsTotalInternal: 0,
  4735  			expectedSyncProxyRulesNoLocalEndpointsTotalExternal: 0,
  4736  		},
  4737  	}
  4738  
  4739  	for _, tc := range testCases {
  4740  		t.Run(tc.name, func(t *testing.T) {
  4741  			_, fp := NewFakeProxier(v1.IPv4Protocol)
  4742  			fp.OnServiceSynced()
  4743  			fp.OnEndpointSlicesSynced()
  4744  
  4745  			serviceName := "svc1"
  4746  			namespaceName := "ns1"
  4747  
  4748  			svc := &v1.Service{
  4749  				ObjectMeta: metav1.ObjectMeta{Name: serviceName, Namespace: namespaceName},
  4750  				Spec: v1.ServiceSpec{
  4751  					ClusterIP: "172.30.1.1",
  4752  					Selector:  map[string]string{"foo": "bar"},
  4753  					Ports:     []v1.ServicePort{{Name: "", Port: 80, Protocol: v1.ProtocolTCP, NodePort: 123}},
  4754  				},
  4755  			}
  4756  			if tc.internalTrafficPolicy != nil {
  4757  				svc.Spec.InternalTrafficPolicy = tc.internalTrafficPolicy
  4758  			}
  4759  			if tc.externalTrafficPolicy != "" {
  4760  				svc.Spec.Type = v1.ServiceTypeNodePort
  4761  				svc.Spec.ExternalTrafficPolicy = tc.externalTrafficPolicy
  4762  			}
  4763  
  4764  			fp.OnServiceAdd(svc)
  4765  
  4766  			endpointSlice := &discovery.EndpointSlice{
  4767  				ObjectMeta: metav1.ObjectMeta{
  4768  					Name:      fmt.Sprintf("%s-1", serviceName),
  4769  					Namespace: namespaceName,
  4770  					Labels:    map[string]string{discovery.LabelServiceName: serviceName},
  4771  				},
  4772  				Ports: []discovery.EndpointPort{{
  4773  					Name:     ptr.To(""),
  4774  					Port:     ptr.To[int32](80),
  4775  					Protocol: ptr.To(v1.ProtocolTCP),
  4776  				}},
  4777  				AddressType: discovery.AddressTypeIPv4,
  4778  			}
  4779  			for _, ep := range tc.endpoints {
  4780  				endpointSlice.Endpoints = append(endpointSlice.Endpoints, discovery.Endpoint{
  4781  					Addresses:  []string{ep.ip},
  4782  					Conditions: discovery.EndpointConditions{Ready: ptr.To(true)},
  4783  					NodeName:   ptr.To(ep.hostname),
  4784  				})
  4785  			}
  4786  
  4787  			fp.OnEndpointSliceAdd(endpointSlice)
  4788  			fp.syncProxyRules()
  4789  			syncProxyRulesNoLocalEndpointsTotalInternal, err := testutil.GetGaugeMetricValue(metrics.SyncProxyRulesNoLocalEndpointsTotal.WithLabelValues("internal"))
  4790  			if err != nil {
  4791  				t.Errorf("failed to get %s value, err: %v", metrics.SyncProxyRulesNoLocalEndpointsTotal.Name, err)
  4792  			}
  4793  
  4794  			if tc.expectedSyncProxyRulesNoLocalEndpointsTotalInternal != int(syncProxyRulesNoLocalEndpointsTotalInternal) {
  4795  				t.Errorf("sync_proxy_rules_no_endpoints_total metric mismatch(internal): got=%d, expected %d", int(syncProxyRulesNoLocalEndpointsTotalInternal), tc.expectedSyncProxyRulesNoLocalEndpointsTotalInternal)
  4796  			}
  4797  
  4798  			syncProxyRulesNoLocalEndpointsTotalExternal, err := testutil.GetGaugeMetricValue(metrics.SyncProxyRulesNoLocalEndpointsTotal.WithLabelValues("external"))
  4799  			if err != nil {
  4800  				t.Errorf("failed to get %s value(external), err: %v", metrics.SyncProxyRulesNoLocalEndpointsTotal.Name, err)
  4801  			}
  4802  
  4803  			if tc.expectedSyncProxyRulesNoLocalEndpointsTotalExternal != int(syncProxyRulesNoLocalEndpointsTotalExternal) {
  4804  				t.Errorf("sync_proxy_rules_no_endpoints_total metric mismatch(internal): got=%d, expected %d", int(syncProxyRulesNoLocalEndpointsTotalExternal), tc.expectedSyncProxyRulesNoLocalEndpointsTotalExternal)
  4805  			}
  4806  		})
  4807  	}
  4808  }
  4809  
  4810  func TestLoadBalancerIngressRouteTypeProxy(t *testing.T) {
  4811  	testCases := []struct {
  4812  		name          string
  4813  		ipModeEnabled bool
  4814  		svcIP         string
  4815  		svcLBIP       string
  4816  		ipMode        *v1.LoadBalancerIPMode
  4817  		expectedRule  bool
  4818  	}{
  4819  		/* LoadBalancerIPMode disabled */
  4820  		{
  4821  			name:          "LoadBalancerIPMode disabled, ipMode Proxy",
  4822  			ipModeEnabled: false,
  4823  			svcIP:         "10.20.30.41",
  4824  			svcLBIP:       "1.2.3.4",
  4825  			ipMode:        ptr.To(v1.LoadBalancerIPModeProxy),
  4826  			expectedRule:  true,
  4827  		},
  4828  		{
  4829  			name:          "LoadBalancerIPMode disabled, ipMode VIP",
  4830  			ipModeEnabled: false,
  4831  			svcIP:         "10.20.30.42",
  4832  			svcLBIP:       "1.2.3.5",
  4833  			ipMode:        ptr.To(v1.LoadBalancerIPModeVIP),
  4834  			expectedRule:  true,
  4835  		},
  4836  		{
  4837  			name:          "LoadBalancerIPMode disabled, ipMode nil",
  4838  			ipModeEnabled: false,
  4839  			svcIP:         "10.20.30.43",
  4840  			svcLBIP:       "1.2.3.6",
  4841  			ipMode:        nil,
  4842  			expectedRule:  true,
  4843  		},
  4844  		/* LoadBalancerIPMode enabled */
  4845  		{
  4846  			name:          "LoadBalancerIPMode enabled, ipMode Proxy",
  4847  			ipModeEnabled: true,
  4848  			svcIP:         "10.20.30.41",
  4849  			svcLBIP:       "1.2.3.4",
  4850  			ipMode:        ptr.To(v1.LoadBalancerIPModeProxy),
  4851  			expectedRule:  false,
  4852  		},
  4853  		{
  4854  			name:          "LoadBalancerIPMode enabled, ipMode VIP",
  4855  			ipModeEnabled: true,
  4856  			svcIP:         "10.20.30.42",
  4857  			svcLBIP:       "1.2.3.5",
  4858  			ipMode:        ptr.To(v1.LoadBalancerIPModeVIP),
  4859  			expectedRule:  true,
  4860  		},
  4861  		{
  4862  			name:          "LoadBalancerIPMode enabled, ipMode nil",
  4863  			ipModeEnabled: true,
  4864  			svcIP:         "10.20.30.43",
  4865  			svcLBIP:       "1.2.3.6",
  4866  			ipMode:        nil,
  4867  			expectedRule:  true,
  4868  		},
  4869  	}
  4870  
  4871  	svcPort := 80
  4872  	svcNodePort := 3001
  4873  	svcPortName := proxy.ServicePortName{
  4874  		NamespacedName: makeNSN("ns1", "svc1"),
  4875  		Port:           "p80",
  4876  		Protocol:       v1.ProtocolTCP,
  4877  	}
  4878  
  4879  	for _, testCase := range testCases {
  4880  		t.Run(testCase.name, func(t *testing.T) {
  4881  			defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.LoadBalancerIPMode, testCase.ipModeEnabled)()
  4882  			nft, fp := NewFakeProxier(v1.IPv4Protocol)
  4883  			makeServiceMap(fp,
  4884  				makeTestService(svcPortName.Namespace, svcPortName.Name, func(svc *v1.Service) {
  4885  					svc.Spec.Type = "LoadBalancer"
  4886  					svc.Spec.ClusterIP = testCase.svcIP
  4887  					svc.Spec.Ports = []v1.ServicePort{{
  4888  						Name:     svcPortName.Port,
  4889  						Port:     int32(svcPort),
  4890  						Protocol: v1.ProtocolTCP,
  4891  						NodePort: int32(svcNodePort),
  4892  					}}
  4893  					svc.Status.LoadBalancer.Ingress = []v1.LoadBalancerIngress{{
  4894  						IP:     testCase.svcLBIP,
  4895  						IPMode: testCase.ipMode,
  4896  					}}
  4897  				}),
  4898  			)
  4899  
  4900  			populateEndpointSlices(fp,
  4901  				makeTestEndpointSlice("ns1", "svc1", 1, func(eps *discovery.EndpointSlice) {
  4902  					eps.AddressType = discovery.AddressTypeIPv4
  4903  					eps.Endpoints = []discovery.Endpoint{{
  4904  						Addresses: []string{"10.180.0.1"},
  4905  					}}
  4906  					eps.Ports = []discovery.EndpointPort{{
  4907  						Name:     ptr.To("p80"),
  4908  						Port:     ptr.To[int32](80),
  4909  						Protocol: ptr.To(v1.ProtocolTCP),
  4910  					}}
  4911  				}),
  4912  			)
  4913  
  4914  			fp.syncProxyRules()
  4915  
  4916  			element := nft.Table.Maps["service-ips"].FindElement(testCase.svcLBIP, "tcp", "80")
  4917  			ruleExists := element != nil
  4918  			if ruleExists != testCase.expectedRule {
  4919  				t.Errorf("unexpected rule for %s", testCase.svcLBIP)
  4920  			}
  4921  		})
  4922  	}
  4923  }
  4924  
  4925  func Test_servicePortChainNameBase(t *testing.T) {
  4926  	testCases := []struct {
  4927  		name     string
  4928  		spn      proxy.ServicePortName
  4929  		protocol string
  4930  		expected string
  4931  	}{
  4932  		{
  4933  			name: "simple",
  4934  			spn: proxy.ServicePortName{
  4935  				NamespacedName: types.NamespacedName{
  4936  					Namespace: "testing",
  4937  					Name:      "service",
  4938  				},
  4939  				Port: "http",
  4940  			},
  4941  			protocol: "tcp",
  4942  			expected: "P4ZYZVCF-testing/service/tcp/http",
  4943  		},
  4944  		{
  4945  			name: "different port, different hash",
  4946  			spn: proxy.ServicePortName{
  4947  				NamespacedName: types.NamespacedName{
  4948  					Namespace: "testing",
  4949  					Name:      "service",
  4950  				},
  4951  				Port: "https",
  4952  			},
  4953  			protocol: "tcp",
  4954  			expected: "LZBRENCP-testing/service/tcp/https",
  4955  		},
  4956  		{
  4957  			name: "max length",
  4958  			spn: proxy.ServicePortName{
  4959  				NamespacedName: types.NamespacedName{
  4960  					Namespace: "very-long-namespace-name-abcdefghijklmnopqrstuvwxyz0123456789xx",
  4961  					Name:      "very-long-service-name-why-would-you-even-do-this-i-mean-really",
  4962  				},
  4963  				Port: "port-443-providing-the-hypertext-transmission-protocol-with-tls",
  4964  			},
  4965  			protocol: "sctp",
  4966  			expected: "KR6NACJP-very-long-namespace-name-abcdefghijklmnopqrstuvwxyz0123456789xx/very-long-service-name-why-would-you-even-do-this-i-mean-really/sctp/port-443-providing-the-hypertext-transmission-protocol-with-tls",
  4967  		},
  4968  	}
  4969  
  4970  	for _, tc := range testCases {
  4971  		t.Run(tc.name, func(t *testing.T) {
  4972  			name := servicePortChainNameBase(&tc.spn, tc.protocol)
  4973  			if name != tc.expected {
  4974  				t.Errorf("expected %q, got %q", tc.expected, name)
  4975  			}
  4976  		})
  4977  	}
  4978  }
  4979  
  4980  func Test_servicePortEndpointChainNameBase(t *testing.T) {
  4981  	testCases := []struct {
  4982  		name     string
  4983  		spn      proxy.ServicePortName
  4984  		protocol string
  4985  		endpoint string
  4986  		expected string
  4987  	}{
  4988  		{
  4989  			name: "simple",
  4990  			spn: proxy.ServicePortName{
  4991  				NamespacedName: types.NamespacedName{
  4992  					Namespace: "testing",
  4993  					Name:      "service",
  4994  				},
  4995  				Port: "http",
  4996  			},
  4997  			protocol: "tcp",
  4998  			endpoint: "10.180.0.1:80",
  4999  			expected: "JO2XBXZR-testing/service/tcp/http__10.180.0.1/80",
  5000  		},
  5001  		{
  5002  			name: "different endpoint, different hash",
  5003  			spn: proxy.ServicePortName{
  5004  				NamespacedName: types.NamespacedName{
  5005  					Namespace: "testing",
  5006  					Name:      "service",
  5007  				},
  5008  				Port: "http",
  5009  			},
  5010  			protocol: "tcp",
  5011  			endpoint: "10.180.0.2:80",
  5012  			expected: "5S6H3H22-testing/service/tcp/http__10.180.0.2/80",
  5013  		},
  5014  		{
  5015  			name: "ipv6",
  5016  			spn: proxy.ServicePortName{
  5017  				NamespacedName: types.NamespacedName{
  5018  					Namespace: "testing",
  5019  					Name:      "service",
  5020  				},
  5021  				Port: "http",
  5022  			},
  5023  			protocol: "tcp",
  5024  			endpoint: "[fd80:abcd:12::a1b2:c3d4:e5f6:9999]:80",
  5025  			expected: "U7E2ET36-testing/service/tcp/http__fd80.abcd.12..a1b2.c3d4.e5f6.9999/80",
  5026  		},
  5027  		{
  5028  			name: "max length without truncation",
  5029  			spn: proxy.ServicePortName{
  5030  				NamespacedName: types.NamespacedName{
  5031  					Namespace: "very-long-namespace-name-abcdefghijklmnopqrstuvwxyz0123456789xx",
  5032  					Name:      "very-long-service-name-why-would-you-even-do-this-i-mean-really",
  5033  				},
  5034  				Port: "port-443-providing-the-hypertext-transmission-protocol-with-tls",
  5035  			},
  5036  			protocol: "sctp",
  5037  			endpoint: "[1234:5678:9abc:def0::abc:1234]:443",
  5038  			expected: "5YS7AFEA-very-long-namespace-name-abcdefghijklmnopqrstuvwxyz0123456789xx/very-long-service-name-why-would-you-even-do-this-i-mean-really/sctp/port-443-providing-the-hypertext-transmission-protocol-with-tls__1234.5678.9abc.def0..abc.1234/443",
  5039  		},
  5040  		{
  5041  			name: "truncated, 1",
  5042  			spn: proxy.ServicePortName{
  5043  				NamespacedName: types.NamespacedName{
  5044  					Namespace: "very-long-namespace-name-abcdefghijklmnopqrstuvwxyz0123456789xx",
  5045  					Name:      "very-long-service-name-why-would-you-even-do-this-i-mean-really",
  5046  				},
  5047  				Port: "port-443-providing-the-hypertext-transmission-protocol-with-tls",
  5048  			},
  5049  			protocol: "sctp",
  5050  			endpoint: "[1234:5678:9abc:def0::abcd:1234:5678]:443",
  5051  			expected: "CI6C53Q3-very-long-namespace-name-abcdefghijklmnopqrstuvwxyz0123456789xx/very-long-service-name-why-would-you-even-do-this-i-mean-really/sctp/port-443-providing-the-hypertext-transmission-protocol-with-tls__1234.5678.9abc.def0..abcd.1234...",
  5052  		},
  5053  		{
  5054  			name: "truncated, 2 (different IP, which is not visible in the result)",
  5055  			spn: proxy.ServicePortName{
  5056  				NamespacedName: types.NamespacedName{
  5057  					Namespace: "very-long-namespace-name-abcdefghijklmnopqrstuvwxyz0123456789xx",
  5058  					Name:      "very-long-service-name-why-would-you-even-do-this-i-mean-really",
  5059  				},
  5060  				Port: "port-443-providing-the-hypertext-transmission-protocol-with-tls",
  5061  			},
  5062  			protocol: "sctp",
  5063  			endpoint: "[1234:5678:9abc:def0::abcd:1234:8765]:443",
  5064  			expected: "2FLXFK6X-very-long-namespace-name-abcdefghijklmnopqrstuvwxyz0123456789xx/very-long-service-name-why-would-you-even-do-this-i-mean-really/sctp/port-443-providing-the-hypertext-transmission-protocol-with-tls__1234.5678.9abc.def0..abcd.1234...",
  5065  		},
  5066  	}
  5067  
  5068  	for _, tc := range testCases {
  5069  		t.Run(tc.name, func(t *testing.T) {
  5070  			name := servicePortEndpointChainNameBase(&tc.spn, tc.protocol, tc.endpoint)
  5071  			if name != tc.expected {
  5072  				t.Errorf("expected %q, got %q", tc.expected, name)
  5073  			}
  5074  		})
  5075  	}
  5076  }