1 /*
2 * Copyright (C) 2018 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 #pragma once
18
19 #include <linux/bpf.h>
20
21 #include <android-base/result.h>
22 #include <android-base/stringprintf.h>
23 #include <android-base/unique_fd.h>
24 #include <utils/Log.h>
25
26 #include "BpfSyscallWrappers.h"
27 #include "bpf/BpfUtils.h"
28
29 namespace android {
30 namespace bpf {
31
32 // This is a class wrapper for eBPF maps. The eBPF map is a special in-kernel
33 // data structure that stores data in <Key, Value> pairs. It can be read/write
34 // from userspace by passing syscalls with the map file descriptor. This class
35 // is used to generalize the procedure of interacting with eBPF maps and hide
36 // the implementation detail from other process. Besides the basic syscalls
37 // wrapper, it also provides some useful helper functions as well as an iterator
38 // nested class to iterate the map more easily.
39 //
40 // NOTE: A kernel eBPF map may be accessed by both kernel and userspace
41 // processes at the same time. Or if the map is pinned as a virtual file, it can
42 // be obtained by multiple eBPF map class object and accessed concurrently.
43 // Though the map class object and the underlying kernel map are thread safe, it
44 // is not safe to iterate over a map while another thread or process is deleting
45 // from it. In this case the iteration can return duplicate entries.
46 template <class Key, class Value>
47 class BpfMap {
48 public:
49 BpfMap<Key, Value>() {};
50
51 // explicitly force no copy constructor, since it would need to dup the fd
52 // (later on, for testing, we still make available a copy assignment operator)
53 BpfMap<Key, Value>(const BpfMap<Key, Value>&) = delete;
54
55 private:
abortOnKeyOrValueSizeMismatch()56 void abortOnKeyOrValueSizeMismatch() {
57 if (!mMapFd.ok()) abort();
58 if (isAtLeastKernelVersion(4, 14, 0)) {
59 if (bpfGetFdKeySize(mMapFd) != sizeof(Key)) abort();
60 if (bpfGetFdValueSize(mMapFd) != sizeof(Value)) abort();
61 }
62 }
63
64 protected:
65 // flag must be within BPF_OBJ_FLAG_MASK, ie. 0, BPF_F_RDONLY, BPF_F_WRONLY
66 BpfMap<Key, Value>(const char* pathname, uint32_t flags) {
67 mMapFd.reset(mapRetrieve(pathname, flags));
68 abortOnKeyOrValueSizeMismatch();
69 }
70
71 public:
72 explicit BpfMap<Key, Value>(const char* pathname) : BpfMap<Key, Value>(pathname, 0) {}
73
74 #ifdef BPF_MAP_MAKE_VISIBLE_FOR_TESTING
75 // All bpf maps should be created by the bpfloader, so this constructor
76 // is reserved for tests
77 BpfMap<Key, Value>(bpf_map_type map_type, uint32_t max_entries, uint32_t map_flags = 0) {
78 mMapFd.reset(createMap(map_type, sizeof(Key), sizeof(Value), max_entries, map_flags));
79 if (!mMapFd.ok()) abort();
80 }
81 #endif
82
getFirstKey()83 base::Result<Key> getFirstKey() const {
84 Key firstKey;
85 if (getFirstMapKey(mMapFd, &firstKey)) {
86 return ErrnoErrorf("Get firstKey map {} failed", mMapFd.get());
87 }
88 return firstKey;
89 }
90
getNextKey(const Key & key)91 base::Result<Key> getNextKey(const Key& key) const {
92 Key nextKey;
93 if (getNextMapKey(mMapFd, &key, &nextKey)) {
94 return ErrnoErrorf("Get next key of map {} failed", mMapFd.get());
95 }
96 return nextKey;
97 }
98
writeValue(const Key & key,const Value & value,uint64_t flags)99 base::Result<void> writeValue(const Key& key, const Value& value, uint64_t flags) {
100 if (writeToMapEntry(mMapFd, &key, &value, flags)) {
101 return ErrnoErrorf("Write to map {} failed", mMapFd.get());
102 }
103 return {};
104 }
105
readValue(const Key key)106 base::Result<Value> readValue(const Key key) const {
107 Value value;
108 if (findMapEntry(mMapFd, &key, &value)) {
109 return ErrnoErrorf("Read value of map {} failed", mMapFd.get());
110 }
111 return value;
112 }
113
deleteValue(const Key & key)114 base::Result<void> deleteValue(const Key& key) {
115 if (deleteMapEntry(mMapFd, &key)) {
116 return ErrnoErrorf("Delete entry from map {} failed", mMapFd.get());
117 }
118 return {};
119 }
120
121 protected:
init(const char * path,int fd)122 [[clang::reinitializes]] base::Result<void> init(const char* path, int fd) {
123 mMapFd.reset(fd);
124 if (!mMapFd.ok()) {
125 return ErrnoErrorf("Pinned map not accessible or does not exist: ({})", path);
126 }
127 // Normally we should return an error here instead of calling abort,
128 // but this cannot happen at runtime without a massive code bug (K/V type mismatch)
129 // and as such it's better to just blow the system up and let the developer fix it.
130 // Crashes are much more likely to be noticed than logs and missing functionality.
131 abortOnKeyOrValueSizeMismatch();
132 return {};
133 }
134
135 public:
136 // Function that tries to get map from a pinned path.
init(const char * path)137 [[clang::reinitializes]] base::Result<void> init(const char* path) {
138 return init(path, mapRetrieveRW(path));
139 }
140
141
142 #ifdef BPF_MAP_MAKE_VISIBLE_FOR_TESTING
143 // due to Android SELinux limitations which prevent map creation by anyone besides the bpfloader
144 // this should only ever be used by test code, it is equivalent to:
145 // .reset(createMap(type, keysize, valuesize, max_entries, map_flags)
146 // TODO: derive map_flags from BpfMap vs BpfMapRO
147 [[clang::reinitializes]] base::Result<void> resetMap(bpf_map_type map_type,
148 uint32_t max_entries,
149 uint32_t map_flags = 0) {
150 mMapFd.reset(createMap(map_type, sizeof(Key), sizeof(Value), max_entries, map_flags));
151 if (!mMapFd.ok()) return ErrnoErrorf("Unable to create map.");
152 return {};
153 }
154 #endif
155
156 // Iterate through the map and handle each key retrieved based on the filter
157 // without modification of map content.
158 base::Result<void> iterate(
159 const std::function<base::Result<void>(const Key& key, const BpfMap<Key, Value>& map)>&
160 filter) const;
161
162 // Iterate through the map and get each <key, value> pair, handle each <key,
163 // value> pair based on the filter without modification of map content.
164 base::Result<void> iterateWithValue(
165 const std::function<base::Result<void>(const Key& key, const Value& value,
166 const BpfMap<Key, Value>& map)>& filter) const;
167
168 // Iterate through the map and handle each key retrieved based on the filter
169 base::Result<void> iterate(
170 const std::function<base::Result<void>(const Key& key, BpfMap<Key, Value>& map)>&
171 filter);
172
173 // Iterate through the map and get each <key, value> pair, handle each <key,
174 // value> pair based on the filter.
175 base::Result<void> iterateWithValue(
176 const std::function<base::Result<void>(const Key& key, const Value& value,
177 BpfMap<Key, Value>& map)>& filter);
178
getMap()179 const base::unique_fd& getMap() const { return mMapFd; };
180
181 #ifdef BPF_MAP_MAKE_VISIBLE_FOR_TESTING
182 // Copy assignment operator - due to need for fd duping, should not be used in non-test code.
183 BpfMap<Key, Value>& operator=(const BpfMap<Key, Value>& other) {
184 if (this != &other) mMapFd.reset(fcntl(other.mMapFd.get(), F_DUPFD_CLOEXEC, 0));
185 return *this;
186 }
187 #else
188 BpfMap<Key, Value>& operator=(const BpfMap<Key, Value>&) = delete;
189 #endif
190
191 // Move assignment operator
192 BpfMap<Key, Value>& operator=(BpfMap<Key, Value>&& other) noexcept {
193 if (this != &other) {
194 mMapFd = std::move(other.mMapFd);
195 other.reset();
196 }
197 return *this;
198 }
199
200 void reset(base::unique_fd fd) = delete;
201
202 #ifdef BPF_MAP_MAKE_VISIBLE_FOR_TESTING
203 // Note that unique_fd.reset() carefully saves and restores the errno,
204 // and BpfMap.reset() won't touch the errno if passed in fd is negative either,
205 // hence you can do something like BpfMap.reset(systemcall()) and then
206 // check BpfMap.isValid() and look at errno and see why systemcall() failed.
reset(int fd)207 [[clang::reinitializes]] void reset(int fd) {
208 mMapFd.reset(fd);
209 if (mMapFd.ok()) abortOnKeyOrValueSizeMismatch();
210 }
211 #endif
212
reset()213 [[clang::reinitializes]] void reset() {
214 mMapFd.reset();
215 }
216
isValid()217 bool isValid() const { return mMapFd.ok(); }
218
clear()219 base::Result<void> clear() {
220 while (true) {
221 auto key = getFirstKey();
222 if (!key.ok()) {
223 if (key.error().code() == ENOENT) return {}; // empty: success
224 return key.error(); // Anything else is an error
225 }
226 auto res = deleteValue(key.value());
227 if (!res.ok()) {
228 // Someone else could have deleted the key, so ignore ENOENT
229 if (res.error().code() == ENOENT) continue;
230 ALOGE("Failed to delete data %s", strerror(res.error().code()));
231 return res.error();
232 }
233 }
234 }
235
isEmpty()236 base::Result<bool> isEmpty() const {
237 auto key = getFirstKey();
238 if (!key.ok()) {
239 // Return error code ENOENT means the map is empty
240 if (key.error().code() == ENOENT) return true;
241 return key.error();
242 }
243 return false;
244 }
245
246 private:
247 base::unique_fd mMapFd;
248 };
249
250 template <class Key, class Value>
iterate(const std::function<base::Result<void> (const Key & key,const BpfMap<Key,Value> & map)> & filter)251 base::Result<void> BpfMap<Key, Value>::iterate(
252 const std::function<base::Result<void>(const Key& key, const BpfMap<Key, Value>& map)>&
253 filter) const {
254 base::Result<Key> curKey = getFirstKey();
255 while (curKey.ok()) {
256 const base::Result<Key>& nextKey = getNextKey(curKey.value());
257 base::Result<void> status = filter(curKey.value(), *this);
258 if (!status.ok()) return status;
259 curKey = nextKey;
260 }
261 if (curKey.error().code() == ENOENT) return {};
262 return curKey.error();
263 }
264
265 template <class Key, class Value>
iterateWithValue(const std::function<base::Result<void> (const Key & key,const Value & value,const BpfMap<Key,Value> & map)> & filter)266 base::Result<void> BpfMap<Key, Value>::iterateWithValue(
267 const std::function<base::Result<void>(const Key& key, const Value& value,
268 const BpfMap<Key, Value>& map)>& filter) const {
269 base::Result<Key> curKey = getFirstKey();
270 while (curKey.ok()) {
271 const base::Result<Key>& nextKey = getNextKey(curKey.value());
272 base::Result<Value> curValue = readValue(curKey.value());
273 if (!curValue.ok()) return curValue.error();
274 base::Result<void> status = filter(curKey.value(), curValue.value(), *this);
275 if (!status.ok()) return status;
276 curKey = nextKey;
277 }
278 if (curKey.error().code() == ENOENT) return {};
279 return curKey.error();
280 }
281
282 template <class Key, class Value>
iterate(const std::function<base::Result<void> (const Key & key,BpfMap<Key,Value> & map)> & filter)283 base::Result<void> BpfMap<Key, Value>::iterate(
284 const std::function<base::Result<void>(const Key& key, BpfMap<Key, Value>& map)>& filter) {
285 base::Result<Key> curKey = getFirstKey();
286 while (curKey.ok()) {
287 const base::Result<Key>& nextKey = getNextKey(curKey.value());
288 base::Result<void> status = filter(curKey.value(), *this);
289 if (!status.ok()) return status;
290 curKey = nextKey;
291 }
292 if (curKey.error().code() == ENOENT) return {};
293 return curKey.error();
294 }
295
296 template <class Key, class Value>
iterateWithValue(const std::function<base::Result<void> (const Key & key,const Value & value,BpfMap<Key,Value> & map)> & filter)297 base::Result<void> BpfMap<Key, Value>::iterateWithValue(
298 const std::function<base::Result<void>(const Key& key, const Value& value,
299 BpfMap<Key, Value>& map)>& filter) {
300 base::Result<Key> curKey = getFirstKey();
301 while (curKey.ok()) {
302 const base::Result<Key>& nextKey = getNextKey(curKey.value());
303 base::Result<Value> curValue = readValue(curKey.value());
304 if (!curValue.ok()) return curValue.error();
305 base::Result<void> status = filter(curKey.value(), curValue.value(), *this);
306 if (!status.ok()) return status;
307 curKey = nextKey;
308 }
309 if (curKey.error().code() == ENOENT) return {};
310 return curKey.error();
311 }
312
313 template <class Key, class Value>
314 class BpfMapRO : public BpfMap<Key, Value> {
315 public:
316 BpfMapRO<Key, Value>() {};
317
318 explicit BpfMapRO<Key, Value>(const char* pathname)
319 : BpfMap<Key, Value>(pathname, BPF_F_RDONLY) {}
320
321 // Function that tries to get map from a pinned path.
init(const char * path)322 [[clang::reinitializes]] base::Result<void> init(const char* path) {
323 return BpfMap<Key,Value>::init(path, mapRetrieveRO(path));
324 }
325 };
326
327 } // namespace bpf
328 } // namespace android
329