• 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 #ifndef BPF_BPFMAP_H
18 #define BPF_BPFMAP_H
19 
20 #include <linux/bpf.h>
21 
22 #include <android-base/stringprintf.h>
23 #include <android-base/unique_fd.h>
24 #include <utils/Log.h>
25 #include "bpf/BpfUtils.h"
26 #include "netdutils/Status.h"
27 #include "netdutils/StatusOr.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 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     class const_iterator {
50       public:
start()51         netdutils::Status start() {
52             if (mMap == nullptr) {
53                 return netdutils::statusFromErrno(EINVAL, "Invalid map iterator");
54             }
55             auto firstKey = mMap->getFirstKey();
56             if (isOk(firstKey)) {
57                 mCurKey = firstKey.value();
58             } else if (firstKey.status().code() == ENOENT) {
59                 // The map is empty.
60                 mMap = nullptr;
61                 memset(&mCurKey, 0, sizeof(Key));
62             } else {
63                 return firstKey.status();
64             }
65             return netdutils::status::ok;
66         }
67 
next()68         netdutils::StatusOr<Key> next() {
69             if (mMap == nullptr) {
70                 return netdutils::statusFromErrno(ENOENT, "Iterating past end of map");
71             }
72             auto nextKey = mMap->getNextKey(mCurKey);
73             if (isOk(nextKey)) {
74                 mCurKey = nextKey.value();
75             } else if (nextKey.status().code() == ENOENT) {
76                 // iterator reached the end of map
77                 mMap = nullptr;
78                 memset(&mCurKey, 0, sizeof(Key));
79             } else {
80                 return nextKey.status();
81             }
82             return mCurKey;
83         }
84 
85         const Key operator*() { return mCurKey; }
86 
87         bool operator==(const const_iterator& other) const {
88             return (mMap == other.mMap) && (mCurKey == other.mCurKey);
89         }
90 
91         bool operator!=(const const_iterator& other) const { return !(*this == other); }
92 
const_iterator(const BpfMap<Key,Value> * map)93         const_iterator(const BpfMap<Key, Value>* map) : mMap(map) {
94             memset(&mCurKey, 0, sizeof(Key));
95         }
96 
97       private:
98         const BpfMap<Key, Value> * mMap;
99         Key mCurKey;
100     };
101 
102     BpfMap<Key, Value>() : mMapFd(-1){};
mMapFd(fd)103     BpfMap<Key, Value>(int fd) : mMapFd(fd){};
104     BpfMap<Key, Value>(bpf_map_type map_type, uint32_t max_entries, uint32_t map_flags) {
105         int map_fd = createMap(map_type, sizeof(Key), sizeof(Value), max_entries, map_flags);
106         if (map_fd < 0) {
107             mMapFd.reset(-1);
108         } else {
109             mMapFd.reset(map_fd);
110         }
111     }
112 
pinToPath(const std::string path)113     netdutils::Status pinToPath(const std::string path) {
114         int ret = mapPin(mMapFd, path.c_str());
115         if (ret) {
116             return netdutils::statusFromErrno(errno,
117                                               base::StringPrintf("pin to %s failed", path.c_str()));
118         }
119         mPinnedPath = path;
120         return netdutils::status::ok;
121     }
122 
getFirstKey()123     netdutils::StatusOr<Key> getFirstKey() const {
124         Key firstKey;
125         if (getFirstMapKey(mMapFd, &firstKey)) {
126             return netdutils::statusFromErrno(
127                 errno, base::StringPrintf("Get firstKey map %d failed", mMapFd.get()));
128         }
129         return firstKey;
130     }
131 
getNextKey(const Key & key)132     netdutils::StatusOr<Key> getNextKey(const Key& key) const {
133         Key nextKey;
134         if (getNextMapKey(mMapFd, const_cast<Key*>(&key), &nextKey)) {
135             return netdutils::statusFromErrno(
136                 errno, base::StringPrintf("Get next key of map %d failed", mMapFd.get()));
137         }
138         return nextKey;
139     }
140 
writeValue(const Key & key,const Value & value,uint64_t flags)141     netdutils::Status writeValue(const Key& key, const Value& value, uint64_t flags) {
142         if (writeToMapEntry(mMapFd, const_cast<Key*>(&key), const_cast<Value*>(&value), flags)) {
143             return netdutils::statusFromErrno(
144                 errno, base::StringPrintf("write to map %d failed", mMapFd.get()));
145         }
146         return netdutils::status::ok;
147     }
148 
readValue(const Key key)149     netdutils::StatusOr<Value> readValue(const Key key) const {
150         Value value;
151         if (findMapEntry(mMapFd, const_cast<Key*>(&key), &value)) {
152             return netdutils::statusFromErrno(
153                 errno, base::StringPrintf("read value of map %d failed", mMapFd.get()));
154         }
155         return value;
156     }
157 
deleteValue(const Key & key)158     netdutils::Status deleteValue(const Key& key) {
159         if (deleteMapEntry(mMapFd, const_cast<Key*>(&key))) {
160             return netdutils::statusFromErrno(
161                 errno, base::StringPrintf("delete entry from map %d failed", mMapFd.get()));
162         }
163         return netdutils::status::ok;
164     }
165 
166     // Function that tries to get map from a pinned path, if the map doesn't
167     // exist yet, create a new one and pinned to the path.
168     netdutils::Status getOrCreate(const uint32_t maxEntries, const char* path,
169                                   const bpf_map_type mapType);
170 
171     // Iterate through the map and handle each key retrieved based on the filter
172     // without modification of map content.
173     netdutils::Status iterate(
174         const std::function<netdutils::Status(const Key& key, const BpfMap<Key, Value>& map)>&
175             filter) const;
176 
177     // Iterate through the map and get each <key, value> pair, handle each <key,
178     // value> pair based on the filter without modification of map content.
179     netdutils::Status iterateWithValue(
180         const std::function<netdutils::Status(const Key& key, const Value& value,
181                                               const BpfMap<Key, Value>& map)>& filter) const;
182 
183     // Iterate through the map and handle each key retrieved based on the filter
184     netdutils::Status iterate(
185         const std::function<netdutils::Status(const Key& key, BpfMap<Key, Value>& map)>& filter);
186 
187     // Iterate through the map and get each <key, value> pair, handle each <key,
188     // value> pair based on the filter.
189     netdutils::Status iterateWithValue(
190         const std::function<netdutils::Status(const Key& key, const Value& value,
191                                               BpfMap<Key, Value>& map)>& filter);
192 
getMap()193     const base::unique_fd& getMap() const { return mMapFd; };
194 
getPinnedPath()195     const std::string getPinnedPath() const { return mPinnedPath; };
196 
197     // Move constructor
198     void operator=(BpfMap<Key, Value>&& other) {
199         mMapFd = std::move(other.mMapFd);
200         if (!other.mPinnedPath.empty()) {
201             mPinnedPath = other.mPinnedPath;
202         } else {
203             mPinnedPath.clear();
204         }
205         other.reset();
206     }
207 
208     void reset(int fd = -1) {
209         mMapFd.reset(fd);
210         mPinnedPath.clear();
211     }
212 
isValid()213     bool isValid() const { return mMapFd != -1; }
214 
begin()215     const_iterator begin() const { return const_iterator(this); }
216 
end()217     const_iterator end() const { return const_iterator(nullptr); }
218 
219   private:
220     base::unique_fd mMapFd;
221     std::string mPinnedPath;
222 };
223 
224 template <class Key, class Value>
getOrCreate(const uint32_t maxEntries,const char * path,bpf_map_type mapType)225 netdutils::Status BpfMap<Key, Value>::getOrCreate(const uint32_t maxEntries, const char* path,
226                                                   bpf_map_type mapType) {
227     int ret = access(path, R_OK);
228     /* Check the pinned location first to check if the map is already there.
229      * otherwise create a new one.
230      */
231     if (ret == 0) {
232         mMapFd = base::unique_fd(mapRetrieve(path, 0));
233         if (mMapFd == -1) {
234             reset();
235             return netdutils::statusFromErrno(
236                 errno,
237                 base::StringPrintf("pinned map not accessible or does not exist: (%s)\n", path));
238         }
239         mPinnedPath = path;
240     } else if (ret == -1 && errno == ENOENT) {
241         mMapFd = base::unique_fd(
242             createMap(mapType, sizeof(Key), sizeof(Value), maxEntries, BPF_F_NO_PREALLOC));
243         if (mMapFd == -1) {
244             reset();
245             return netdutils::statusFromErrno(errno,
246                                               base::StringPrintf("map create failed!: %s", path));
247         }
248         netdutils::Status pinStatus = pinToPath(path);
249         if (!isOk(pinStatus)) {
250             reset();
251             return pinStatus;
252         }
253         mPinnedPath = path;
254     } else {
255         return netdutils::statusFromErrno(
256             errno, base::StringPrintf("pinned map not accessible: %s", path));
257     }
258     return netdutils::status::ok;
259 }
260 
261 template <class Key, class Value>
iterate(const std::function<netdutils::Status (const Key & key,const BpfMap<Key,Value> & map)> & filter)262 netdutils::Status BpfMap<Key, Value>::iterate(
263     const std::function<netdutils::Status(const Key& key, const BpfMap<Key, Value>& map)>& filter)
264     const {
265     const_iterator itr = this->begin();
266     RETURN_IF_NOT_OK(itr.start());
267     while (itr != this->end()) {
268         Key prevKey = *itr;
269         netdutils::Status advanceStatus = itr.next();
270         RETURN_IF_NOT_OK(filter(prevKey, *this));
271         RETURN_IF_NOT_OK(advanceStatus);
272     }
273     return netdutils::status::ok;
274 }
275 
276 template <class Key, class Value>
iterateWithValue(const std::function<netdutils::Status (const Key & key,const Value & value,const BpfMap<Key,Value> & map)> & filter)277 netdutils::Status BpfMap<Key, Value>::iterateWithValue(
278     const std::function<netdutils::Status(const Key& key, const Value& value,
279                                           const BpfMap<Key, Value>& map)>& filter) const {
280     const_iterator itr = this->begin();
281     RETURN_IF_NOT_OK(itr.start());
282     while (itr != this->end()) {
283         Key prevKey = *itr;
284         Value prevValue;
285         ASSIGN_OR_RETURN(prevValue, this->readValue(prevKey));
286         netdutils::Status advanceStatus = itr.next();
287         RETURN_IF_NOT_OK(filter(prevKey, prevValue, *this));
288         RETURN_IF_NOT_OK(advanceStatus);
289     }
290     return netdutils::status::ok;
291 }
292 
293 template <class Key, class Value>
iterate(const std::function<netdutils::Status (const Key & key,BpfMap<Key,Value> & map)> & filter)294 netdutils::Status BpfMap<Key, Value>::iterate(
295     const std::function<netdutils::Status(const Key& key, BpfMap<Key, Value>& map)>& filter) {
296     const_iterator itr = this->begin();
297     RETURN_IF_NOT_OK(itr.start());
298     while (itr != this->end()) {
299         Key prevKey = *itr;
300         netdutils::Status advanceStatus = itr.next();
301         RETURN_IF_NOT_OK(filter(prevKey, *this));
302         RETURN_IF_NOT_OK(advanceStatus);
303     }
304     return netdutils::status::ok;
305 }
306 
307 template <class Key, class Value>
iterateWithValue(const std::function<netdutils::Status (const Key & key,const Value & value,BpfMap<Key,Value> & map)> & filter)308 netdutils::Status BpfMap<Key, Value>::iterateWithValue(
309     const std::function<netdutils::Status(const Key& key, const Value& value,
310                                           BpfMap<Key, Value>& map)>& filter) {
311     const_iterator itr = this->begin();
312     RETURN_IF_NOT_OK(itr.start());
313     while (itr != this->end()) {
314         Key prevKey = *itr;
315         Value prevValue;
316         ASSIGN_OR_RETURN(prevValue, this->readValue(prevKey));
317         netdutils::Status advanceStatus = itr.next();
318         RETURN_IF_NOT_OK(filter(prevKey, prevValue, *this));
319         RETURN_IF_NOT_OK(advanceStatus);
320     }
321     return netdutils::status::ok;
322 }
323 
324 }  // namespace bpf
325 }  // namespace android
326 
327 #endif
328