• 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-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