github.com/cilium/cilium@v1.16.2/pkg/datapath/linux/probes/probes.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 4 package probes 5 6 import ( 7 "bytes" 8 "encoding/json" 9 "errors" 10 "fmt" 11 "io" 12 "net" 13 "os" 14 "path/filepath" 15 "strings" 16 "sync" 17 "text/template" 18 19 "github.com/cilium/ebpf" 20 "github.com/cilium/ebpf/asm" 21 "github.com/cilium/ebpf/features" 22 "github.com/cilium/ebpf/link" 23 "github.com/google/gopacket" 24 "github.com/google/gopacket/layers" 25 "golang.org/x/sys/unix" 26 27 "github.com/cilium/cilium/pkg/command/exec" 28 "github.com/cilium/cilium/pkg/defaults" 29 "github.com/cilium/cilium/pkg/logging" 30 "github.com/cilium/cilium/pkg/logging/logfields" 31 "github.com/cilium/cilium/pkg/netns" 32 ) 33 34 var ( 35 log = logging.DefaultLogger.WithField(logfields.LogSubsys, "probes") 36 once sync.Once 37 probeManager *ProbeManager 38 tpl = template.New("headerfile") 39 ) 40 41 func init() { 42 const content = ` 43 /* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ 44 /* Copyright Authors of Cilium */ 45 46 /* THIS FILE WAS GENERATED DURING AGENT STARTUP. */ 47 48 #pragma once 49 50 {{- if not .Common}} 51 #include "features.h" 52 {{- end}} 53 54 {{- range $key, $value := .Features}} 55 {{- if $value}} 56 #define {{$key}} 1 57 {{end}} 58 {{- end}} 59 ` 60 var err error 61 tpl, err = tpl.Parse(content) 62 if err != nil { 63 log.WithError(err).Fatal("could not parse headerfile template") 64 } 65 } 66 67 // ErrNotSupported indicates that a feature is not supported by the current kernel. 68 var ErrNotSupported = errors.New("not supported") 69 70 // KernelParam is a type based on string which represents CONFIG_* kernel 71 // parameters which usually have values "y", "n" or "m". 72 type KernelParam string 73 74 // Enabled checks whether the kernel parameter is enabled. 75 func (kp KernelParam) Enabled() bool { 76 return kp == "y" 77 } 78 79 // Module checks whether the kernel parameter is enabled as a module. 80 func (kp KernelParam) Module() bool { 81 return kp == "m" 82 } 83 84 // kernelOption holds information about kernel parameters to probe. 85 type kernelOption struct { 86 Description string 87 Enabled bool 88 CanBeModule bool 89 } 90 91 type ProgramHelper struct { 92 Program ebpf.ProgramType 93 Helper asm.BuiltinFunc 94 } 95 96 type miscFeatures struct { 97 HaveFibIfindex bool 98 } 99 100 type FeatureProbes struct { 101 ProgramHelpers map[ProgramHelper]bool 102 Misc miscFeatures 103 } 104 105 // SystemConfig contains kernel configuration and sysctl parameters related to 106 // BPF functionality. 107 type SystemConfig struct { 108 UnprivilegedBpfDisabled int `json:"unprivileged_bpf_disabled"` 109 BpfJitEnable int `json:"bpf_jit_enable"` 110 BpfJitHarden int `json:"bpf_jit_harden"` 111 BpfJitKallsyms int `json:"bpf_jit_kallsyms"` 112 BpfJitLimit int `json:"bpf_jit_limit"` 113 ConfigBpf KernelParam `json:"CONFIG_BPF"` 114 ConfigBpfSyscall KernelParam `json:"CONFIG_BPF_SYSCALL"` 115 ConfigHaveEbpfJit KernelParam `json:"CONFIG_HAVE_EBPF_JIT"` 116 ConfigBpfJit KernelParam `json:"CONFIG_BPF_JIT"` 117 ConfigBpfJitAlwaysOn KernelParam `json:"CONFIG_BPF_JIT_ALWAYS_ON"` 118 ConfigCgroups KernelParam `json:"CONFIG_CGROUPS"` 119 ConfigCgroupBpf KernelParam `json:"CONFIG_CGROUP_BPF"` 120 ConfigCgroupNetClassID KernelParam `json:"CONFIG_CGROUP_NET_CLASSID"` 121 ConfigSockCgroupData KernelParam `json:"CONFIG_SOCK_CGROUP_DATA"` 122 ConfigBpfEvents KernelParam `json:"CONFIG_BPF_EVENTS"` 123 ConfigKprobeEvents KernelParam `json:"CONFIG_KPROBE_EVENTS"` 124 ConfigUprobeEvents KernelParam `json:"CONFIG_UPROBE_EVENTS"` 125 ConfigTracing KernelParam `json:"CONFIG_TRACING"` 126 ConfigFtraceSyscalls KernelParam `json:"CONFIG_FTRACE_SYSCALLS"` 127 ConfigFunctionErrorInjection KernelParam `json:"CONFIG_FUNCTION_ERROR_INJECTION"` 128 ConfigBpfKprobeOverride KernelParam `json:"CONFIG_BPF_KPROBE_OVERRIDE"` 129 ConfigNet KernelParam `json:"CONFIG_NET"` 130 ConfigXdpSockets KernelParam `json:"CONFIG_XDP_SOCKETS"` 131 ConfigLwtunnelBpf KernelParam `json:"CONFIG_LWTUNNEL_BPF"` 132 ConfigNetActBpf KernelParam `json:"CONFIG_NET_ACT_BPF"` 133 ConfigNetClsBpf KernelParam `json:"CONFIG_NET_CLS_BPF"` 134 ConfigNetClsAct KernelParam `json:"CONFIG_NET_CLS_ACT"` 135 ConfigNetSchIngress KernelParam `json:"CONFIG_NET_SCH_INGRESS"` 136 ConfigXfrm KernelParam `json:"CONFIG_XFRM"` 137 ConfigIPRouteClassID KernelParam `json:"CONFIG_IP_ROUTE_CLASSID"` 138 ConfigIPv6Seg6Bpf KernelParam `json:"CONFIG_IPV6_SEG6_BPF"` 139 ConfigBpfLircMode2 KernelParam `json:"CONFIG_BPF_LIRC_MODE2"` 140 ConfigBpfStreamParser KernelParam `json:"CONFIG_BPF_STREAM_PARSER"` 141 ConfigNetfilterXtMatchBpf KernelParam `json:"CONFIG_NETFILTER_XT_MATCH_BPF"` 142 ConfigBpfilter KernelParam `json:"CONFIG_BPFILTER"` 143 ConfigBpfilterUmh KernelParam `json:"CONFIG_BPFILTER_UMH"` 144 ConfigTestBpf KernelParam `json:"CONFIG_TEST_BPF"` 145 ConfigKernelHz KernelParam `json:"CONFIG_HZ"` 146 } 147 148 // MapTypes contains bools indicating which types of BPF maps the currently 149 // running kernel supports. 150 type MapTypes struct { 151 HaveHashMapType bool `json:"have_hash_map_type"` 152 HaveArrayMapType bool `json:"have_array_map_type"` 153 HaveProgArrayMapType bool `json:"have_prog_array_map_type"` 154 HavePerfEventArrayMapType bool `json:"have_perf_event_array_map_type"` 155 HavePercpuHashMapType bool `json:"have_percpu_hash_map_type"` 156 HavePercpuArrayMapType bool `json:"have_percpu_array_map_type"` 157 HaveStackTraceMapType bool `json:"have_stack_trace_map_type"` 158 HaveCgroupArrayMapType bool `json:"have_cgroup_array_map_type"` 159 HaveLruHashMapType bool `json:"have_lru_hash_map_type"` 160 HaveLruPercpuHashMapType bool `json:"have_lru_percpu_hash_map_type"` 161 HaveLpmTrieMapType bool `json:"have_lpm_trie_map_type"` 162 HaveArrayOfMapsMapType bool `json:"have_array_of_maps_map_type"` 163 HaveHashOfMapsMapType bool `json:"have_hash_of_maps_map_type"` 164 HaveDevmapMapType bool `json:"have_devmap_map_type"` 165 HaveSockmapMapType bool `json:"have_sockmap_map_type"` 166 HaveCpumapMapType bool `json:"have_cpumap_map_type"` 167 HaveXskmapMapType bool `json:"have_xskmap_map_type"` 168 HaveSockhashMapType bool `json:"have_sockhash_map_type"` 169 HaveCgroupStorageMapType bool `json:"have_cgroup_storage_map_type"` 170 HaveReuseportSockarrayMapType bool `json:"have_reuseport_sockarray_map_type"` 171 HavePercpuCgroupStorageMapType bool `json:"have_percpu_cgroup_storage_map_type"` 172 HaveQueueMapType bool `json:"have_queue_map_type"` 173 HaveStackMapType bool `json:"have_stack_map_type"` 174 } 175 176 // Features contains BPF feature checks returned by bpftool. 177 type Features struct { 178 SystemConfig `json:"system_config"` 179 MapTypes `json:"map_types"` 180 } 181 182 // ProbeManager is a manager of BPF feature checks. 183 type ProbeManager struct { 184 features Features 185 } 186 187 // NewProbeManager returns a new instance of ProbeManager - a manager of BPF 188 // feature checks. 189 func NewProbeManager() *ProbeManager { 190 newProbeManager := func() { 191 probeManager = &ProbeManager{} 192 probeManager.features = probeManager.Probe() 193 } 194 once.Do(newProbeManager) 195 return probeManager 196 } 197 198 // Probe probes the underlying kernel for features. 199 func (*ProbeManager) Probe() Features { 200 var features Features 201 out, err := exec.WithTimeout( 202 defaults.ExecTimeout, 203 "bpftool", "-j", "feature", "probe", 204 ).CombinedOutput(log, true) 205 if err != nil { 206 log.WithError(err).Fatal("could not run bpftool") 207 } 208 if err := json.Unmarshal(out, &features); err != nil { 209 log.WithError(err).Fatal("could not parse bpftool output") 210 } 211 return features 212 } 213 214 // SystemConfigProbes performs a check of kernel configuration parameters. It 215 // returns an error when parameters required by Cilium are not enabled. It logs 216 // warnings when optional parameters are not enabled. 217 // 218 // When kernel config file is not found, bpftool can't probe kernel configuration 219 // parameter real setting, so only return error log when kernel config file exists 220 // and kernel configuration parameter setting is disabled 221 func (p *ProbeManager) SystemConfigProbes() error { 222 var notFound bool 223 if !p.KernelConfigAvailable() { 224 notFound = true 225 log.Info("Kernel config file not found: if the agent fails to start, check the system requirements at https://docs.cilium.io/en/stable/operations/system_requirements") 226 } 227 requiredParams := p.GetRequiredConfig() 228 for param, kernelOption := range requiredParams { 229 if !kernelOption.Enabled && !notFound { 230 module := "" 231 if kernelOption.CanBeModule { 232 module = " or module" 233 } 234 return fmt.Errorf("%s kernel parameter%s is required (needed for: %s)", param, module, kernelOption.Description) 235 } 236 } 237 optionalParams := p.GetOptionalConfig() 238 for param, kernelOption := range optionalParams { 239 if !kernelOption.Enabled && !notFound { 240 module := "" 241 if kernelOption.CanBeModule { 242 module = " or module" 243 } 244 log.Warningf("%s optional kernel parameter%s is not in kernel (needed for: %s)", param, module, kernelOption.Description) 245 } 246 } 247 return nil 248 } 249 250 // GetRequiredConfig performs a check of mandatory kernel configuration options. It 251 // returns a map indicating which required kernel parameters are enabled - and which are not. 252 // GetRequiredConfig is being used by CLI "cilium kernel-check". 253 func (p *ProbeManager) GetRequiredConfig() map[KernelParam]kernelOption { 254 config := p.features.SystemConfig 255 coreInfraDescription := "Essential eBPF infrastructure" 256 kernelParams := make(map[KernelParam]kernelOption) 257 258 kernelParams["CONFIG_BPF"] = kernelOption{ 259 Enabled: config.ConfigBpf.Enabled(), 260 Description: coreInfraDescription, 261 CanBeModule: false, 262 } 263 kernelParams["CONFIG_BPF_SYSCALL"] = kernelOption{ 264 Enabled: config.ConfigBpfSyscall.Enabled(), 265 Description: coreInfraDescription, 266 CanBeModule: false, 267 } 268 kernelParams["CONFIG_NET_SCH_INGRESS"] = kernelOption{ 269 Enabled: config.ConfigNetSchIngress.Enabled() || config.ConfigNetSchIngress.Module(), 270 Description: coreInfraDescription, 271 CanBeModule: true, 272 } 273 kernelParams["CONFIG_NET_CLS_BPF"] = kernelOption{ 274 Enabled: config.ConfigNetClsBpf.Enabled() || config.ConfigNetClsBpf.Module(), 275 Description: coreInfraDescription, 276 CanBeModule: true, 277 } 278 kernelParams["CONFIG_NET_CLS_ACT"] = kernelOption{ 279 Enabled: config.ConfigNetClsAct.Enabled(), 280 Description: coreInfraDescription, 281 CanBeModule: false, 282 } 283 kernelParams["CONFIG_BPF_JIT"] = kernelOption{ 284 Enabled: config.ConfigBpfJit.Enabled(), 285 Description: coreInfraDescription, 286 CanBeModule: false, 287 } 288 kernelParams["CONFIG_HAVE_EBPF_JIT"] = kernelOption{ 289 Enabled: config.ConfigHaveEbpfJit.Enabled(), 290 Description: coreInfraDescription, 291 CanBeModule: false, 292 } 293 294 return kernelParams 295 } 296 297 // GetOptionalConfig performs a check of *optional* kernel configuration options. It 298 // returns a map indicating which optional/non-mandatory kernel parameters are enabled. 299 // GetOptionalConfig is being used by CLI "cilium kernel-check". 300 func (p *ProbeManager) GetOptionalConfig() map[KernelParam]kernelOption { 301 config := p.features.SystemConfig 302 kernelParams := make(map[KernelParam]kernelOption) 303 304 kernelParams["CONFIG_CGROUP_BPF"] = kernelOption{ 305 Enabled: config.ConfigCgroupBpf.Enabled(), 306 Description: "Host Reachable Services and Sockmap optimization", 307 CanBeModule: false, 308 } 309 kernelParams["CONFIG_LWTUNNEL_BPF"] = kernelOption{ 310 Enabled: config.ConfigLwtunnelBpf.Enabled(), 311 Description: "Lightweight Tunnel hook for IP-in-IP encapsulation", 312 CanBeModule: false, 313 } 314 kernelParams["CONFIG_BPF_EVENTS"] = kernelOption{ 315 Enabled: config.ConfigBpfEvents.Enabled(), 316 Description: "Visibility and congestion management with datapath", 317 CanBeModule: false, 318 } 319 320 return kernelParams 321 } 322 323 // KernelConfigAvailable checks if the Kernel Config is available on the 324 // system or not. 325 func (p *ProbeManager) KernelConfigAvailable() bool { 326 // Check Kernel Config is available or not. 327 // We are replicating BPFTools logic here to check if kernel config is available 328 // https://elixir.bootlin.com/linux/v5.7/source/tools/bpf/bpftool/feature.c#L390 329 info := unix.Utsname{} 330 err := unix.Uname(&info) 331 if err != nil { 332 return false 333 } 334 release := strings.TrimSpace(string(bytes.Trim(info.Release[:], "\x00"))) 335 336 // Any error checking these files will return Kernel config not found error 337 if _, err := os.Stat(fmt.Sprintf("/boot/config-%s", release)); err != nil { 338 if _, err = os.Stat("/proc/config.gz"); err != nil { 339 return false 340 } 341 } 342 343 return true 344 } 345 346 // HaveProgramHelper is a wrapper around features.HaveProgramHelper() to 347 // check if a certain BPF program/helper copmbination is supported by the kernel. 348 // On unexpected probe results this function will terminate with log.Fatal(). 349 func HaveProgramHelper(pt ebpf.ProgramType, helper asm.BuiltinFunc) error { 350 err := features.HaveProgramHelper(pt, helper) 351 if errors.Is(err, ebpf.ErrNotSupported) { 352 return err 353 } 354 if err != nil { 355 log.WithError(err).WithField("programtype", pt).WithField("helper", helper).Fatal("failed to probe helper") 356 } 357 return nil 358 } 359 360 // HaveLargeInstructionLimit is a wrapper around features.HaveLargeInstructions() 361 // to check if the kernel supports the 1 Million instruction limit. 362 // On unexpected probe results this function will terminate with log.Fatal(). 363 func HaveLargeInstructionLimit() error { 364 err := features.HaveLargeInstructions() 365 if errors.Is(err, ebpf.ErrNotSupported) { 366 return err 367 } 368 if err != nil { 369 log.WithError(err).Fatal("failed to probe large instruction limit") 370 } 371 return nil 372 } 373 374 // HaveBoundedLoops is a wrapper around features.HaveBoundedLoops() 375 // to check if the kernel supports bounded loops in BPF programs. 376 // On unexpected probe results this function will terminate with log.Fatal(). 377 func HaveBoundedLoops() error { 378 err := features.HaveBoundedLoops() 379 if errors.Is(err, ebpf.ErrNotSupported) { 380 return err 381 } 382 if err != nil { 383 log.WithError(err).Fatal("failed to probe bounded loops") 384 } 385 return nil 386 } 387 388 // HaveFibIfindex checks if kernel has d1c362e1dd68 ("bpf: Always return target 389 // ifindex in bpf_fib_lookup") which is 5.10+. This got merged in the same kernel 390 // as the new redirect helpers. 391 func HaveFibIfindex() error { 392 return features.HaveProgramHelper(ebpf.SchedCLS, asm.FnRedirectPeer) 393 } 394 395 // HaveV2ISA is a wrapper around features.HaveV2ISA() to check if the kernel 396 // supports the V2 ISA. 397 // On unexpected probe results this function will terminate with log.Fatal(). 398 func HaveV2ISA() error { 399 err := features.HaveV2ISA() 400 if errors.Is(err, ebpf.ErrNotSupported) { 401 return err 402 } 403 if err != nil { 404 log.WithError(err).Fatal("failed to probe V2 ISA") 405 } 406 return nil 407 } 408 409 // HaveV3ISA is a wrapper around features.HaveV3ISA() to check if the kernel 410 // supports the V3 ISA. 411 // On unexpected probe results this function will terminate with log.Fatal(). 412 func HaveV3ISA() error { 413 err := features.HaveV3ISA() 414 if errors.Is(err, ebpf.ErrNotSupported) { 415 return err 416 } 417 if err != nil { 418 log.WithError(err).Fatal("failed to probe V3 ISA") 419 } 420 return nil 421 } 422 423 // HaveTCX returns nil if the running kernel supports attaching bpf programs to 424 // tcx hooks. 425 var HaveTCX = sync.OnceValue(func() error { 426 prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{ 427 Type: ebpf.SchedCLS, 428 Instructions: asm.Instructions{ 429 asm.Mov.Imm(asm.R0, 0), 430 asm.Return(), 431 }, 432 License: "Apache-2.0", 433 }) 434 if err != nil { 435 return err 436 } 437 defer prog.Close() 438 439 ns, err := netns.New() 440 if err != nil { 441 return fmt.Errorf("create netns: %w", err) 442 } 443 defer ns.Close() 444 445 // link.AttachTCX already performs its own feature detection and returns 446 // ebpf.ErrNotSupported if the host kernel doesn't have tcx. 447 return ns.Do(func() error { 448 l, err := link.AttachTCX(link.TCXOptions{ 449 Program: prog, 450 Attach: ebpf.AttachTCXIngress, 451 Interface: 1, // lo 452 Anchor: link.Tail(), 453 }) 454 if err != nil { 455 return fmt.Errorf("creating link: %w", err) 456 } 457 if err := l.Close(); err != nil { 458 return fmt.Errorf("closing link: %w", err) 459 } 460 461 return nil 462 }) 463 }) 464 465 // HaveOuterSourceIPSupport tests whether the kernel support setting the outer 466 // source IP address via the bpf_skb_set_tunnel_key BPF helper. We can't rely 467 // on the verifier to reject a program using the new support because the 468 // verifier just accepts any argument size for that helper; non-supported 469 // fields will simply not be used. Instead, we set the outer source IP and 470 // retrieve it with bpf_skb_get_tunnel_key right after. If the retrieved value 471 // equals the value set, we have a confirmation the kernel supports it. 472 func HaveOuterSourceIPSupport() (err error) { 473 defer func() { 474 if err != nil && !errors.Is(err, ebpf.ErrNotSupported) { 475 log.WithError(err).Fatal("failed to probe for outer source IP support") 476 } 477 }() 478 479 progSpec := &ebpf.ProgramSpec{ 480 Name: "set_tunnel_key_probe", 481 Type: ebpf.SchedACT, 482 License: "GPL", 483 } 484 progSpec.Instructions = asm.Instructions{ 485 asm.Mov.Reg(asm.R8, asm.R1), 486 487 asm.Mov.Imm(asm.R2, 0), 488 asm.StoreMem(asm.RFP, -8, asm.R2, asm.DWord), 489 asm.StoreMem(asm.RFP, -16, asm.R2, asm.DWord), 490 asm.StoreMem(asm.RFP, -24, asm.R2, asm.DWord), 491 asm.StoreMem(asm.RFP, -32, asm.R2, asm.DWord), 492 asm.StoreMem(asm.RFP, -40, asm.R2, asm.DWord), 493 asm.Mov.Imm(asm.R2, 42), 494 asm.StoreMem(asm.RFP, -44, asm.R2, asm.Word), 495 asm.Mov.Reg(asm.R2, asm.RFP), 496 asm.Add.Imm(asm.R2, -44), 497 asm.Mov.Imm(asm.R3, 44), // sizeof(struct bpf_tunnel_key) when setting the outer source IP is supported. 498 asm.Mov.Imm(asm.R4, 0), 499 asm.FnSkbSetTunnelKey.Call(), 500 501 asm.Mov.Reg(asm.R1, asm.R8), 502 asm.Mov.Reg(asm.R2, asm.RFP), 503 asm.Add.Imm(asm.R2, -44), 504 asm.Mov.Imm(asm.R3, 44), 505 asm.Mov.Imm(asm.R4, 0), 506 asm.FnSkbGetTunnelKey.Call(), 507 508 asm.LoadMem(asm.R0, asm.RFP, -44, asm.Word), 509 asm.Return(), 510 } 511 prog, err := ebpf.NewProgram(progSpec) 512 if err != nil { 513 return err 514 } 515 defer prog.Close() 516 517 pkt := []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} 518 ret, _, err := prog.Test(pkt) 519 if err != nil { 520 return err 521 } 522 if ret != 42 { 523 return ebpf.ErrNotSupported 524 } 525 return nil 526 } 527 528 // HaveSKBAdjustRoomL2RoomMACSupport tests whether the kernel supports the `bpf_skb_adjust_room` helper 529 // with the `BPF_ADJ_ROOM_MAC` mode. To do so, we create a program that requests the passed in SKB 530 // to be expanded by 20 bytes. The helper checks the `mode` argument and will return -ENOSUPP if 531 // the mode is unknown. Otherwise it should resize the SKB by 20 bytes and return 0. 532 func HaveSKBAdjustRoomL2RoomMACSupport() (err error) { 533 defer func() { 534 if err != nil && !errors.Is(err, ebpf.ErrNotSupported) { 535 log.WithError(err).Fatal("failed to probe for bpf_skb_adjust_room L2 room MAC support") 536 } 537 }() 538 539 progSpec := &ebpf.ProgramSpec{ 540 Name: "adjust_mac_room", 541 Type: ebpf.SchedCLS, 542 License: "GPL", 543 } 544 progSpec.Instructions = asm.Instructions{ 545 asm.Mov.Imm(asm.R2, 20), // len_diff 546 asm.Mov.Imm(asm.R3, 1), // mode: BPF_ADJ_ROOM_MAC 547 asm.Mov.Imm(asm.R4, 0), // flags: 0 548 asm.FnSkbAdjustRoom.Call(), 549 asm.Return(), 550 } 551 prog, err := ebpf.NewProgram(progSpec) 552 if err != nil { 553 return err 554 } 555 defer prog.Close() 556 557 // This is a Eth + IPv4 + UDP + data packet. The helper relies on a valid packet being passed in 558 // since it wants to know offsets of the different layers. 559 buf := gopacket.NewSerializeBuffer() 560 err = gopacket.SerializeLayers(buf, gopacket.SerializeOptions{}, 561 &layers.Ethernet{ 562 DstMAC: net.HardwareAddr{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 563 SrcMAC: net.HardwareAddr{0x0e, 0xf5, 0x16, 0x3d, 0x6b, 0xab}, 564 EthernetType: layers.EthernetTypeIPv4, 565 }, 566 &layers.IPv4{ 567 Version: 4, 568 IHL: 5, 569 Length: 49, 570 Id: 0xCECB, 571 TTL: 64, 572 Protocol: layers.IPProtocolUDP, 573 SrcIP: net.IPv4(0xc0, 0xa8, 0xb2, 0x56), 574 DstIP: net.IPv4(0xc0, 0xa8, 0xb2, 0xff), 575 }, 576 &layers.UDP{ 577 SrcPort: 23939, 578 DstPort: 32412, 579 }, 580 gopacket.Payload("M-SEARCH * HTTP/1.1\x0d\x0a"), 581 ) 582 if err != nil { 583 return fmt.Errorf("craft packet: %w", err) 584 } 585 586 ret, _, err := prog.Test(buf.Bytes()) 587 if err != nil { 588 return err 589 } 590 if ret != 0 { 591 return ebpf.ErrNotSupported 592 } 593 return nil 594 } 595 596 // HaveDeadCodeElim tests whether the kernel supports dead code elimination. 597 func HaveDeadCodeElim() error { 598 spec := ebpf.ProgramSpec{ 599 Name: "test", 600 Type: ebpf.XDP, 601 Instructions: asm.Instructions{ 602 asm.Mov.Imm(asm.R1, 0), 603 asm.JEq.Imm(asm.R1, 1, "else"), 604 asm.Mov.Imm(asm.R0, 2), 605 asm.Ja.Label("end"), 606 asm.Mov.Imm(asm.R0, 3).WithSymbol("else"), 607 asm.Return().WithSymbol("end"), 608 }, 609 } 610 611 prog, err := ebpf.NewProgram(&spec) 612 if err != nil { 613 return fmt.Errorf("loading program: %w", err) 614 } 615 616 info, err := prog.Info() 617 if err != nil { 618 return fmt.Errorf("get prog info: %w", err) 619 } 620 infoInst, err := info.Instructions() 621 if err != nil { 622 return fmt.Errorf("get instructions: %w", err) 623 } 624 625 for _, inst := range infoInst { 626 if inst.OpCode.Class().IsJump() && inst.OpCode.JumpOp() != asm.Exit { 627 return fmt.Errorf("Jump instruction found in the final program, no dead code elimination performed") 628 } 629 } 630 631 return nil 632 } 633 634 // HaveIPv6Support tests whether kernel can open an IPv6 socket. This will 635 // also implicitly auto-load IPv6 kernel module if available and not yet 636 // loaded. 637 func HaveIPv6Support() error { 638 fd, err := unix.Socket(unix.AF_INET6, unix.SOCK_STREAM, 0) 639 if errors.Is(err, unix.EAFNOSUPPORT) || errors.Is(err, unix.EPROTONOSUPPORT) { 640 return ErrNotSupported 641 } 642 unix.Close(fd) 643 return nil 644 } 645 646 // CreateHeaderFiles creates C header files with macros indicating which BPF 647 // features are available in the kernel. 648 func CreateHeaderFiles(headerDir string, probes *FeatureProbes) error { 649 common, err := os.Create(filepath.Join(headerDir, "features.h")) 650 if err != nil { 651 return fmt.Errorf("could not create common features header file: %w", err) 652 } 653 defer common.Close() 654 if err := writeCommonHeader(common, probes); err != nil { 655 return fmt.Errorf("could not write common features header file: %w", err) 656 } 657 658 skb, err := os.Create(filepath.Join(headerDir, "features_skb.h")) 659 if err != nil { 660 return fmt.Errorf("could not create skb related features header file: %w", err) 661 } 662 defer skb.Close() 663 if err := writeSkbHeader(skb, probes); err != nil { 664 return fmt.Errorf("could not write skb related features header file: %w", err) 665 } 666 667 xdp, err := os.Create(filepath.Join(headerDir, "features_xdp.h")) 668 if err != nil { 669 return fmt.Errorf("could not create xdp related features header file: %w", err) 670 } 671 defer xdp.Close() 672 if err := writeXdpHeader(xdp, probes); err != nil { 673 return fmt.Errorf("could not write xdp related features header file: %w", err) 674 } 675 676 return nil 677 } 678 679 // ExecuteHeaderProbes probes the kernel for a specific set of BPF features 680 // which are currently used to generate various feature macros for the datapath. 681 // The probe results returned in FeatureProbes are then used in the respective 682 // function that writes the actual C macro definitions. 683 // Further needed probes should be added here, while new macro strings need to 684 // be added in the correct `write*Header()` function. 685 func ExecuteHeaderProbes() *FeatureProbes { 686 probes := FeatureProbes{ 687 ProgramHelpers: make(map[ProgramHelper]bool), 688 Misc: miscFeatures{}, 689 } 690 691 progHelpers := []ProgramHelper{ 692 // common probes 693 {ebpf.CGroupSock, asm.FnGetNetnsCookie}, 694 {ebpf.CGroupSockAddr, asm.FnGetNetnsCookie}, 695 {ebpf.CGroupSockAddr, asm.FnGetSocketCookie}, 696 {ebpf.CGroupSock, asm.FnJiffies64}, 697 {ebpf.CGroupSockAddr, asm.FnJiffies64}, 698 {ebpf.SchedCLS, asm.FnJiffies64}, 699 {ebpf.XDP, asm.FnJiffies64}, 700 {ebpf.CGroupSockAddr, asm.FnSkLookupTcp}, 701 {ebpf.CGroupSockAddr, asm.FnSkLookupUdp}, 702 {ebpf.CGroupSockAddr, asm.FnGetCurrentCgroupId}, 703 {ebpf.CGroupSock, asm.FnSetRetval}, 704 {ebpf.SchedCLS, asm.FnRedirectNeigh}, 705 {ebpf.SchedCLS, asm.FnRedirectPeer}, 706 707 // skb related probes 708 {ebpf.SchedCLS, asm.FnSkbChangeTail}, 709 {ebpf.SchedCLS, asm.FnCsumLevel}, 710 711 // xdp related probes 712 {ebpf.XDP, asm.FnXdpGetBuffLen}, 713 {ebpf.XDP, asm.FnXdpLoadBytes}, 714 {ebpf.XDP, asm.FnXdpStoreBytes}, 715 } 716 for _, ph := range progHelpers { 717 probes.ProgramHelpers[ph] = (HaveProgramHelper(ph.Program, ph.Helper) == nil) 718 } 719 720 probes.Misc.HaveFibIfindex = (HaveFibIfindex() == nil) 721 722 return &probes 723 } 724 725 // writeCommonHeader defines macross for bpf/include/bpf/features.h 726 func writeCommonHeader(writer io.Writer, probes *FeatureProbes) error { 727 features := map[string]bool{ 728 "HAVE_NETNS_COOKIE": probes.ProgramHelpers[ProgramHelper{ebpf.CGroupSock, asm.FnGetNetnsCookie}] && 729 probes.ProgramHelpers[ProgramHelper{ebpf.CGroupSockAddr, asm.FnGetNetnsCookie}], 730 "HAVE_SOCKET_COOKIE": probes.ProgramHelpers[ProgramHelper{ebpf.CGroupSockAddr, asm.FnGetSocketCookie}], 731 "HAVE_JIFFIES": probes.ProgramHelpers[ProgramHelper{ebpf.CGroupSock, asm.FnJiffies64}] && 732 probes.ProgramHelpers[ProgramHelper{ebpf.CGroupSockAddr, asm.FnJiffies64}] && 733 probes.ProgramHelpers[ProgramHelper{ebpf.SchedCLS, asm.FnJiffies64}] && 734 probes.ProgramHelpers[ProgramHelper{ebpf.XDP, asm.FnJiffies64}], 735 "HAVE_CGROUP_ID": probes.ProgramHelpers[ProgramHelper{ebpf.CGroupSockAddr, asm.FnGetCurrentCgroupId}], 736 "HAVE_SET_RETVAL": probes.ProgramHelpers[ProgramHelper{ebpf.CGroupSock, asm.FnSetRetval}], 737 "HAVE_FIB_NEIGH": probes.ProgramHelpers[ProgramHelper{ebpf.SchedCLS, asm.FnRedirectNeigh}], 738 "HAVE_FIB_IFINDEX": probes.Misc.HaveFibIfindex, 739 } 740 741 return writeFeatureHeader(writer, features, true) 742 } 743 744 // writeSkbHeader defines macros for bpf/include/bpf/features_skb.h 745 func writeSkbHeader(writer io.Writer, probes *FeatureProbes) error { 746 featuresSkb := map[string]bool{ 747 "HAVE_CSUM_LEVEL": probes.ProgramHelpers[ProgramHelper{ebpf.SchedCLS, asm.FnCsumLevel}], 748 } 749 750 return writeFeatureHeader(writer, featuresSkb, false) 751 } 752 753 // writeXdpHeader defines macros for bpf/include/bpf/features_xdp.h 754 func writeXdpHeader(writer io.Writer, probes *FeatureProbes) error { 755 featuresXdp := map[string]bool{ 756 "HAVE_XDP_GET_BUFF_LEN": probes.ProgramHelpers[ProgramHelper{ebpf.XDP, asm.FnXdpGetBuffLen}], 757 "HAVE_XDP_LOAD_BYTES": probes.ProgramHelpers[ProgramHelper{ebpf.XDP, asm.FnXdpLoadBytes}], 758 "HAVE_XDP_STORE_BYTES": probes.ProgramHelpers[ProgramHelper{ebpf.XDP, asm.FnXdpStoreBytes}], 759 } 760 761 return writeFeatureHeader(writer, featuresXdp, false) 762 } 763 764 func writeFeatureHeader(writer io.Writer, features map[string]bool, common bool) error { 765 input := struct { 766 Common bool 767 Features map[string]bool 768 }{ 769 Common: common, 770 Features: features, 771 } 772 773 if err := tpl.Execute(writer, input); err != nil { 774 return fmt.Errorf("could not write template: %w", err) 775 } 776 777 return nil 778 } 779 780 // HaveBatchAPI checks if kernel supports batched bpf map lookup API. 781 func HaveBatchAPI() error { 782 spec := ebpf.MapSpec{ 783 Type: ebpf.LRUHash, 784 KeySize: 1, 785 ValueSize: 1, 786 MaxEntries: 2, 787 } 788 m, err := ebpf.NewMapWithOptions(&spec, ebpf.MapOptions{}) 789 if err != nil { 790 return ErrNotSupported 791 } 792 defer m.Close() 793 var cursor ebpf.MapBatchCursor 794 _, err = m.BatchLookup(&cursor, []byte{0}, []byte{0}, nil) // only do one batched lookup 795 if err != nil { 796 if errors.Is(err, ebpf.ErrNotSupported) { 797 return ErrNotSupported 798 } 799 return nil 800 } 801 return nil 802 }