• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/log.h>
22 #include <android-base/result.h>
23 #include <android-base/stringprintf.h>
24 #include <android-base/unique_fd.h>
25 
26 #include "BpfSyscallWrappers.h"
27 #include "bpf/BpfUtils.h"
28 
29 #include <cstdio>
30 #include <functional>
31 
32 namespace android {
33 namespace bpf {
34 
35 using base::Result;
36 using base::unique_fd;
37 using std::function;
38 
39 #ifdef BPF_MAP_MAKE_VISIBLE_FOR_TESTING
40 #undef BPFMAP_VERBOSE_ABORT
41 #define BPFMAP_VERBOSE_ABORT
42 #endif
43 
44 [[noreturn]] __attribute__((__format__(__printf__, 2, 3))) static inline
Abort(int __unused error,const char * __unused fmt,...)45 void Abort(int __unused error, const char* __unused fmt, ...) {
46 #ifdef BPFMAP_VERBOSE_ABORT
47     va_list va;
48     va_start(va, fmt);
49 
50     fflush(stdout);
51     vfprintf(stderr, fmt, va);
52     if (error) fprintf(stderr, "; errno=%d [%s]", error, strerror(error));
53     putc('\n', stderr);
54     fflush(stderr);
55 
56     va_end(va);
57 #endif
58 
59     abort();
60 }
61 
62 
63 // This is a class wrapper for eBPF maps. The eBPF map is a special in-kernel
64 // data structure that stores data in <Key, Value> pairs. It can be read/write
65 // from userspace by passing syscalls with the map file descriptor. This class
66 // is used to generalize the procedure of interacting with eBPF maps and hide
67 // the implementation detail from other process. Besides the basic syscalls
68 // wrapper, it also provides some useful helper functions as well as an iterator
69 // nested class to iterate the map more easily.
70 //
71 // NOTE: A kernel eBPF map may be accessed by both kernel and userspace
72 // processes at the same time. Or if the map is pinned as a virtual file, it can
73 // be obtained by multiple eBPF map class object and accessed concurrently.
74 // Though the map class object and the underlying kernel map are thread safe, it
75 // is not safe to iterate over a map while another thread or process is deleting
76 // from it. In this case the iteration can return duplicate entries.
77 template <class Key, class Value>
78 class BpfMapRO {
79   public:
80     BpfMapRO<Key, Value>() {};
81 
82     // explicitly force no copy constructor, since it would need to dup the fd
83     // (later on, for testing, we still make available a copy assignment operator)
84     BpfMapRO<Key, Value>(const BpfMapRO<Key, Value>&) = delete;
85 
86   protected:
abortOnMismatch(bool writable)87     void abortOnMismatch(bool writable) const {
88         if (!mMapFd.ok()) Abort(errno, "mMapFd %d is not valid", mMapFd.get());
89         if (isAtLeastKernelVersion(4, 14, 0)) {
90             int flags = bpfGetFdMapFlags(mMapFd);
91             if (flags < 0) Abort(errno, "bpfGetFdMapFlags fail: flags=%d", flags);
92             if (flags & BPF_F_WRONLY) Abort(0, "map is write-only (flags=0x%X)", flags);
93             if (writable && (flags & BPF_F_RDONLY))
94                 Abort(0, "writable map is actually read-only (flags=0x%X)", flags);
95             int keySize = bpfGetFdKeySize(mMapFd);
96             if (keySize != sizeof(Key))
97                 Abort(errno, "map key size mismatch (expected=%zu, actual=%d)",
98                       sizeof(Key), keySize);
99             int valueSize = bpfGetFdValueSize(mMapFd);
100             if (valueSize != sizeof(Value))
101                 Abort(errno, "map value size mismatch (expected=%zu, actual=%d)",
102                       sizeof(Value), valueSize);
103         }
104     }
105 
106   public:
107     explicit BpfMapRO<Key, Value>(const char* pathname) {
108         mMapFd.reset(mapRetrieveRO(pathname));
109         abortOnMismatch(/* writable */ false);
110     }
111 
getFirstKey()112     Result<Key> getFirstKey() const {
113         Key firstKey;
114         if (getFirstMapKey(mMapFd, &firstKey)) {
115             return ErrnoErrorf("BpfMap::getFirstKey() failed");
116         }
117         return firstKey;
118     }
119 
getNextKey(const Key & key)120     Result<Key> getNextKey(const Key& key) const {
121         Key nextKey;
122         if (getNextMapKey(mMapFd, &key, &nextKey)) {
123             return ErrnoErrorf("BpfMap::getNextKey() failed");
124         }
125         return nextKey;
126     }
127 
readValue(const Key key)128     Result<Value> readValue(const Key key) const {
129         Value value;
130         if (findMapEntry(mMapFd, &key, &value)) {
131             return ErrnoErrorf("BpfMap::readValue() failed");
132         }
133         return value;
134     }
135 
136   protected:
init(const char * path,int fd,bool writable)137     [[clang::reinitializes]] Result<void> init(const char* path, int fd, bool writable) {
138         mMapFd.reset(fd);
139         if (!mMapFd.ok()) {
140             return ErrnoErrorf("Pinned map not accessible or does not exist: ({})", path);
141         }
142         // Normally we should return an error here instead of calling abort,
143         // but this cannot happen at runtime without a massive code bug (K/V type mismatch)
144         // and as such it's better to just blow the system up and let the developer fix it.
145         // Crashes are much more likely to be noticed than logs and missing functionality.
146         abortOnMismatch(writable);
147         return {};
148     }
149 
150   public:
151     // Function that tries to get map from a pinned path.
init(const char * path)152     [[clang::reinitializes]] Result<void> init(const char* path) {
153         return init(path, mapRetrieveRO(path), /* writable */ false);
154     }
155 
156     // Iterate through the map and handle each key retrieved based on the filter
157     // without modification of map content.
158     Result<void> iterate(
159             const function<Result<void>(const Key& key,
160                                         const BpfMapRO<Key, Value>& map)>& 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     Result<void> iterateWithValue(
165             const function<Result<void>(const Key& key, const Value& value,
166                                         const BpfMapRO<Key, Value>& map)>& filter) const;
167 
168 #ifdef BPF_MAP_MAKE_VISIBLE_FOR_TESTING
getMap()169     const unique_fd& getMap() const { return mMapFd; };
170 
171     // Copy assignment operator - due to need for fd duping, should not be used in non-test code.
172     BpfMapRO<Key, Value>& operator=(const BpfMapRO<Key, Value>& other) {
173         if (this != &other) mMapFd.reset(fcntl(other.mMapFd.get(), F_DUPFD_CLOEXEC, 0));
174         return *this;
175     }
176 #else
177     BpfMapRO<Key, Value>& operator=(const BpfMapRO<Key, Value>&) = delete;
178 #endif
179 
180     // Move assignment operator
181     BpfMapRO<Key, Value>& operator=(BpfMapRO<Key, Value>&& other) noexcept {
182         if (this != &other) {
183             mMapFd = std::move(other.mMapFd);
184             other.reset();
185         }
186         return *this;
187     }
188 
189 #ifdef BPF_MAP_MAKE_VISIBLE_FOR_TESTING
190     // Note that unique_fd.reset() carefully saves and restores the errno,
191     // and BpfMap.reset() won't touch the errno if passed in fd is negative either,
192     // hence you can do something like BpfMap.reset(systemcall()) and then
193     // check BpfMap.isValid() and look at errno and see why systemcall() failed.
reset(int fd)194     [[clang::reinitializes]] void reset(int fd) {
195         mMapFd.reset(fd);
196         if (mMapFd.ok()) abortOnMismatch(/* writable */ false);  // false isn't ideal
197     }
198 
199     // unique_fd has an implicit int conversion defined, which combined with the above
200     // reset(int) would result in double ownership of the fd, hence we either need a custom
201     // implementation of reset(unique_fd), or to delete it and thus cause compile failures
202     // to catch this and prevent it.
203     void reset(unique_fd fd) = delete;
204 #endif
205 
reset()206     [[clang::reinitializes]] void reset() {
207         mMapFd.reset();
208     }
209 
isValid()210     bool isValid() const { return mMapFd.ok(); }
211 
isEmpty()212     Result<bool> isEmpty() const {
213         auto key = getFirstKey();
214         if (key.ok()) return false;
215         if (key.error().code() == ENOENT) return true;
216         return key.error();
217     }
218 
219   protected:
220     unique_fd mMapFd;
221 };
222 
223 template <class Key, class Value>
iterate(const function<Result<void> (const Key & key,const BpfMapRO<Key,Value> & map)> & filter)224 Result<void> BpfMapRO<Key, Value>::iterate(
225         const function<Result<void>(const Key& key,
226                                     const BpfMapRO<Key, Value>& map)>& filter) const {
227     Result<Key> curKey = getFirstKey();
228     while (curKey.ok()) {
229         const Result<Key>& nextKey = getNextKey(curKey.value());
230         Result<void> status = filter(curKey.value(), *this);
231         if (!status.ok()) return status;
232         curKey = nextKey;
233     }
234     if (curKey.error().code() == ENOENT) return {};
235     return curKey.error();
236 }
237 
238 template <class Key, class Value>
iterateWithValue(const function<Result<void> (const Key & key,const Value & value,const BpfMapRO<Key,Value> & map)> & filter)239 Result<void> BpfMapRO<Key, Value>::iterateWithValue(
240         const function<Result<void>(const Key& key, const Value& value,
241                                     const BpfMapRO<Key, Value>& map)>& filter) const {
242     Result<Key> curKey = getFirstKey();
243     while (curKey.ok()) {
244         const Result<Key>& nextKey = getNextKey(curKey.value());
245         Result<Value> curValue = readValue(curKey.value());
246         if (!curValue.ok()) return curValue.error();
247         Result<void> status = filter(curKey.value(), curValue.value(), *this);
248         if (!status.ok()) return status;
249         curKey = nextKey;
250     }
251     if (curKey.error().code() == ENOENT) return {};
252     return curKey.error();
253 }
254 
255 template <class Key, class Value>
256 class BpfMap : public BpfMapRO<Key, Value> {
257   protected:
258     using BpfMapRO<Key, Value>::mMapFd;
259     using BpfMapRO<Key, Value>::abortOnMismatch;
260 
261   public:
262     using BpfMapRO<Key, Value>::getFirstKey;
263     using BpfMapRO<Key, Value>::getNextKey;
264     using BpfMapRO<Key, Value>::readValue;
265 
266     BpfMap<Key, Value>() {};
267 
268     explicit BpfMap<Key, Value>(const char* pathname) {
269         mMapFd.reset(mapRetrieveRW(pathname));
270         abortOnMismatch(/* writable */ true);
271     }
272 
273     // Function that tries to get map from a pinned path.
init(const char * path)274     [[clang::reinitializes]] Result<void> init(const char* path) {
275         return BpfMapRO<Key,Value>::init(path, mapRetrieveRW(path), /* writable */ true);
276     }
277 
writeValue(const Key & key,const Value & value,uint64_t flags)278     Result<void> writeValue(const Key& key, const Value& value, uint64_t flags) {
279         if (writeToMapEntry(mMapFd, &key, &value, flags)) {
280             return ErrnoErrorf("BpfMap::writeValue() failed");
281         }
282         return {};
283     }
284 
deleteValue(const Key & key)285     Result<void> deleteValue(const Key& key) {
286         if (deleteMapEntry(mMapFd, &key)) {
287             return ErrnoErrorf("BpfMap::deleteValue() failed");
288         }
289         return {};
290     }
291 
clear()292     Result<void> clear() {
293         while (true) {
294             auto key = getFirstKey();
295             if (!key.ok()) {
296                 if (key.error().code() == ENOENT) return {};  // empty: success
297                 return key.error();                           // Anything else is an error
298             }
299             auto res = deleteValue(key.value());
300             if (!res.ok()) {
301                 // Someone else could have deleted the key, so ignore ENOENT
302                 if (res.error().code() == ENOENT) continue;
303                 ALOGE("Failed to delete data %s", strerror(res.error().code()));
304                 return res.error();
305             }
306         }
307     }
308 
309 #ifdef BPF_MAP_MAKE_VISIBLE_FOR_TESTING
310     [[clang::reinitializes]] Result<void> resetMap(bpf_map_type map_type,
311                                                    uint32_t max_entries,
312                                                    uint32_t map_flags = 0) {
313         if (map_flags & BPF_F_WRONLY) Abort(0, "map_flags is write-only");
314         if (map_flags & BPF_F_RDONLY) Abort(0, "map_flags is read-only");
315         mMapFd.reset(createMap(map_type, sizeof(Key), sizeof(Value), max_entries,
316                                map_flags));
317         if (!mMapFd.ok()) return ErrnoErrorf("BpfMap::resetMap() failed");
318         abortOnMismatch(/* writable */ true);
319         return {};
320     }
321 #endif
322 
323     // Iterate through the map and handle each key retrieved based on the filter
324     // without modification of map content.
325     Result<void> iterate(
326             const function<Result<void>(const Key& key,
327                                         const BpfMap<Key, Value>& map)>& filter) const;
328 
329     // Iterate through the map and get each <key, value> pair, handle each <key,
330     // value> pair based on the filter without modification of map content.
331     Result<void> iterateWithValue(
332             const function<Result<void>(const Key& key, const Value& value,
333                                         const BpfMap<Key, Value>& map)>& filter) const;
334 
335     // Iterate through the map and handle each key retrieved based on the filter
336     Result<void> iterate(
337             const function<Result<void>(const Key& key,
338                                         BpfMap<Key, Value>& map)>& filter);
339 
340     // Iterate through the map and get each <key, value> pair, handle each <key,
341     // value> pair based on the filter.
342     Result<void> iterateWithValue(
343             const function<Result<void>(const Key& key, const Value& value,
344                                         BpfMap<Key, Value>& map)>& filter);
345 
346 };
347 
348 template <class Key, class Value>
iterate(const function<Result<void> (const Key & key,const BpfMap<Key,Value> & map)> & filter)349 Result<void> BpfMap<Key, Value>::iterate(
350         const function<Result<void>(const Key& key,
351                                     const BpfMap<Key, Value>& map)>& filter) const {
352     Result<Key> curKey = getFirstKey();
353     while (curKey.ok()) {
354         const Result<Key>& nextKey = getNextKey(curKey.value());
355         Result<void> status = filter(curKey.value(), *this);
356         if (!status.ok()) return status;
357         curKey = nextKey;
358     }
359     if (curKey.error().code() == ENOENT) return {};
360     return curKey.error();
361 }
362 
363 template <class Key, class Value>
iterateWithValue(const function<Result<void> (const Key & key,const Value & value,const BpfMap<Key,Value> & map)> & filter)364 Result<void> BpfMap<Key, Value>::iterateWithValue(
365         const function<Result<void>(const Key& key, const Value& value,
366                                     const BpfMap<Key, Value>& map)>& filter) const {
367     Result<Key> curKey = getFirstKey();
368     while (curKey.ok()) {
369         const Result<Key>& nextKey = getNextKey(curKey.value());
370         Result<Value> curValue = readValue(curKey.value());
371         if (!curValue.ok()) return curValue.error();
372         Result<void> status = filter(curKey.value(), curValue.value(), *this);
373         if (!status.ok()) return status;
374         curKey = nextKey;
375     }
376     if (curKey.error().code() == ENOENT) return {};
377     return curKey.error();
378 }
379 
380 template <class Key, class Value>
iterate(const function<Result<void> (const Key & key,BpfMap<Key,Value> & map)> & filter)381 Result<void> BpfMap<Key, Value>::iterate(
382         const function<Result<void>(const Key& key,
383                                     BpfMap<Key, Value>& map)>& filter) {
384     Result<Key> curKey = getFirstKey();
385     while (curKey.ok()) {
386         const Result<Key>& nextKey = getNextKey(curKey.value());
387         Result<void> status = filter(curKey.value(), *this);
388         if (!status.ok()) return status;
389         curKey = nextKey;
390     }
391     if (curKey.error().code() == ENOENT) return {};
392     return curKey.error();
393 }
394 
395 template <class Key, class Value>
iterateWithValue(const function<Result<void> (const Key & key,const Value & value,BpfMap<Key,Value> & map)> & filter)396 Result<void> BpfMap<Key, Value>::iterateWithValue(
397         const function<Result<void>(const Key& key, const Value& value,
398                                     BpfMap<Key, Value>& map)>& filter) {
399     Result<Key> curKey = getFirstKey();
400     while (curKey.ok()) {
401         const Result<Key>& nextKey = getNextKey(curKey.value());
402         Result<Value> curValue = readValue(curKey.value());
403         if (!curValue.ok()) return curValue.error();
404         Result<void> status = filter(curKey.value(), curValue.value(), *this);
405         if (!status.ok()) return status;
406         curKey = nextKey;
407     }
408     if (curKey.error().code() == ENOENT) return {};
409     return curKey.error();
410 }
411 
412 }  // namespace bpf
413 }  // namespace android
414