version.go (3199B)
1 package internal 2 3 import ( 4 "fmt" 5 "sync" 6 7 "github.com/cilium/ebpf/internal/unix" 8 ) 9 10 const ( 11 // Version constant used in ELF binaries indicating that the loader needs to 12 // substitute the eBPF program's version with the value of the kernel's 13 // KERNEL_VERSION compile-time macro. Used for compatibility with BCC, gobpf 14 // and RedSift. 15 MagicKernelVersion = 0xFFFFFFFE 16 ) 17 18 var ( 19 kernelVersion = struct { 20 once sync.Once 21 version Version 22 err error 23 }{} 24 ) 25 26 // A Version in the form Major.Minor.Patch. 27 type Version [3]uint16 28 29 // NewVersion creates a version from a string like "Major.Minor.Patch". 30 // 31 // Patch is optional. 32 func NewVersion(ver string) (Version, error) { 33 var major, minor, patch uint16 34 n, _ := fmt.Sscanf(ver, "%d.%d.%d", &major, &minor, &patch) 35 if n < 2 { 36 return Version{}, fmt.Errorf("invalid version: %s", ver) 37 } 38 return Version{major, minor, patch}, nil 39 } 40 41 // NewVersionFromCode creates a version from a LINUX_VERSION_CODE. 42 func NewVersionFromCode(code uint32) Version { 43 return Version{ 44 uint16(uint8(code >> 16)), 45 uint16(uint8(code >> 8)), 46 uint16(uint8(code)), 47 } 48 } 49 50 func (v Version) String() string { 51 if v[2] == 0 { 52 return fmt.Sprintf("v%d.%d", v[0], v[1]) 53 } 54 return fmt.Sprintf("v%d.%d.%d", v[0], v[1], v[2]) 55 } 56 57 // Less returns true if the version is less than another version. 58 func (v Version) Less(other Version) bool { 59 for i, a := range v { 60 if a == other[i] { 61 continue 62 } 63 return a < other[i] 64 } 65 return false 66 } 67 68 // Unspecified returns true if the version is all zero. 69 func (v Version) Unspecified() bool { 70 return v[0] == 0 && v[1] == 0 && v[2] == 0 71 } 72 73 // Kernel implements the kernel's KERNEL_VERSION macro from linux/version.h. 74 // It represents the kernel version and patch level as a single value. 75 func (v Version) Kernel() uint32 { 76 77 // Kernels 4.4 and 4.9 have their SUBLEVEL clamped to 255 to avoid 78 // overflowing into PATCHLEVEL. 79 // See kernel commit 9b82f13e7ef3 ("kbuild: clamp SUBLEVEL to 255"). 80 s := v[2] 81 if s > 255 { 82 s = 255 83 } 84 85 // Truncate members to uint8 to prevent them from spilling over into 86 // each other when overflowing 8 bits. 87 return uint32(uint8(v[0]))<<16 | uint32(uint8(v[1]))<<8 | uint32(uint8(s)) 88 } 89 90 // KernelVersion returns the version of the currently running kernel. 91 func KernelVersion() (Version, error) { 92 kernelVersion.once.Do(func() { 93 kernelVersion.version, kernelVersion.err = detectKernelVersion() 94 }) 95 96 if kernelVersion.err != nil { 97 return Version{}, kernelVersion.err 98 } 99 return kernelVersion.version, nil 100 } 101 102 // detectKernelVersion returns the version of the running kernel. 103 func detectKernelVersion() (Version, error) { 104 vc, err := vdsoVersion() 105 if err != nil { 106 return Version{}, err 107 } 108 return NewVersionFromCode(vc), nil 109 } 110 111 // KernelRelease returns the release string of the running kernel. 112 // Its format depends on the Linux distribution and corresponds to directory 113 // names in /lib/modules by convention. Some examples are 5.15.17-1-lts and 114 // 4.19.0-16-amd64. 115 func KernelRelease() (string, error) { 116 var uname unix.Utsname 117 if err := unix.Uname(&uname); err != nil { 118 return "", fmt.Errorf("uname failed: %w", err) 119 } 120 121 return unix.ByteSliceToString(uname.Release[:]), nil 122 }