diff options
author | android-build-team Robot <android-build-team-robot@google.com> | 2018-05-02 04:55:22 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2018-05-02 04:55:22 +0000 |
commit | 86a19ec168229df79b54746a6f90ebd6f5802348 (patch) | |
tree | 60c3f1ab520bb96a4ac43beadebf213780b5a95c | |
parent | 83030f95f1951efd0a4ef9acbec1bc0c796ea928 (diff) | |
parent | b905757231225cd2d37cd10aeee1bb16e4a544d2 (diff) | |
download | netd-86a19ec168229df79b54746a6f90ebd6f5802348.tar.gz |
Merge "Merge all bpf programs into one" into pi-dev
-rw-r--r-- | bpfloader/Android.bp | 5 | ||||
-rw-r--r-- | bpfloader/Android.mk | 37 | ||||
-rw-r--r-- | bpfloader/BpfLoader.cpp | 163 | ||||
-rw-r--r-- | bpfloader/bpf_egress.c | 23 | ||||
-rw-r--r-- | bpfloader/bpf_ingress.c | 23 | ||||
-rw-r--r-- | bpfloader/bpf_kern.c (renamed from bpfloader/xt_bpf_ingress_prog.c) | 20 | ||||
-rw-r--r-- | bpfloader/bpf_kern.o | bin | 0 -> 6232 bytes | |||
-rw-r--r-- | bpfloader/cgroup_bpf_egress_prog.o | bin | 2832 -> 0 bytes | |||
-rw-r--r-- | bpfloader/cgroup_bpf_ingress_prog.o | bin | 2832 -> 0 bytes | |||
-rw-r--r-- | bpfloader/xt_bpf_egress_prog.c | 25 | ||||
-rw-r--r-- | bpfloader/xt_bpf_egress_prog.o | bin | 824 -> 0 bytes | |||
-rw-r--r-- | bpfloader/xt_bpf_ingress_prog.o | bin | 824 -> 0 bytes | |||
-rw-r--r-- | libbpf/include/bpf/bpf_shared.h | 5 | ||||
-rw-r--r-- | libnetdutils/include/netdutils/Slice.h | 8 |
14 files changed, 132 insertions, 177 deletions
diff --git a/bpfloader/Android.bp b/bpfloader/Android.bp index 9a759d16..46b8a1f0 100644 --- a/bpfloader/Android.bp +++ b/bpfloader/Android.bp @@ -41,10 +41,7 @@ cc_binary { ], required: [ - "cgroup_bpf_ingress_prog.o", - "cgroup_bpf_egress_prog.o", - "xt_bpf_ingress_prog.o", - "xt_bpf_egress_prog.o", + "bpf_kern.o", ], } diff --git a/bpfloader/Android.mk b/bpfloader/Android.mk index 005abef8..13933767 100644 --- a/bpfloader/Android.mk +++ b/bpfloader/Android.mk @@ -1,43 +1,10 @@ LOCAL_PATH:= $(call my-dir) ####################################### -# bpf_ingress.o +# bpf_kern.o include $(CLEAR_VARS) -LOCAL_MODULE := cgroup_bpf_ingress_prog.o -LOCAL_SRC_FILES := $(LOCAL_MODULE) -LOCAL_MODULE_CLASS := ETC -LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/bpf - -include $(BUILD_PREBUILT) - -####################################### -# bpf_egress.o -include $(CLEAR_VARS) - -LOCAL_MODULE := cgroup_bpf_egress_prog.o -LOCAL_SRC_FILES := $(LOCAL_MODULE) -LOCAL_MODULE_CLASS := ETC -LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/bpf - -include $(BUILD_PREBUILT) - -####################################### -# xt_bpf_ingress_prog.o -include $(CLEAR_VARS) - -LOCAL_MODULE := xt_bpf_ingress_prog.o -LOCAL_SRC_FILES := $(LOCAL_MODULE) -LOCAL_MODULE_CLASS := ETC -LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/bpf - -include $(BUILD_PREBUILT) - -####################################### -# xt_bpf_egress_prog.o -include $(CLEAR_VARS) - -LOCAL_MODULE := xt_bpf_egress_prog.o +LOCAL_MODULE := bpf_kern.o LOCAL_SRC_FILES := $(LOCAL_MODULE) LOCAL_MODULE_CLASS := ETC LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/bpf 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"); } diff --git a/bpfloader/bpf_egress.c b/bpfloader/bpf_egress.c deleted file mode 100644 index 5cf648cb..00000000 --- a/bpfloader/bpf_egress.c +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <linux/bpf.h> -#include "bpf_kern.h" - -ELF_SEC(BPF_PROG_SEC_NAME) -int bpf_cgroup_egress(struct __sk_buff* skb) { - return bpf_traffic_account(skb, BPF_EGRESS); -} diff --git a/bpfloader/bpf_ingress.c b/bpfloader/bpf_ingress.c deleted file mode 100644 index 4bd51704..00000000 --- a/bpfloader/bpf_ingress.c +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <linux/bpf.h> -#include "bpf_kern.h" - -ELF_SEC(BPF_PROG_SEC_NAME) -int bpf_cgroup_ingress(struct __sk_buff* skb) { - return bpf_traffic_account(skb, BPF_INGRESS); -} diff --git a/bpfloader/xt_bpf_ingress_prog.c b/bpfloader/bpf_kern.c index 5f102553..3a1668f9 100644 --- a/bpfloader/xt_bpf_ingress_prog.c +++ b/bpfloader/bpf_kern.c @@ -17,7 +17,25 @@ #include <linux/bpf.h> #include "bpf_kern.h" -ELF_SEC(BPF_PROG_SEC_NAME) + +ELF_SEC(BPF_CGROUP_INGRESS_PROG_NAME) +int bpf_cgroup_ingress(struct __sk_buff* skb) { + return bpf_traffic_account(skb, BPF_INGRESS); +} + +ELF_SEC(BPF_CGROUP_EGRESS_PROG_NAME) +int bpf_cgroup_egress(struct __sk_buff* skb) { + return bpf_traffic_account(skb, BPF_EGRESS); +} + +ELF_SEC(XT_BPF_EGRESS_PROG_NAME) +int xt_bpf_egress_prog(struct __sk_buff* skb) { + uint32_t key = skb->ifindex; + bpf_update_stats(skb, IFACE_STATS_MAP, BPF_EGRESS, &key); + return BPF_PASS; +} + +ELF_SEC(XT_BPF_INGRESS_PROG_NAME) int xt_bpf_ingress_prog(struct __sk_buff* skb) { uint32_t key = skb->ifindex; bpf_update_stats(skb, IFACE_STATS_MAP, BPF_INGRESS, &key); diff --git a/bpfloader/bpf_kern.o b/bpfloader/bpf_kern.o Binary files differnew file mode 100644 index 00000000..32d8e876 --- /dev/null +++ b/bpfloader/bpf_kern.o diff --git a/bpfloader/cgroup_bpf_egress_prog.o b/bpfloader/cgroup_bpf_egress_prog.o Binary files differdeleted file mode 100644 index f68fd025..00000000 --- a/bpfloader/cgroup_bpf_egress_prog.o +++ /dev/null diff --git a/bpfloader/cgroup_bpf_ingress_prog.o b/bpfloader/cgroup_bpf_ingress_prog.o Binary files differdeleted file mode 100644 index 18cfbbca..00000000 --- a/bpfloader/cgroup_bpf_ingress_prog.o +++ /dev/null diff --git a/bpfloader/xt_bpf_egress_prog.c b/bpfloader/xt_bpf_egress_prog.c deleted file mode 100644 index 3f4baced..00000000 --- a/bpfloader/xt_bpf_egress_prog.c +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <linux/bpf.h> -#include "bpf_kern.h" - -ELF_SEC(BPF_PROG_SEC_NAME) -int xt_bpf_egress_prog(struct __sk_buff* skb) { - uint32_t key = skb->ifindex; - bpf_update_stats(skb, IFACE_STATS_MAP, BPF_EGRESS, &key); - return BPF_PASS; -} diff --git a/bpfloader/xt_bpf_egress_prog.o b/bpfloader/xt_bpf_egress_prog.o Binary files differdeleted file mode 100644 index 739b3dab..00000000 --- a/bpfloader/xt_bpf_egress_prog.o +++ /dev/null diff --git a/bpfloader/xt_bpf_ingress_prog.o b/bpfloader/xt_bpf_ingress_prog.o Binary files differdeleted file mode 100644 index 20e71bc0..00000000 --- a/bpfloader/xt_bpf_ingress_prog.o +++ /dev/null diff --git a/libbpf/include/bpf/bpf_shared.h b/libbpf/include/bpf/bpf_shared.h index 2c9d3291..4a4904d2 100644 --- a/libbpf/include/bpf/bpf_shared.h +++ b/libbpf/include/bpf/bpf_shared.h @@ -16,7 +16,10 @@ // const values shared by both kernel program and userspace bpfloader -#define BPF_PROG_SEC_NAME "kern_prog" +#define BPF_CGROUP_INGRESS_PROG_NAME "cgroup_ingress_prog" +#define BPF_CGROUP_EGRESS_PROG_NAME "cgroup_egress_prog" +#define XT_BPF_INGRESS_PROG_NAME "xt_ingress_prog" +#define XT_BPF_EGRESS_PROG_NAME "xt_egress_prog" #define COOKIE_TAG_MAP 0xbfceaaffffffffff #define UID_COUNTERSET_MAP 0xbfdceeafffffffff diff --git a/libnetdutils/include/netdutils/Slice.h b/libnetdutils/include/netdutils/Slice.h index f3f794be..f194514b 100644 --- a/libnetdutils/include/netdutils/Slice.h +++ b/libnetdutils/include/netdutils/Slice.h @@ -147,6 +147,14 @@ inline bool operator!=(const Slice& lhs, const Slice& rhs) { std::ostream& operator<<(std::ostream& os, const Slice& slice); +// Return suffix of Slice s starting at the first match of byte c. If no matched +// byte, return an empty Slice. +inline const Slice findFirstMatching(const Slice s, uint8_t c) { + uint8_t* match = (uint8_t*)memchr(s.base(), c, s.size()); + if (!match) return Slice(); + return drop(s, match - s.base()); +} + } // namespace netdutils } // namespace android |