Implement nlmsg<T>

Bug: 162032964
Test: custom code to parse RTM_NEWLINK messages
Change-Id: I1ee4fb65d5b555c7665b7c42e6810efce7c726b6
diff --git a/automotive/can/1.0/default/libnl++/include/libnl++/nlmsg.h b/automotive/can/1.0/default/libnl++/include/libnl++/nlmsg.h
new file mode 100644
index 0000000..d6a797b
--- /dev/null
+++ b/automotive/can/1.0/default/libnl++/include/libnl++/nlmsg.h
@@ -0,0 +1,94 @@
+/*
+ * 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 <libnl++/nlbuf.h>
+
+namespace android::nl {
+
+/**
+ * In-place Netlink message parser.
+ *
+ * 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.
+ */
+template <typename T>
+class nlmsg {
+  public:
+    /**
+     * Validate buffer contents as a message carrying T data and create instance of nlmsg.
+     *
+     * \param buf Buffer containing the message.
+     * \return Parsed message or nullopt, if the buffer data is invalid.
+     */
+    static std::optional<nlmsg<T>> parse(nlbuf<nlmsghdr> buf) {
+        const auto& [nlOk, nlHeader] = buf.getFirst();
+        if (!nlOk) return std::nullopt;
+
+        const auto& [dataOk, dataHeader] = buf.data<T>().getFirst();
+        if (!dataOk) return std::nullopt;
+
+        const auto attributes = buf.data<nlattr>(sizeof(T));
+
+        return nlmsg<T>(nlHeader, dataHeader, attributes);
+    }
+
+    /**
+     * Validate buffer contents as a message of a given type and create instance of nlmsg.
+     *
+     * \param buf Buffer containing the message.
+     * \param msgtypes Acceptable message types (within a specific Netlink protocol)
+     * \return Parsed message or nullopt, if the buffer data is invalid or message type
+     *         doesn't match.
+     */
+    static std::optional<nlmsg<T>> parse(nlbuf<nlmsghdr> buf, std::set<nlmsgtype_t> msgtypes) {
+        const auto& [nlOk, nlHeader] = buf.getFirst();  // we're doing it twice, but it's fine
+        if (!nlOk) return std::nullopt;
+
+        if (msgtypes.count(nlHeader.nlmsg_type) == 0) return std::nullopt;
+
+        return parse(buf);
+    }
+
+    /**
+     * Netlink message header.
+     *
+     * This is a generic Netlink header containing information such as message flags.
+     */
+    const nlmsghdr& header;
+
+    /**
+     * Netlink message data.
+     *
+     * This is a payload specific to a given message type.
+     */
+    const T& data;
+
+    /**
+     * Netlink message attributes.
+     */
+    const nlbuf<nlattr> attributes;
+
+    const T* operator->() const { return &data; }
+
+  private:
+    nlmsg(const nlmsghdr& nlHeader, const T& dataHeader, nlbuf<nlattr> attributes)
+        : header(nlHeader), data(dataHeader), attributes(attributes) {}
+};
+
+}  // namespace android::nl