|
12 | 12 | #include <linux/btf.h>
|
13 | 13 | #include <linux/filter.h>
|
14 | 14 | #include <linux/kernel.h>
|
| 15 | +#include <linux/version.h> |
15 | 16 |
|
16 | 17 | #include "bpf.h"
|
17 | 18 | #include "libbpf.h"
|
18 | 19 | #include "libbpf_internal.h"
|
19 | 20 |
|
| 21 | +/* On Ubuntu LINUX_VERSION_CODE doesn't correspond to info.release, |
| 22 | + * but Ubuntu provides /proc/version_signature file, as described at |
| 23 | + * https://ubuntu.com/kernel, with an example contents below, which we |
| 24 | + * can use to get a proper LINUX_VERSION_CODE. |
| 25 | + * |
| 26 | + * Ubuntu 5.4.0-12.15-generic 5.4.8 |
| 27 | + * |
| 28 | + * In the above, 5.4.8 is what kernel is actually expecting, while |
| 29 | + * uname() call will return 5.4.0 in info.release. |
| 30 | + */ |
| 31 | +static __u32 get_ubuntu_kernel_version(void) |
| 32 | +{ |
| 33 | + const char *ubuntu_kver_file = "/proc/version_signature"; |
| 34 | + __u32 major, minor, patch; |
| 35 | + int ret; |
| 36 | + FILE *f; |
| 37 | + |
| 38 | + if (faccessat(AT_FDCWD, ubuntu_kver_file, R_OK, AT_EACCESS) != 0) |
| 39 | + return 0; |
| 40 | + |
| 41 | + f = fopen(ubuntu_kver_file, "r"); |
| 42 | + if (!f) |
| 43 | + return 0; |
| 44 | + |
| 45 | + ret = fscanf(f, "%*s %*s %u.%u.%u\n", &major, &minor, &patch); |
| 46 | + fclose(f); |
| 47 | + if (ret != 3) |
| 48 | + return 0; |
| 49 | + |
| 50 | + return KERNEL_VERSION(major, minor, patch); |
| 51 | +} |
| 52 | + |
| 53 | +/* On Debian LINUX_VERSION_CODE doesn't correspond to info.release. |
| 54 | + * Instead, it is provided in info.version. An example content of |
| 55 | + * Debian 10 looks like the below. |
| 56 | + * |
| 57 | + * utsname::release 4.19.0-22-amd64 |
| 58 | + * utsname::version #1 SMP Debian 4.19.260-1 (2022-09-29) |
| 59 | + * |
| 60 | + * In the above, 4.19.260 is what kernel is actually expecting, while |
| 61 | + * uname() call will return 4.19.0 in info.release. |
| 62 | + */ |
| 63 | +static __u32 get_debian_kernel_version(struct utsname *info) |
| 64 | +{ |
| 65 | + __u32 major, minor, patch; |
| 66 | + char *p; |
| 67 | + |
| 68 | + p = strstr(info->version, "Debian "); |
| 69 | + if (!p) { |
| 70 | + /* This is not a Debian kernel. */ |
| 71 | + return 0; |
| 72 | + } |
| 73 | + |
| 74 | + if (sscanf(p, "Debian %u.%u.%u", &major, &minor, &patch) != 3) |
| 75 | + return 0; |
| 76 | + |
| 77 | + return KERNEL_VERSION(major, minor, patch); |
| 78 | +} |
| 79 | + |
| 80 | +__u32 get_kernel_version(void) |
| 81 | +{ |
| 82 | + __u32 major, minor, patch, version; |
| 83 | + struct utsname info; |
| 84 | + |
| 85 | + /* Check if this is an Ubuntu kernel. */ |
| 86 | + version = get_ubuntu_kernel_version(); |
| 87 | + if (version != 0) |
| 88 | + return version; |
| 89 | + |
| 90 | + uname(&info); |
| 91 | + |
| 92 | + /* Check if this is a Debian kernel. */ |
| 93 | + version = get_debian_kernel_version(&info); |
| 94 | + if (version != 0) |
| 95 | + return version; |
| 96 | + |
| 97 | + if (sscanf(info.release, "%u.%u.%u", &major, &minor, &patch) != 3) |
| 98 | + return 0; |
| 99 | + |
| 100 | + return KERNEL_VERSION(major, minor, patch); |
| 101 | +} |
| 102 | + |
20 | 103 | static int probe_prog_load(enum bpf_prog_type prog_type,
|
21 | 104 | const struct bpf_insn *insns, size_t insns_cnt,
|
22 | 105 | char *log_buf, size_t log_buf_sz)
|
|
0 commit comments