diff options
Diffstat (limited to 'bpfloader/BpfLoader.cpp')
-rw-r--r-- | bpfloader/BpfLoader.cpp | 163 |
1 files changed, 98 insertions, 65 deletions
diff --git a/bpfloader/BpfLoader.cpp b/bpfloader/BpfLoader.cpp index 26fb99dd..4b7f74ae 100644 --- a/bpfloader/BpfLoader.cpp +++ b/bpfloader/BpfLoader.cpp @@ -50,11 +50,7 @@ using android::base::unique_fd; using android::netdutils::Slice; #define BPF_PROG_PATH "/system/etc/bpf" - -#define INGRESS_PROG BPF_PROG_PATH"/cgroup_bpf_ingress_prog.o" -#define EGRESS_PROG BPF_PROG_PATH"/cgroup_bpf_egress_prog.o" -#define XT_BPF_INGRESS_PROG BPF_PROG_PATH "/xt_bpf_ingress_prog.o" -#define XT_BPF_EGRESS_PROG BPF_PROG_PATH "/xt_bpf_egress_prog.o" +#define BPF_PROG_SRC BPF_PROG_PATH "/bpf_kern.o" #define MAP_LD_CMD_HEAD 0x18 #define FAIL(...) \ @@ -112,7 +108,24 @@ struct ReplacePattern { } }; -int loadProg(const char* path, const std::vector<ReplacePattern> &mapPatterns) { +Slice cgroupIngressProg; +Slice cgroupEgressProg; +Slice xtIngressProg; +Slice xtEgressProg; + +Slice getProgFromMem(Slice buffer, Elf64_Shdr* section) { + uint64_t progSize = (uint64_t)section->sh_size; + Slice progSection = take(drop(buffer, section->sh_offset), progSize); + if (progSection.size() < progSize) FAIL("programSection out of bound\n"); + char* progArray = new char[progSize]; + Slice progCopy(progArray, progSize); + if (copy(progCopy, progSection) != progSize) { + FAIL("program cannot be extracted"); + } + return progCopy; +} + +void parseProgramsFromFile(const char* path) { int fd = open(path, O_RDONLY); if (fd == -1) { FAIL("Failed to open %s program: %s", path, strerror(errno)); @@ -127,86 +140,99 @@ int loadProg(const char* path, const std::vector<ReplacePattern> &mapPatterns) { if ((uint32_t)fileLen < sizeof(Elf64_Ehdr)) FAIL("file size too small for Elf64_Ehdr"); - Elf64_Ehdr* elf = (Elf64_Ehdr*)baseAddr; + Slice buffer(baseAddr, fileLen); + + Slice elfHeader = take(buffer, sizeof(Elf64_Ehdr)); + + if (elfHeader.size() < sizeof(Elf64_Ehdr)) FAIL("bpf buffer does not have complete elf header"); + Elf64_Ehdr* elf = (Elf64_Ehdr*)elfHeader.base(); // Find section names string table. This is the section whose index is e_shstrndx. - if (elf->e_shstrndx == SHN_UNDEF || - elf->e_shoff + (elf->e_shstrndx + 1) * sizeof(Elf64_Shdr) > (uint32_t)fileLen) { + if (elf->e_shstrndx == SHN_UNDEF) { FAIL("cannot locate namesSection\n"); } + size_t totalSectionSize = (elf->e_shnum) * sizeof(Elf64_Shdr); + Slice sections = take(drop(buffer, elf->e_shoff), totalSectionSize); + if (sections.size() < totalSectionSize) { + FAIL("sections corrupted"); + } - Elf64_Shdr* sections = (Elf64_Shdr*)(baseAddr + elf->e_shoff); - - Elf64_Shdr* namesSection = sections + elf->e_shstrndx; + Slice namesSection = take(drop(sections, elf->e_shstrndx * sizeof(Elf64_Shdr)), + sizeof(Elf64_Shdr)); + if (namesSection.size() != sizeof(Elf64_Shdr)) { + FAIL("namesSection corrupted"); + } + size_t strTabOffset = ((Elf64_Shdr*) namesSection.base())->sh_offset; + size_t strTabSize = ((Elf64_Shdr*) namesSection.base())->sh_size; - if (namesSection->sh_offset + namesSection->sh_size > (uint32_t)fileLen) - FAIL("namesSection out of bound\n"); + Slice strTab = take(drop(buffer, strTabOffset), strTabSize); + if (strTab.size() < strTabSize) { + FAIL("string table out of bound\n"); + } - const char* strTab = baseAddr + namesSection->sh_offset; - void* progSection = nullptr; - uint64_t progSize = 0; for (int i = 0; i < elf->e_shnum; i++) { - Elf64_Shdr* section = sections + i; - if (((char*)section - baseAddr) + sizeof(Elf64_Shdr) > (uint32_t)fileLen) { - FAIL("next section is out of bound\n"); + Slice section = take(drop(sections, i * sizeof(Elf64_Shdr)), sizeof(Elf64_Shdr)); + if (section.size() < sizeof(Elf64_Shdr)) { + FAIL("section %d is out of bound, section size: %zu, header size: %zu, total size: %zu", + i, section.size(), sizeof(Elf64_Shdr), sections.size()); } - - if (!strcmp(strTab + section->sh_name, BPF_PROG_SEC_NAME)) { - progSection = baseAddr + section->sh_offset; - progSize = (uint64_t)section->sh_size; - break; + Elf64_Shdr* sectionPtr = (Elf64_Shdr*)section.base(); + Slice nameSlice = drop(strTab, sectionPtr->sh_name); + if (nameSlice.size() == 0) { + FAIL("nameSlice out of bound, i: %d, strTabSize: %zu, sh_name: %u", i, strTabSize, + sectionPtr->sh_name); + } + if (!strcmp((char *)nameSlice.base(), BPF_CGROUP_INGRESS_PROG_NAME)) { + cgroupIngressProg = getProgFromMem(buffer, sectionPtr); + } else if (!strcmp((char *)nameSlice.base(), BPF_CGROUP_EGRESS_PROG_NAME)) { + cgroupEgressProg = getProgFromMem(buffer, sectionPtr); + } else if (!strcmp((char *)nameSlice.base(), XT_BPF_INGRESS_PROG_NAME)) { + xtIngressProg = getProgFromMem(buffer, sectionPtr); + } else if (!strcmp((char *)nameSlice.base(), XT_BPF_EGRESS_PROG_NAME)) { + xtEgressProg = getProgFromMem(buffer, sectionPtr); } } +} - if (!progSection) FAIL("program section not found"); - if ((char*)progSection - baseAddr + progSize > (uint32_t)fileLen) - FAIL("programSection out of bound\n"); - - char* prog = new char[progSize](); - memcpy(prog, progSection, progSize); - - - char* mapHead = prog; - while ((uint64_t)(mapHead - prog + MAP_CMD_SIZE) < progSize) { +int loadProg(Slice prog, bpf_prog_type type, const std::vector<ReplacePattern>& mapPatterns) { + if (prog.size() == 0) { + FAIL("Couldn't find or parse program type %d", type); + } + Slice remaining = prog; + while (remaining.size() >= MAP_CMD_SIZE) { // Scan the program, examining all possible places that might be the start of a map load - // operation (i.e., all byes of value MAP_LD_CMD_HEAD). - // + // operation (i.e., all bytes of value MAP_LD_CMD_HEAD). // In each of these places, check whether it is the start of one of the patterns we want to // replace, and if so, replace it. - mapHead = (char*)memchr(mapHead, MAP_LD_CMD_HEAD, progSize); - if (!mapHead) break; + Slice mapHead = findFirstMatching(remaining, MAP_LD_CMD_HEAD); + if (mapHead.size() < MAP_CMD_SIZE) break; + bool replaced = false; for (const auto& pattern : mapPatterns) { - if (!memcmp(mapHead, pattern.search.data(), MAP_CMD_SIZE)) { - memcpy(mapHead, pattern.replace.data(), MAP_CMD_SIZE); + if (!memcmp(mapHead.base(), pattern.search.data(), MAP_CMD_SIZE)) { + memcpy(mapHead.base(), pattern.replace.data(), MAP_CMD_SIZE); + replaced = true; + break; } } - mapHead++; + remaining = drop(mapHead, replaced ? MAP_CMD_SIZE : sizeof(uint8_t)); } - Slice insns = Slice(prog, progSize); char bpf_log_buf[LOG_BUF_SIZE]; Slice bpfLog = Slice(bpf_log_buf, sizeof(bpf_log_buf)); - if (strcmp(path, XT_BPF_INGRESS_PROG) && strcmp(path, XT_BPF_EGRESS_PROG)) { - return bpfProgLoad(BPF_PROG_TYPE_CGROUP_SKB, insns, "Apache 2.0", 0, bpfLog); - } - return bpfProgLoad(BPF_PROG_TYPE_SOCKET_FILTER, insns, "Apache 2.0", 0, bpfLog); + return bpfProgLoad(type, prog, "Apache 2.0", 0, bpfLog); } int loadAndAttachProgram(bpf_attach_type type, const char* path, const char* name, std::vector<ReplacePattern> mapPatterns) { - unique_fd cg_fd(open(CGROUP_ROOT_PATH, O_DIRECTORY | O_RDONLY | O_CLOEXEC)); - if (cg_fd < 0) { - FAIL("Failed to open the cgroup directory"); - } unique_fd fd; if (type == BPF_CGROUP_INET_INGRESS) { - fd.reset(loadProg(INGRESS_PROG, mapPatterns)); + fd.reset(loadProg(cgroupIngressProg, BPF_PROG_TYPE_CGROUP_SKB, mapPatterns)); } else if (type == BPF_CGROUP_INET_EGRESS) { - fd.reset(loadProg(EGRESS_PROG, mapPatterns)); - } else if (!strcmp(name, "xt_bpf_ingress_prog")) { - fd.reset(loadProg(XT_BPF_INGRESS_PROG, mapPatterns)); - } else if (!strcmp(name, "xt_bpf_egress_prog")) { - fd.reset(loadProg(XT_BPF_EGRESS_PROG, mapPatterns)); + fd.reset(loadProg(cgroupEgressProg, BPF_PROG_TYPE_CGROUP_SKB, mapPatterns)); + } else if (!strcmp(name, XT_BPF_INGRESS_PROG_NAME)) { + fd.reset(loadProg(xtIngressProg, BPF_PROG_TYPE_SOCKET_FILTER, mapPatterns)); + } else if (!strcmp(name, XT_BPF_EGRESS_PROG_NAME)) { + fd.reset(loadProg(xtEgressProg, BPF_PROG_TYPE_SOCKET_FILTER, mapPatterns)); } else { FAIL("Unrecognized program type: %s", name); } @@ -216,6 +242,10 @@ int loadAndAttachProgram(bpf_attach_type type, const char* path, const char* nam } int ret = 0; if (type == BPF_CGROUP_INET_EGRESS || type == BPF_CGROUP_INET_INGRESS) { + unique_fd cg_fd(open(CGROUP_ROOT_PATH, O_DIRECTORY | O_RDONLY | O_CLOEXEC)); + if (cg_fd < 0) { + FAIL("Failed to open the cgroup directory"); + } ret = attachProgram(type, fd, cg_fd); if (ret) { FAIL("%s attach failed: %s", name, strerror(errno)); @@ -245,6 +275,7 @@ using android::bpf::UID_STATS_MAP_PATH; using android::bpf::XT_BPF_EGRESS_PROG_PATH; using android::bpf::XT_BPF_INGRESS_PROG_PATH; using android::bpf::ReplacePattern; +using android::bpf::loadAndAttachProgram; static void usage(void) { ALOGE( "Usage: ./bpfloader [-i] [-e]\n" @@ -297,30 +328,32 @@ int main(int argc, char** argv) { FAIL("unknown argument %c", opt); } } + android::bpf::parseProgramsFromFile(BPF_PROG_SRC); + if (doIngress) { - ret = android::bpf::loadAndAttachProgram(BPF_CGROUP_INET_INGRESS, BPF_INGRESS_PROG_PATH, - "ingress_prog", mapPatterns); + ret = loadAndAttachProgram(BPF_CGROUP_INET_INGRESS, BPF_INGRESS_PROG_PATH, + BPF_CGROUP_INGRESS_PROG_NAME, mapPatterns); if (ret) { FAIL("Failed to set up ingress program"); } } if (doEgress) { - ret = android::bpf::loadAndAttachProgram(BPF_CGROUP_INET_EGRESS, BPF_EGRESS_PROG_PATH, - "egress_prog", mapPatterns); + ret = loadAndAttachProgram(BPF_CGROUP_INET_EGRESS, BPF_EGRESS_PROG_PATH, + BPF_CGROUP_EGRESS_PROG_NAME, mapPatterns); if (ret) { FAIL("Failed to set up ingress program"); } } if (doPrerouting) { - ret = android::bpf::loadAndAttachProgram( - MAX_BPF_ATTACH_TYPE, XT_BPF_INGRESS_PROG_PATH, "xt_bpf_ingress_prog", mapPatterns); + ret = loadAndAttachProgram(MAX_BPF_ATTACH_TYPE, XT_BPF_INGRESS_PROG_PATH, + XT_BPF_INGRESS_PROG_NAME, mapPatterns); if (ret) { FAIL("Failed to set up xt_bpf program"); } } if (doMangle) { - ret = android::bpf::loadAndAttachProgram( - MAX_BPF_ATTACH_TYPE, XT_BPF_EGRESS_PROG_PATH, "xt_bpf_egress_prog", mapPatterns); + ret = loadAndAttachProgram(MAX_BPF_ATTACH_TYPE, XT_BPF_EGRESS_PROG_PATH, + XT_BPF_EGRESS_PROG_NAME, mapPatterns); if (ret) { FAIL("Failed to set up xt_bpf program"); } |