github.com/cilium/cilium@v1.16.2/pkg/datapath/loader/loader_test.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 4 package loader 5 6 import ( 7 "context" 8 "fmt" 9 "net/netip" 10 "os" 11 "path/filepath" 12 "testing" 13 "time" 14 15 "github.com/cilium/ebpf/rlimit" 16 "github.com/stretchr/testify/assert" 17 "github.com/stretchr/testify/require" 18 "github.com/vishvananda/netlink" 19 20 "github.com/cilium/cilium/pkg/bpf" 21 "github.com/cilium/cilium/pkg/datapath/loader/metrics" 22 "github.com/cilium/cilium/pkg/datapath/tables" 23 "github.com/cilium/cilium/pkg/defaults" 24 "github.com/cilium/cilium/pkg/maps/callsmap" 25 "github.com/cilium/cilium/pkg/option" 26 "github.com/cilium/cilium/pkg/testutils" 27 ) 28 29 var ( 30 contextTimeout = 10 * time.Second 31 benchTimeout = 5*time.Minute + 5*time.Second 32 33 bpfDir = filepath.Join("..", "..", "..", "bpf") 34 ) 35 36 func initEndpoint(tb testing.TB, ep *testutils.TestEndpoint) { 37 testutils.PrivilegedTest(tb) 38 39 require.Nil(tb, rlimit.RemoveMemlock()) 40 41 ep.State = tb.TempDir() 42 for _, iface := range []string{ep.InterfaceName(), defaults.SecondHostDevice} { 43 link := netlink.Dummy{ 44 LinkAttrs: netlink.LinkAttrs{ 45 Name: iface, 46 }, 47 } 48 if err := netlink.LinkAdd(&link); err != nil { 49 if !os.IsExist(err) { 50 tb.Fatalf("Failed to add link: %s", err) 51 } 52 } 53 tb.Cleanup(func() { 54 if err := netlink.LinkDel(&link); err != nil { 55 tb.Fatalf("Failed to delete link: %s", err) 56 } 57 }) 58 } 59 60 tb.Cleanup(func() { 61 files, err := filepath.Glob("/sys/fs/bpf/tc/globals/test_*") 62 require.Nil(tb, err) 63 for _, f := range files { 64 assert.Nil(tb, os.Remove(f)) 65 } 66 }) 67 } 68 69 func getDirs(tb testing.TB) *directoryInfo { 70 return &directoryInfo{ 71 Library: bpfDir, 72 Runtime: bpfDir, 73 State: bpfDir, 74 Output: tb.TempDir(), 75 } 76 } 77 78 func getEpDirs(ep *testutils.TestEndpoint) *directoryInfo { 79 return &directoryInfo{ 80 Library: bpfDir, 81 Runtime: bpfDir, 82 State: ep.StateDir(), 83 Output: ep.StateDir(), 84 } 85 } 86 87 func testReloadDatapath(t *testing.T, ep *testutils.TestEndpoint) { 88 ctx, cancel := context.WithTimeout(context.Background(), contextTimeout) 89 defer cancel() 90 stats := &metrics.SpanStat{} 91 92 l := newTestLoader(t) 93 _, err := l.ReloadDatapath(ctx, ep, stats) 94 require.NoError(t, err) 95 } 96 97 // TestCompileOrLoadDefaultEndpoint checks that the datapath can be compiled 98 // and loaded. 99 func TestCompileOrLoadDefaultEndpoint(t *testing.T) { 100 ep := testutils.NewTestEndpoint() 101 initEndpoint(t, &ep) 102 testReloadDatapath(t, &ep) 103 } 104 105 // TestCompileOrLoadHostEndpoint is the same as 106 // TestCompileAndLoadDefaultEndpoint, but for the host endpoint. 107 func TestCompileOrLoadHostEndpoint(t *testing.T) { 108 109 callsmap.HostMapName = fmt.Sprintf("test_%s", callsmap.MapName) 110 callsmap.NetdevMapName = fmt.Sprintf("test_%s", callsmap.MapName) 111 112 hostEp := testutils.NewTestHostEndpoint() 113 initEndpoint(t, &hostEp) 114 115 testReloadDatapath(t, &hostEp) 116 } 117 118 // TestReload compiles and attaches the datapath. 119 func TestReload(t *testing.T) { 120 ctx, cancel := context.WithTimeout(context.Background(), contextTimeout) 121 defer cancel() 122 123 ep := testutils.NewTestEndpoint() 124 initEndpoint(t, &ep) 125 126 dirInfo := getEpDirs(&ep) 127 err := compileDatapath(ctx, dirInfo, false, log) 128 require.NoError(t, err) 129 130 l, err := netlink.LinkByName(ep.InterfaceName()) 131 require.NoError(t, err) 132 133 objPath := fmt.Sprintf("%s/%s", dirInfo.Output, endpointObj) 134 linkDir := testutils.TempBPFFS(t) 135 136 for range 2 { 137 spec, err := bpf.LoadCollectionSpec(objPath) 138 require.NoError(t, err) 139 140 coll, commit, err := loadDatapath(spec, nil, nil) 141 require.NoError(t, err) 142 143 require.NoError(t, attachSKBProgram(l, coll.Programs[symbolFromEndpoint], 144 symbolFromEndpoint, linkDir, netlink.HANDLE_MIN_INGRESS, true)) 145 require.NoError(t, attachSKBProgram(l, coll.Programs[symbolToEndpoint], 146 symbolToEndpoint, linkDir, netlink.HANDLE_MIN_EGRESS, true)) 147 148 require.NoError(t, commit()) 149 150 coll.Close() 151 } 152 } 153 154 func testCompileFailure(t *testing.T, ep *testutils.TestEndpoint) { 155 ctx, cancel := context.WithTimeout(context.Background(), contextTimeout) 156 defer cancel() 157 158 exit := make(chan struct{}) 159 defer close(exit) 160 go func() { 161 select { 162 case <-time.After(100 * time.Millisecond): 163 cancel() 164 case <-exit: 165 break 166 } 167 }() 168 169 l := newTestLoader(t) 170 timeout := time.Now().Add(contextTimeout) 171 var err error 172 stats := &metrics.SpanStat{} 173 for err == nil && time.Now().Before(timeout) { 174 _, err = l.ReloadDatapath(ctx, ep, stats) 175 } 176 require.Error(t, err) 177 } 178 179 // TestCompileFailureDefaultEndpoint attempts to compile then cancels the 180 // context and ensures that the failure paths may be hit. 181 func TestCompileFailureDefaultEndpoint(t *testing.T) { 182 ep := testutils.NewTestEndpoint() 183 initEndpoint(t, &ep) 184 testCompileFailure(t, &ep) 185 } 186 187 // TestCompileFailureHostEndpoint is the same as 188 // TestCompileFailureDefaultEndpoint, but for the host endpoint. 189 func TestCompileFailureHostEndpoint(t *testing.T) { 190 hostEp := testutils.NewTestHostEndpoint() 191 initEndpoint(t, &hostEp) 192 testCompileFailure(t, &hostEp) 193 } 194 195 func TestBPFMasqAddrs(t *testing.T) { 196 old4 := option.Config.EnableIPv4Masquerade 197 option.Config.EnableIPv4Masquerade = true 198 old6 := option.Config.EnableIPv4Masquerade 199 option.Config.EnableIPv6Masquerade = true 200 t.Cleanup(func() { 201 option.Config.EnableIPv4Masquerade = old4 202 option.Config.EnableIPv6Masquerade = old6 203 }) 204 205 l := newTestLoader(t) 206 207 masq4, masq6 := l.bpfMasqAddrs("test") 208 require.Equal(t, masq4.IsValid(), false) 209 require.Equal(t, masq6.IsValid(), false) 210 211 newConfig := *l.nodeConfig.Load() 212 213 newConfig.NodeAddresses = []tables.NodeAddress{ 214 { 215 Addr: netip.MustParseAddr("1.0.0.1"), 216 NodePort: true, 217 Primary: true, 218 DeviceName: "test", 219 }, 220 { 221 Addr: netip.MustParseAddr("1000::1"), 222 NodePort: true, 223 Primary: true, 224 DeviceName: "test", 225 }, 226 { 227 Addr: netip.MustParseAddr("2.0.0.2"), 228 NodePort: false, 229 Primary: true, 230 DeviceName: tables.WildcardDeviceName, 231 }, 232 { 233 Addr: netip.MustParseAddr("2000::2"), 234 NodePort: false, 235 Primary: true, 236 DeviceName: tables.WildcardDeviceName, 237 }, 238 } 239 l.nodeConfig.Store(&newConfig) 240 241 masq4, masq6 = l.bpfMasqAddrs("test") 242 require.Equal(t, masq4.String(), "1.0.0.1") 243 require.Equal(t, masq6.String(), "1000::1") 244 245 masq4, masq6 = l.bpfMasqAddrs("unknown") 246 require.Equal(t, masq4.String(), "2.0.0.2") 247 require.Equal(t, masq6.String(), "2000::2") 248 } 249 250 // BenchmarkCompileOnly benchmarks the just the entire compilation process. 251 func BenchmarkCompileOnly(b *testing.B) { 252 ctx, cancel := context.WithTimeout(context.Background(), benchTimeout) 253 defer cancel() 254 255 dirInfo := getDirs(b) 256 option.Config.Debug = true 257 258 b.ResetTimer() 259 for i := 0; i < b.N; i++ { 260 if err := compileDatapath(ctx, dirInfo, false, log); err != nil { 261 b.Fatal(err) 262 } 263 } 264 } 265 266 // BenchmarkReplaceDatapath compiles the datapath program, then benchmarks only 267 // the loading of the program into the kernel. 268 func BenchmarkReplaceDatapath(b *testing.B) { 269 ctx, cancel := context.WithTimeout(context.Background(), benchTimeout) 270 defer cancel() 271 272 ep := testutils.NewTestEndpoint() 273 initEndpoint(b, &ep) 274 275 dirInfo := getEpDirs(&ep) 276 277 if err := compileDatapath(ctx, dirInfo, false, log); err != nil { 278 b.Fatal(err) 279 } 280 281 objPath := fmt.Sprintf("%s/%s", dirInfo.Output, endpointObj) 282 b.ResetTimer() 283 for i := 0; i < b.N; i++ { 284 spec, err := bpf.LoadCollectionSpec(objPath) 285 if err != nil { 286 b.Fatal(err) 287 } 288 289 coll, commit, err := loadDatapath(spec, nil, nil) 290 if err != nil { 291 b.Fatal(err) 292 } 293 if err := commit(); err != nil { 294 b.Fatalf("committing bpf pins: %s", err) 295 } 296 coll.Close() 297 } 298 }