1/*
2 * Copyright (c) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include <cerrno>
17#include <fcntl.h>
18#include <filesystem>
19#include <functional>
20#include <iostream>
21#include <linux/bpf.h>
22#include <linux/if_ether.h>
23#include <arpa/inet.h>
24#include <map>
25#include <memory.h>
26#include <string>
27#include <sys/mount.h>
28#include <sys/resource.h>
29#include <sys/syscall.h>
30#include <unistd.h>
31#include <vector>
32
33#include "bpf_def.h"
34#include "bpf_loader.h"
35#include "elf_types.hpp"
36#include "elfio.hpp"
37#include "elfio_relocation.hpp"
38#include "net_manager_constants.h"
39#include "netnative_log_wrapper.h"
40#include "securec.h"
41
42#define DEFINE_SECTION_NAME(name) \
43    {                             \
44        name, strlen(name)        \
45    }
46
47#define DEFINE_PROG_TYPE(progName, progType) \
48    {                                        \
49        progName, progType                   \
50    }
51#define DEFINE_ATTACH_TYPE(progName, attachType, needExpectedAttach) \
52    {                                                               \
53        progName, attachType, needExpectedAttach                    \
54    }
55
56namespace OHOS::NetManagerStandard {
57static constexpr const char *BPF_DIR = "/sys/fs/bpf";
58static constexpr const char *CGROUP_DIR = "/sys/fs/cgroup";
59static constexpr const char *MAPS_DIR = "/sys/fs/bpf/netsys/maps";
60static constexpr const char *PROGS_DIR = "/sys/fs/bpf/netsys/progs";
61
62// There is no limit to the size of SECTION_NAMES.
63static const struct SectionName {
64    const char *sectionName;
65    size_t sectionNameLength;
66} SECTION_NAMES[] = {
67    DEFINE_SECTION_NAME("kprobe/"),
68    DEFINE_SECTION_NAME("kretprobe/"),
69    DEFINE_SECTION_NAME("tracepoint/"),
70    DEFINE_SECTION_NAME("raw_tracepoint/"),
71    DEFINE_SECTION_NAME("xdp"),
72    DEFINE_SECTION_NAME("perf_event/"),
73    DEFINE_SECTION_NAME("socket"),
74    DEFINE_SECTION_NAME("cgroup/"),
75    DEFINE_SECTION_NAME("sockops"),
76    DEFINE_SECTION_NAME("sk_skb"),
77    DEFINE_SECTION_NAME("sk_msg"),
78    DEFINE_SECTION_NAME("cgroup_skb"),
79    DEFINE_SECTION_NAME("xdp_packet_parser"),
80    DEFINE_SECTION_NAME("schedcls"),
81    DEFINE_SECTION_NAME("classifier"),
82    DEFINE_SECTION_NAME("cgroup_sock"),
83    DEFINE_SECTION_NAME("cgroup_addr"),
84};
85
86static const constexpr struct {
87    const char *event;
88    bpf_prog_type progType;
89} PROG_TYPES[] = {
90    DEFINE_PROG_TYPE("socket", BPF_PROG_TYPE_SOCKET_FILTER),
91    DEFINE_PROG_TYPE("cgroup_skb", BPF_PROG_TYPE_CGROUP_SKB),
92    DEFINE_PROG_TYPE("xdp", BPF_PROG_TYPE_XDP),
93    DEFINE_PROG_TYPE("xdp_packet_parser", BPF_PROG_TYPE_XDP),
94    DEFINE_PROG_TYPE("schedcls", BPF_PROG_TYPE_SCHED_CLS),
95    DEFINE_PROG_TYPE("classifier", BPF_PROG_TYPE_SCHED_CLS),
96    DEFINE_PROG_TYPE("cgroup_sock", BPF_PROG_TYPE_CGROUP_SOCK),
97    DEFINE_PROG_TYPE("cgroup_addr", BPF_PROG_TYPE_CGROUP_SOCK_ADDR),
98};
99
100static const constexpr struct {
101    const char *progName;
102    bpf_attach_type attachType;
103    bool needExpectedAttach;
104} PROG_ATTACH_TYPES[] = {
105    DEFINE_ATTACH_TYPE("cgroup_sock_inet_create_socket", BPF_CGROUP_INET_SOCK_CREATE, false),
106    DEFINE_ATTACH_TYPE("cgroup_sock_inet_release_socket", BPF_CGROUP_INET_SOCK_RELEASE, true),
107    DEFINE_ATTACH_TYPE("cgroup_skb_uid_ingress", BPF_CGROUP_INET_INGRESS, false),
108    DEFINE_ATTACH_TYPE("cgroup_skb_uid_egress", BPF_CGROUP_INET_EGRESS, false),
109    DEFINE_ATTACH_TYPE("cgroup_addr_bind4", BPF_CGROUP_INET4_BIND, true),
110    DEFINE_ATTACH_TYPE("cgroup_addr_bind6", BPF_CGROUP_INET6_BIND, true),
111    DEFINE_ATTACH_TYPE("cgroup_addr_connect4", BPF_CGROUP_INET4_CONNECT, true),
112    DEFINE_ATTACH_TYPE("cgroup_addr_connect6", BPF_CGROUP_INET6_CONNECT, true),
113    DEFINE_ATTACH_TYPE("cgroup_addr_sendmsg4", BPF_CGROUP_UDP4_SENDMSG, true),
114    DEFINE_ATTACH_TYPE("cgroup_addr_sendmsg6", BPF_CGROUP_UDP6_SENDMSG, true),
115};
116
117int32_t g_sockFd = -1;
118
119struct BpfMapData {
120    BpfMapData() : fd(0)
121    {
122        if (memset_s(&def, sizeof(def), 0, sizeof(def)) != EOK) {
123            NETNATIVE_LOGE("memset_s error");
124        }
125    }
126
127    int32_t fd;
128    std::string name;
129    bpf_map_def def{};
130};
131
132template <typename type> inline uint64_t PtrToU64(const type ptr)
133{
134    return static_cast<uint64_t>(reinterpret_cast<uintptr_t>(ptr));
135}
136
137inline bool EndsWith(const std::string &str, const std::string &searchFor)
138{
139    if (searchFor.size() > str.size()) {
140        return false;
141    }
142
143    std::string source = str.substr(str.size() - searchFor.size(), searchFor.size());
144    return source == searchFor;
145}
146
147inline int32_t SysBpf(bpf_cmd cmd, bpf_attr *attr, uint32_t size)
148{
149    return static_cast<int32_t>(syscall(__NR_bpf, cmd, attr, size));
150}
151
152inline int32_t SysBpfObjGet(const std::string &pathName, uint32_t fileFlags)
153{
154    bpf_attr attr = {};
155    if (memset_s(&attr, sizeof(attr), 0, sizeof(attr)) != EOK) {
156        return NETMANAGER_ERROR;
157    }
158    attr.pathname = PtrToU64(pathName.c_str());
159    attr.file_flags = fileFlags;
160    return SysBpf(BPF_OBJ_GET, &attr, sizeof(attr));
161}
162
163inline int32_t SysBpfObjPin(int32_t fd, const std::string &pathName)
164{
165    bpf_attr attr = {};
166
167    if (memset_s(&attr, sizeof(attr), 0, sizeof(attr)) != EOK) {
168        return NETMANAGER_ERROR;
169    }
170    attr.pathname = PtrToU64(pathName.c_str());
171    if (fd < 0) {
172        return NETMANAGER_ERROR;
173    }
174    attr.bpf_fd = static_cast<uint32_t>(fd);
175
176    return SysBpf(BPF_OBJ_PIN, &attr, sizeof(attr));
177}
178
179inline int32_t SysBpfProgLoad(bpf_attr *attr, uint32_t size)
180{
181    int32_t fd = SysBpf(BPF_PROG_LOAD, attr, size);
182    while (fd < 0 && errno == EAGAIN) {
183        fd = SysBpf(BPF_PROG_LOAD, attr, size);
184    }
185
186    return fd;
187}
188
189inline int32_t SysBpfObjDetach(bpf_attach_type type, const int progFd, const int cgFd)
190{
191    bpf_attr attr = {};
192
193    if (memset_s(&attr, sizeof(attr), 0, sizeof(attr)) != EOK) {
194        return NETMANAGER_ERROR;
195    }
196    attr.target_fd = cgFd;
197    attr.attach_bpf_fd = progFd;
198    attr.attach_type = type;
199
200    return SysBpf(BPF_PROG_DETACH, &attr, sizeof(attr));
201}
202
203inline int32_t SysBpfObjAttach(bpf_attach_type type, const int progFd, const int cgFd)
204{
205    bpf_attr attr = {};
206
207    if (memset_s(&attr, sizeof(attr), 0, sizeof(attr)) != EOK) {
208        return NETMANAGER_ERROR;
209    }
210    attr.target_fd = cgFd;
211    attr.attach_bpf_fd = progFd;
212    attr.attach_type = type;
213    attr.attach_flags = BPF_F_ALLOW_MULTI;
214
215    return SysBpf(BPF_PROG_ATTACH, &attr, sizeof(attr));
216}
217
218inline bool MatchSecName(const std::string &name)
219{
220    auto matchFunc = [name](const SectionName &sec) -> bool {
221        if (name.size() < sec.sectionNameLength) {
222            return false;
223        }
224        return memcmp(name.c_str(), sec.sectionName, sec.sectionNameLength) == 0;
225    };
226    auto size = sizeof(SECTION_NAMES) / sizeof(SectionName);
227    return std::any_of(SECTION_NAMES, SECTION_NAMES + size, matchFunc);
228}
229
230inline int32_t UnPin(const std::string &path)
231{
232    return unlink(path.c_str());
233}
234
235class ElfLoader {
236public:
237    explicit ElfLoader(std::string path) : path_(std::move(path)), kernVersion_(0) {}
238
239    ElfLoadError Unload() const
240    {
241        const struct {
242            uint32_t index;
243            const char *infoMsg;
244            std::function<ElfLoadError()> fun;
245            const char *errorMsg;
246        } funList[]{
247            {1, "path is valid", isPathValid_, "path is not valid"},
248            {2, "load elf file ok", loadElfFile_, "load elf file failed"},
249            {3, "load elf map section ok", loadElfMapsSection_, "load elf map section failed"},
250            {4, "delete maps ok", deleteMaps_, "delete maps failed"},
251            {5, "unload progs ok", unloadProgs_, "unload progs ok"},
252        };
253
254        for (const auto &fun : funList) {
255            auto ret = fun.fun();
256            if (ret != ELF_LOAD_ERR_NONE) {
257                NETNATIVE_LOGE("error msg is %{public}s", fun.errorMsg);
258                return static_cast<ElfLoadError>(ret);
259            }
260            NETNATIVE_LOGI("the %{public}u step: %{public}s", fun.index, fun.infoMsg);
261        }
262
263        return ELF_LOAD_ERR_NONE;
264    }
265
266    ElfLoadError Load() const
267    {
268        const struct {
269            uint32_t index;
270            const char *infoMsg;
271            std::function<ElfLoadError()> fun;
272            const char *errorMsg;
273        } funList[]{
274            {1, "path is valid", isPathValid_, "path is not valid"},
275            {2, "make directories fs ok", makeDirectories, "make directories fs failed"},
276            {3, "load elf file ok", loadElfFile_, "load elf file failed"},
277            {4, "version is valid", isVersionValid_, "version is not valid"},
278            {5, "set license and version ok", setLicenseAndVersion_, "set license and version failed"},
279            {6, "load elf map section ok", loadElfMapsSection_, "load elf map section failed"},
280            {7, "set rlimit ok", setRlimit_, "set rlimit failed"},
281            {8, "create maps ok", createMaps_, "create maps failed"},
282            {9, "parse relocation ok", parseRelocation_, "parse relocation failed"},
283            {10, "load progs ok", loadProgs_, "load progs failed"},
284        };
285        for (const auto &fun : funList) {
286            auto ret = fun.fun();
287            if (ret != ELF_LOAD_ERR_NONE) {
288                NETNATIVE_LOGE("error msg is %{public}s", fun.errorMsg);
289                return static_cast<ElfLoadError>(ret);
290            }
291            NETNATIVE_LOGI("the %{public}u step: %{public}s", fun.index, fun.infoMsg);
292        }
293
294        return ELF_LOAD_ERR_NONE;
295    }
296
297private:
298    bool CheckPath()
299    {
300        if (path_.empty() || !std::filesystem::exists(path_) || std::filesystem::is_directory(path_)) {
301            return false;
302        }
303
304        return true;
305    }
306
307    bool IsPathValid()
308    {
309        if (!CheckPath()) {
310            return false;
311        }
312        return EndsWith(path_, ".o");
313    }
314
315    bool LoadElfFile()
316    {
317        return elfIo_.load(path_);
318    }
319
320    bool IsVersionValid()
321    {
322        return elfIo_.get_version() == ELFIO::EV_CURRENT;
323    }
324
325    static bool SetRlimit()
326    {
327        rlimit r = {RLIM_INFINITY, RLIM_INFINITY};
328        return setrlimit(RLIMIT_MEMLOCK, &r) >= 0;
329    }
330
331    static bool IsMounted(const std::string &dir)
332    {
333        std::ifstream ifs("/proc/mounts", std::ios::in);
334        if (!ifs.is_open()) {
335            return false;
336        }
337
338        std::string s;
339        while (std::getline(ifs, s)) {
340            if (s.find(dir) != std::string::npos) {
341                ifs.close();
342                return true;
343            }
344        }
345        ifs.close();
346        return false;
347    }
348
349    static bool MakeDir(const std::string &dir)
350    {
351        if (std::filesystem::exists(dir) && std::filesystem::is_directory(dir)) {
352            return true;
353        }
354        if (!std::filesystem::exists(dir)) {
355            if (!std::filesystem::create_directories(std::filesystem::path(dir))) {
356                NETNATIVE_LOGE("filesystem make dir err %{public}d", errno);
357                return false;
358            }
359            return true;
360        }
361        if (!std::filesystem::is_directory(dir)) {
362            NETNATIVE_LOGE("%{public}s is a file", dir.c_str());
363            return false;
364        }
365        return true;
366    }
367
368    static bool MakeDirectories()
369    {
370        if (IsMounted(BPF_DIR) && std::filesystem::exists(MAPS_DIR) && std::filesystem::is_directory(MAPS_DIR) &&
371            std::filesystem::exists(PROGS_DIR) && std::filesystem::is_directory(PROGS_DIR)) {
372            NETNATIVE_LOGE("%{public}s", "bpf directories are exists");
373            return true;
374        }
375        if (!IsMounted(BPF_DIR) && mount(BPF_DIR, BPF_DIR, "bpf", MS_RELATIME, nullptr) < 0) {
376            NETNATIVE_LOGE("mount bpf fs failed: errno = %{public}d", errno);
377            return false;
378        }
379        if (!MakeDir(MAPS_DIR)) {
380            NETNATIVE_LOGE("can not make dir: %{public}s", MAPS_DIR);
381            return false;
382        }
383        if (!MakeDir(PROGS_DIR)) {
384            NETNATIVE_LOGE("can not make dir: %{public}s", PROGS_DIR);
385            return false;
386        }
387
388        if (!IsMounted(CGROUP_DIR) && mount(CGROUP_DIR, CGROUP_DIR, "cgroup2", MS_RELATIME, nullptr) < 0) {
389            NETNATIVE_LOGE("mount cgroup fs failed: errno = %{public}d", errno);
390            return false;
391        }
392        return true;
393    }
394
395    bool SetLicenseAndVersion()
396    {
397        return std::all_of(elfIo_.sections.begin(), elfIo_.sections.end(), [this](const auto &section) {
398            if (section->get_name() == "license") {
399                license_ = section->get_data();
400                if (license_.empty()) {
401                    return false;
402                }
403            } else if (section->get_name() == "version") {
404                try {
405                    kernVersion_ = std::stoi(section->get_data());
406                } catch (const std::invalid_argument& e) {
407                    NETNATIVE_LOGE("invalid_argument");
408                    return false;
409                } catch (const std::out_of_range& e) {
410                    NETNATIVE_LOGE("out_of_range");
411                    return false;
412                }
413                if (kernVersion_ == 0) {
414                    return false;
415                }
416            }
417
418            return true;
419        });
420    }
421
422    std::map<ELFIO::Elf64_Addr, std::string> LoadElfMapSectionCore()
423    {
424        std::map<ELFIO::Elf64_Addr, std::string> mapName;
425        for (const auto &section : elfIo_.sections) {
426            if (section->get_type() != ELFIO::SHT_SYMTAB && section->get_type() != ELFIO::SHT_DYNSYM) {
427                continue;
428            }
429            ELFIO::symbol_section_accessor symbols(elfIo_, section.get());
430            for (ELFIO::Elf_Xword i = 0; i < symbols.get_symbols_num(); i++) {
431                std::string name;
432                ELFIO::Elf64_Addr value = 0;
433                ELFIO::Elf_Xword size = 0;
434                unsigned char bind = 0;
435                unsigned char type = 0;
436                ELFIO::Elf_Half elfSection = 0;
437                unsigned char other = 0;
438                symbols.get_symbol(i, name, value, size, bind, type, elfSection, other);
439                if (type != ELFIO::STT_OBJECT || !EndsWith(name, "_map")) {
440                    continue;
441                }
442                if (mapName.find(value) != mapName.end()) {
443                    return {};
444                }
445                mapName[value] = name;
446            }
447        }
448        return mapName;
449    }
450
451    bool LoadElfMapsSection()
452    {
453        if (elfIo_.sections.size() == 0) {
454            return false;
455        }
456
457        auto it = std::find_if(elfIo_.sections.begin(), elfIo_.sections.end(),
458                               [](const auto &section) { return section->get_name() == "maps"; });
459        if (it == elfIo_.sections.end()) {
460            return true;
461        }
462
463        ELFIO::section *mapsSection = it->get();
464        auto defs = reinterpret_cast<const bpf_map_def *>(mapsSection->get_data());
465        auto mapNum = mapsSection->get_size() / sizeof(bpf_map_def);
466        for (size_t i = 0; i < static_cast<size_t>(mapNum); i++) {
467            BpfMapData map;
468            map.def = defs[i];
469            maps_.emplace_back(map);
470        }
471        auto mapName = LoadElfMapSectionCore();
472        if (mapName.size() != maps_.size()) {
473            return false;
474        }
475        size_t mapIndex = 0;
476        for (const auto &[addr, name] : mapName) {
477            maps_[mapIndex].name = name;
478            ++mapIndex;
479        }
480
481        return true;
482    }
483
484    static void PrintMapAttr(const bpf_attr &attr)
485    {
486        NETNATIVE_LOGI("%{public}s", "BPF_MAP_CREATE:");
487        NETNATIVE_LOGI("  .map_type    = %{public}u", attr.map_type);
488        NETNATIVE_LOGI("  .key_size    = %{public}u", attr.key_size);
489        NETNATIVE_LOGI("  .value_size  = %{public}u", attr.value_size);
490        NETNATIVE_LOGI("  .max_entries = %{public}u", attr.max_entries);
491        NETNATIVE_LOGI("  .map_flags   = %{public}u", attr.map_flags);
492        NETNATIVE_LOGI("  .map_name    = %{public}s", attr.map_name);
493    }
494
495    static int32_t BpfCreateMapNode(const BpfMapData &map)
496    {
497        bpf_attr attr = {};
498        if (memset_s(&attr, sizeof(attr), 0, sizeof(attr)) != EOK) {
499            return NETMANAGER_ERROR;
500        }
501        attr.map_type = map.def.type;
502        attr.key_size = map.def.key_size;
503        attr.value_size = map.def.value_size;
504        attr.max_entries = map.def.max_entries;
505        attr.map_flags = map.def.map_flags;
506        if (!map.name.empty()) {
507            if (memcpy_s(attr.map_name, sizeof(attr.map_name) - 1, map.name.c_str(),
508                         std::min<size_t>(map.name.size(), sizeof(attr.map_name) - 1)) != EOK) {
509                NETNATIVE_LOGE("Failed copy map name %{public}s", map.name.c_str());
510                return NETMANAGER_ERROR;
511            }
512        }
513        attr.numa_node = (map.def.map_flags & static_cast<unsigned int>(BPF_F_NUMA_NODE)) ? map.def.numa_node : 0;
514        PrintMapAttr(attr);
515
516        auto fd = SysBpf(BPF_MAP_CREATE, &attr, sizeof(attr));
517        if (fd < 0) {
518            NETNATIVE_LOGE("__NR_bpf, BPF_MAP_CREATE failed %{public}d %{public}d %{public}d", __NR_bpf, fd, errno);
519        }
520        return fd;
521    }
522
523    bool CreateMaps()
524    {
525        for (auto &map : maps_) {
526            auto fd = BpfCreateMapNode(map);
527            if (fd < 0) {
528                NETNATIVE_LOGE("Failed create map (%{public}s): %{public}d", map.name.c_str(), fd);
529                return false;
530            }
531
532            map.fd = fd;
533            std::string mapPinLocation = std::string(MAPS_DIR) + "/" + map.name;
534            if (access(mapPinLocation.c_str(), F_OK) == 0) {
535                NETNATIVE_LOGI("map: %{public}s has already been pinned", mapPinLocation.c_str());
536            } else {
537                if (SysBpfObjPin(fd, mapPinLocation) < 0) {
538                    NETNATIVE_LOGE("Failed to pin map: %{public}s, errno = %{public}d", mapPinLocation.c_str(), errno);
539                    return false;
540                }
541            }
542        }
543        return true;
544    }
545
546    bool DeleteMaps()
547    {
548        return std::all_of(maps_.begin(), maps_.end(), [](const auto &map) {
549            std::string mapPinLocation = std::string(MAPS_DIR) + "/" + map.name;
550            if (access(mapPinLocation.c_str(), F_OK) == 0) {
551                auto ret = UnPin(mapPinLocation);
552                return ret >= 0;
553            }
554            return true;
555        });
556    }
557
558    bool ApplyRelocation(bpf_insn *insn, ELFIO::section *section) const
559    {
560        if (insn == nullptr || section == nullptr || section->get_entry_size() == 0) {
561            return false;
562        }
563
564        auto size = section->get_size() / section->get_entry_size();
565        if (size == 0) {
566            return false;
567        }
568
569        ELFIO::Elf64_Addr offset = 0;
570        ELFIO::Elf64_Addr symbolValue = 0;
571        std::string symbolName;
572        ELFIO::Elf_Word type = 0;
573        ELFIO::Elf_Sxword addend = 0;
574        ELFIO::Elf_Sxword calcValue = 0;
575        ELFIO::relocation_section_accessor relocation(elfIo_, section);
576        for (size_t i = 0; i < size; i++) {
577            relocation.get_entry(i, offset, symbolValue, symbolName, type, addend, calcValue);
578            uint32_t index = offset / sizeof(bpf_insn);
579            if (insn[index].code != (BPF_LD | BPF_IMM | BPF_DW)) {
580                NETNATIVE_LOGE("Invalid relo for insn[%{public}u].code 0x%{public}x 0x%{public}x", index,
581                               insn[index].code, (BPF_LD | BPF_IMM | BPF_DW));
582                continue;
583            }
584
585            size_t mapIdx;
586            bool match = false;
587            for (mapIdx = 0; mapIdx < maps_.size(); mapIdx++) {
588                if (maps_[mapIdx].name == symbolName) {
589                    match = true;
590                    break;
591                }
592            }
593            if (!match) {
594                NETNATIVE_LOGE("Invalid relo for insn[%{public}u] no map_data match %{public}s index %{public}zu",
595                               index, section->get_name().c_str(), i);
596                continue;
597            }
598            insn[index].src_reg = BPF_PSEUDO_MAP_FD;
599            insn[index].imm = maps_[mapIdx].fd;
600        }
601        return true;
602    }
603
604    int32_t BpfLoadProgram(std::string &progName, bpf_prog_type type, const bpf_insn *insns, size_t insnsCnt)
605    {
606        if (insns == nullptr) {
607            return NETMANAGER_ERROR;
608        }
609
610        bpf_attr attr = {};
611        if (memset_s(&attr, sizeof(attr), 0, sizeof(attr)) != EOK) {
612            return NETMANAGER_ERROR;
613        }
614        attr.prog_type = type;
615        if (kernVersion_ < 0) {
616            return NETMANAGER_ERROR;
617        }
618        attr.kern_version = static_cast<uint32_t>(kernVersion_);
619        attr.insn_cnt = static_cast<uint32_t>(insnsCnt);
620        attr.insns = PtrToU64(insns);
621        attr.license = PtrToU64(license_.c_str());
622        for (const auto &prog : PROG_ATTACH_TYPES) {
623            if (prog.progName != nullptr && progName == prog.progName) {
624                if (prog.needExpectedAttach) {
625                    attr.expected_attach_type = prog.attachType;
626                }
627                break;
628            }
629        }
630
631        return SysBpfProgLoad(&attr, sizeof(attr));
632    }
633
634    static bpf_prog_type ConvertEventToProgType(const std::string &event)
635    {
636        for (const auto &prog : PROG_TYPES) {
637            size_t size = strlen(prog.event);
638            if (event.size() < size) {
639                continue;
640            }
641            if (memcmp(event.c_str(), prog.event, size) == 0) {
642                return prog.progType;
643            }
644        }
645        return static_cast<bpf_prog_type>(NETMANAGER_ERROR);
646    }
647
648    static bool DoAttach(int32_t progFd, const std::string &progName)
649    {
650        if (progName.size() < 1) {
651            NETNATIVE_LOGE("progName is null");
652            return false;
653        }
654        NETNATIVE_LOG_D("The progName = %{public}s", progName.c_str());
655
656        for (const auto &prog : PROG_ATTACH_TYPES) {
657            if (prog.progName != nullptr && progName == prog.progName) {
658                int cgroupFd = open(CGROUP_DIR, O_DIRECTORY | O_RDONLY | O_CLOEXEC);
659                if (cgroupFd < 0) {
660                    NETNATIVE_LOGE("open CGROUP_DIR failed: errno = %{public}d", errno);
661                    return false;
662                }
663
664                if (SysBpfObjAttach(prog.attachType, progFd, cgroupFd) < NETSYS_SUCCESS) {
665                    NETNATIVE_LOGE("attach %{pubic}s failed: errno = %{public}d", progName.c_str(), errno);
666                    close(cgroupFd);
667                    return false;
668                }
669
670                close(cgroupFd);
671                return true;
672            }
673        }
674
675        return true;
676    }
677
678    static void DoDetach(const std::string &progPinLocation, const std::string &progName)
679    {
680        if (progName.size() < 1) {
681            NETNATIVE_LOGE("progName is null");
682            return;
683        }
684        NETNATIVE_LOG_D("The progName = %{public}s", progName.c_str());
685
686        for (const auto &prog : PROG_ATTACH_TYPES) {
687            if (prog.progName != nullptr && progName == prog.progName) {
688                int cgroupFd = open(CGROUP_DIR, O_DIRECTORY | O_RDONLY | O_CLOEXEC);
689                if (cgroupFd < NETSYS_SUCCESS) {
690                    NETNATIVE_LOGE("open CGROUP_DIR failed: errno = %{public}d", errno);
691                    return;
692                }
693
694                auto progFd = SysBpfObjGet(progPinLocation, 0);
695                if (progFd < NETSYS_SUCCESS) {
696                    close(cgroupFd);
697                    return;
698                }
699
700                if (SysBpfObjDetach(prog.attachType, progFd, cgroupFd) < NETSYS_SUCCESS) {
701                    NETNATIVE_LOGE("detach %{pubic}s failed: errno = %{public}d", progName.c_str(), errno);
702                    close(cgroupFd);
703                    return;
704                }
705
706                close(cgroupFd);
707                return;
708            }
709        }
710    }
711
712    bool LoadProg(const std::string &event, const bpf_insn *insn, size_t insnCnt)
713    {
714        auto progType = ConvertEventToProgType(event);
715        if (progType < NETSYS_SUCCESS) {
716            NETNATIVE_LOGE("unsupported program type: %{public}s", event.c_str());
717            return false;
718        }
719
720        if (insn == nullptr) {
721            NETNATIVE_LOGE("insn is null");
722            return false;
723        }
724
725        std::string progName = event;
726        std::replace(progName.begin(), progName.end(), '/', '_');
727        int32_t progFd = BpfLoadProgram(progName, progType, insn, insnCnt);
728        if (progFd < NETSYS_SUCCESS) {
729            NETNATIVE_LOGE("Failed to load bpf prog, error = %{public}d", errno);
730            return false;
731        }
732
733        std::string progPinLocation = std::string(PROGS_DIR) + "/" + progName;
734        if (access(progPinLocation.c_str(), F_OK) == 0) {
735            NETNATIVE_LOGI("prog: %{public}s has already been pinned", progPinLocation.c_str());
736        } else {
737            if (SysBpfObjPin(progFd, progPinLocation) < NETSYS_SUCCESS) {
738                NETNATIVE_LOGE("Failed to pin prog: %{public}s, errno = %{public}d", progPinLocation.c_str(), errno);
739                close(progFd);
740                return false;
741            }
742        }
743
744        /* attach socket filter */
745        if (progType == BPF_PROG_TYPE_SOCKET_FILTER) {
746            if (g_sockFd < 0) {
747                NETNATIVE_LOGE("create socket failed, %{public}d, err: %{public}d", g_sockFd, errno);
748                close(progFd);
749                /* return true to ignore this prog */
750                return true;
751            }
752            if (setsockopt(g_sockFd, SOL_SOCKET, SO_ATTACH_BPF, &progFd, sizeof(progFd)) < 0) {
753                NETNATIVE_LOGE("attach socket failed, err: %{public}d", errno);
754                close(g_sockFd);
755                g_sockFd = -1;
756            }
757            close(progFd);
758            return true;
759        } else {
760            auto ret = DoAttach(progFd, progName);
761            close(progFd);
762            return ret;
763        }
764    }
765
766    bool ParseRelocation()
767    {
768        return std::all_of(elfIo_.sections.begin(), elfIo_.sections.end(), [this](auto &section) -> bool {
769            if (section->get_type() != ELFIO::SHT_REL) {
770                return true;
771            }
772
773            auto info = section->get_info();
774            auto progSec = elfIo_.sections[info];
775            if (progSec == nullptr) {
776                return true;
777            }
778
779            if (progSec->get_type() != ELFIO::SHT_PROGBITS || ((progSec->get_flags() & ELFIO::SHF_EXECINSTR) == 0)) {
780                return true;
781            }
782
783            auto insn = reinterpret_cast<bpf_insn *>(const_cast<char *>(progSec->get_data()));
784            if (insn == nullptr) {
785                return false;
786            }
787            if (!ApplyRelocation(insn, section.get())) {
788                return false;
789            }
790            return true;
791        });
792    }
793
794    bool UnloadProgs()
795    {
796        if (g_sockFd > 0) {
797            close(g_sockFd);
798            g_sockFd = -1;
799        }
800        return std::all_of(elfIo_.sections.begin(), elfIo_.sections.end(), [this](const auto &section) -> bool {
801            if (!MatchSecName(section->get_name())) {
802                return true;
803            }
804
805            std::string progName = section->get_name();
806            std::replace(progName.begin(), progName.end(), '/', '_');
807            std::string progPinLocation = std::string(PROGS_DIR) + "/" + progName;
808            if (access(progPinLocation.c_str(), F_OK) == 0) {
809                DoDetach(progPinLocation, progName);
810
811                return UnPin(progPinLocation) < NETSYS_SUCCESS ? false : true;
812            }
813            return true;
814        });
815    }
816
817    bool LoadProgs()
818    {
819        if (g_sockFd > 0) {
820            close(g_sockFd);
821        }
822        g_sockFd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
823        return std::all_of(elfIo_.sections.begin(), elfIo_.sections.end(), [this](const auto &section) -> bool {
824            if (!MatchSecName(section->get_name())) {
825                return true;
826            }
827            return LoadProg(section->get_name(), reinterpret_cast<const bpf_insn *>(section->get_data()),
828                            section->get_size() / sizeof(bpf_insn));
829        });
830    }
831
832    std::string path_;
833    ELFIO::elfio elfIo_;
834    std::string license_;
835    int32_t kernVersion_;
836    std::vector<BpfMapData> maps_;
837
838    std::function<ElfLoadError()> isPathValid_ = [this]() -> ElfLoadError {
839        if (!IsPathValid()) {
840            return ELF_LOAD_ERR_PATH_INVALID;
841        }
842        return ELF_LOAD_ERR_NONE;
843    };
844
845    std::function<ElfLoadError()> makeDirectories = []() -> ElfLoadError {
846        if (!MakeDirectories()) {
847            return ELF_LOAD_ERR_MAKE_DIR_FAIL;
848        }
849        return ELF_LOAD_ERR_NONE;
850    };
851
852    std::function<ElfLoadError()> loadElfFile_ = [this]() -> ElfLoadError {
853        if (!LoadElfFile()) {
854            return ELF_LOAD_ERR_LOAD_FILE_FAIL;
855        }
856        return ELF_LOAD_ERR_NONE;
857    };
858
859    std::function<ElfLoadError()> isVersionValid_ = [this]() -> ElfLoadError {
860        if (!IsVersionValid()) {
861            return ELF_LOAD_ERR_GET_VERSION_FAIL;
862        }
863        return ELF_LOAD_ERR_NONE;
864    };
865
866    std::function<ElfLoadError()> setLicenseAndVersion_ = [this]() -> ElfLoadError {
867        if (!SetLicenseAndVersion()) {
868            return ELF_LOAD_ERR_SELECT_LICENSE_AND_VERSION_FAIL;
869        }
870        return ELF_LOAD_ERR_NONE;
871    };
872
873    std::function<ElfLoadError()> loadElfMapsSection_ = [this]() -> ElfLoadError {
874        if (!LoadElfMapsSection()) {
875            return ELF_LOAD_ERR_LOAD_MAP_SECTION_FAIL;
876        }
877        return ELF_LOAD_ERR_NONE;
878    };
879
880    std::function<ElfLoadError()> setRlimit_ = []() -> ElfLoadError {
881        if (!SetRlimit()) {
882            return ELF_LOAD_ERR_SET_RLIMIT_FAIL;
883        }
884        return ELF_LOAD_ERR_NONE;
885    };
886
887    std::function<ElfLoadError()> createMaps_ = [this]() -> ElfLoadError {
888        if (!CreateMaps()) {
889            return ELF_LOAD_ERR_CREATE_MAP_FAIL;
890        }
891        return ELF_LOAD_ERR_NONE;
892    };
893
894    std::function<ElfLoadError()> parseRelocation_ = [this]() -> ElfLoadError {
895        if (!ParseRelocation()) {
896            return ELF_LOAD_ERR_PARSE_RELOCATION_FAIL;
897        }
898        return ELF_LOAD_ERR_NONE;
899    };
900
901    std::function<ElfLoadError()> loadProgs_ = [this]() -> ElfLoadError {
902        if (!LoadProgs()) {
903            return ELF_LOAD_ERR_LOAD_PROGS_FAIL;
904        }
905        return ELF_LOAD_ERR_NONE;
906    };
907
908    std::function<ElfLoadError()> deleteMaps_ = [this]() -> ElfLoadError {
909        if (!DeleteMaps()) {
910            return ELF_LOAD_ERR_DELETE_MAP_FAIL;
911        }
912        return ELF_LOAD_ERR_NONE;
913    };
914
915    std::function<ElfLoadError()> unloadProgs_ = [this]() -> ElfLoadError {
916        if (!UnloadProgs()) {
917            return ELF_LOAD_ERR_UNLOAD_PROGS_FAIL;
918        }
919        return ELF_LOAD_ERR_NONE;
920    };
921};
922
923ElfLoadError LoadElf(const std::string &elfPath)
924{
925    ElfLoader loader(elfPath);
926    return loader.Load();
927}
928
929ElfLoadError UnloadElf(const std::string &elfPath)
930{
931    ElfLoader loader(elfPath);
932    return loader.Unload();
933}
934} // namespace OHOS::NetManagerStandard
935