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 <linux/rtnetlink.h> 21 22 #include <string> 23 24 namespace android::netdevice { 25 26 typedef unsigned short rtattrtype_t; // as in rtnetlink.h 27 typedef __u16 nlmsgtype_t; // as in netlink.h 28 29 /** Implementation details, do not use outside NetlinkRequest template. */ 30 namespace impl { 31 32 struct rtattr* addattr_l(struct nlmsghdr* n, size_t maxLen, rtattrtype_t type, const void* data, 33 size_t dataLen); 34 struct rtattr* addattr_nest(struct nlmsghdr* n, size_t maxLen, rtattrtype_t type); 35 void addattr_nest_end(struct nlmsghdr* n, struct rtattr* nest); 36 37 } // namespace impl 38 39 /** 40 * Wrapper around NETLINK_ROUTE messages, to build them in C++ style. 41 * 42 * \param T specific message header (such as struct ifinfomsg) 43 * \param BUFSIZE how much space to reserve for payload (not counting the header size) 44 */ 45 template <class T, unsigned int BUFSIZE = 128> 46 struct NetlinkRequest { 47 /** 48 * Create empty message. 49 * 50 * \param type Message type (such as RTM_NEWLINK) 51 * \param flags Message flags (such as NLM_F_REQUEST) 52 */ NetlinkRequestNetlinkRequest53 NetlinkRequest(nlmsgtype_t type, uint16_t flags) { 54 mRequest.nlmsg.nlmsg_len = NLMSG_LENGTH(sizeof(mRequest.data)); 55 mRequest.nlmsg.nlmsg_type = type; 56 mRequest.nlmsg.nlmsg_flags = flags; 57 } 58 59 /** \return pointer to raw netlink message header. */ headerNetlinkRequest60 struct nlmsghdr* header() { 61 return &mRequest.nlmsg; 62 } 63 /** Reference to message-specific header. */ dataNetlinkRequest64 T& data() { return mRequest.data; } 65 66 /** 67 * Adds an attribute of a simple type. 68 * 69 * If this method fails (i.e. due to insufficient space), the message will be marked 70 * as bad (\see isGood). 71 * 72 * \param type attribute type (such as IFLA_IFNAME) 73 * \param attr attribute data 74 */ 75 template <class A> addattrNetlinkRequest76 void addattr(rtattrtype_t type, const A& attr) { 77 if (!mIsGood) return; 78 auto ap = impl::addattr_l(&mRequest.nlmsg, sizeof(mRequest), type, &attr, sizeof(attr)); 79 if (ap == nullptr) mIsGood = false; 80 } 81 82 template <> addattrNetlinkRequest83 void addattr(rtattrtype_t type, const std::string& s) { 84 if (!mIsGood) return; 85 auto ap = impl::addattr_l(&mRequest.nlmsg, sizeof(mRequest), type, s.c_str(), s.size() + 1); 86 if (ap == nullptr) mIsGood = false; 87 } 88 89 /** Guard class to frame nested attributes. See nest(int). */ 90 struct Nest { NestNetlinkRequest::Nest91 Nest(NetlinkRequest& req, rtattrtype_t type) : mReq(req), mAttr(req.nestStart(type)) {} ~NestNetlinkRequest::Nest92 ~Nest() { mReq.nestEnd(mAttr); } 93 94 private: 95 NetlinkRequest& mReq; 96 struct rtattr* mAttr; 97 98 DISALLOW_COPY_AND_ASSIGN(Nest); 99 }; 100 101 /** 102 * Add nested attribute. 103 * 104 * The returned object is a guard for auto-nesting children inside the argument attribute. 105 * When the Nest object goes out of scope, the nesting attribute is closed. 106 * 107 * Example usage nesting IFLA_CAN_BITTIMING inside IFLA_INFO_DATA, which is nested 108 * inside IFLA_LINKINFO: 109 * NetlinkRequest<struct ifinfomsg> req(RTM_NEWLINK, NLM_F_REQUEST); 110 * { 111 * auto linkinfo = req.nest(IFLA_LINKINFO); 112 * req.addattr(IFLA_INFO_KIND, "can"); 113 * { 114 * auto infodata = req.nest(IFLA_INFO_DATA); 115 * req.addattr(IFLA_CAN_BITTIMING, bitTimingStruct); 116 * } 117 * } 118 * // use req 119 * 120 * \param type attribute type (such as IFLA_LINKINFO) 121 */ nestNetlinkRequest122 Nest nest(int type) { return Nest(*this, type); } 123 124 /** 125 * Indicates, whether the message is in a good state. 126 * 127 * The bad state is usually a result of payload buffer being too small. 128 * You can modify BUFSIZE template parameter to fix this. 129 */ isGoodNetlinkRequest130 bool isGood() const { return mIsGood; } 131 132 private: 133 bool mIsGood = true; 134 135 struct { 136 struct nlmsghdr nlmsg; 137 T data; 138 char buf[BUFSIZE]; 139 } mRequest = {}; 140 nestStartNetlinkRequest141 struct rtattr* nestStart(rtattrtype_t type) { 142 if (!mIsGood) return nullptr; 143 auto attr = impl::addattr_nest(&mRequest.nlmsg, sizeof(mRequest), type); 144 if (attr == nullptr) mIsGood = false; 145 return attr; 146 } 147 nestEndNetlinkRequest148 void nestEnd(struct rtattr* nest) { 149 if (mIsGood && nest != nullptr) impl::addattr_nest_end(&mRequest.nlmsg, nest); 150 } 151 }; 152 153 } // namespace android::netdevice 154