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 }