blob: 15f54f325463c43741966ad0c6d0b6f4aec54f65 [file] [log] [blame]
Davide Pesavento50b92262018-07-11 12:28:31 -04001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/*
3 * Copyright (c) 2013-2018 Regents of the University of California.
4 *
5 * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
6 *
7 * ndn-cxx library is free software: you can redistribute it and/or modify it under the
8 * terms of the GNU Lesser General Public License as published by the Free Software
9 * Foundation, either version 3 of the License, or (at your option) any later version.
10 *
11 * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
12 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
13 * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
14 *
15 * You should have received copies of the GNU General Public License and GNU Lesser
16 * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
17 * <http://www.gnu.org/licenses/>.
18 *
19 * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
20 *
21 * @author Davide Pesavento <davide.pesavento@lip6.fr>
22 */
23
24#ifndef NDN_NET_NETLINK_UTIL_HPP
25#define NDN_NET_NETLINK_UTIL_HPP
26
27#include "../../common.hpp"
28#include "../ethernet.hpp"
29
30#ifndef NDN_CXX_HAVE_RTNETLINK
31#error "This file should not be included ..."
32#endif
33
34#include <linux/netlink.h>
35#include <linux/rtnetlink.h>
36#include <string.h>
37
38#include <cstring>
39#include <map>
40
41#include <boost/asio/ip/address.hpp>
42
43namespace ndn {
44namespace net {
45
46template<typename T>
47constexpr size_t
48getAttributeLength(const T* attr);
49
50template<>
51constexpr size_t
52getAttributeLength(const nlattr* attr)
53{
54 return attr->nla_len;
55}
56
57template<>
58constexpr size_t
59getAttributeLength(const rtattr* attr)
60{
61 return attr->rta_len;
62}
63
64template<typename T>
65constexpr size_t
66getAttributeLengthAligned(const T* attr);
67
68template<>
69constexpr size_t
70getAttributeLengthAligned(const nlattr* attr)
71{
72 return NLA_ALIGN(attr->nla_len);
73}
74
75template<>
76constexpr size_t
77getAttributeLengthAligned(const rtattr* attr)
78{
79 return RTA_ALIGN(attr->rta_len);
80}
81
82template<typename T>
83constexpr uint16_t
84getAttributeType(const T* attr);
85
86template<>
87constexpr uint16_t
88getAttributeType(const nlattr* attr)
89{
90 return attr->nla_type & NLA_TYPE_MASK;
91}
92
93template<>
94constexpr uint16_t
95getAttributeType(const rtattr* attr)
96{
97 return attr->rta_type;
98}
99
100template<typename T>
101const uint8_t*
102getAttributeValue(const T* attr);
103
104template<>
105inline const uint8_t*
106getAttributeValue(const nlattr* attr)
107{
108 return reinterpret_cast<const uint8_t*>(attr) + NLA_HDRLEN;
109}
110
111template<>
112inline const uint8_t*
113getAttributeValue(const rtattr* attr)
114{
115 return reinterpret_cast<const uint8_t*>(RTA_DATA(const_cast<rtattr*>(attr)));
116}
117
118template<typename T>
119constexpr size_t
120getAttributeValueLength(const T* attr);
121
122template<>
123constexpr size_t
124getAttributeValueLength(const nlattr* attr)
125{
126 return attr->nla_len - NLA_HDRLEN;
127}
128
129template<>
130constexpr size_t
131getAttributeValueLength(const rtattr* attr)
132{
133 return RTA_PAYLOAD(attr);
134}
135
136template<typename T>
137class NetlinkMessageAttributes;
138
139class NetlinkMessage
140{
141public:
142 explicit
143 NetlinkMessage(const uint8_t* buf, size_t buflen) noexcept
144 : m_msg(reinterpret_cast<const nlmsghdr*>(buf))
145 , m_length(buflen)
146 {
147 BOOST_ASSERT(buf != nullptr);
148 }
149
150 const nlmsghdr&
151 operator*() const noexcept
152 {
153 return *m_msg;
154 }
155
156 const nlmsghdr*
157 operator->() const noexcept
158 {
159 return m_msg;
160 }
161
162 bool
163 isValid() const noexcept
164 {
165 return NLMSG_OK(m_msg, m_length);
166 }
167
168 NetlinkMessage
169 getNext() const noexcept
170 {
171 BOOST_ASSERT(isValid());
172
173 // mimic NLMSG_NEXT
174 auto thisLen = NLMSG_ALIGN(m_msg->nlmsg_len);
175 return NetlinkMessage{reinterpret_cast<const uint8_t*>(m_msg) + thisLen, m_length - thisLen};
176 }
177
178 template<typename T>
179 const T*
180 getPayload() const noexcept
181 {
182 BOOST_ASSERT(isValid());
183
184 if (m_msg->nlmsg_len < NLMSG_LENGTH(sizeof(T)))
185 return nullptr;
186
187 return reinterpret_cast<const T*>(NLMSG_DATA(const_cast<nlmsghdr*>(m_msg)));
188 }
189
190 template<typename AttributeT, typename PayloadT>
191 NetlinkMessageAttributes<AttributeT>
192 getAttributes(const PayloadT* p) const noexcept
193 {
194 BOOST_ASSERT(isValid());
195
196 auto begin = reinterpret_cast<const uint8_t*>(p) + NLMSG_ALIGN(sizeof(PayloadT));
197 auto length = NLMSG_PAYLOAD(m_msg, sizeof(PayloadT));
198 return NetlinkMessageAttributes<AttributeT>{reinterpret_cast<const AttributeT*>(begin), length};
199 }
200
201private:
202 const nlmsghdr* m_msg;
203 size_t m_length;
204};
205
206template<typename T>
207class NetlinkMessageAttributes
208{
209 // empty type used to implement tag dispatching in getAttributeByType()
210 template<typename U>
211 struct AttrValueTypeTag {};
212
213public:
214 explicit
215 NetlinkMessageAttributes(const T* begin, size_t length) noexcept
216 {
217 for (; isAttrValid(begin, length); begin = getNextAttr(begin, length)) {
218 m_attrs[getAttributeType(begin)] = begin;
219 }
220 }
221
222 size_t
223 size() const noexcept
224 {
225 return m_attrs.size();
226 }
227
228 template<typename U>
229 optional<U>
230 getAttributeByType(uint16_t attrType) const
231 {
232 auto it = m_attrs.find(attrType);
233 if (it == m_attrs.end())
234 return nullopt;
235
236 return convertAttrValue(getAttributeValue(it->second),
237 getAttributeValueLength(it->second),
238 AttrValueTypeTag<U>{});
239 }
240
241private:
242 static bool
243 isAttrValid(const T* attr, size_t nBytesRemaining) noexcept
244 {
245 return attr != nullptr &&
246 nBytesRemaining >= sizeof(T) &&
247 getAttributeLength(attr) >= sizeof(T) &&
248 getAttributeLength(attr) <= nBytesRemaining;
249 }
250
251 static const T*
252 getNextAttr(const T* attr, size_t& nBytesRemaining) noexcept
253 {
254 auto len = getAttributeLengthAligned(attr);
255 if (len > nBytesRemaining) // prevent integer underflow
256 return nullptr;
257
258 nBytesRemaining -= len;
259 return reinterpret_cast<const T*>(reinterpret_cast<const uint8_t*>(attr) + len);
260 }
261
262 template<typename Integral>
263 static std::enable_if_t<std::is_integral<Integral>::value, optional<Integral>>
264 convertAttrValue(const uint8_t* val, size_t len, AttrValueTypeTag<Integral>)
265 {
266 if (len < sizeof(Integral))
267 return nullopt;
268
269 Integral i;
270 std::memcpy(&i, val, sizeof(Integral));
271 return i;
272 }
273
274 static optional<std::string>
275 convertAttrValue(const uint8_t* val, size_t len, AttrValueTypeTag<std::string>)
276 {
277 auto str = reinterpret_cast<const char*>(val);
278 if (::strnlen(str, len) < len)
279 return std::string(str);
280 else
281 return nullopt;
282 }
283
284 static optional<ethernet::Address>
285 convertAttrValue(const uint8_t* val, size_t len, AttrValueTypeTag<ethernet::Address>)
286 {
287 if (len < ethernet::ADDR_LEN)
288 return nullopt;
289
290 return ethernet::Address(val);
291 }
292
293 template<typename IpAddress>
294 static std::enable_if_t<std::is_same<IpAddress, boost::asio::ip::address_v4>::value ||
295 std::is_same<IpAddress, boost::asio::ip::address_v6>::value, optional<IpAddress>>
296 convertAttrValue(const uint8_t* val, size_t len, AttrValueTypeTag<IpAddress>)
297 {
298 typename IpAddress::bytes_type bytes;
299 if (len < bytes.size())
300 return nullopt;
301
302 std::copy_n(val, bytes.size(), bytes.begin());
303 return IpAddress(bytes);
304 }
305
306private:
307 std::map<uint16_t, const T*> m_attrs;
308};
309
310inline const char*
311nlmsgTypeToString(uint16_t type) noexcept
312{
313#define NLMSG_STRINGIFY(x) case NLMSG_##x: return "<" #x ">"
314#define RTM_STRINGIFY(x) case RTM_##x: return "<" #x ">"
315 switch (type) {
316 NLMSG_STRINGIFY(NOOP);
317 NLMSG_STRINGIFY(ERROR);
318 NLMSG_STRINGIFY(DONE);
319 NLMSG_STRINGIFY(OVERRUN);
320 RTM_STRINGIFY(NEWLINK);
321 RTM_STRINGIFY(DELLINK);
322 RTM_STRINGIFY(GETLINK);
323 RTM_STRINGIFY(NEWADDR);
324 RTM_STRINGIFY(DELADDR);
325 RTM_STRINGIFY(GETADDR);
326 RTM_STRINGIFY(NEWROUTE);
327 RTM_STRINGIFY(DELROUTE);
328 RTM_STRINGIFY(GETROUTE);
329 default:
330 return "";
331 }
332#undef NLMSG_STRINGIFY
333#undef RTM_STRINGIFY
334}
335
336} // namespace net
337} // namespace ndn
338
339#endif // NDN_NET_NETLINK_UTIL_HPP