version.go 3.1 KB

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