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