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