add support for marking ebpf programs as being only for some kernel versions

Some ebpf code cannot be loaded on too old kernels.

Sometimes we want a different - more advanced - version of an ebpf program
to be loaded on a newer kernel.

Test: build, atest
Signed-off-by: Maciej Żenczykowski <maze@google.com>
Change-Id: I9e93e7246951916e6d60544575337a7a19c82886
This commit is contained in:
Maciej Żenczykowski 2020-02-19 14:23:59 -08:00
commit 07375e26a3
6 changed files with 57 additions and 61 deletions

View file

@ -96,6 +96,24 @@ int setrlimitForTest() {
return res;
}
#define KVER(a, b, c) ((a)*65536 + (b)*256 + (c))
unsigned kernelVersion() {
struct utsname buf;
int ret = uname(&buf);
if (ret) return 0;
unsigned kver_major;
unsigned kver_minor;
unsigned kver_sub;
char dummy;
ret = sscanf(buf.release, "%u.%u.%u%c", &kver_major, &kver_minor, &kver_sub, &dummy);
// Check the device kernel version
if (ret < 3) return 0;
return KVER(kver_major, kver_minor, kver_sub);
}
std::string BpfLevelToString(BpfLevel bpfLevel) {
switch (bpfLevel) {
case BpfLevel::NONE:
@ -123,23 +141,12 @@ static BpfLevel getUncachedBpfSupportLevel() {
// Check if the device is shipped originally with android P.
if (api_level < MINIMUM_API_REQUIRED) return BpfLevel::NONE;
struct utsname buf;
int ret = uname(&buf);
if (ret) return BpfLevel::NONE;
unsigned kver = kernelVersion();
int kernel_version_major;
int kernel_version_minor;
char dummy;
ret = sscanf(buf.release, "%d.%d%c", &kernel_version_major, &kernel_version_minor, &dummy);
// Check the device kernel version
if (ret < 2) return BpfLevel::NONE;
if (kernel_version_major > 5) return BpfLevel::EXTENDED_5_4;
if (kernel_version_major == 5 && kernel_version_minor >= 4) return BpfLevel::EXTENDED_5_4;
if (kernel_version_major == 5) return BpfLevel::EXTENDED_4_19;
if (kernel_version_major == 4 && kernel_version_minor >= 19) return BpfLevel::EXTENDED_4_19;
if (kernel_version_major == 4 && kernel_version_minor >= 14) return BpfLevel::EXTENDED_4_14;
if (kernel_version_major == 4 && kernel_version_minor >= 9) return BpfLevel::BASIC_4_9;
if (kver >= KVER(5, 4, 0)) return BpfLevel::EXTENDED_5_4;
if (kver >= KVER(4, 19, 0)) return BpfLevel::EXTENDED_4_19;
if (kver >= KVER(4, 14, 0)) return BpfLevel::EXTENDED_4_14;
if (kver >= KVER(4, 9, 0)) return BpfLevel::BASIC_4_9;
return BpfLevel::NONE;
}

View file

@ -30,6 +30,7 @@
#include "../progs/include/bpf_map_def.h"
#include "LoaderUtils.h"
#include "bpf/BpfUtils.h"
#include "include/libbpf_android.h"
#include <cstdlib>
@ -531,9 +532,10 @@ static void applyMapRelo(ifstream& elfFile, vector<unique_fd> &mapFds, vector<co
}
static int loadCodeSections(const char* elfPath, vector<codeSection>& cs, const string& license) {
int ret, fd, kvers;
unsigned kvers = kernelVersion();
int ret, fd;
if ((kvers = getMachineKvers()) < 0) return -1;
if (!kvers) return -1;
string fname = pathToFilename(string(elfPath), true);
@ -541,6 +543,11 @@ static int loadCodeSections(const char* elfPath, vector<codeSection>& cs, const
string progPinLoc;
bool reuse = false;
if (cs[i].prog_def.has_value()) {
if (kvers < cs[i].prog_def->min_kver) continue;
if (kvers >= cs[i].prog_def->max_kver) continue;
}
// Format of pin location is
// /sys/fs/bpf/prog_<filename>_<mapname>
progPinLoc = string(BPF_FS_PATH) + "prog_" + fname + "_" + cs[i].name;

View file

@ -32,43 +32,6 @@ static std::string pathToFilename(const std::string& path, bool noext = false) {
return ret;
}
static int getMachineKvers(void) {
struct utsname un;
char* unameOut;
int nums[3]; // maj, min, sub
if (uname(&un)) return -1;
unameOut = un.release;
std::string s = unameOut;
std::string token;
size_t pos = 0;
int cur_num = 0;
while ((pos = s.find('.')) != std::string::npos && cur_num < 3) {
token = s.substr(0, pos);
s.erase(0, pos + 1);
if ((pos = token.find('-')) != std::string::npos) token = token.substr(0, pos);
nums[cur_num++] = stoi(token);
}
if ((pos = s.find('-')) != std::string::npos)
token = s.substr(0, pos);
else
token = s;
if (token.length() > 0 && cur_num < 3) {
nums[cur_num++] = stoi(token);
}
if (cur_num != 3)
return -1;
else
return (65536 * nums[0] + 256 * nums[1] + nums[2]);
}
static void deslash(std::string& s) {
std::replace(s.begin(), s.end(), '/', '_');
}

View file

@ -148,6 +148,7 @@ inline int detachProgram(bpf_attach_type type, const base::unique_fd& cg_fd) {
uint64_t getSocketCookie(int sockFd);
int synchronizeKernelRCU();
int setrlimitForTest();
unsigned kernelVersion();
std::string BpfLevelToString(BpfLevel BpfLevel);
BpfLevel getBpfSupportLevel();

View file

@ -98,10 +98,25 @@ static unsigned long long (*bpf_get_current_pid_tgid)(void) = (void*) BPF_FUNC_g
static unsigned long long (*bpf_get_current_uid_gid)(void) = (void*) BPF_FUNC_get_current_uid_gid;
static unsigned long long (*bpf_get_smp_processor_id)(void) = (void*) BPF_FUNC_get_smp_processor_id;
#define DEFINE_BPF_PROG(SECTION_NAME, prog_uid, prog_gid, the_prog) \
const struct bpf_prog_def SEC("progs") the_prog##_def = { \
.uid = (prog_uid), \
.gid = (prog_gid), \
}; \
SEC(SECTION_NAME) \
#define KVER_NONE 0
#define KVER(a, b, c) ((a)*65536 + (b)*256 + (c))
#define KVER_INF 0xFFFFFFFF
// programs requiring a kernel version >= min_kv && < max_kv
#define DEFINE_BPF_PROG_KVER_RANGE(SECTION_NAME, prog_uid, prog_gid, the_prog, min_kv, max_kv) \
const struct bpf_prog_def SEC("progs") the_prog##_def = { \
.uid = (prog_uid), \
.gid = (prog_gid), \
.min_kver = (min_kv), \
.max_kver = (max_kv), \
}; \
SEC(SECTION_NAME) \
int the_prog
// programs requiring a kernel version >= min_kv
#define DEFINE_BPF_PROG_KVER(SECTION_NAME, prog_uid, prog_gid, the_prog, min_kv) \
DEFINE_BPF_PROG_KVER_RANGE(SECTION_NAME, prog_uid, prog_gid, the_prog, min_kv, KVER_INF)
// programs with no kernel version requirements
#define DEFINE_BPF_PROG(SECTION_NAME, prog_uid, prog_gid, the_prog) \
DEFINE_BPF_PROG_KVER_RANGE(SECTION_NAME, prog_uid, prog_gid, the_prog, 0, KVER_INF)

View file

@ -57,4 +57,7 @@ struct bpf_map_def {
struct bpf_prog_def {
unsigned int uid;
unsigned int gid;
unsigned int min_kver; // KERNEL_MAJOR * 65536 + KERNEL_MINOR * 256 + KERNEL_SUB
unsigned int max_kver; // ie. 0x40900 for Linux 4.9 - but beware of hexadecimal for >= 10
};