Home | History | Annotate | Download | only in bpf
      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