github.com/cilium/cilium@v1.16.2/pkg/datapath/loader/xdp_test.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package loader
     5  
     6  import (
     7  	"errors"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/cilium/ebpf"
    12  	"github.com/cilium/ebpf/link"
    13  	"github.com/stretchr/testify/require"
    14  	"github.com/vishvananda/netlink"
    15  
    16  	"github.com/cilium/cilium/pkg/bpf"
    17  	"github.com/cilium/cilium/pkg/option"
    18  	"github.com/cilium/cilium/pkg/testutils"
    19  	"github.com/cilium/cilium/pkg/testutils/netns"
    20  )
    21  
    22  func TestMaybeUnloadObsoleteXDPPrograms(t *testing.T) {
    23  	testutils.PrivilegedTest(t)
    24  
    25  	ns := netns.NewNetNS(t)
    26  
    27  	ns.Do(func() error {
    28  		h, err := netlink.NewHandle()
    29  		require.NoError(t, err)
    30  
    31  		veth0 := &netlink.Veth{
    32  			LinkAttrs: netlink.LinkAttrs{Name: "veth0"},
    33  			PeerName:  "veth2",
    34  		}
    35  		err = h.LinkAdd(veth0)
    36  		require.NoError(t, err)
    37  
    38  		veth1 := &netlink.Veth{
    39  			LinkAttrs: netlink.LinkAttrs{Name: "veth1"},
    40  			PeerName:  "veth3",
    41  		}
    42  		err = h.LinkAdd(veth1)
    43  		require.NoError(t, err)
    44  
    45  		prog := mustXDPProgram(t, symbolFromHostNetdevXDP)
    46  		basePath := testutils.TempBPFFS(t)
    47  		veth0LinkPath := bpffsDeviceLinksDir(basePath, veth0)
    48  		require.NoError(t, bpf.MkdirBPF(veth0LinkPath))
    49  		veth1LinkPath := bpffsDeviceLinksDir(basePath, veth1)
    50  		require.NoError(t, bpf.MkdirBPF(veth1LinkPath))
    51  		// need to use symbolFromHostNetdevXDP as progName here as maybeUnloadObsoleteXDPPrograms explicitly uses that name.
    52  		err = attachXDPProgram(veth0, prog, symbolFromHostNetdevXDP, veth0LinkPath, link.XDPDriverMode)
    53  		require.NoError(t, err)
    54  
    55  		err = attachXDPProgram(veth1, prog, symbolFromHostNetdevXDP, veth1LinkPath, link.XDPDriverMode)
    56  		require.NoError(t, err)
    57  
    58  		newTestLoader(t).maybeUnloadObsoleteXDPPrograms(
    59  			[]string{"veth0"}, option.XDPModeLinkDriver, basePath,
    60  		)
    61  
    62  		require.NoError(t, testutils.WaitUntil(func() bool {
    63  			v1, err := h.LinkByName("veth1")
    64  			require.NoError(t, err)
    65  			if v1.Attrs().Xdp != nil {
    66  				return v1.Attrs().Xdp.Attached == false
    67  			}
    68  			return true
    69  		}, time.Second))
    70  
    71  		v0, err := h.LinkByName("veth0")
    72  		require.NoError(t, err)
    73  		require.NotNil(t, v0.Attrs().Xdp)
    74  		require.True(t, v0.Attrs().Xdp.Attached)
    75  
    76  		err = netlink.LinkDel(veth0)
    77  		require.NoError(t, err)
    78  
    79  		err = netlink.LinkDel(veth1)
    80  		require.NoError(t, err)
    81  
    82  		return nil
    83  	})
    84  }
    85  
    86  // Attach a program to a clean dummy device, no replacing necessary.
    87  func TestAttachXDP(t *testing.T) {
    88  	testutils.PrivilegedTest(t)
    89  
    90  	ns := netns.NewNetNS(t)
    91  
    92  	ns.Do(func() error {
    93  		veth := &netlink.Veth{
    94  			LinkAttrs: netlink.LinkAttrs{Name: "veth0"},
    95  			PeerName:  "veth1",
    96  		}
    97  		err := netlink.LinkAdd(veth)
    98  		require.NoError(t, err)
    99  
   100  		prog := mustXDPProgram(t, "test")
   101  		basePath := testutils.TempBPFFS(t)
   102  
   103  		err = attachXDPProgram(veth, prog, "test", basePath, link.XDPGenericMode)
   104  		require.NoError(t, err)
   105  
   106  		err = netlink.LinkDel(veth)
   107  		require.NoError(t, err)
   108  
   109  		return nil
   110  	})
   111  }
   112  
   113  // Replace an existing program attached using netlink attach.
   114  func TestAttachXDPWithPreviousAttach(t *testing.T) {
   115  	testutils.PrivilegedTest(t)
   116  
   117  	ns := netns.NewNetNS(t)
   118  
   119  	ns.Do(func() error {
   120  		veth := &netlink.Veth{
   121  			LinkAttrs: netlink.LinkAttrs{Name: "veth0"},
   122  			PeerName:  "veth1",
   123  		}
   124  		err := netlink.LinkAdd(veth)
   125  		require.NoError(t, err)
   126  
   127  		prog := mustXDPProgram(t, "test")
   128  		basePath := testutils.TempBPFFS(t)
   129  
   130  		err = netlink.LinkSetXdpFdWithFlags(veth, prog.FD(), int(link.XDPGenericMode))
   131  		require.NoError(t, err)
   132  
   133  		err = attachXDPProgram(veth, prog, "test", basePath, link.XDPGenericMode)
   134  		require.NoError(t, err)
   135  
   136  		err = netlink.LinkDel(veth)
   137  		require.NoError(t, err)
   138  
   139  		return nil
   140  	})
   141  }
   142  
   143  // On kernels that support it, make sure an existing bpf_link can be updated.
   144  func TestAttachXDPWithExistingLink(t *testing.T) {
   145  	testutils.PrivilegedTest(t)
   146  
   147  	ns := netns.NewNetNS(t)
   148  
   149  	ns.Do(func() error {
   150  		veth := &netlink.Veth{
   151  			LinkAttrs: netlink.LinkAttrs{Name: "veth0"},
   152  			PeerName:  "veth1",
   153  		}
   154  		err := netlink.LinkAdd(veth)
   155  		require.NoError(t, err)
   156  
   157  		prog := mustXDPProgram(t, "test")
   158  
   159  		// Probe XDP bpf_link support by manually attaching a Program and
   160  		// immediately closing the link when it succeeds.
   161  		l, err := link.AttachXDP(link.XDPOptions{
   162  			Program:   prog,
   163  			Interface: veth.Attrs().Index,
   164  			Flags:     link.XDPGenericMode,
   165  		})
   166  		if errors.Is(err, ebpf.ErrNotSupported) {
   167  			t.Skip("bpf_link is not supported")
   168  		}
   169  		require.NoError(t, err)
   170  		require.NoError(t, l.Close())
   171  
   172  		basePath := testutils.TempBPFFS(t)
   173  		pinDir := bpffsDeviceLinksDir(basePath, veth)
   174  		require.NoError(t, bpf.MkdirBPF(pinDir))
   175  
   176  		// At this point, we know bpf_link is supported, so attachXDPProgram should
   177  		// use it.
   178  		err = attachXDPProgram(veth, prog, "test", pinDir, link.XDPGenericMode)
   179  		require.NoError(t, err)
   180  
   181  		// Attach the same program again. This should update the existing link.
   182  		err = attachXDPProgram(veth, prog, "test", pinDir, link.XDPGenericMode)
   183  		require.NoError(t, err)
   184  
   185  		// Detach the program.
   186  		err = newTestLoader(t).DetachXDP(veth.Attrs().Name, basePath, "test")
   187  		require.NoError(t, err)
   188  
   189  		err = netlink.LinkDel(veth)
   190  		require.NoError(t, err)
   191  
   192  		return nil
   193  	})
   194  }
   195  
   196  // Detach an XDP program that was attached using netlink.
   197  func TestDetachXDPWithPreviousAttach(t *testing.T) {
   198  	testutils.PrivilegedTest(t)
   199  
   200  	ns := netns.NewNetNS(t)
   201  	ns.Do(func() error {
   202  		var veth netlink.Link = &netlink.Veth{
   203  			LinkAttrs: netlink.LinkAttrs{Name: "veth0"},
   204  			PeerName:  "veth1",
   205  		}
   206  		err := netlink.LinkAdd(veth)
   207  		require.NoError(t, err)
   208  
   209  		prog := mustXDPProgram(t, "test")
   210  		basePath := testutils.TempBPFFS(t)
   211  
   212  		err = netlink.LinkSetXdpFdWithFlags(veth, prog.FD(), int(link.XDPGenericMode))
   213  		require.NoError(t, err)
   214  		require.True(t, getLink(t, veth).Attrs().Xdp.Attached)
   215  
   216  		// Detach with the wrong name, leaving the program attached.
   217  		err = newTestLoader(t).DetachXDP(veth.Attrs().Name, basePath, "foo")
   218  		require.NoError(t, err)
   219  		require.True(t, getLink(t, veth).Attrs().Xdp.Attached)
   220  
   221  		err = newTestLoader(t).DetachXDP(veth.Attrs().Name, basePath, "test")
   222  		require.NoError(t, err)
   223  		require.False(t, getLink(t, veth).Attrs().Xdp.Attached)
   224  
   225  		require.NoError(t, netlink.LinkDel(veth))
   226  
   227  		return nil
   228  	})
   229  }
   230  
   231  func getLink(tb testing.TB, link netlink.Link) netlink.Link {
   232  	tb.Helper()
   233  
   234  	req, err := netlink.LinkByIndex(link.Attrs().Index)
   235  	require.NoError(tb, err)
   236  
   237  	return req
   238  }