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: 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 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 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){}; 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 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 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 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 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 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 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 193 const base::unique_fd& getMap() const { return mMapFd; }; 194 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 213 bool isValid() const { return mMapFd != -1; } 214 215 const_iterator begin() const { return const_iterator(this); } 216 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> 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> 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> 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> 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> 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