github.com/looshlee/cilium@v1.6.12/test/runtime/lb.go (about)

     1  // Copyright 2017-2019 Authors of Cilium
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package RuntimeTest
    16  
    17  import (
    18  	"fmt"
    19  	"os"
    20  
    21  	. "github.com/cilium/cilium/test/ginkgo-ext"
    22  	"github.com/cilium/cilium/test/helpers"
    23  	"github.com/cilium/cilium/test/helpers/constants"
    24  
    25  	. "github.com/onsi/gomega"
    26  )
    27  
    28  var _ = Describe("RuntimeLB", func() {
    29  	var (
    30  		vm          *helpers.SSHMeta
    31  		monitorStop = func() error { return nil }
    32  	)
    33  
    34  	BeforeAll(func() {
    35  		vm = helpers.InitRuntimeHelper(helpers.Runtime, logger)
    36  		ExpectCiliumReady(vm)
    37  	})
    38  
    39  	AfterAll(func() {
    40  		vm.ServiceDelAll().ExpectSuccess()
    41  		vm.CloseSSHClient()
    42  	})
    43  
    44  	JustBeforeEach(func() {
    45  		monitorStop = vm.MonitorStart()
    46  	})
    47  
    48  	JustAfterEach(func() {
    49  		vm.ValidateNoErrorsInLogs(CurrentGinkgoTestDescription().Duration)
    50  		Expect(monitorStop()).To(BeNil(), "cannot stop monitor command")
    51  	})
    52  
    53  	AfterFailed(func() {
    54  		vm.ReportFailed(
    55  			"cilium service list",
    56  			"cilium bpf lb list",
    57  			"cilium policy get")
    58  	})
    59  
    60  	AfterEach(func() {
    61  		cleanupLBDevice(vm)
    62  	}, 500)
    63  
    64  	images := map[string]string{
    65  		helpers.Httpd1: constants.HttpdImage,
    66  		helpers.Httpd2: constants.HttpdImage,
    67  		helpers.Client: constants.NetperfImage,
    68  	}
    69  
    70  	createContainers := func() {
    71  		By("Creating containers for traffic test")
    72  
    73  		for k, v := range images {
    74  			vm.ContainerCreate(k, v, helpers.CiliumDockerNetwork, fmt.Sprintf("-l id.%s", k))
    75  		}
    76  		epStatus := vm.WaitEndpointsReady()
    77  		Expect(epStatus).Should(BeTrue())
    78  	}
    79  
    80  	deleteContainers := func() {
    81  		for k := range images {
    82  			vm.ContainerRm(k)
    83  		}
    84  	}
    85  
    86  	BeforeEach(func() {
    87  		vm.ServiceDelAll().ExpectSuccess()
    88  	}, 500)
    89  
    90  	It("validates basic service management functionality", func() {
    91  		result := vm.ServiceAdd(1, "[::]:80", []string{"[::1]:90", "[::2]:91"})
    92  		result.ExpectSuccess("unexpected failure to add service")
    93  		result = vm.ServiceGet(1)
    94  		result.ExpectSuccess("unexpected failure to retrieve service")
    95  		frontendAddress, err := vm.ServiceGetFrontendAddress(1)
    96  		Expect(err).Should(BeNil())
    97  		Expect(frontendAddress).To(ContainSubstring("[::]:80"),
    98  			"failed to retrieve frontend address: %q", result.Output())
    99  
   100  		//TODO: This need to be with Wait,Timeout
   101  		helpers.Sleep(5)
   102  
   103  		By("Checking that BPF maps are updated based on service configuration")
   104  
   105  		result = vm.ExecCilium("bpf lb list")
   106  		result.ExpectSuccess("bpf lb map cannot be retrieved correctly")
   107  		Expect(result.Output()).To(ContainSubstring("[::1]:90"), fmt.Sprintf(
   108  			"service backends not added to BPF map: %q", result.Output()))
   109  
   110  		By("Adding services that should not be allowed")
   111  
   112  		result = vm.ServiceAdd(0, "[::]:10000", []string{"[::1]:90", "[::2]:91"})
   113  		result.ExpectFail("unexpected success adding service with id 0")
   114  		result = vm.ServiceAdd(-1, "[::]:10000", []string{"[::1]:90", "[::2]:91"})
   115  		result.ExpectFail("unexpected success adding service with id -1")
   116  		result = vm.ServiceAdd(1, "[::]:10000", []string{"[::1]:90", "[::2]:91"})
   117  		result.ExpectFail("unexpected success adding service with duplicate id 1")
   118  		result = vm.ServiceAdd(2, "2.2.2.2:0", []string{"3.3.3.3:90", "4.4.4.4:91"})
   119  		result.ExpectFail("unexpected success adding service with L3=>L4 redirect")
   120  
   121  		By("Adding duplicate service FE address (IPv6)")
   122  
   123  		//Trying to create a new service with id 10, that conflicts with the FE addr on id=1
   124  		result = vm.ServiceAdd(10, "[::]:80", []string{"[::1]:90", "[::2]:91"})
   125  		result.ExpectFail("unexpected success adding service with duplicate frontend address (id 10)")
   126  		result = vm.ServiceGet(10)
   127  		result.ExpectFail("unexpected success fetching service with id 10, service should not be present")
   128  
   129  		By("Deleting IPv6 service")
   130  		result = vm.ServiceDel(1)
   131  		result.ExpectSuccess("unexpected failure deleting service with id 1")
   132  
   133  		By("Creating a valid IPv4 service with id 1")
   134  
   135  		result = vm.ServiceAdd(1, "127.0.0.1:80", []string{"127.0.0.1:90", "127.0.0.1:91"})
   136  		result.ExpectSuccess("unexpected failure adding valid service")
   137  		result = vm.ServiceGet(1)
   138  		result.ExpectSuccess("unexpected failure to retrieve service")
   139  
   140  		By("Adding duplicate service FE address (IPv4)")
   141  
   142  		result = vm.ServiceAdd(20, "127.0.0.1:80", []string{"127.0.0.1:90", "127.0.0.1:91"})
   143  		result.ExpectFail("unexpected success adding service with duplicate frontend address (id 20)")
   144  		result = vm.ServiceGet(20)
   145  		result.ExpectFail("unexpected success fetching service with id 20, service should not be present")
   146  	}, 500)
   147  
   148  	Context("With Containers", func() {
   149  
   150  		BeforeAll(func() {
   151  			createContainers()
   152  		})
   153  
   154  		AfterAll(func() {
   155  			deleteContainers()
   156  		})
   157  
   158  		It("validates that services work for L3 (IP) loadbalancing", func() {
   159  			err := createLBDevice(vm)
   160  			if err != nil {
   161  				log.Errorf("error creating interface: %s", err)
   162  			}
   163  			Expect(err).Should(BeNil())
   164  
   165  			httpd1, err := vm.ContainerInspectNet(helpers.Httpd1)
   166  			Expect(err).Should(BeNil())
   167  			httpd2, err := vm.ContainerInspectNet(helpers.Httpd2)
   168  			Expect(err).Should(BeNil())
   169  
   170  			By("Creating services")
   171  
   172  			services := map[string][]string{
   173  				"2.2.2.2:0": {
   174  					fmt.Sprintf("%s:0", httpd1[helpers.IPv4]),
   175  					fmt.Sprintf("%s:0", httpd2[helpers.IPv4]),
   176  				},
   177  				"[f00d::1:1]:0": {
   178  					fmt.Sprintf("[%s]:0", httpd1[helpers.IPv6]),
   179  					fmt.Sprintf("[%s]:0", httpd2[helpers.IPv6]),
   180  				},
   181  				"3.3.3.3:0": {
   182  					fmt.Sprintf("%s:0", "10.0.2.15"),
   183  				},
   184  				"[f00d::1:2]:0": {
   185  					fmt.Sprintf("[%s]:0", "fd02:1:1:1:1:1:1:1"),
   186  				},
   187  			}
   188  			svc := 1
   189  			for fe, be := range services {
   190  				status := vm.ServiceAdd(svc, fe, be)
   191  				status.ExpectSuccess(fmt.Sprintf("failed to create service %s=>%v", fe, be))
   192  				svc++
   193  			}
   194  
   195  			By("Pinging host => bpf_lb => container")
   196  
   197  			status := vm.Exec(helpers.Ping("2.2.2.2"))
   198  			status.ExpectSuccess("failed to ping service IP from host")
   199  			// FIXME GH-2889: createLBDevice() doesn't configure host IPv6
   200  			//status = vm.Exec(helpers.Ping6("f00d::1:1"))
   201  			//status.ExpectSuccess("failed to ping service IP from host")
   202  
   203  			By("Pinging container => bpf_lb => container")
   204  
   205  			status = vm.ContainerExec(helpers.Client, helpers.Ping("2.2.2.2"))
   206  			status.ExpectSuccess("failed to ping service IP 2.2.2.2")
   207  			status = vm.ContainerExec(helpers.Client, helpers.Ping6("f00d::1:1"))
   208  			status.ExpectSuccess("failed to ping service IP f00d::1:1")
   209  
   210  			By("Pinging container => bpf_lb => host")
   211  
   212  			status = vm.ContainerExec(helpers.Client, helpers.Ping("3.3.3.3"))
   213  			status.ExpectSuccess("failed to ping service IP 3.3.3.3")
   214  			status = vm.ContainerExec(helpers.Client, helpers.Ping("f00d::1:2"))
   215  			status.ExpectSuccess("failed to ping service IP f00d::1:2")
   216  
   217  			By("Configuring services to point to own IP via service")
   218  
   219  			vm.ServiceDelAll().ExpectSuccess()
   220  			loopbackServices := map[string]string{
   221  				"2.2.2.2:0":     fmt.Sprintf("%s:0", httpd1[helpers.IPv4]),
   222  				"[f00d::1:1]:0": fmt.Sprintf("[%s]:0", httpd1[helpers.IPv6]),
   223  			}
   224  			svc = 1
   225  			for fe, be := range loopbackServices {
   226  				status := vm.ServiceAdd(svc, fe, []string{be})
   227  				status.ExpectSuccess(fmt.Sprintf("failed to create service %s=>%v", fe, be))
   228  				svc++
   229  			}
   230  
   231  			By("Pinging from server1 to its own service IP")
   232  
   233  			status = vm.ContainerExec(helpers.Httpd1, helpers.Ping("2.2.2.2"))
   234  			status.ExpectSuccess("failed to ping service IP 2.2.2.2")
   235  			status = vm.ContainerExec(helpers.Httpd1, helpers.Ping6("f00d::1:1"))
   236  			status.ExpectSuccess("failed to ping service IP f00d::1:1")
   237  		}, 500)
   238  
   239  		It("validates that services work for L4 (IP+Port) loadbalancing", func() {
   240  			err := createLBDevice(vm)
   241  			if err != nil {
   242  				log.Errorf("error creating interface: %s", err)
   243  			}
   244  			Expect(err).Should(BeNil())
   245  
   246  			httpd1, err := vm.ContainerInspectNet(helpers.Httpd1)
   247  			Expect(err).Should(BeNil())
   248  			httpd2, err := vm.ContainerInspectNet(helpers.Httpd2)
   249  			Expect(err).Should(BeNil())
   250  
   251  			By("Creating services")
   252  
   253  			services := map[string][]string{
   254  				"2.2.2.2:80": {
   255  					fmt.Sprintf("%s:80", httpd1[helpers.IPv4]),
   256  					fmt.Sprintf("%s:80", httpd2[helpers.IPv4]),
   257  				},
   258  				"[f00d::1:1]:80": {
   259  					fmt.Sprintf("[%s]:80", httpd1[helpers.IPv6]),
   260  					fmt.Sprintf("[%s]:80", httpd2[helpers.IPv6]),
   261  				},
   262  			}
   263  			svc := 1
   264  			for fe, be := range services {
   265  				status := vm.ServiceAdd(svc, fe, be)
   266  				status.ExpectSuccess("failed to create service %s=>%v", fe, be)
   267  				svc++
   268  			}
   269  
   270  			By("Making HTTP requests from container => bpf_lb => container")
   271  
   272  			for ip := range services {
   273  				url := fmt.Sprintf("http://%s/public", ip)
   274  				status := vm.ContainerExec(helpers.Client, helpers.CurlFail(url))
   275  				status.ExpectSuccess(fmt.Sprintf("failed to fetch via URL %s", url))
   276  			}
   277  		}, 500)
   278  
   279  		It("validates service recovery on restart", func() {
   280  			service := "2.2.2.2:80"
   281  			svcID := 1
   282  			testCmd := helpers.CurlFail(fmt.Sprintf("http://%s/public", service))
   283  
   284  			httpd1, err := vm.ContainerInspectNet("httpd1")
   285  			Expect(err).Should(BeNil())
   286  			httpd2, err := vm.ContainerInspectNet("httpd2")
   287  			Expect(err).Should(BeNil())
   288  
   289  			status := vm.ServiceAdd(svcID, service, []string{
   290  				fmt.Sprintf("%s:80", httpd1["IPv4"]),
   291  				fmt.Sprintf("%s:80", httpd2["IPv4"])})
   292  			status.ExpectSuccess("failed to create service %s=>{httpd1,httpd2}", service)
   293  
   294  			By("Making HTTP request via the service before restart")
   295  
   296  			status = vm.ContainerExec(helpers.Client, testCmd)
   297  			status.ExpectSuccess("Failed to fetch URL via service")
   298  			oldSvc := vm.ServiceList()
   299  			oldSvc.ExpectSuccess("Cannot retrieve service list")
   300  
   301  			By("Fetching service state before restart")
   302  
   303  			oldSvcIds, err := vm.ServiceGetIds()
   304  			Expect(err).Should(BeNil())
   305  			oldBpfLB, err := vm.BpfLBList(false)
   306  			Expect(err).Should(BeNil())
   307  
   308  			err = vm.RestartCilium()
   309  			Expect(err).Should(BeNil(), "restarting Cilium failed")
   310  
   311  			By("Checking that the service was restored correctly")
   312  
   313  			svcIds, err := vm.ServiceGetIds()
   314  			Expect(err).Should(BeNil())
   315  			Expect(len(svcIds)).Should(Equal(len(oldSvcIds)),
   316  				"Service ids %s do not match old service ids %s", svcIds, oldSvcIds)
   317  			newSvc := vm.ServiceList()
   318  			newSvc.ExpectSuccess("Cannot retrieve service list after restart")
   319  			newSvc.ExpectEqual(oldSvc.Output().String(), "Service list does not match")
   320  
   321  			By("Checking that BPF LB maps match the service")
   322  
   323  			newBpfLB, err := vm.BpfLBList(false)
   324  			Expect(err).Should(BeNil(), "Cannot retrieve bpf lb list after restart")
   325  			Expect(oldBpfLB).Should(Equal(newBpfLB))
   326  			svcSync, err := vm.ServiceIsSynced(svcID)
   327  			Expect(err).Should(BeNil(), "Service is not sync with BPF LB")
   328  			Expect(svcSync).Should(BeTrue())
   329  
   330  			By("Making HTTP request via the service after restart")
   331  
   332  			status = vm.ContainerExec("client", testCmd)
   333  			status.ExpectSuccess("Failed to fetch URL via service")
   334  		})
   335  	})
   336  
   337  	Context("Services Policies", func() {
   338  
   339  		BeforeAll(func() {
   340  			vm.SampleContainersActions(helpers.Create, helpers.CiliumDockerNetwork)
   341  		})
   342  
   343  		AfterAll(func() {
   344  			vm.SampleContainersActions(helpers.Delete, helpers.CiliumDockerNetwork)
   345  		})
   346  
   347  		AfterEach(func() {
   348  			vm.PolicyDelAll().ExpectSuccess()
   349  			vm.ServiceDelAll().ExpectSuccess()
   350  
   351  			status := vm.ExecCilium(fmt.Sprintf("config %s=false",
   352  				helpers.OptionConntrackLocal))
   353  			status.ExpectSuccess()
   354  		})
   355  
   356  		testServicesWithPolicies := func(svcPort int) {
   357  			ready := vm.WaitEndpointsReady()
   358  			Expect(ready).To(BeTrue())
   359  
   360  			httpd1, err := vm.ContainerInspectNet(helpers.Httpd1)
   361  			Expect(err).Should(BeNil())
   362  			httpd2, err := vm.ContainerInspectNet(helpers.Httpd2)
   363  			Expect(err).Should(BeNil())
   364  
   365  			By("Configuring services")
   366  
   367  			service1 := fmt.Sprintf("2.2.2.100:%d", svcPort)
   368  			service2 := fmt.Sprintf("[f00d::1:1]:%d", svcPort)
   369  			service3 := fmt.Sprintf("2.2.2.101:%d", svcPort)
   370  			services := map[string]string{
   371  				service1: fmt.Sprintf("%s:80", httpd1[helpers.IPv4]),
   372  				service2: fmt.Sprintf("[%s]:80", httpd2[helpers.IPv6]),
   373  				service3: fmt.Sprintf("%s:80", httpd2[helpers.IPv4]),
   374  			}
   375  			svc := 100
   376  			for fe, be := range services {
   377  				status := vm.ServiceAdd(svc, fe, []string{be})
   378  				status.ExpectSuccess(fmt.Sprintf("failed to create service %s=>%s", fe, be))
   379  				svc++
   380  			}
   381  
   382  			getHTTP := func(service, target string) string {
   383  				return helpers.CurlFail(fmt.Sprintf(
   384  					"http://%s/%s", service, target))
   385  			}
   386  
   387  			_, err = vm.PolicyImportAndWait(vm.GetFullPath(policiesL7JSON), helpers.HelperTimeout)
   388  			Expect(err).Should(BeNil())
   389  
   390  			By("Making HTTP request to service with ingress policy")
   391  
   392  			status := vm.ContainerExec(helpers.App1, getHTTP(service1, helpers.Public))
   393  			status.ExpectSuccess()
   394  			status = vm.ContainerExec(helpers.App3, getHTTP(service1, helpers.Public))
   395  			status.ExpectFail()
   396  
   397  			By("Making HTTP request via egress policy to service IP")
   398  
   399  			status = vm.ContainerExec(helpers.App2, getHTTP(service2, helpers.Public))
   400  			status.ExpectSuccess()
   401  			status = vm.ContainerExec(helpers.App2, getHTTP(service2, helpers.Private))
   402  			status.ExpectFail()
   403  			status = vm.ContainerExec(helpers.App2, getHTTP(service3, helpers.Public))
   404  			status.ExpectSuccess()
   405  
   406  			By("Making HTTP requests to service with multiple ingress policies")
   407  
   408  			vm.PolicyDelAll()
   409  			_, err = vm.PolicyImportAndWait(vm.GetFullPath(multL7PoliciesJSON), helpers.HelperTimeout)
   410  			Expect(err).Should(BeNil())
   411  
   412  			status = vm.ContainerExec(helpers.App1, getHTTP(service1, helpers.Public))
   413  			status.ExpectSuccess()
   414  			status = vm.ContainerExec(helpers.App1, getHTTP(service1, helpers.Private))
   415  			status.ExpectFail()
   416  			status = vm.ContainerExec(helpers.App3, getHTTP(service1, helpers.Public))
   417  			status.ExpectFail()
   418  
   419  			By("Making HTTP requests via multiple egress policies to service IP")
   420  
   421  			status = vm.ContainerExec(helpers.App2, getHTTP(service2, helpers.Public))
   422  			status.ExpectSuccess()
   423  			status = vm.ContainerExec(helpers.App2, getHTTP(service2, helpers.Private))
   424  			status.ExpectFail()
   425  			status = vm.ContainerExec(helpers.App2, getHTTP(service3, helpers.Public))
   426  			status.ExpectSuccess()
   427  		}
   428  
   429  		It("tests with conntrack enabled", func() {
   430  			status := vm.ExecCilium(fmt.Sprintf("config %s=true",
   431  				helpers.OptionConntrackLocal))
   432  			status.ExpectSuccess()
   433  			testServicesWithPolicies(80)
   434  		})
   435  
   436  		It("tests with conntrack disabled", func() {
   437  			status := vm.ExecCilium(fmt.Sprintf("config %s=false",
   438  				helpers.OptionConntrackLocal))
   439  			status.ExpectSuccess()
   440  			testServicesWithPolicies(80)
   441  		})
   442  
   443  		/* Policy is written against egress to port 80, so when an
   444  		 * app makes requests on port 1234, the service translation
   445  		 * should occur before applying the egress policy.
   446  		 */
   447  		It("tests with service performing L4 port mapping", func() {
   448  			testServicesWithPolicies(1234)
   449  		})
   450  	})
   451  })
   452  
   453  // createLBDevice instantiates a device with the bpf_lb program to handle
   454  // loadbalancing as though it were attached to a physical device on the system.
   455  // This is implemented through a veth pair with two ends, lbtest1 and lbtest2.
   456  // bpf_lb is attached to ingress at lbtest2, so when traffic is sent through
   457  // lbtest1 it is forwarded through the veth pair into lbtest2 where the BPF
   458  // program executes the services functionality.
   459  //
   460  // The following traffic is routed to lbtest1 (so it goes to the LB BPF prog):
   461  // * 3.3.3.3/32
   462  // * 2.2.2.2/32
   463  // * f00d:1:1/128
   464  // * fbfb::10:10/128
   465  //
   466  // Additionally, the following IPs are associated with the cilium_host device,
   467  // so they may be used as backends for services and they will receive a
   468  // response from the host:
   469  // * 10.0.2.15 (inherited from virtualbox VM configuration)
   470  // * fd02:1:1:1:1:1:1:1 (explicitly configured below)
   471  func createLBDevice(node *helpers.SSHMeta) error {
   472  	script := `#!/bin/bash
   473  function mac2array() {
   474      echo "{0x${1//:/,0x}}"
   475  }
   476  
   477  ip link add lbtest1 type veth peer name lbtest2
   478  ip link set lbtest1 up
   479  
   480  # Route f00d::1:1 IPv6 packets to a fantasy router ("fbfb::10:10") behind lbtest1
   481  ip -6 route add fbfb::10:10/128 dev lbtest1
   482  MAC=$(ip link show lbtest1 | grep ether | awk '{print $2}')
   483  ip neigh add fbfb::10:10 lladdr $MAC dev lbtest1
   484  ip -6 route add f00d::1:1/128 via fbfb::10:10
   485  
   486  # Route 2.2.2.2 IPv4 packets to a fantasy router ("3.3.3.3") behind lbtest1
   487  ip route add 3.3.3.3/32 dev lbtest1
   488  MAC=$(ip link show lbtest1 | grep ether | awk '{print $2}')
   489  ip neigh add 3.3.3.3 lladdr $MAC dev lbtest1
   490  ip route add 2.2.2.2/32 via 3.3.3.3
   491  
   492  ip link set lbtest2 up
   493  
   494  LIB=/var/lib/cilium/bpf
   495  RUN=/var/run/cilium/state
   496  NH_IFINDEX=$(cat /sys/class/net/cilium_host/ifindex)
   497  NH_MAC=$(ip link show cilium_host | grep ether | awk '{print $2}')
   498  NH_MAC="{.addr=$(mac2array $NH_MAC)}"
   499  CLANG_OPTS="-D__NR_CPUS__=$(nproc) -DLB_L3 -DLB_REDIRECT=$NH_IFINDEX -DLB_DSTMAC=$NH_MAC -DCALLS_MAP=lbtest -O2 -target bpf -I. -I$LIB -I$LIB/include -I$RUN/globals -DDEBUG -Wno-address-of-packed-member -Wno-unknown-warning-option"
   500  touch netdev_config.h
   501  clang $CLANG_OPTS -c $LIB/bpf_lb.c -o tmp_lb.o
   502  
   503  tc qdisc del dev lbtest2 clsact 2> /dev/null || true
   504  tc qdisc add dev lbtest2 clsact
   505  tc filter add dev lbtest2 ingress bpf da obj tmp_lb.o sec from-netdev
   506  `
   507  	By("Creating LB device to handle service requests")
   508  	scriptName := "create_veth_interface"
   509  	log.Infof("generating veth script: %s", scriptName)
   510  	err := helpers.RenderTemplateToFile(scriptName, script, os.ModePerm)
   511  	if err != nil {
   512  		return err
   513  	}
   514  
   515  	// filesystem is mounted at path /vagrant on VM
   516  	scriptPath := fmt.Sprintf("%s/%s", helpers.BasePath, scriptName)
   517  
   518  	ipAddrCmd := "sudo ip addr add fd02:1:1:1:1:1:1:1 dev cilium_host"
   519  	res := node.Exec(ipAddrCmd)
   520  	log.Infof("output of %q: %s", ipAddrCmd, res.CombineOutput())
   521  
   522  	log.Infof("running script %s", scriptPath)
   523  	runScriptCmd := fmt.Sprintf("sudo %s", scriptPath)
   524  	res = node.Exec(runScriptCmd)
   525  	log.Infof("output of %q: %s", runScriptCmd, res.CombineOutput())
   526  	log.Infof("removing file %q", scriptName)
   527  	err = os.Remove(scriptName)
   528  	return err
   529  }
   530  
   531  func cleanupLBDevice(node *helpers.SSHMeta) {
   532  	ipAddrCmd := "sudo ip addr del fd02:1:1:1:1:1:1:1/128 dev cilium_host"
   533  	res := node.Exec(ipAddrCmd)
   534  	log.Infof("output of %q: %s", ipAddrCmd, res.CombineOutput())
   535  
   536  	ipLinkCmd := "sudo ip link del dev lbtest1"
   537  	res = node.Exec(ipLinkCmd)
   538  	log.Infof("output of %q: %s", ipLinkCmd, res.CombineOutput())
   539  }