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