github.com/cilium/cilium@v1.16.2/pkg/egressgateway/manager_privileged_test.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package egressgateway
     5  
     6  import (
     7  	"context"
     8  	"fmt"
     9  	"net/netip"
    10  	"testing"
    11  	"time"
    12  
    13  	"github.com/cilium/ebpf/rlimit"
    14  	"github.com/cilium/hive/hivetest"
    15  	"github.com/google/uuid"
    16  	"github.com/spf13/afero"
    17  	"github.com/stretchr/testify/require"
    18  	"github.com/vishvananda/netlink"
    19  	"k8s.io/apimachinery/pkg/types"
    20  
    21  	"github.com/cilium/cilium/pkg/bpf"
    22  	"github.com/cilium/cilium/pkg/datapath/linux/sysctl"
    23  	"github.com/cilium/cilium/pkg/hive"
    24  	"github.com/cilium/cilium/pkg/identity"
    25  	cilium_api_v2 "github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2"
    26  	"github.com/cilium/cilium/pkg/k8s/resource"
    27  	slimv1 "github.com/cilium/cilium/pkg/k8s/slim/k8s/apis/meta/v1"
    28  	k8sTypes "github.com/cilium/cilium/pkg/k8s/types"
    29  	"github.com/cilium/cilium/pkg/labels"
    30  	"github.com/cilium/cilium/pkg/lock"
    31  	"github.com/cilium/cilium/pkg/maps/egressmap"
    32  	"github.com/cilium/cilium/pkg/node/addressing"
    33  	nodeTypes "github.com/cilium/cilium/pkg/node/types"
    34  	"github.com/cilium/cilium/pkg/option"
    35  	"github.com/cilium/cilium/pkg/testutils"
    36  	testidentity "github.com/cilium/cilium/pkg/testutils/identity"
    37  )
    38  
    39  const (
    40  	testInterface1 = "cilium_egw1"
    41  	testInterface2 = "cilium_egw2"
    42  
    43  	node1 = "k8s1"
    44  	node2 = "k8s2"
    45  
    46  	node1IP = "192.168.1.1"
    47  	node2IP = "192.168.1.2"
    48  
    49  	ep1IP = "10.0.0.1"
    50  	ep2IP = "10.0.0.2"
    51  	ep3IP = "10.0.0.3"
    52  
    53  	destCIDR        = "1.1.1.0/24"
    54  	destCIDR3       = "1.1.3.0/24"
    55  	allZeroDestCIDR = "0.0.0.0/0"
    56  	excludedCIDR1   = "1.1.1.22/32"
    57  	excludedCIDR2   = "1.1.1.240/30"
    58  
    59  	egressIP1   = "192.168.101.1"
    60  	egressCIDR1 = "192.168.101.1/24"
    61  	egressIP2   = "192.168.102.1"
    62  	egressCIDR2 = "192.168.102.1/24"
    63  
    64  	zeroIP4 = "0.0.0.0"
    65  
    66  	// Special values for gatewayIP, see pkg/egressgateway/manager.go
    67  	gatewayNotFoundValue     = "0.0.0.0"
    68  	gatewayExcludedCIDRValue = "0.0.0.1"
    69  
    70  	// Special values for egressIP, see pkg/egressgateway/manager.go
    71  	egressIPNotFoundValue = "0.0.0.0"
    72  )
    73  
    74  var (
    75  	ep1Labels = map[string]string{"test-key": "test-value-1"}
    76  	ep2Labels = map[string]string{"test-key": "test-value-2"}
    77  
    78  	identityAllocator = testidentity.NewMockIdentityAllocator(nil)
    79  
    80  	nodeGroupNotFoundLabels = map[string]string{"label1": "notfound"}
    81  	nodeGroup1Labels        = map[string]string{"label1": "1"}
    82  	nodeGroup2Labels        = map[string]string{"label2": "2"}
    83  )
    84  
    85  type egressRule struct {
    86  	sourceIP  string
    87  	destCIDR  string
    88  	egressIP  string
    89  	gatewayIP string
    90  }
    91  
    92  type parsedEgressRule struct {
    93  	sourceIP  netip.Addr
    94  	destCIDR  netip.Prefix
    95  	egressIP  netip.Addr
    96  	gatewayIP netip.Addr
    97  }
    98  
    99  type rpFilterSetting struct {
   100  	iFaceName       string
   101  	rpFilterSetting string
   102  }
   103  
   104  type EgressGatewayTestSuite struct {
   105  	manager   *Manager
   106  	policies  fakeResource[*Policy]
   107  	nodes     fakeResource[*cilium_api_v2.CiliumNode]
   108  	endpoints fakeResource[*k8sTypes.CiliumEndpoint]
   109  	sysctl    sysctl.Sysctl
   110  }
   111  
   112  func setupEgressGatewayTestSuite(t *testing.T) *EgressGatewayTestSuite {
   113  	testutils.PrivilegedTest(t)
   114  
   115  	bpf.CheckOrMountFS("")
   116  	err := rlimit.RemoveMemlock()
   117  	require.NoError(t, err)
   118  
   119  	nodeTypes.SetName(node1)
   120  
   121  	k := &EgressGatewayTestSuite{}
   122  	k.policies = make(fakeResource[*Policy])
   123  	k.nodes = make(fakeResource[*cilium_api_v2.CiliumNode])
   124  	k.endpoints = make(fakeResource[*k8sTypes.CiliumEndpoint])
   125  	k.sysctl = sysctl.NewDirectSysctl(afero.NewOsFs(), "/proc")
   126  
   127  	lc := hivetest.Lifecycle(t)
   128  	policyMap := egressmap.CreatePrivatePolicyMap(lc, egressmap.DefaultPolicyConfig)
   129  
   130  	k.manager, err = newEgressGatewayManager(Params{
   131  		Lifecycle:         lc,
   132  		Config:            Config{1 * time.Millisecond},
   133  		DaemonConfig:      &option.DaemonConfig{ConfigPatchMutex: new(lock.RWMutex)},
   134  		IdentityAllocator: identityAllocator,
   135  		PolicyMap:         policyMap,
   136  		Policies:          k.policies,
   137  		Nodes:             k.nodes,
   138  		Endpoints:         k.endpoints,
   139  		Sysctl:            k.sysctl,
   140  	})
   141  	require.NoError(t, err)
   142  	require.NotNil(t, k.manager)
   143  
   144  	return k
   145  }
   146  
   147  func TestEgressGatewayCEGPParser(t *testing.T) {
   148  	setupEgressGatewayTestSuite(t)
   149  	// must specify name
   150  	policy := policyParams{
   151  		name:            "",
   152  		destinationCIDR: destCIDR,
   153  		iface:           testInterface1,
   154  	}
   155  
   156  	cegp, _ := newCEGP(&policy)
   157  	_, err := ParseCEGP(cegp)
   158  	require.Error(t, err)
   159  
   160  	// catch nil DestinationCIDR field
   161  	policy = policyParams{
   162  		name:  "policy-1",
   163  		iface: testInterface1,
   164  	}
   165  
   166  	cegp, _ = newCEGP(&policy)
   167  	cegp.Spec.DestinationCIDRs = nil
   168  	_, err = ParseCEGP(cegp)
   169  	require.Error(t, err)
   170  
   171  	// must specify at least one DestinationCIDR
   172  	policy = policyParams{
   173  		name:  "policy-1",
   174  		iface: testInterface1,
   175  	}
   176  
   177  	cegp, _ = newCEGP(&policy)
   178  	_, err = ParseCEGP(cegp)
   179  	require.Error(t, err)
   180  
   181  	// catch nil EgressGateway field
   182  	policy = policyParams{
   183  		name:            "policy-1",
   184  		destinationCIDR: destCIDR,
   185  		iface:           testInterface1,
   186  	}
   187  
   188  	cegp, _ = newCEGP(&policy)
   189  	cegp.Spec.EgressGateway = nil
   190  	_, err = ParseCEGP(cegp)
   191  	require.Error(t, err)
   192  
   193  	// must specify some sort of endpoint selector
   194  	policy = policyParams{
   195  		name:            "policy-1",
   196  		destinationCIDR: destCIDR,
   197  		iface:           testInterface1,
   198  	}
   199  
   200  	cegp, _ = newCEGP(&policy)
   201  	cegp.Spec.Selectors[0].NamespaceSelector = nil
   202  	cegp.Spec.Selectors[0].PodSelector = nil
   203  	_, err = ParseCEGP(cegp)
   204  	require.Error(t, err)
   205  
   206  	// can't specify both egress iface and IP
   207  	policy = policyParams{
   208  		name:            "policy-1",
   209  		destinationCIDR: destCIDR,
   210  		iface:           testInterface1,
   211  		egressIP:        egressIP1,
   212  	}
   213  
   214  	cegp, _ = newCEGP(&policy)
   215  	_, err = ParseCEGP(cegp)
   216  	require.Error(t, err)
   217  }
   218  
   219  func TestEgressGatewayManager(t *testing.T) {
   220  	k := setupEgressGatewayTestSuite(t)
   221  	createTestInterface(t, k.sysctl, testInterface1, egressCIDR1)
   222  	createTestInterface(t, k.sysctl, testInterface2, egressCIDR2)
   223  
   224  	policyMap := k.manager.policyMap
   225  	egressGatewayManager := k.manager
   226  	reconciliationEventsCount := egressGatewayManager.reconciliationEventsCount.Load()
   227  
   228  	k.policies.sync(t)
   229  	k.nodes.sync(t)
   230  	k.endpoints.sync(t)
   231  
   232  	reconciliationEventsCount = waitForReconciliationRun(t, egressGatewayManager, reconciliationEventsCount)
   233  
   234  	node1 := newCiliumNode(node1, node1IP, nodeGroup1Labels)
   235  	k.nodes.process(t, resource.Event[*cilium_api_v2.CiliumNode]{
   236  		Kind:   resource.Upsert,
   237  		Object: node1.ToCiliumNode(),
   238  	})
   239  	reconciliationEventsCount = waitForReconciliationRun(t, egressGatewayManager, reconciliationEventsCount)
   240  
   241  	node2 := newCiliumNode(node2, node2IP, nodeGroup2Labels)
   242  	k.nodes.process(t, resource.Event[*cilium_api_v2.CiliumNode]{
   243  		Kind:   resource.Upsert,
   244  		Object: node2.ToCiliumNode(),
   245  	})
   246  	reconciliationEventsCount = waitForReconciliationRun(t, egressGatewayManager, reconciliationEventsCount)
   247  
   248  	// Create a new policy
   249  	policy1 := policyParams{
   250  		name:            "policy-1",
   251  		endpointLabels:  ep1Labels,
   252  		destinationCIDR: destCIDR,
   253  		nodeLabels:      nodeGroup1Labels,
   254  		iface:           testInterface1,
   255  	}
   256  
   257  	addPolicy(t, k.policies, &policy1)
   258  	reconciliationEventsCount = waitForReconciliationRun(t, egressGatewayManager, reconciliationEventsCount)
   259  
   260  	assertRPFilter(t, k.sysctl, []rpFilterSetting{
   261  		{iFaceName: testInterface1, rpFilterSetting: "2"},
   262  		{iFaceName: testInterface2, rpFilterSetting: "1"},
   263  	})
   264  	assertEgressRules(t, policyMap, []egressRule{})
   265  
   266  	// Add a new endpoint & ID which matches policy-1
   267  	ep1, id1 := newEndpointAndIdentity("ep-1", ep1IP, ep1Labels)
   268  	addEndpoint(t, k.endpoints, &ep1)
   269  	reconciliationEventsCount = waitForReconciliationRun(t, egressGatewayManager, reconciliationEventsCount)
   270  
   271  	assertEgressRules(t, policyMap, []egressRule{
   272  		{ep1IP, destCIDR, egressIP1, node1IP},
   273  	})
   274  
   275  	// Update the endpoint labels in order for it to not be a match
   276  	id1 = updateEndpointAndIdentity(&ep1, id1, map[string]string{})
   277  	addEndpoint(t, k.endpoints, &ep1)
   278  	reconciliationEventsCount = waitForReconciliationRun(t, egressGatewayManager, reconciliationEventsCount)
   279  
   280  	assertEgressRules(t, policyMap, []egressRule{})
   281  
   282  	// Restore the old endpoint lables in order for it to be a match
   283  	id1 = updateEndpointAndIdentity(&ep1, id1, ep1Labels)
   284  	addEndpoint(t, k.endpoints, &ep1)
   285  	reconciliationEventsCount = waitForReconciliationRun(t, egressGatewayManager, reconciliationEventsCount)
   286  
   287  	assertEgressRules(t, policyMap, []egressRule{
   288  		{ep1IP, destCIDR, egressIP1, node1IP},
   289  	})
   290  
   291  	// Changing the DestCIDR to 0.0.0.0 results in a conflict with
   292  	// the existing IP rules. Test that the manager is able to
   293  	// resolve this conflict.
   294  	policy1.destinationCIDR = allZeroDestCIDR
   295  	addPolicy(t, k.policies, &policy1)
   296  	reconciliationEventsCount = waitForReconciliationRun(t, egressGatewayManager, reconciliationEventsCount)
   297  
   298  	assertEgressRules(t, policyMap, []egressRule{
   299  		{ep1IP, allZeroDestCIDR, egressIP1, node1IP},
   300  	})
   301  
   302  	// Restore old DestCIDR
   303  	policy1.destinationCIDR = destCIDR
   304  	addPolicy(t, k.policies, &policy1)
   305  
   306  	// Create a new policy
   307  	addPolicy(t, k.policies, &policyParams{
   308  		name:            "policy-2",
   309  		endpointLabels:  ep2Labels,
   310  		destinationCIDR: destCIDR,
   311  		nodeLabels:      nodeGroup2Labels,
   312  		iface:           testInterface1,
   313  	})
   314  	reconciliationEventsCount = waitForReconciliationRun(t, egressGatewayManager, reconciliationEventsCount)
   315  
   316  	assertEgressRules(t, policyMap, []egressRule{
   317  		{ep1IP, destCIDR, egressIP1, node1IP},
   318  	})
   319  
   320  	// Add a new endpoint and ID which matches policy-2
   321  	ep2, _ := newEndpointAndIdentity("ep-2", ep2IP, ep2Labels)
   322  	addEndpoint(t, k.endpoints, &ep2)
   323  	reconciliationEventsCount = waitForReconciliationRun(t, egressGatewayManager, reconciliationEventsCount)
   324  
   325  	assertEgressRules(t, policyMap, []egressRule{
   326  		{ep1IP, destCIDR, egressIP1, node1IP},
   327  		{ep2IP, destCIDR, zeroIP4, node2IP},
   328  	})
   329  
   330  	// Test excluded CIDRs by adding one to policy-1
   331  	addPolicy(t, k.policies, &policyParams{
   332  		name:            "policy-1",
   333  		endpointLabels:  ep1Labels,
   334  		destinationCIDR: destCIDR,
   335  		excludedCIDRs:   []string{excludedCIDR1},
   336  		nodeLabels:      nodeGroup1Labels,
   337  		iface:           testInterface1,
   338  	})
   339  	reconciliationEventsCount = waitForReconciliationRun(t, egressGatewayManager, reconciliationEventsCount)
   340  
   341  	assertEgressRules(t, policyMap, []egressRule{
   342  		{ep1IP, destCIDR, egressIP1, node1IP},
   343  		{ep1IP, excludedCIDR1, egressIP1, gatewayExcludedCIDRValue},
   344  		{ep2IP, destCIDR, zeroIP4, node2IP},
   345  	})
   346  
   347  	// Add a second excluded CIDR to policy-1
   348  	addPolicy(t, k.policies, &policyParams{
   349  		name:            "policy-1",
   350  		endpointLabels:  ep1Labels,
   351  		destinationCIDR: destCIDR,
   352  		excludedCIDRs:   []string{excludedCIDR1, excludedCIDR2},
   353  		nodeLabels:      nodeGroup1Labels,
   354  		iface:           testInterface1,
   355  	})
   356  	reconciliationEventsCount = waitForReconciliationRun(t, egressGatewayManager, reconciliationEventsCount)
   357  
   358  	assertEgressRules(t, policyMap, []egressRule{
   359  		{ep1IP, destCIDR, egressIP1, node1IP},
   360  		{ep1IP, excludedCIDR1, egressIP1, gatewayExcludedCIDRValue},
   361  		{ep1IP, excludedCIDR2, egressIP1, gatewayExcludedCIDRValue},
   362  		{ep2IP, destCIDR, zeroIP4, node2IP},
   363  	})
   364  
   365  	// Remove the first excluded CIDR from policy-1
   366  	addPolicy(t, k.policies, &policyParams{
   367  		name:            "policy-1",
   368  		endpointLabels:  ep1Labels,
   369  		destinationCIDR: destCIDR,
   370  		excludedCIDRs:   []string{excludedCIDR2},
   371  		nodeLabels:      nodeGroup1Labels,
   372  		iface:           testInterface1,
   373  	})
   374  	reconciliationEventsCount = waitForReconciliationRun(t, egressGatewayManager, reconciliationEventsCount)
   375  
   376  	assertEgressRules(t, policyMap, []egressRule{
   377  		{ep1IP, destCIDR, egressIP1, node1IP},
   378  		{ep1IP, excludedCIDR2, egressIP1, gatewayExcludedCIDRValue},
   379  		{ep2IP, destCIDR, zeroIP4, node2IP},
   380  	})
   381  
   382  	// Remove the second excluded CIDR
   383  	addPolicy(t, k.policies, &policyParams{
   384  		name:            "policy-1",
   385  		endpointLabels:  ep1Labels,
   386  		destinationCIDR: destCIDR,
   387  		nodeLabels:      nodeGroup1Labels,
   388  		iface:           testInterface1,
   389  	})
   390  	reconciliationEventsCount = waitForReconciliationRun(t, egressGatewayManager, reconciliationEventsCount)
   391  
   392  	assertEgressRules(t, policyMap, []egressRule{
   393  		{ep1IP, destCIDR, egressIP1, node1IP},
   394  		{ep2IP, destCIDR, zeroIP4, node2IP},
   395  	})
   396  
   397  	// Test matching no gateway
   398  	addPolicy(t, k.policies, &policyParams{
   399  		name:            "policy-1",
   400  		endpointLabels:  ep1Labels,
   401  		destinationCIDR: destCIDR,
   402  		nodeLabels:      nodeGroupNotFoundLabels,
   403  		iface:           testInterface1,
   404  	})
   405  	reconciliationEventsCount = waitForReconciliationRun(t, egressGatewayManager, reconciliationEventsCount)
   406  
   407  	assertEgressRules(t, policyMap, []egressRule{
   408  		{ep1IP, destCIDR, zeroIP4, gatewayNotFoundValue},
   409  		{ep2IP, destCIDR, zeroIP4, node2IP},
   410  	})
   411  
   412  	// Test a policy without valid egressIP
   413  	addPolicy(t, k.policies, &policyParams{
   414  		name:            "policy-3",
   415  		endpointLabels:  ep1Labels,
   416  		destinationCIDR: destCIDR3,
   417  		nodeLabels:      nodeGroup1Labels,
   418  		iface:           "no_interface",
   419  	})
   420  	reconciliationEventsCount = waitForReconciliationRun(t, egressGatewayManager, reconciliationEventsCount)
   421  
   422  	assertEgressRules(t, policyMap, []egressRule{
   423  		{ep1IP, destCIDR, zeroIP4, gatewayNotFoundValue},
   424  		{ep1IP, destCIDR3, egressIPNotFoundValue, node1IP},
   425  		{ep2IP, destCIDR, zeroIP4, node2IP},
   426  	})
   427  
   428  	// Update the endpoint labels in order for it to not be a match
   429  	_ = updateEndpointAndIdentity(&ep1, id1, map[string]string{})
   430  	addEndpoint(t, k.endpoints, &ep1)
   431  	waitForReconciliationRun(t, egressGatewayManager, reconciliationEventsCount)
   432  
   433  	assertEgressRules(t, policyMap, []egressRule{
   434  		{ep2IP, destCIDR, zeroIP4, node2IP},
   435  	})
   436  }
   437  
   438  func TestEndpointDataStore(t *testing.T) {
   439  	k := setupEgressGatewayTestSuite(t)
   440  
   441  	createTestInterface(t, k.sysctl, testInterface1, egressCIDR1)
   442  
   443  	policyMap := k.manager.policyMap
   444  	egressGatewayManager := k.manager
   445  
   446  	k.policies.sync(t)
   447  	k.nodes.sync(t)
   448  	k.endpoints.sync(t)
   449  
   450  	reconciliationEventsCount := egressGatewayManager.reconciliationEventsCount.Load()
   451  
   452  	node1 := newCiliumNode(node1, node1IP, nodeGroup1Labels)
   453  	k.nodes.process(t, resource.Event[*cilium_api_v2.CiliumNode]{
   454  		Kind:   resource.Upsert,
   455  		Object: node1.ToCiliumNode(),
   456  	})
   457  	reconciliationEventsCount = waitForReconciliationRun(t, egressGatewayManager, reconciliationEventsCount)
   458  
   459  	// Create a new policy
   460  	policy1 := policyParams{
   461  		name:            "policy-1",
   462  		endpointLabels:  ep1Labels,
   463  		destinationCIDR: destCIDR,
   464  		nodeLabels:      nodeGroup1Labels,
   465  		iface:           testInterface1,
   466  	}
   467  
   468  	addPolicy(t, k.policies, &policy1)
   469  	reconciliationEventsCount = waitForReconciliationRun(t, egressGatewayManager, reconciliationEventsCount)
   470  
   471  	assertEgressRules(t, policyMap, []egressRule{})
   472  
   473  	// Add a new endpoint & ID which matches policy-1
   474  	ep1, _ := newEndpointAndIdentity("ep-1", ep1IP, ep1Labels)
   475  	addEndpoint(t, k.endpoints, &ep1)
   476  	reconciliationEventsCount = waitForReconciliationRun(t, egressGatewayManager, reconciliationEventsCount)
   477  
   478  	assertEgressRules(t, policyMap, []egressRule{
   479  		{ep1IP, destCIDR, egressIP1, node1IP},
   480  	})
   481  
   482  	// Simulate statefulset pod migrations to a different node.
   483  
   484  	// Produce a new endpoint ep2 similar to ep1 - with the same name & labels, but with a different IP address.
   485  	// The ep1 will be deleted.
   486  	ep2, _ := newEndpointAndIdentity(ep1.Name, ep2IP, ep1Labels)
   487  
   488  	// Test event order: add new -> delete old
   489  	addEndpoint(t, k.endpoints, &ep2)
   490  	reconciliationEventsCount = waitForReconciliationRun(t, egressGatewayManager, reconciliationEventsCount)
   491  	deleteEndpoint(t, k.endpoints, &ep1)
   492  	reconciliationEventsCount = waitForReconciliationRun(t, egressGatewayManager, reconciliationEventsCount)
   493  
   494  	assertEgressRules(t, policyMap, []egressRule{
   495  		{ep2IP, destCIDR, egressIP1, node1IP},
   496  	})
   497  
   498  	// Produce a new endpoint ep3 similar to ep2 (and ep1) - with the same name & labels, but with a different IP address.
   499  	ep3, _ := newEndpointAndIdentity(ep1.Name, ep3IP, ep1Labels)
   500  
   501  	// Test event order: delete old -> update new
   502  	deleteEndpoint(t, k.endpoints, &ep2)
   503  	reconciliationEventsCount = waitForReconciliationRun(t, egressGatewayManager, reconciliationEventsCount)
   504  	addEndpoint(t, k.endpoints, &ep3)
   505  	waitForReconciliationRun(t, egressGatewayManager, reconciliationEventsCount)
   506  
   507  	assertEgressRules(t, policyMap, []egressRule{
   508  		{ep3IP, destCIDR, egressIP1, node1IP},
   509  	})
   510  }
   511  
   512  func TestCell(t *testing.T) {
   513  	err := hive.New(Cell).Populate(hivetest.Logger(t))
   514  	if err != nil {
   515  		t.Fatal(err)
   516  	}
   517  }
   518  
   519  func createTestInterface(tb testing.TB, sysctl sysctl.Sysctl, iface string, addr string) {
   520  	tb.Helper()
   521  
   522  	la := netlink.NewLinkAttrs()
   523  	la.Name = iface
   524  	dummy := &netlink.Dummy{LinkAttrs: la}
   525  	if err := netlink.LinkAdd(dummy); err != nil {
   526  		tb.Fatal(err)
   527  	}
   528  
   529  	link, err := netlink.LinkByName(iface)
   530  	if err != nil {
   531  		tb.Fatal(err)
   532  	}
   533  
   534  	tb.Cleanup(func() {
   535  		if err := netlink.LinkDel(link); err != nil {
   536  			tb.Error(err)
   537  		}
   538  	})
   539  
   540  	if err := netlink.LinkSetUp(link); err != nil {
   541  		tb.Fatal(err)
   542  	}
   543  
   544  	a, _ := netlink.ParseAddr(addr)
   545  	if err := netlink.AddrAdd(link, a); err != nil {
   546  		tb.Fatal(err)
   547  	}
   548  
   549  	ensureRPFilterIsEnabled(tb, sysctl, iface)
   550  }
   551  
   552  func ensureRPFilterIsEnabled(tb testing.TB, sysctl sysctl.Sysctl, iface string) {
   553  	rpFilterSetting := []string{"net", "ipv4", "conf", iface, "rp_filter"}
   554  
   555  	for i := 0; i < 10; i++ {
   556  		if err := sysctl.Enable(rpFilterSetting); err != nil {
   557  			tb.Fatal(err)
   558  		}
   559  
   560  		time.Sleep(100 * time.Millisecond)
   561  
   562  		if val, err := sysctl.Read(rpFilterSetting); err == nil {
   563  			if val == "1" {
   564  				return
   565  			}
   566  		}
   567  	}
   568  
   569  	tb.Fatal("failed to enable rp_filter")
   570  }
   571  
   572  func waitForReconciliationRun(tb testing.TB, egressGatewayManager *Manager, currentRun uint64) uint64 {
   573  	for i := 0; i < 100; i++ {
   574  		count := egressGatewayManager.reconciliationEventsCount.Load()
   575  		if count > currentRun {
   576  			return count
   577  		}
   578  
   579  		time.Sleep(10 * time.Millisecond)
   580  	}
   581  
   582  	tb.Fatal("Reconciliation is taking too long to run")
   583  	return 0
   584  }
   585  
   586  func newCiliumNode(name, nodeIP string, nodeLabels map[string]string) nodeTypes.Node {
   587  	return nodeTypes.Node{
   588  		Name:   name,
   589  		Labels: nodeLabels,
   590  		IPAddresses: []nodeTypes.Address{
   591  			{
   592  				Type: addressing.NodeInternalIP,
   593  				IP:   netip.MustParseAddr(nodeIP).AsSlice(),
   594  			},
   595  		},
   596  	}
   597  }
   598  
   599  // Mock the creation of endpoint and its corresponding identity, returns endpoint and ID.
   600  func newEndpointAndIdentity(name, ip string, epLabels map[string]string) (k8sTypes.CiliumEndpoint, *identity.Identity) {
   601  	id, _, _ := identityAllocator.AllocateIdentity(context.Background(), labels.Map2Labels(epLabels, labels.LabelSourceK8s), true, identity.InvalidIdentity)
   602  
   603  	return k8sTypes.CiliumEndpoint{
   604  		ObjectMeta: slimv1.ObjectMeta{
   605  			Name: name,
   606  			UID:  types.UID(uuid.New().String()),
   607  		},
   608  		Identity: &cilium_api_v2.EndpointIdentity{
   609  			ID: int64(id.ID),
   610  		},
   611  		Networking: &cilium_api_v2.EndpointNetworking{
   612  			Addressing: cilium_api_v2.AddressPairList{
   613  				&cilium_api_v2.AddressPair{
   614  					IPV4: ip,
   615  				},
   616  			},
   617  		},
   618  	}, id
   619  }
   620  
   621  // Mock the update of endpoint and its corresponding identity, with new labels. Returns new ID.
   622  func updateEndpointAndIdentity(endpoint *k8sTypes.CiliumEndpoint, oldID *identity.Identity, newEpLabels map[string]string) *identity.Identity {
   623  	ctx := context.Background()
   624  
   625  	identityAllocator.Release(ctx, oldID, true)
   626  	newID, _, _ := identityAllocator.AllocateIdentity(ctx, labels.Map2Labels(newEpLabels, labels.LabelSourceK8s), true, identity.InvalidIdentity)
   627  	endpoint.Identity.ID = int64(newID.ID)
   628  	return newID
   629  }
   630  
   631  func parseEgressRule(sourceIP, destCIDR, egressIP, gatewayIP string) parsedEgressRule {
   632  	sip := netip.MustParseAddr(sourceIP)
   633  	dc := netip.MustParsePrefix(destCIDR)
   634  	eip := netip.MustParseAddr(egressIP)
   635  	gip := netip.MustParseAddr(gatewayIP)
   636  
   637  	return parsedEgressRule{
   638  		sourceIP:  sip,
   639  		destCIDR:  dc,
   640  		egressIP:  eip,
   641  		gatewayIP: gip,
   642  	}
   643  }
   644  
   645  func assertEgressRules(t *testing.T, policyMap egressmap.PolicyMap, rules []egressRule) {
   646  	t.Helper()
   647  
   648  	err := tryAssertEgressRules(policyMap, rules)
   649  	require.NoError(t, err)
   650  }
   651  
   652  func tryAssertEgressRules(policyMap egressmap.PolicyMap, rules []egressRule) error {
   653  	parsedRules := []parsedEgressRule{}
   654  	for _, r := range rules {
   655  		parsedRules = append(parsedRules, parseEgressRule(r.sourceIP, r.destCIDR, r.egressIP, r.gatewayIP))
   656  	}
   657  
   658  	for _, r := range parsedRules {
   659  		policyVal, err := policyMap.Lookup(r.sourceIP, r.destCIDR)
   660  		if err != nil {
   661  			return fmt.Errorf("cannot lookup policy entry: %w", err)
   662  		}
   663  
   664  		if policyVal.GetEgressAddr() != r.egressIP {
   665  			return fmt.Errorf("mismatched egress IP")
   666  		}
   667  
   668  		if policyVal.GetGatewayAddr() != r.gatewayIP {
   669  			return fmt.Errorf("mismatched gateway IP")
   670  		}
   671  	}
   672  
   673  	untrackedRule := false
   674  	policyMap.IterateWithCallback(
   675  		func(key *egressmap.EgressPolicyKey4, val *egressmap.EgressPolicyVal4) {
   676  			for _, r := range parsedRules {
   677  				if key.Match(r.sourceIP, r.destCIDR) && val.Match(r.egressIP, r.gatewayIP) {
   678  					return
   679  				}
   680  			}
   681  
   682  			untrackedRule = true
   683  		})
   684  
   685  	if untrackedRule {
   686  		return fmt.Errorf("Untracked egress policy")
   687  	}
   688  
   689  	return nil
   690  }
   691  
   692  func assertRPFilter(t *testing.T, sysctl sysctl.Sysctl, rpFilterSettings []rpFilterSetting) {
   693  	t.Helper()
   694  
   695  	err := tryAssertRPFilterSettings(sysctl, rpFilterSettings)
   696  	require.NoError(t, err)
   697  }
   698  
   699  func tryAssertRPFilterSettings(sysctl sysctl.Sysctl, rpFilterSettings []rpFilterSetting) error {
   700  	for _, setting := range rpFilterSettings {
   701  		if val, err := sysctl.Read([]string{"net", "ipv4", "conf", setting.iFaceName, "rp_filter"}); err != nil {
   702  			return fmt.Errorf("failed to read rp_filter")
   703  		} else if val != setting.rpFilterSetting {
   704  			return fmt.Errorf("mismatched rp_filter iface: %s rp_filter: %s", setting.iFaceName, val)
   705  		}
   706  	}
   707  
   708  	return nil
   709  }