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 <libnl++/Attributes.h>
20 #include <libnl++/Buffer.h>
21 
22 #include <set>
23 
24 namespace android::nl {
25 
26 /**
27  * In-place Netlink message parser.
28  *
29  * This is a C++-style, memory safe(r) implementation of linux/netlink.h macros accessing Netlink
30  * message contents. The class doesn't own the underlying data, so the instance is valid as long as
31  * the source buffer is allocated and unmodified.
32  *
33  * WARNING: this class is NOT thread-safe (it's safe to be used in multithreaded application, but
34  * a single instance can only be used by a single thread - the one owning the underlying buffer).
35  */
36 template <typename T>
37 class Message {
38   public:
39     /**
40      * Validate buffer contents as a message carrying T data and create instance of parsed message.
41      *
42      * \param buf Buffer containing the message.
43      * \return Parsed message or nullopt, if the buffer data is invalid.
44      */
parse(Buffer<nlmsghdr> buf)45     static std::optional<Message<T>> parse(Buffer<nlmsghdr> buf) {
46         const auto& [nlOk, nlHeader] = buf.getFirst();
47         if (!nlOk) return std::nullopt;
48 
49         const auto& [dataOk, dataHeader] = buf.data<T>().getFirst();
50         if (!dataOk) return std::nullopt;
51 
52         const auto attributes = buf.data<nlattr>(sizeof(T));
53 
54         return Message<T>(nlHeader, dataHeader, attributes);
55     }
56 
57     /**
58      * Validate buffer contents as a message of a given type and create instance of parsed message.
59      *
60      * \param buf Buffer containing the message.
61      * \param msgtypes Acceptable message types (within a specific Netlink protocol)
62      * \return Parsed message or nullopt, if the buffer data is invalid or message type
63      *         doesn't match.
64      */
parse(Buffer<nlmsghdr> buf,const std::set<nlmsgtype_t> & msgtypes)65     static std::optional<Message<T>> parse(Buffer<nlmsghdr> buf,
66                                            const std::set<nlmsgtype_t>& msgtypes) {
67         const auto& [nlOk, nlHeader] = buf.getFirst();  // we're doing it twice, but it's fine
68         if (!nlOk) return std::nullopt;
69 
70         if (msgtypes.count(nlHeader.nlmsg_type) == 0) return std::nullopt;
71 
72         return parse(buf);
73     }
74 
75     /**
76      * Netlink message header.
77      *
78      * This is a generic Netlink header containing information such as message flags.
79      */
80     const nlmsghdr& header;
81 
82     /**
83      * Netlink message data.
84      *
85      * This is a payload specific to a given message type.
86      */
87     const T& data;
88 
89     /**
90      * Netlink message attributes.
91      */
92     const Attributes attributes;
93 
94     const T* operator->() const { return &data; }
95 
96   private:
Message(const nlmsghdr & nlHeader,const T & dataHeader,Attributes attributes)97     Message(const nlmsghdr& nlHeader, const T& dataHeader, Attributes attributes)
98         : header(nlHeader), data(dataHeader), attributes(attributes) {}
99 };
100 
101 }  // namespace android::nl
102