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