1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifndef BPF_BPFUTILS_H
18 #define BPF_BPFUTILS_H
19 
20 #include <linux/bpf.h>
21 #include <linux/if_ether.h>
22 #include <linux/unistd.h>
23 #include <net/if.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <sys/socket.h>
27 
28 #include <string>
29 
30 #include "android-base/unique_fd.h"
31 
32 #define ptr_to_u64(x) ((uint64_t)(uintptr_t)(x))
33 
34 namespace android {
35 namespace bpf {
36 
37 enum class BpfLevel {
38     // Devices shipped before P or kernel version is lower than 4.9 do not
39     // have eBPF enabled.
40     NONE,
41     // Devices shipped in P with android 4.9 kernel only have the basic eBPF
42     // functionalities such as xt_bpf and cgroup skb filter.
43     BASIC_4_9,
44     // For devices that have 4.14 kernel. It supports advanced features like
45     // map_in_map and cgroup socket filter.
46     EXTENDED_4_14,
47     EXTENDED_4_19,
48     EXTENDED_5_4,
49 };
50 
51 constexpr const int OVERFLOW_COUNTERSET = 2;
52 
53 constexpr const uint64_t NONEXISTENT_COOKIE = 0;
54 
55 constexpr const int MINIMUM_API_REQUIRED = 28;
56 
57 /* Note: bpf_attr is a union which might have a much larger size then the anonymous struct portion
58  * of it that we are using.  The kernel's bpf() system call will perform a strict check to ensure
59  * all unused portions are zero.  It will fail with E2BIG if we don't fully zero bpf_attr.
60  */
61 
bpf(int cmd,const bpf_attr & attr)62 inline int bpf(int cmd, const bpf_attr& attr) {
63     return syscall(__NR_bpf, cmd, &attr, sizeof(attr));
64 }
65 
createMap(bpf_map_type map_type,uint32_t key_size,uint32_t value_size,uint32_t max_entries,uint32_t map_flags)66 inline int createMap(bpf_map_type map_type, uint32_t key_size, uint32_t value_size,
67                      uint32_t max_entries, uint32_t map_flags) {
68     return bpf(BPF_MAP_CREATE, {
69                                        .map_type = map_type,
70                                        .key_size = key_size,
71                                        .value_size = value_size,
72                                        .max_entries = max_entries,
73                                        .map_flags = map_flags,
74                                });
75 }
76 
writeToMapEntry(const base::unique_fd & map_fd,const void * key,const void * value,uint64_t flags)77 inline int writeToMapEntry(const base::unique_fd& map_fd, const void* key, const void* value,
78                            uint64_t flags) {
79     return bpf(BPF_MAP_UPDATE_ELEM, {
80                                             .map_fd = static_cast<__u32>(map_fd.get()),
81                                             .key = ptr_to_u64(key),
82                                             .value = ptr_to_u64(value),
83                                             .flags = flags,
84                                     });
85 }
86 
findMapEntry(const base::unique_fd & map_fd,const void * key,void * value)87 inline int findMapEntry(const base::unique_fd& map_fd, const void* key, void* value) {
88     return bpf(BPF_MAP_LOOKUP_ELEM, {
89                                             .map_fd = static_cast<__u32>(map_fd.get()),
90                                             .key = ptr_to_u64(key),
91                                             .value = ptr_to_u64(value),
92                                     });
93 }
94 
deleteMapEntry(const base::unique_fd & map_fd,const void * key)95 inline int deleteMapEntry(const base::unique_fd& map_fd, const void* key) {
96     return bpf(BPF_MAP_DELETE_ELEM, {
97                                             .map_fd = static_cast<__u32>(map_fd.get()),
98                                             .key = ptr_to_u64(key),
99                                     });
100 }
101 
getNextMapKey(const base::unique_fd & map_fd,const void * key,void * next_key)102 inline int getNextMapKey(const base::unique_fd& map_fd, const void* key, void* next_key) {
103     return bpf(BPF_MAP_GET_NEXT_KEY, {
104                                              .map_fd = static_cast<__u32>(map_fd.get()),
105                                              .key = ptr_to_u64(key),
106                                              .next_key = ptr_to_u64(next_key),
107                                      });
108 }
109 
getFirstMapKey(const base::unique_fd & map_fd,void * firstKey)110 inline int getFirstMapKey(const base::unique_fd& map_fd, void* firstKey) {
111     return getNextMapKey(map_fd, NULL, firstKey);
112 }
113 
bpfFdPin(const base::unique_fd & map_fd,const char * pathname)114 inline int bpfFdPin(const base::unique_fd& map_fd, const char* pathname) {
115     return bpf(BPF_OBJ_PIN, {
116                                     .pathname = ptr_to_u64(pathname),
117                                     .bpf_fd = static_cast<__u32>(map_fd.get()),
118                             });
119 }
120 
bpfFdGet(const char * pathname,uint32_t flag)121 inline int bpfFdGet(const char* pathname, uint32_t flag) {
122     return bpf(BPF_OBJ_GET, {
123                                     .pathname = ptr_to_u64(pathname),
124                                     .file_flags = flag,
125                             });
126 }
127 
mapRetrieve(const char * pathname,uint32_t flag)128 inline int mapRetrieve(const char* pathname, uint32_t flag) {
129     return bpfFdGet(pathname, flag);
130 }
131 
mapRetrieveRW(const char * pathname)132 inline int mapRetrieveRW(const char* pathname) {
133     return mapRetrieve(pathname, 0);
134 }
135 
mapRetrieveRO(const char * pathname)136 inline int mapRetrieveRO(const char* pathname) {
137     return mapRetrieve(pathname, BPF_F_RDONLY);
138 }
139 
mapRetrieveWO(const char * pathname)140 inline int mapRetrieveWO(const char* pathname) {
141     return mapRetrieve(pathname, BPF_F_WRONLY);
142 }
143 
retrieveProgram(const char * pathname)144 inline int retrieveProgram(const char* pathname) {
145     return bpfFdGet(pathname, BPF_F_RDONLY);
146 }
147 
attachProgram(bpf_attach_type type,const base::unique_fd & prog_fd,const base::unique_fd & cg_fd)148 inline int attachProgram(bpf_attach_type type, const base::unique_fd& prog_fd,
149                          const base::unique_fd& cg_fd) {
150     return bpf(BPF_PROG_ATTACH, {
151                                         .target_fd = static_cast<__u32>(cg_fd.get()),
152                                         .attach_bpf_fd = static_cast<__u32>(prog_fd.get()),
153                                         .attach_type = type,
154                                 });
155 }
156 
detachProgram(bpf_attach_type type,const base::unique_fd & cg_fd)157 inline int detachProgram(bpf_attach_type type, const base::unique_fd& cg_fd) {
158     return bpf(BPF_PROG_DETACH, {
159                                         .target_fd = static_cast<__u32>(cg_fd.get()),
160                                         .attach_type = type,
161                                 });
162 }
163 
164 uint64_t getSocketCookie(int sockFd);
165 int synchronizeKernelRCU();
166 int setrlimitForTest();
167 unsigned kernelVersion();
168 std::string BpfLevelToString(BpfLevel BpfLevel);
169 BpfLevel getBpfSupportLevel();
170 
isBpfSupported()171 inline bool isBpfSupported() {
172     return getBpfSupportLevel() != BpfLevel::NONE;
173 }
174 
175 #define SKIP_IF_BPF_NOT_SUPPORTED                                                    \
176     do {                                                                             \
177         if (!android::bpf::isBpfSupported()) {                                       \
178             GTEST_LOG_(INFO) << "This test is skipped since bpf is not available\n"; \
179             return;                                                                  \
180         }                                                                            \
181     } while (0)
182 
183 #define SKIP_IF_BPF_SUPPORTED                       \
184     do {                                            \
185         if (android::bpf::isBpfSupported()) return; \
186     } while (0)
187 
188 #define SKIP_IF_EXTENDED_BPF_NOT_SUPPORTED                                                \
189     do {                                                                                  \
190         if (android::bpf::getBpfSupportLevel() < android::bpf::BpfLevel::EXTENDED_4_14) { \
191             GTEST_LOG_(INFO) << "This test is skipped since extended bpf feature"         \
192                              << "not supported\n";                                        \
193             return;                                                                       \
194         }                                                                                 \
195     } while (0)
196 
197 }  // namespace bpf
198 }  // namespace android
199 
200 #endif
201