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