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++/Buffer.h>
20 #include <libnl++/types.h>
21 
22 #include <map>
23 #include <sstream>
24 #include <variant>
25 
26 namespace android::nl::protocols {
27 
28 struct AttributeDefinition;
29 
30 /**
31  * Mapping between nlattrtype_t identifier and attribute definition.
32  *
33  * The map contains values for all nlattrtype_t identifiers - if some is missing, a generic
34  * definition with a identifier as its name will be generated.
35  *
36  * It's possible to define a default attribute to return instead of to_string of its identifier
37  * (useful for nested attribute lists). In such case, an entry with id=std::nullopt needs to be
38  * present in the map.
39  */
40 class AttributeMap : private std::map<std::optional<nlattrtype_t>, AttributeDefinition> {
41   public:
42     using std::map<std::optional<nlattrtype_t>, AttributeDefinition>::value_type;
43 
44     AttributeMap(const std::initializer_list<value_type> attrTypes);
45 
46     const AttributeDefinition operator[](nlattrtype_t nla_type) const;
47 };
48 
49 /**
50  * Attribute definition.
51  *
52  * Describes the name and type (optionally sub types, in case of Nested attribute)
53  * for a given message attribute.
54  */
55 struct AttributeDefinition {
56     enum class DataType : uint8_t {
57         /**
58          * Binary blob (or attribute of unknown type).
59          */
60         Raw,
61 
62         /**
63          * Nested attribute (with or without NLA_F_NESTED).
64          */
65         Nested,
66 
67         /**
68          * Non-null terminated string.
69          *
70          * The length of the string is determined by the size of an attribute.
71          */
72         String,
73 
74         /**
75          * Null terminated string.
76          */
77         StringNul,
78 
79         /**
80          * Unsigned integer of size 8, 16, 32 or 64 bits.
81          */
82         Uint,
83 
84         /**
85          * Structure which printer is defined in ops ToStream variant.
86          */
87         Struct,
88 
89         /**
90          * Flag attribute.
91          *
92          * The attribute doesn't have any contents. The flag is set when the attribute is present,
93          * it's not when it's absent from attribute list.
94          */
95         Flag,
96     };
97     enum class Flags : uint8_t {
98         Verbose = (1 << 0),
99     };
100     using ToStream = std::function<void(std::stringstream& ss, const Buffer<nlattr> attr)>;
101 
102     std::string name;
103     DataType dataType = DataType::Raw;
104     std::variant<AttributeMap, ToStream> ops = AttributeMap{};
105 
106     /**
107      * Attribute flags.
108      *
109      * It's not really a bitmask flag set (since you are not supposed to compare enum class by
110      * bitmask), but std::set<Flags> bumps compile time from 16s to 3m. Let's leave it as-is for
111      * now and revisit if we get some flags that can be used in pairs. When it happens, review all
112      * uses of the flags field to include the "&" operator and not "==".
113      */
114     Flags flags = {};
115 };
116 
117 /**
118  * General message type's kind.
119  *
120  * For example, RTM_NEWLINK is a NEW kind. For details, please see "Flags values"
121  * section in linux/netlink.h.
122  */
123 enum class MessageGenre {
124     Unknown,
125     Get,
126     New,
127     Delete,
128     Ack,
129 };
130 
131 /**
132  * Message family descriptor.
133  *
134  * Describes the structure of all message types with the same header and attributes.
135  */
136 class MessageDescriptor {
137   public:
138     struct MessageDetails {
139         std::string name;
140         MessageGenre genre;
141     };
142     typedef std::map<nlmsgtype_t, MessageDetails> MessageDetailsMap;
143 
144   public:
145     virtual ~MessageDescriptor();
146 
147     size_t getContentsSize() const;
148     const MessageDetailsMap& getMessageDetailsMap() const;
149     const AttributeMap& getAttributeMap() const;
150     MessageDetails getMessageDetails(nlmsgtype_t msgtype) const;
151     virtual void dataToStream(std::stringstream& ss, const Buffer<nlmsghdr> hdr) const = 0;
152 
153     /**
154      * Message tracking for stateful protocols (such as NETLINK_GENERIC).
155      *
156      * \param hdr Message to track
157      */
158     virtual void track(const Buffer<nlmsghdr> hdr);
159 
160     static MessageDetails getMessageDetails(
161             const std::optional<std::reference_wrapper<MessageDescriptor>>& msgDescMaybe,
162             nlmsgtype_t msgtype);
163 
164   protected:
165     MessageDescriptor(const std::string& name, const MessageDetailsMap&& messageDetails,
166                       const AttributeMap&& attrTypes, size_t contentsSize);
167 
168   private:
169     const std::string mName;
170     const size_t mContentsSize;
171     const MessageDetailsMap mMessageDetails;
172     const AttributeMap mAttributeMap;
173 };
174 
175 /**
176  * Message definition template.
177  *
178  * A convenience initialization helper of a message descriptor.
179  */
180 template <typename T>
181 class MessageDefinition : public MessageDescriptor {
182   public:
183     MessageDefinition(  //
184             const std::string& name,
185             const std::initializer_list<MessageDescriptor::MessageDetailsMap::value_type> msgDet,
186             const std::initializer_list<AttributeMap::value_type> attrTypes = {})
MessageDescriptor(name,msgDet,attrTypes,sizeof (T))187         : MessageDescriptor(name, msgDet, attrTypes, sizeof(T)) {}
188 
dataToStream(std::stringstream & ss,const Buffer<nlmsghdr> hdr)189     void dataToStream(std::stringstream& ss, const Buffer<nlmsghdr> hdr) const override {
190         const auto& [ok, msg] = hdr.data<T>().getFirst();
191         if (!ok) {
192             ss << "{incomplete payload}";
193             return;
194         }
195 
196         toStream(ss, msg);
197     }
198 
199   protected:
200     virtual void toStream(std::stringstream& ss, const T& data) const = 0;
201 };
202 
203 }  // namespace android::nl::protocols
204