xref: /aosp_15_r20/hardware/interfaces/automotive/can/1.0/default/libnl++/include/libnl++/MessageFactory.h (revision 4d7e907c777eeecc4c5bd7cf640a754fac206ff7)
1 /*
2  * Copyright (C) 2019 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 <android-base/macros.h>
20 #include <libnl++/Buffer.h>
21 #include <libnl++/types.h>
22 
23 #include <linux/netlink.h>
24 
25 #include <string>
26 
27 namespace android::nl {
28 
29 static constexpr uint16_t kDefaultFlags = NLM_F_REQUEST | NLM_F_ACK;
30 static constexpr uint16_t kCreateFlags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK;
31 
32 class MessageFactoryBase {
33   protected:
34     static nlattr* add(nlmsghdr* msg, size_t maxLen, nlattrtype_t type, const void* data,
35                        size_t dataLen);
36     static void closeNested(nlmsghdr* msg, nlattr* nested);
37 };
38 
39 /**
40  * Wrapper around NETLINK_ROUTE messages, to build them in C++ style.
41  *
42  * \param T Message payload type (such as ifinfomsg).
43  * \param BUFSIZE how much space to reserve for attributes.
44  */
45 template <class T, unsigned int BUFSIZE = 128>
46 class MessageFactory : private MessageFactoryBase {
47     struct alignas(NLMSG_ALIGNTO) Message {
48         nlmsghdr header;
49         T data;
50         uint8_t attributesBuffer[BUFSIZE];
51     };
52 
53   public:
54     /**
55      * Create empty message.
56      *
57      * \param type Message type (such as RTM_NEWLINK).
58      * \param flags Message flags (such as NLM_F_REQUEST).
59      */
60     MessageFactory(nlmsgtype_t type, uint16_t flags = kDefaultFlags)
61         : header(mMessage.header), data(mMessage.data) {
62         mMessage.header.nlmsg_len = offsetof(Message, attributesBuffer);
63         mMessage.header.nlmsg_type = type;
64         mMessage.header.nlmsg_flags = flags;
65     }
66 
67     /**
68      * Netlink message header.
69      *
70      * This is a generic Netlink header containing information such as message flags.
71      */
72     nlmsghdr& header;
73 
74     /**
75      * Netlink message data.
76      *
77      * This is a payload specific to a given message type.
78      */
79     T& data;
80 
81     T* operator->() { return &mMessage.data; }
82 
83     /**
84      * Build netlink message.
85      *
86      * In fact, this operation is almost a no-op, since the factory builds the message in a single
87      * buffer, using native data structures.
88      *
89      * A likely failure case is when the BUFSIZE template parameter is too small to acommodate
90      * added attributes. In such a case, please increase this parameter.
91      *
92      * \return Netlink message or std::nullopt in case of failure.
93      */
build()94     std::optional<Buffer<nlmsghdr>> build() const {
95         if (!mIsGood) return std::nullopt;
96         return {{&mMessage.header, mMessage.header.nlmsg_len}};
97     }
98 
99     /**
100      * Adds an attribute of a trivially copyable type.
101      *
102      * Template specializations may extend this function for other types, such as std::string.
103      *
104      * If this method fails (i.e. due to insufficient space), a warning will be printed to the log
105      * and the message will be marked as bad, causing later \see build call to fail.
106      *
107      * \param type attribute type (such as IFLA_IFNAME)
108      * \param attr attribute data
109      */
110     template <class A>
add(nlattrtype_t type,const A & attr)111     void add(nlattrtype_t type, const A& attr) {
112         addInternal(type, &attr, sizeof(attr));
113     }
114 
115     // It will always send the last null character, otherwise use addBuffer
116     // variant instead
117     template <>
add(nlattrtype_t type,const std::string & s)118     void add(nlattrtype_t type, const std::string& s) {
119         addInternal(type, s.c_str(), s.size() + 1);
120     }
121 
addBuffer(nlattrtype_t type,const std::string_view & s)122     void addBuffer(nlattrtype_t type, const std::string_view& s) {
123         addInternal(type, s.data(), s.size());
124     }
125 
126     /** Guard class to frame nested attributes. \see addNested(nlattrtype_t). */
127     class [[nodiscard]] NestedGuard {
128       public:
NestedGuard(MessageFactory & req,nlattrtype_t type)129         NestedGuard(MessageFactory& req, nlattrtype_t type)
130             : mReq(req), mAttr(req.addInternal(type)) {}
~NestedGuard()131         ~NestedGuard() { closeNested(&mReq.mMessage.header, mAttr); }
132 
133       private:
134         MessageFactory& mReq;
135         nlattr* mAttr;
136 
137         DISALLOW_COPY_AND_ASSIGN(NestedGuard);
138     };
139 
140     /**
141      * Add nested attribute.
142      *
143      * The returned object is a guard for auto-nesting children inside the argument attribute.
144      * When the guard object goes out of scope, the nesting attribute is closed.
145      *
146      * Example usage nesting IFLA_CAN_BITTIMING inside IFLA_INFO_DATA, which is nested
147      * inside IFLA_LINKINFO:
148      *    MessageFactory<ifinfomsg> req(RTM_NEWLINK, NLM_F_REQUEST);
149      *    {
150      *        auto linkinfo = req.addNested(IFLA_LINKINFO);
151      *        req.addBuffer(IFLA_INFO_KIND, "can");
152      *        {
153      *            auto infodata = req.addNested(IFLA_INFO_DATA);
154      *            req.add(IFLA_CAN_BITTIMING, bitTimingStruct);
155      *        }
156      *    }
157      *    // use req
158      *
159      * \param type attribute type (such as IFLA_LINKINFO)
160      */
addNested(nlattrtype_t type)161     NestedGuard addNested(nlattrtype_t type) { return {*this, type}; }
162 
163   private:
164     Message mMessage = {};
165     bool mIsGood = true;
166 
167     nlattr* addInternal(nlattrtype_t type, const void* data = nullptr, size_t len = 0) {
168         if (!mIsGood) return nullptr;
169         auto attr = MessageFactoryBase::add(&mMessage.header, sizeof(mMessage), type, data, len);
170         if (attr == nullptr) mIsGood = false;
171         return attr;
172     }
173 };
174 
175 }  // namespace android::nl
176