Implement attribute map.
Bug: 162032964
Test: custom code to parse RTM_NEWLINK messages
Change-Id: Ib07b0a4e553307e2ddaf7359e25f332660a29aec
diff --git a/automotive/can/1.0/default/libnl++/Android.bp b/automotive/can/1.0/default/libnl++/Android.bp
index cffefe7..c4eb805 100644
--- a/automotive/can/1.0/default/libnl++/Android.bp
+++ b/automotive/can/1.0/default/libnl++/Android.bp
@@ -31,6 +31,7 @@
"protocols/MessageDefinition.cpp",
"protocols/NetlinkProtocol.cpp",
"protocols/all.cpp",
+ "Attributes.cpp",
"NetlinkRequest.cpp",
"NetlinkSocket.cpp",
"common.cpp",
diff --git a/automotive/can/1.0/default/libnl++/Attributes.cpp b/automotive/can/1.0/default/libnl++/Attributes.cpp
new file mode 100644
index 0000000..0f3c7f8
--- /dev/null
+++ b/automotive/can/1.0/default/libnl++/Attributes.cpp
@@ -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
diff --git a/automotive/can/1.0/default/libnl++/include/libnl++/Attributes.h b/automotive/can/1.0/default/libnl++/include/libnl++/Attributes.h
new file mode 100644
index 0000000..082b97a
--- /dev/null
+++ b/automotive/can/1.0/default/libnl++/include/libnl++/Attributes.h
@@ -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
diff --git a/automotive/can/1.0/default/libnl++/include/libnl++/nlbuf.h b/automotive/can/1.0/default/libnl++/include/libnl++/nlbuf.h
index 4c0e581..7625020 100644
--- a/automotive/can/1.0/default/libnl++/include/libnl++/nlbuf.h
+++ b/automotive/can/1.0/default/libnl++/include/libnl++/nlbuf.h
@@ -54,6 +54,11 @@
public:
/**
+ * Constructs empty buffer of size 0.
+ */
+ nlbuf() : mData(nullptr), mBufferEnd(nullptr) {}
+
+ /**
* Constructor for nlbuf.
*
* \param data A pointer to the data the nlbuf wraps.
@@ -68,8 +73,8 @@
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 @@
* 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 = {};
diff --git a/automotive/can/1.0/default/libnl++/include/libnl++/nlmsg.h b/automotive/can/1.0/default/libnl++/include/libnl++/nlmsg.h
index d6a797b..e9c9238 100644
--- a/automotive/can/1.0/default/libnl++/include/libnl++/nlmsg.h
+++ b/automotive/can/1.0/default/libnl++/include/libnl++/nlmsg.h
@@ -16,6 +16,7 @@
#pragma once
+#include <libnl++/Attributes.h>
#include <libnl++/nlbuf.h>
namespace android::nl {
@@ -26,6 +27,9 @@
* 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 @@
/**
* 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) {}
};