Implement attribute map.
Bug: 162032964 Test: custom code to parse RTM_NEWLINK messages Change-Id: Ib07b0a4e553307e2ddaf7359e25f332660a29aec
This commit is contained in:
parent
47915045da
commit
34eb83aef2
5 changed files with 266 additions and 5 deletions
|
@ -31,6 +31,7 @@ cc_library_static {
|
|||
"protocols/MessageDefinition.cpp",
|
||||
"protocols/NetlinkProtocol.cpp",
|
||||
"protocols/all.cpp",
|
||||
"Attributes.cpp",
|
||||
"NetlinkRequest.cpp",
|
||||
"NetlinkSocket.cpp",
|
||||
"common.cpp",
|
||||
|
|
80
automotive/can/1.0/default/libnl++/Attributes.cpp
Normal file
80
automotive/can/1.0/default/libnl++/Attributes.cpp
Normal file
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <libnl++/Attributes.h>
|
||||
|
||||
namespace android::nl {
|
||||
|
||||
Attributes::Attributes() {}
|
||||
|
||||
Attributes::Attributes(nlbuf<nlattr> buffer) : nlbuf<nlattr>(buffer) {}
|
||||
|
||||
const Attributes::Index& Attributes::index() const {
|
||||
if (mIndex.has_value()) return *mIndex;
|
||||
|
||||
mIndex = Index();
|
||||
auto& index = *mIndex;
|
||||
|
||||
for (auto attr : static_cast<nlbuf<nlattr>>(*this)) {
|
||||
index.emplace(attr->nla_type, attr);
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
bool Attributes::contains(nlattrtype_t attrtype) const {
|
||||
return index().count(attrtype) > 0;
|
||||
}
|
||||
|
||||
/* Parser specializations for selected types (more to come if necessary). */
|
||||
|
||||
template <>
|
||||
Attributes Attributes::parse(nlbuf<nlattr> buf) {
|
||||
return buf.data<nlattr>();
|
||||
}
|
||||
|
||||
template <>
|
||||
std::string Attributes::parse(nlbuf<nlattr> buf) {
|
||||
const auto rawString = buf.data<char>().getRaw();
|
||||
return std::string(rawString.ptr(), rawString.len());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static T parseUnsigned(nlbuf<nlattr> buf) {
|
||||
return buf.data<T>().copyFirst();
|
||||
}
|
||||
|
||||
template <>
|
||||
uint8_t Attributes::parse(nlbuf<nlattr> buf) {
|
||||
return parseUnsigned<uint8_t>(buf);
|
||||
}
|
||||
|
||||
template <>
|
||||
uint16_t Attributes::parse(nlbuf<nlattr> buf) {
|
||||
return parseUnsigned<uint16_t>(buf);
|
||||
}
|
||||
|
||||
template <>
|
||||
uint32_t Attributes::parse(nlbuf<nlattr> buf) {
|
||||
return parseUnsigned<uint32_t>(buf);
|
||||
}
|
||||
|
||||
template <>
|
||||
uint64_t Attributes::parse(nlbuf<nlattr> buf) {
|
||||
return parseUnsigned<uint64_t>(buf);
|
||||
}
|
||||
|
||||
} // namespace android::nl
|
170
automotive/can/1.0/default/libnl++/include/libnl++/Attributes.h
Normal file
170
automotive/can/1.0/default/libnl++/include/libnl++/Attributes.h
Normal file
|
@ -0,0 +1,170 @@
|
|||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <libnl++/nlbuf.h>
|
||||
#include <libnl++/types.h>
|
||||
#include <utils/Mutex.h>
|
||||
|
||||
#include <map>
|
||||
|
||||
namespace android::nl {
|
||||
|
||||
/**
|
||||
* Netlink attribute map.
|
||||
*
|
||||
* This is a C++-style, memory safe(r) implementation of linux/netlink.h macros accessing Netlink
|
||||
* message attributes. The class doesn't own the underlying data, so the instance is valid as long
|
||||
* as the source buffer is allocated and unmodified.
|
||||
*
|
||||
* WARNING: this class is NOT thread-safe (it's safe to be used in multithreaded application, but
|
||||
* a single instance can only be used by a single thread - the one owning the underlying buffer).
|
||||
*/
|
||||
class Attributes : private nlbuf<nlattr> {
|
||||
public:
|
||||
/**
|
||||
* Constructs empty attribute map.
|
||||
*/
|
||||
Attributes();
|
||||
|
||||
/**
|
||||
* Construct attribute map from underlying buffer.
|
||||
*
|
||||
* \param buffer Source buffer pointing at the first attribute.
|
||||
*/
|
||||
Attributes(nlbuf<nlattr> buffer);
|
||||
|
||||
/**
|
||||
* Checks, if the map contains given attribute type (key).
|
||||
*
|
||||
* \param attrtype Attribute type (such as IFLA_IFNAME).
|
||||
* \return true if attribute is in the map, false otherwise.
|
||||
*/
|
||||
bool contains(nlattrtype_t attrtype) const;
|
||||
|
||||
/**
|
||||
* Fetches attribute of a given type by copying it.
|
||||
*
|
||||
* While this is quite efficient for simple types, fetching nested attribute creates a new copy
|
||||
* of child attribute map. This may be costly if you calculate the index for child maps multiple
|
||||
* times. Examples below.
|
||||
*
|
||||
* BAD:
|
||||
* ```
|
||||
* const auto flags = msg->attributes.
|
||||
* get<nl::Attributes>(IFLA_AF_SPEC).
|
||||
* get<nl::Attributes>(AF_INET6). // IFLA_AF_SPEC index lazy-calculated
|
||||
* get<uint32_t>(IFLA_INET6_FLAGS); // AF_INET6 index lazy-calculated
|
||||
* const auto& cacheinfo = msg->attributes.
|
||||
* get<nl::Attributes>(IFLA_AF_SPEC). // new instance of IFLA_AF_SPEC index
|
||||
* get<nl::Attributes>(AF_INET6). // IFLA_AF_SPEC index calculated again
|
||||
* getStruct<ifla_cacheinfo>(IFLA_INET6_CACHEINFO); // AF_INET6 calculated again
|
||||
* ```
|
||||
*
|
||||
* GOOD:
|
||||
* ```
|
||||
* const auto inet6 = msg->attributes.
|
||||
* get<nl::Attributes>(IFLA_AF_SPEC).
|
||||
* get<nl::Attributes>(AF_INET6);
|
||||
* const auto flags = inet6.get<uint32_t>(IFLA_INET6_FLAGS); // AF_INET6 index lazy-calculated
|
||||
* const auto& cache = inet6.getStruct<ifla_cacheinfo>(IFLA_INET6_CACHEINFO); // index reused
|
||||
* ```
|
||||
*
|
||||
* If the attribute doesn't exists, default value of a given type is returned and warning
|
||||
* spawned into the log. To check for attribute existence, \see contains(nlattrtype_t).
|
||||
*
|
||||
* \param attrtype Attribute to fetch.
|
||||
* \return Attribute value.
|
||||
*/
|
||||
template <typename T>
|
||||
T get(nlattrtype_t attrtype) const {
|
||||
const auto& ind = index();
|
||||
const auto it = ind.find(attrtype);
|
||||
if (it == ind.end()) {
|
||||
LOG(WARNING) << "Netlink attribute is missing: " << attrtype;
|
||||
return T{};
|
||||
}
|
||||
|
||||
return parse<T>(it->second);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches a reference to a given attribute's data.
|
||||
*
|
||||
* This method is intended for arbitrary structures not specialized with get(nlattrtype_t)
|
||||
* template and slightly more efficient for larger payloads due to not copying its data.
|
||||
*
|
||||
* If the attribute doesn't exists, a reference to empty value of a given type is returned and
|
||||
* warning spawned into the log. To check for attribute existence, \see contains(nlattrtype_t).
|
||||
*
|
||||
* \param attrtype Attribute to fetch.
|
||||
* \return Reference to the attribute's data.
|
||||
*/
|
||||
template <typename T>
|
||||
const T& getStruct(nlattrtype_t attrtype) const {
|
||||
const auto& ind = index();
|
||||
const auto it = ind.find(attrtype);
|
||||
if (it == ind.end()) {
|
||||
LOG(WARNING) << "Netlink attribute is missing: " << attrtype;
|
||||
static const T empty = {};
|
||||
return empty;
|
||||
}
|
||||
|
||||
const auto& [ok, val] = it->second.data<T>().getFirst();
|
||||
if (!ok) LOG(WARNING) << "Can't fetch structure of size " << sizeof(T);
|
||||
return val;
|
||||
}
|
||||
|
||||
private:
|
||||
using Index = std::map<nlattrtype_t, nlbuf<nlattr>>;
|
||||
|
||||
/**
|
||||
* Attribute index.
|
||||
*
|
||||
* Since this field is not protected by mutex, the use of \see index() dependent methods
|
||||
* (such as \see get(nlattrtype_t)) is not thread-safe. This is a compromise made based on the
|
||||
* following assumptions:
|
||||
*
|
||||
* 1. Most (or even all) use-cases involve attribute parsing in the same thread as where the
|
||||
* buffer was allocated. This is partly forced by a dependence of nlmsg lifecycle on the
|
||||
* underlying data buffer.
|
||||
*
|
||||
* 2. Index calculation and access would come with performance penalty never justified in most
|
||||
* or all use cases (see the previous point). Since Index is not a trivially assignable data
|
||||
* structure, it's not possible to use it with atomic types only and avoid mutexes.
|
||||
*/
|
||||
mutable std::optional<Index> mIndex;
|
||||
|
||||
/**
|
||||
* Lazy-calculate and cache index.
|
||||
*
|
||||
* \return Attribute index.
|
||||
*/
|
||||
const Index& index() const;
|
||||
|
||||
/**
|
||||
* Parse attribute data into a specific type.
|
||||
*
|
||||
* \param buf Raw attribute data.
|
||||
* \return Parsed data.
|
||||
*/
|
||||
template <typename T>
|
||||
static T parse(nlbuf<nlattr> buf);
|
||||
};
|
||||
|
||||
} // namespace android::nl
|
|
@ -53,6 +53,11 @@ class nlbuf {
|
|||
static constexpr size_t hdrlen = align(sizeof(T));
|
||||
|
||||
public:
|
||||
/**
|
||||
* Constructs empty buffer of size 0.
|
||||
*/
|
||||
nlbuf() : mData(nullptr), mBufferEnd(nullptr) {}
|
||||
|
||||
/**
|
||||
* Constructor for nlbuf.
|
||||
*
|
||||
|
@ -68,8 +73,8 @@ class nlbuf {
|
|||
|
||||
std::pair<bool, const T&> getFirst() const {
|
||||
if (!ok()) {
|
||||
static const T dummy = {};
|
||||
return {false, dummy};
|
||||
static const T empty = {};
|
||||
return {false, empty};
|
||||
}
|
||||
return {true, *mData};
|
||||
}
|
||||
|
@ -78,7 +83,8 @@ class nlbuf {
|
|||
* Copy the first element of the buffer.
|
||||
*
|
||||
* This is a memory-safe cast operation, useful for reading e.g. uint32_t values
|
||||
* from 1-byte buffer.
|
||||
* from 1-byte buffer. If the buffer is smaller than the copied type, the rest is
|
||||
* padded with default constructor output (usually zeros).
|
||||
*/
|
||||
T copyFirst() const {
|
||||
T val = {};
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <libnl++/Attributes.h>
|
||||
#include <libnl++/nlbuf.h>
|
||||
|
||||
namespace android::nl {
|
||||
|
@ -26,6 +27,9 @@ namespace android::nl {
|
|||
* This is a C++-style, memory safe(r) implementation of linux/netlink.h macros accessing Netlink
|
||||
* message contents. The class doesn't own the underlying data, so the instance is valid as long as
|
||||
* the source buffer is allocated and unmodified.
|
||||
*
|
||||
* WARNING: this class is NOT thread-safe (it's safe to be used in multithreaded application, but
|
||||
* a single instance can only be used by a single thread - the one owning the underlying buffer).
|
||||
*/
|
||||
template <typename T>
|
||||
class nlmsg {
|
||||
|
@ -82,12 +86,12 @@ class nlmsg {
|
|||
/**
|
||||
* Netlink message attributes.
|
||||
*/
|
||||
const nlbuf<nlattr> attributes;
|
||||
const Attributes attributes;
|
||||
|
||||
const T* operator->() const { return &data; }
|
||||
|
||||
private:
|
||||
nlmsg(const nlmsghdr& nlHeader, const T& dataHeader, nlbuf<nlattr> attributes)
|
||||
nlmsg(const nlmsghdr& nlHeader, const T& dataHeader, Attributes attributes)
|
||||
: header(nlHeader), data(dataHeader), attributes(attributes) {}
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue