blob: c32573722b1d21b4c0b719215178067bdfb3ba08 [file] [log] [blame]
Davide Pesaventoa0b2a2c2018-07-19 01:01:06 -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#include "netlink-socket.hpp"
25#include "netlink-message.hpp"
26#include "../../util/logger.hpp"
27#include "../../util/time.hpp"
28
29#include <cerrno>
Davide Pesavento10774992018-08-19 18:47:50 -040030#include <linux/genetlink.h>
Davide Pesaventoa0b2a2c2018-07-19 01:01:06 -040031#include <sys/socket.h>
32
33#include <boost/asio/write.hpp>
34
35#ifndef SOL_NETLINK
36#define SOL_NETLINK 270
37#endif
38#ifndef RTEXT_FILTER_SKIP_STATS
39#define RTEXT_FILTER_SKIP_STATS (1 << 3)
40#endif
41
42NDN_LOG_INIT(ndn.NetworkMonitor);
43
44namespace ndn {
45namespace net {
46
47NetlinkSocket::NetlinkSocket(boost::asio::io_service& io)
48 : m_sock(make_shared<boost::asio::posix::stream_descriptor>(io))
49 , m_pid(0)
50 , m_seqNum(static_cast<uint32_t>(time::system_clock::now().time_since_epoch().count()))
51 , m_buffer(16 * 1024) // 16 KiB
52{
53}
54
55NetlinkSocket::~NetlinkSocket()
56{
57 boost::system::error_code ec;
58 m_sock->close(ec);
59}
60
61void
62NetlinkSocket::open(int protocol)
63{
64 int fd = ::socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, protocol);
65 if (fd < 0) {
66 BOOST_THROW_EXCEPTION(Error("Cannot create netlink socket ("s + std::strerror(errno) + ")"));
67 }
68 m_sock->assign(fd);
69
70 // increase socket receive buffer to 1MB to avoid losing messages
71 const int bufsize = 1 * 1024 * 1024;
72 if (::setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &bufsize, sizeof(bufsize)) < 0) {
73 // not a fatal error
74 NDN_LOG_DEBUG("setting SO_RCVBUF failed: " << std::strerror(errno));
75 }
76
77 // enable control messages for received packets to get the destination group
78 const int one = 1;
79 if (::setsockopt(fd, SOL_NETLINK, NETLINK_PKTINFO, &one, sizeof(one)) < 0) {
80 BOOST_THROW_EXCEPTION(Error("Cannot enable NETLINK_PKTINFO ("s + std::strerror(errno) + ")"));
81 }
82
83 sockaddr_nl addr{};
84 addr.nl_family = AF_NETLINK;
85 if (::bind(fd, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) < 0) {
86 BOOST_THROW_EXCEPTION(Error("Cannot bind netlink socket ("s + std::strerror(errno) + ")"));
87 }
88
89 // find out what pid has been assigned to us
90 socklen_t len = sizeof(addr);
91 if (::getsockname(fd, reinterpret_cast<sockaddr*>(&addr), &len) < 0) {
92 BOOST_THROW_EXCEPTION(Error("Cannot obtain netlink socket address ("s + std::strerror(errno) + ")"));
93 }
94 if (len != sizeof(addr)) {
95 BOOST_THROW_EXCEPTION(Error("Wrong address length (" + to_string(len) + ")"));
96 }
97 if (addr.nl_family != AF_NETLINK) {
98 BOOST_THROW_EXCEPTION(Error("Wrong address family (" + to_string(addr.nl_family) + ")"));
99 }
100 m_pid = addr.nl_pid;
101 NDN_LOG_TRACE("our pid is " << m_pid);
102
103#ifdef NDN_CXX_HAVE_NETLINK_EXT_ACK
104 // enable extended ACK reporting
105 if (::setsockopt(fd, SOL_NETLINK, NETLINK_EXT_ACK, &one, sizeof(one)) < 0) {
106 // not a fatal error
107 NDN_LOG_DEBUG("setting NETLINK_EXT_ACK failed: " << std::strerror(errno));
108 }
109#endif // NDN_CXX_HAVE_NETLINK_EXT_ACK
110}
111
112void
113NetlinkSocket::joinGroup(int group)
114{
115 if (::setsockopt(m_sock->native_handle(), SOL_NETLINK, NETLINK_ADD_MEMBERSHIP,
116 &group, sizeof(group)) < 0) {
117 BOOST_THROW_EXCEPTION(Error("Cannot join netlink group " + to_string(group) +
118 " (" + std::strerror(errno) + ")"));
119 }
120}
121
122void
Davide Pesaventodc5bb962018-08-12 23:40:38 -0400123NetlinkSocket::registerNotificationCallback(MessageCallback cb)
Davide Pesaventoa0b2a2c2018-07-19 01:01:06 -0400124{
Davide Pesaventodc5bb962018-08-12 23:40:38 -0400125 registerRequestCallback(0, std::move(cb));
126}
Davide Pesaventoa0b2a2c2018-07-19 01:01:06 -0400127
Davide Pesaventodc5bb962018-08-12 23:40:38 -0400128void
129NetlinkSocket::registerRequestCallback(uint32_t seq, MessageCallback cb)
130{
131 if (cb == nullptr) {
132 m_pendingRequests.erase(seq);
133 }
134 else {
135 bool wasEmpty = m_pendingRequests.empty();
136 m_pendingRequests.emplace(seq, std::move(cb));
137 if (wasEmpty)
138 asyncWait();
139 }
Davide Pesaventoa0b2a2c2018-07-19 01:01:06 -0400140}
141
Davide Pesavento10774992018-08-19 18:47:50 -0400142std::string
143NetlinkSocket::nlmsgTypeToString(uint16_t type) const
Davide Pesaventoa0b2a2c2018-07-19 01:01:06 -0400144{
Davide Pesavento10774992018-08-19 18:47:50 -0400145#define NLMSG_STRINGIFY(x) case NLMSG_##x: return to_string(type) + "<" #x ">"
Davide Pesaventoa0b2a2c2018-07-19 01:01:06 -0400146 switch (type) {
147 NLMSG_STRINGIFY(NOOP);
148 NLMSG_STRINGIFY(ERROR);
149 NLMSG_STRINGIFY(DONE);
150 NLMSG_STRINGIFY(OVERRUN);
Davide Pesaventoa0b2a2c2018-07-19 01:01:06 -0400151 default:
Davide Pesavento10774992018-08-19 18:47:50 -0400152 return to_string(type);
Davide Pesaventoa0b2a2c2018-07-19 01:01:06 -0400153 }
154#undef NLMSG_STRINGIFY
Davide Pesaventoa0b2a2c2018-07-19 01:01:06 -0400155}
156
157void
158NetlinkSocket::asyncWait()
159{
160 m_sock->async_read_some(boost::asio::null_buffers(),
161 // capture a copy of 'm_sock' to prevent its deallocation while the handler is still pending
162 [this, sock = m_sock] (const boost::system::error_code& ec, size_t) {
163 if (!sock->is_open() || ec == boost::asio::error::operation_aborted) {
164 // socket was closed, ignore the error
165 NDN_LOG_DEBUG("netlink socket closed or operation aborted");
166 }
167 else if (ec) {
168 NDN_LOG_ERROR("read failed: " << ec.message());
169 BOOST_THROW_EXCEPTION(Error("Netlink socket read error (" + ec.message() + ")"));
170 }
171 else {
172 receiveAndValidate();
Davide Pesaventodc5bb962018-08-12 23:40:38 -0400173 if (!m_pendingRequests.empty())
174 asyncWait();
Davide Pesaventoa0b2a2c2018-07-19 01:01:06 -0400175 }
176 });
177}
178
179void
180NetlinkSocket::receiveAndValidate()
181{
182 sockaddr_nl sender{};
183 iovec iov{};
184 iov.iov_base = m_buffer.data();
185 iov.iov_len = m_buffer.size();
186 uint8_t cmsgBuffer[CMSG_SPACE(sizeof(nl_pktinfo))];
187 msghdr msg{};
188 msg.msg_name = &sender;
189 msg.msg_namelen = sizeof(sender);
190 msg.msg_iov = &iov;
191 msg.msg_iovlen = 1;
192 msg.msg_control = cmsgBuffer;
193 msg.msg_controllen = sizeof(cmsgBuffer);
194
195 ssize_t nBytesRead = ::recvmsg(m_sock->native_handle(), &msg, 0);
196 if (nBytesRead < 0) {
197 std::string errorString = std::strerror(errno);
198 if (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK) {
199 // not a fatal error
200 NDN_LOG_DEBUG("recvmsg failed: " << errorString);
201 return;
202 }
203 NDN_LOG_ERROR("recvmsg failed: " << errorString);
204 BOOST_THROW_EXCEPTION(Error("Netlink socket receive error (" + errorString + ")"));
205 }
206
207 NDN_LOG_TRACE("read " << nBytesRead << " bytes from netlink socket");
208
209 if (msg.msg_flags & MSG_TRUNC) {
210 NDN_LOG_ERROR("truncated message");
211 BOOST_THROW_EXCEPTION(Error("Received truncated netlink message"));
212 // TODO: grow the buffer and start over
213 }
214
215 if (msg.msg_namelen >= sizeof(sender) && sender.nl_pid != 0) {
216 NDN_LOG_TRACE("ignoring message from pid=" << sender.nl_pid);
217 return;
218 }
219
220 uint32_t nlGroup = 0;
221 for (cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); cmsg != nullptr; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
222 if (cmsg->cmsg_level == SOL_NETLINK &&
223 cmsg->cmsg_type == NETLINK_PKTINFO &&
224 cmsg->cmsg_len == CMSG_LEN(sizeof(nl_pktinfo))) {
225 const nl_pktinfo* pktinfo = reinterpret_cast<nl_pktinfo*>(CMSG_DATA(cmsg));
226 nlGroup = pktinfo->group;
227 }
228 }
229
230 NetlinkMessage nlmsg(m_buffer.data(), static_cast<size_t>(nBytesRead));
231 for (; nlmsg.isValid(); nlmsg = nlmsg.getNext()) {
232 NDN_LOG_TRACE("parsing " << (nlmsg->nlmsg_flags & NLM_F_MULTI ? "multi-part " : "") <<
Davide Pesavento10774992018-08-19 18:47:50 -0400233 "message type=" << nlmsgTypeToString(nlmsg->nlmsg_type) <<
Davide Pesaventoa0b2a2c2018-07-19 01:01:06 -0400234 " len=" << nlmsg->nlmsg_len <<
235 " seq=" << nlmsg->nlmsg_seq <<
236 " pid=" << nlmsg->nlmsg_pid <<
237 " group=" << nlGroup);
238
Davide Pesaventodc5bb962018-08-12 23:40:38 -0400239 auto cbIt = m_pendingRequests.end();
240 if (nlGroup != 0) {
241 // it's a multicast notification
242 cbIt = m_pendingRequests.find(0);
243 }
244 else if (nlmsg->nlmsg_pid == m_pid) {
245 // it's for us
246 cbIt = m_pendingRequests.find(nlmsg->nlmsg_seq);
247 }
248 else {
249 NDN_LOG_TRACE("pid mismatch, ignoring");
Davide Pesaventoa0b2a2c2018-07-19 01:01:06 -0400250 continue;
251 }
252
Davide Pesaventodc5bb962018-08-12 23:40:38 -0400253 if (cbIt == m_pendingRequests.end()) {
254 NDN_LOG_TRACE("no handler registered, ignoring");
255 continue;
256 }
257 else if (nlmsg->nlmsg_flags & NLM_F_DUMP_INTR) {
Davide Pesaventoa0b2a2c2018-07-19 01:01:06 -0400258 NDN_LOG_ERROR("dump is inconsistent");
259 BOOST_THROW_EXCEPTION(Error("Inconsistency detected in netlink dump"));
260 // TODO: discard the rest of the message and retry the dump
261 }
Davide Pesaventodc5bb962018-08-12 23:40:38 -0400262 else {
263 // invoke the callback
264 BOOST_ASSERT(cbIt->second);
265 cbIt->second(nlmsg);
266 }
Davide Pesaventoa0b2a2c2018-07-19 01:01:06 -0400267
Davide Pesaventodc5bb962018-08-12 23:40:38 -0400268 // garbage collect the handler if we don't need it anymore:
269 // do it only if this is a reply message (i.e. not a notification) and either
270 // (1) it's not a multi-part message, in which case this is the only fragment, or
271 // (2) it's the last fragment of a multi-part message
272 if (nlGroup == 0 && (!(nlmsg->nlmsg_flags & NLM_F_MULTI) || nlmsg->nlmsg_type == NLMSG_DONE)) {
273 NDN_LOG_TRACE("removing handler for seq=" << nlmsg->nlmsg_seq);
274 BOOST_ASSERT(cbIt != m_pendingRequests.end());
275 m_pendingRequests.erase(cbIt);
Davide Pesaventoa0b2a2c2018-07-19 01:01:06 -0400276 }
277 }
278}
279
280RtnlSocket::RtnlSocket(boost::asio::io_service& io)
281 : NetlinkSocket(io)
282{
283}
284
285void
286RtnlSocket::open()
287{
288 NDN_LOG_TRACE("opening rtnetlink socket");
289 NetlinkSocket::open(NETLINK_ROUTE);
290}
291
292void
Davide Pesaventodc5bb962018-08-12 23:40:38 -0400293RtnlSocket::sendDumpRequest(uint16_t nlmsgType, MessageCallback cb)
Davide Pesaventoa0b2a2c2018-07-19 01:01:06 -0400294{
295 struct RtnlRequest
296 {
297 nlmsghdr nlh;
298 alignas(NLMSG_ALIGNTO) ifinfomsg ifi;
299 alignas(NLMSG_ALIGNTO) rtattr rta;
300 alignas(NLMSG_ALIGNTO) uint32_t rtext; // space for IFLA_EXT_MASK
301 };
302
303 auto request = make_shared<RtnlRequest>();
304 request->nlh.nlmsg_type = nlmsgType;
305 request->nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
306 request->nlh.nlmsg_seq = ++m_seqNum;
307 request->nlh.nlmsg_pid = m_pid;
308 request->ifi.ifi_family = AF_UNSPEC;
309 request->rta.rta_type = IFLA_EXT_MASK;
310 request->rta.rta_len = RTA_LENGTH(sizeof(request->rtext));
311 request->rtext = RTEXT_FILTER_SKIP_STATS;
312 request->nlh.nlmsg_len = NLMSG_SPACE(sizeof(ifinfomsg)) + request->rta.rta_len;
313
Davide Pesaventodc5bb962018-08-12 23:40:38 -0400314 registerRequestCallback(request->nlh.nlmsg_seq, std::move(cb));
315
Davide Pesaventoa0b2a2c2018-07-19 01:01:06 -0400316 boost::asio::async_write(*m_sock, boost::asio::buffer(request.get(), request->nlh.nlmsg_len),
317 // capture 'request' to prevent its premature deallocation
Davide Pesavento10774992018-08-19 18:47:50 -0400318 [this, request] (const boost::system::error_code& ec, size_t) {
Davide Pesaventoa0b2a2c2018-07-19 01:01:06 -0400319 if (!ec) {
Davide Pesavento10774992018-08-19 18:47:50 -0400320 NDN_LOG_TRACE("sent dump request type=" << nlmsgTypeToString(request->nlh.nlmsg_type)
Davide Pesaventoa0b2a2c2018-07-19 01:01:06 -0400321 << " seq=" << request->nlh.nlmsg_seq);
322 }
323 else if (ec != boost::asio::error::operation_aborted) {
324 NDN_LOG_ERROR("write failed: " << ec.message());
325 BOOST_THROW_EXCEPTION(Error("Failed to send netlink request (" + ec.message() + ")"));
326 }
327 });
328}
329
Davide Pesavento10774992018-08-19 18:47:50 -0400330std::string
331RtnlSocket::nlmsgTypeToString(uint16_t type) const
332{
333#define RTM_STRINGIFY(x) case RTM_##x: return to_string(type) + "<" #x ">"
334 switch (type) {
335 RTM_STRINGIFY(NEWLINK);
336 RTM_STRINGIFY(DELLINK);
337 RTM_STRINGIFY(GETLINK);
338 RTM_STRINGIFY(NEWADDR);
339 RTM_STRINGIFY(DELADDR);
340 RTM_STRINGIFY(GETADDR);
341 RTM_STRINGIFY(NEWROUTE);
342 RTM_STRINGIFY(DELROUTE);
343 RTM_STRINGIFY(GETROUTE);
344 default:
345 return NetlinkSocket::nlmsgTypeToString(type);
346 }
347#undef RTM_STRINGIFY
348}
349
350GenlSocket::GenlSocket(boost::asio::io_service& io)
351 : NetlinkSocket(io)
352{
353 m_cachedFamilyIds["nlctrl"] = GENL_ID_CTRL;
354}
355
356void
357GenlSocket::open()
358{
359 NDN_LOG_TRACE("opening genetlink socket");
360 NetlinkSocket::open(NETLINK_GENERIC);
361}
362
363void
364GenlSocket::sendRequest(const std::string& familyName, uint8_t command,
365 const void* payload, size_t payloadLen,
366 MessageCallback messageCb, std::function<void()> errorCb)
367{
368 auto it = m_cachedFamilyIds.find(familyName);
369 if (it != m_cachedFamilyIds.end()) {
370 if (it->second >= GENL_MIN_ID) {
371 sendRequest(it->second, command, payload, payloadLen, std::move(messageCb));
372 }
373 else if (errorCb) {
374 errorCb();
375 }
376 return;
377 }
378
379 auto ret = m_familyResolvers.emplace(std::piecewise_construct,
380 std::forward_as_tuple(familyName),
381 std::forward_as_tuple(familyName, *this));
382 auto& resolver = ret.first->second;
383 if (ret.second) {
384 // cache the result
385 resolver.onResolved.connectSingleShot([=] (uint16_t familyId) {
386 m_cachedFamilyIds[familyName] = familyId;
387 });
388 resolver.onError.connectSingleShot([=] {
389 m_cachedFamilyIds[familyName] = 0;
390 });
391 }
392 resolver.onResolved.connectSingleShot([=, cb = std::move(messageCb)] (uint16_t familyId) {
393 sendRequest(familyId, command, payload, payloadLen, std::move(cb));
394 });
395 if (errorCb) {
396 resolver.onError.connectSingleShot(std::move(errorCb));
397 }
398}
399
400void
401GenlSocket::sendRequest(uint16_t familyId, uint8_t command,
402 const void* payload, size_t payloadLen, MessageCallback cb)
403{
404 struct GenlRequestHeader
405 {
406 alignas(NLMSG_ALIGNTO) nlmsghdr nlh;
407 alignas(NLMSG_ALIGNTO) genlmsghdr genlh;
408 };
409 static_assert(sizeof(GenlRequestHeader) == NLMSG_SPACE(GENL_HDRLEN), "");
410
411 auto hdr = make_shared<GenlRequestHeader>();
412 hdr->nlh.nlmsg_len = sizeof(GenlRequestHeader) + payloadLen;
413 hdr->nlh.nlmsg_type = familyId;
414 hdr->nlh.nlmsg_flags = NLM_F_REQUEST;
415 hdr->nlh.nlmsg_seq = ++m_seqNum;
416 hdr->nlh.nlmsg_pid = m_pid;
417 hdr->genlh.cmd = command;
418 hdr->genlh.version = 1;
419
420 registerRequestCallback(hdr->nlh.nlmsg_seq, std::move(cb));
421
422 std::array<boost::asio::const_buffer, 2> bufs = {
423 boost::asio::buffer(hdr.get(), sizeof(GenlRequestHeader)),
424 boost::asio::buffer(payload, payloadLen)
425 };
426 boost::asio::async_write(*m_sock, bufs,
427 // capture 'hdr' to prevent its premature deallocation
428 [this, hdr] (const boost::system::error_code& ec, size_t) {
429 if (!ec) {
430 NDN_LOG_TRACE("sent genl request type=" << nlmsgTypeToString(hdr->nlh.nlmsg_type) <<
431 " cmd=" << static_cast<unsigned>(hdr->genlh.cmd) <<
432 " seq=" << hdr->nlh.nlmsg_seq);
433 }
434 else if (ec != boost::asio::error::operation_aborted) {
435 NDN_LOG_ERROR("write failed: " << ec.message());
436 BOOST_THROW_EXCEPTION(Error("Failed to send netlink request (" + ec.message() + ")"));
437 }
438 });
439}
440
441GenlFamilyResolver::GenlFamilyResolver(std::string familyName, GenlSocket& socket)
442 : m_sock(socket)
443 , m_family(std::move(familyName))
444{
445 if (m_family.size() >= GENL_NAMSIZ) {
446 BOOST_THROW_EXCEPTION(std::invalid_argument("netlink family name '" + m_family + "' too long"));
447 }
448
449 NDN_LOG_TRACE("resolving netlink family " << m_family);
450 asyncResolve();
451}
452
453void
454GenlFamilyResolver::asyncResolve()
455{
456 struct FamilyNameAttribute
457 {
458 alignas(NLMSG_ALIGNTO) nlattr nla;
459 alignas(NLMSG_ALIGNTO) char name[GENL_NAMSIZ];
460 };
461
462 auto attr = make_shared<FamilyNameAttribute>();
463 attr->nla.nla_type = CTRL_ATTR_FAMILY_NAME;
464 attr->nla.nla_len = NLA_HDRLEN + m_family.size() + 1;
465 ::strncpy(attr->name, m_family.data(), GENL_NAMSIZ);
466
467 m_sock.sendRequest(GENL_ID_CTRL, CTRL_CMD_GETFAMILY, attr.get(), attr->nla.nla_len,
468 // capture 'attr' to prevent its premature deallocation
469 [this, attr] (const auto& msg) { this->handleResolve(msg); });
470}
471
472void
473GenlFamilyResolver::handleResolve(const NetlinkMessage& nlmsg)
474{
475 switch (nlmsg->nlmsg_type) {
476 case NLMSG_ERROR: {
477 const nlmsgerr* err = nlmsg.getPayload<nlmsgerr>();
478 if (err == nullptr) {
479 NDN_LOG_WARN("malformed nlmsgerr");
480 }
481 else if (err->error != 0) {
482 NDN_LOG_DEBUG("failed to resolve netlink family " << m_family << ": "
483 << std::strerror(std::abs(err->error)));
484 }
485 onError();
486 break;
487 }
488
489 case GENL_ID_CTRL: {
490 const genlmsghdr* genlh = nlmsg.getPayload<genlmsghdr>();
491 if (genlh == nullptr) {
492 NDN_LOG_WARN("malformed genlmsghdr");
493 return onError();
494 }
495 if (genlh->cmd != CTRL_CMD_NEWFAMILY) {
496 NDN_LOG_WARN("unexpected genl cmd=" << static_cast<unsigned>(genlh->cmd));
497 return onError();
498 }
499
500 auto attrs = nlmsg.getAttributes<nlattr>(genlh);
501 auto familyName = attrs.getAttributeByType<std::string>(CTRL_ATTR_FAMILY_NAME);
502 if (familyName && *familyName != m_family) {
503 NDN_LOG_WARN("CTRL_ATTR_FAMILY_NAME mismatch: " << *familyName << " != " << m_family);
504 return onError();
505 }
506 auto familyId = attrs.getAttributeByType<uint16_t>(CTRL_ATTR_FAMILY_ID);
507 if (!familyId) {
508 NDN_LOG_WARN("missing CTRL_ATTR_FAMILY_ID");
509 return onError();
510 }
511 if (*familyId < GENL_MIN_ID) {
512 NDN_LOG_WARN("invalid CTRL_ATTR_FAMILY_ID=" << *familyId);
513 return onError();
514 }
515
516 NDN_LOG_TRACE("resolved netlink family name=" << m_family << " id=" << *familyId);
517 onResolved(*familyId);
518 break;
519 }
520
521 default: {
522 NDN_LOG_WARN("unexpected message type");
523 onError();
524 break;
525 }
526 }
527}
528
529std::string
530GenlSocket::nlmsgTypeToString(uint16_t type) const
531{
532 if (type >= GENL_MIN_ID) {
533 for (const auto& p : m_cachedFamilyIds) {
534 if (p.second == type) {
535 return to_string(type) + "<" + p.first + ">";
536 }
537 }
538 }
539
540 return NetlinkSocket::nlmsgTypeToString(type);
541}
542
Davide Pesaventoa0b2a2c2018-07-19 01:01:06 -0400543} // namespace net
544} // namespace ndn