1 /* 2 * Copyright (C) 2020 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 <android-base/logging.h> 20 #include <libnl++/Buffer.h> 21 #include <libnl++/types.h> 22 #include <utils/Mutex.h> 23 24 #include <map> 25 26 namespace android::nl { 27 28 /** 29 * Netlink attribute map. 30 * 31 * This is a C++-style, memory safe(r) implementation of linux/netlink.h macros accessing Netlink 32 * message attributes. The class doesn't own the underlying data, so the instance is valid as long 33 * as the source buffer is allocated and unmodified. 34 * 35 * WARNING: this class is NOT thread-safe (it's safe to be used in multithreaded application, but 36 * a single instance can only be used by a single thread - the one owning the underlying buffer). 37 */ 38 class Attributes : private Buffer<nlattr> { 39 public: 40 /** 41 * Constructs empty attribute map. 42 */ 43 Attributes(); 44 45 /** 46 * Construct attribute map from underlying buffer. 47 * 48 * \param buffer Source buffer pointing at the first attribute. 49 */ 50 Attributes(Buffer<nlattr> buffer); 51 52 /** 53 * Checks, if the map contains given attribute type (key). 54 * 55 * \param attrtype Attribute type (such as IFLA_IFNAME). 56 * \return true if attribute is in the map, false otherwise. 57 */ 58 bool contains(nlattrtype_t attrtype) const; 59 60 /** 61 * Fetches attribute of a given type by copying it. 62 * 63 * While this is quite efficient for simple types, fetching nested attribute creates a new copy 64 * of child attribute map. This may be costly if you calculate the index for child maps multiple 65 * times. Examples below. 66 * 67 * BAD: 68 * ``` 69 * const auto flags = msg->attributes. 70 * get<nl::Attributes>(IFLA_AF_SPEC). 71 * get<nl::Attributes>(AF_INET6). // IFLA_AF_SPEC index lazy-calculated 72 * get<uint32_t>(IFLA_INET6_FLAGS); // AF_INET6 index lazy-calculated 73 * const auto& cacheinfo = msg->attributes. 74 * get<nl::Attributes>(IFLA_AF_SPEC). // new instance of IFLA_AF_SPEC index 75 * get<nl::Attributes>(AF_INET6). // IFLA_AF_SPEC index calculated again 76 * getStruct<ifla_cacheinfo>(IFLA_INET6_CACHEINFO); // AF_INET6 calculated again 77 * ``` 78 * 79 * GOOD: 80 * ``` 81 * const auto inet6 = msg->attributes. 82 * get<nl::Attributes>(IFLA_AF_SPEC). 83 * get<nl::Attributes>(AF_INET6); 84 * const auto flags = inet6.get<uint32_t>(IFLA_INET6_FLAGS); // AF_INET6 index lazy-calculated 85 * const auto& cache = inet6.getStruct<ifla_cacheinfo>(IFLA_INET6_CACHEINFO); // index reused 86 * ``` 87 * 88 * If the attribute doesn't exists, default value of a given type is returned and warning 89 * spawned into the log. To check for attribute existence, \see contains(nlattrtype_t). 90 * 91 * \param attrtype Attribute to fetch. 92 * \return Attribute value. 93 */ 94 template <typename T> get(nlattrtype_t attrtype)95 T get(nlattrtype_t attrtype) const { 96 const auto buffer = getBuffer(attrtype); 97 if (!buffer.has_value()) { 98 LOG(WARNING) << "Netlink attribute is missing: " << attrtype; 99 return T{}; 100 } 101 102 return parse<T>(*buffer); 103 } 104 105 /** 106 * Fetches underlying buffer of a given attribute. 107 * 108 * This is a low-level access method unlikely to be useful in most cases. Please consider 109 * using #get instead. 110 * 111 * \param attrtype Attribute to fetch 112 * \return Attribute buffer. 113 */ getBuffer(nlattrtype_t attrtype)114 std::optional<Buffer<nlattr>> getBuffer(nlattrtype_t attrtype) const { 115 const auto& ind = index(); 116 const auto it = ind.find(attrtype); 117 if (it == ind.end()) return std::nullopt; 118 return it->second; 119 } 120 121 /** 122 * Fetches a reference to a given attribute's data. 123 * 124 * This method is intended for arbitrary structures not specialized with get(nlattrtype_t) 125 * template and slightly more efficient for larger payloads due to not copying its data. 126 * 127 * If the attribute doesn't exists, a reference to empty value of a given type is returned and 128 * warning spawned into the log. To check for attribute existence, \see contains(nlattrtype_t). 129 * 130 * \param attrtype Attribute to fetch. 131 * \return Reference to the attribute's data. 132 */ 133 template <typename T> getStruct(nlattrtype_t attrtype)134 const T& getStruct(nlattrtype_t attrtype) const { 135 const auto& ind = index(); 136 const auto it = ind.find(attrtype); 137 if (it == ind.end()) { 138 LOG(WARNING) << "Netlink attribute is missing: " << attrtype; 139 static const T empty = {}; 140 return empty; 141 } 142 143 const auto& [ok, val] = it->second.data<T>().getFirst(); 144 if (!ok) LOG(WARNING) << "Can't fetch structure of size " << sizeof(T); 145 return val; 146 } 147 148 private: 149 using Index = std::map<nlattrtype_t, Buffer<nlattr>>; 150 151 /** 152 * Attribute index. 153 * 154 * Since this field is not protected by mutex, the use of \see index() dependent methods 155 * (such as \see get(nlattrtype_t)) is not thread-safe. This is a compromise made based on the 156 * following assumptions: 157 * 158 * 1. Most (or even all) use-cases involve attribute parsing in the same thread as where the 159 * buffer was allocated. This is partly forced by a dependence of nlmsg lifecycle on the 160 * underlying data buffer. 161 * 162 * 2. Index calculation and access would come with performance penalty never justified in most 163 * or all use cases (see the previous point). Since Index is not a trivially assignable data 164 * structure, it's not possible to use it with atomic types only and avoid mutexes. 165 */ 166 mutable std::optional<Index> mIndex; 167 168 /** 169 * Lazy-calculate and cache index. 170 * 171 * \return Attribute index. 172 */ 173 const Index& index() const; 174 175 /** 176 * Parse attribute data into a specific type. 177 * 178 * \param buf Raw attribute data. 179 * \return Parsed data. 180 */ 181 template <typename T> 182 static T parse(Buffer<nlattr> buf); 183 }; 184 185 } // namespace android::nl 186