github.com/cilium/ebpf@v0.15.1-0.20240517100537-8079b37aa138/internal/version.go (about)

     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  // A Version in the form Major.Minor.Patch.
    19  type Version [3]uint16
    20  
    21  // NewVersion creates a version from a string like "Major.Minor.Patch".
    22  //
    23  // Patch is optional.
    24  func NewVersion(ver string) (Version, error) {
    25  	var major, minor, patch uint16
    26  	n, _ := fmt.Sscanf(ver, "%d.%d.%d", &major, &minor, &patch)
    27  	if n < 2 {
    28  		return Version{}, fmt.Errorf("invalid version: %s", ver)
    29  	}
    30  	return Version{major, minor, patch}, nil
    31  }
    32  
    33  // NewVersionFromCode creates a version from a LINUX_VERSION_CODE.
    34  func NewVersionFromCode(code uint32) Version {
    35  	return Version{
    36  		uint16(uint8(code >> 16)),
    37  		uint16(uint8(code >> 8)),
    38  		uint16(uint8(code)),
    39  	}
    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  
    49  // Less returns true if the version is less than another version.
    50  func (v Version) Less(other Version) bool {
    51  	for i, a := range v {
    52  		if a == other[i] {
    53  			continue
    54  		}
    55  		return a < other[i]
    56  	}
    57  	return false
    58  }
    59  
    60  // Unspecified returns true if the version is all zero.
    61  func (v Version) Unspecified() bool {
    62  	return v[0] == 0 && v[1] == 0 && v[2] == 0
    63  }
    64  
    65  // Kernel implements the kernel's KERNEL_VERSION macro from linux/version.h.
    66  // It represents the kernel version and patch level as a single value.
    67  func (v Version) Kernel() uint32 {
    68  
    69  	// Kernels 4.4 and 4.9 have their SUBLEVEL clamped to 255 to avoid
    70  	// overflowing into PATCHLEVEL.
    71  	// See kernel commit 9b82f13e7ef3 ("kbuild: clamp SUBLEVEL to 255").
    72  	s := v[2]
    73  	if s > 255 {
    74  		s = 255
    75  	}
    76  
    77  	// Truncate members to uint8 to prevent them from spilling over into
    78  	// each other when overflowing 8 bits.
    79  	return uint32(uint8(v[0]))<<16 | uint32(uint8(v[1]))<<8 | uint32(uint8(s))
    80  }
    81  
    82  // KernelVersion returns the version of the currently running kernel.
    83  var KernelVersion = sync.OnceValues(func() (Version, error) {
    84  	return detectKernelVersion()
    85  })
    86  
    87  // detectKernelVersion returns the version of the running kernel.
    88  func detectKernelVersion() (Version, error) {
    89  	vc, err := vdsoVersion()
    90  	if err != nil {
    91  		return Version{}, err
    92  	}
    93  	return NewVersionFromCode(vc), nil
    94  }
    95  
    96  // KernelRelease returns the release string of the running kernel.
    97  // Its format depends on the Linux distribution and corresponds to directory
    98  // names in /lib/modules by convention. Some examples are 5.15.17-1-lts and
    99  // 4.19.0-16-amd64.
   100  func KernelRelease() (string, error) {
   101  	var uname unix.Utsname
   102  	if err := unix.Uname(&uname); err != nil {
   103  		return "", fmt.Errorf("uname failed: %w", err)
   104  	}
   105  
   106  	return unix.ByteSliceToString(uname.Release[:]), nil
   107  }