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 add(type, &attr, sizeof(attr)); 110 } 111 112 template <> add(nlattrtype_t type,const std::string & s)113 void add(nlattrtype_t type, const std::string& s) { 114 add(type, s.c_str(), s.size() + 1); 115 } 116 117 /** Guard class to frame nested attributes. \see addNested(nlattrtype_t). */ 118 class [[nodiscard]] NestedGuard { 119 public: NestedGuard(MessageFactory & req,nlattrtype_t type)120 NestedGuard(MessageFactory & req, nlattrtype_t type) : mReq(req), mAttr(req.add(type)) {} ~NestedGuard()121 ~NestedGuard() { closeNested(&mReq.mMessage.header, mAttr); } 122 123 private: 124 MessageFactory& mReq; 125 nlattr* mAttr; 126 127 DISALLOW_COPY_AND_ASSIGN(NestedGuard); 128 }; 129 130 /** 131 * Add nested attribute. 132 * 133 * The returned object is a guard for auto-nesting children inside the argument attribute. 134 * When the guard object goes out of scope, the nesting attribute is closed. 135 * 136 * Example usage nesting IFLA_CAN_BITTIMING inside IFLA_INFO_DATA, which is nested 137 * inside IFLA_LINKINFO: 138 * MessageFactory<ifinfomsg> req(RTM_NEWLINK, NLM_F_REQUEST); 139 * { 140 * auto linkinfo = req.addNested(IFLA_LINKINFO); 141 * req.add(IFLA_INFO_KIND, "can"); 142 * { 143 * auto infodata = req.addNested(IFLA_INFO_DATA); 144 * req.add(IFLA_CAN_BITTIMING, bitTimingStruct); 145 * } 146 * } 147 * // use req 148 * 149 * \param type attribute type (such as IFLA_LINKINFO) 150 */ addNested(nlattrtype_t type)151 NestedGuard addNested(nlattrtype_t type) { return {*this, type}; } 152 153 private: 154 Message mMessage = {}; 155 bool mIsGood = true; 156 157 nlattr* add(nlattrtype_t type, const void* data = nullptr, size_t len = 0) { 158 if (!mIsGood) return nullptr; 159 auto attr = MessageFactoryBase::add(&mMessage.header, sizeof(mMessage), type, data, len); 160 if (attr == nullptr) mIsGood = false; 161 return attr; 162 } 163 }; 164 165 } // namespace android::nl 166