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