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