NS-3 based Named Data Networking (NDN) simulator
ndnSIM 2.3: NDN, CCN, CCNx, content centric networks
API Documentation
network-monitor-impl-rtnl.cpp
Go to the documentation of this file.
1 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2 /*
3  * Copyright (c) 2013-2017 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 
25 #include "linux-if-constants.hpp"
26 #include "../network-address.hpp"
27 #include "../network-interface.hpp"
28 #include "../../util/logger.hpp"
29 #include "../../util/time.hpp"
30 
31 #include <boost/asio/write.hpp>
32 
33 #include <cerrno>
34 #include <cstdlib>
35 #include <net/if_arp.h>
36 #include <sys/socket.h>
37 
38 NDN_LOG_INIT(ndn.NetworkMonitor);
39 
40 namespace ndn {
41 namespace net {
42 
44  : m_socket(make_shared<boost::asio::posix::stream_descriptor>(io))
45  , m_pid(0)
46  , m_sequenceNo(static_cast<uint32_t>(time::system_clock::now().time_since_epoch().count()))
47  , m_isEnumeratingLinks(false)
48  , m_isEnumeratingAddresses(false)
49 {
50  initSocket();
51  asyncRead();
52 
53  NDN_LOG_TRACE("enumerating links");
54  sendDumpRequest(RTM_GETLINK);
55  m_isEnumeratingLinks = true;
56 }
57 
59 {
60  boost::system::error_code error;
61  m_socket->close(error);
62 }
63 
64 shared_ptr<const NetworkInterface>
65 NetworkMonitorImplRtnl::getNetworkInterface(const std::string& ifname) const
66 {
67  for (const auto& e : m_interfaces) {
68  if (e.second->getName() == ifname)
69  return e.second;
70  }
71  return nullptr;
72 }
73 
74 std::vector<shared_ptr<const NetworkInterface>>
76 {
77  std::vector<shared_ptr<const NetworkInterface>> v;
78  v.reserve(m_interfaces.size());
79 
80  for (const auto& e : m_interfaces) {
81  v.push_back(e.second);
82  }
83  return v;
84 }
85 
86 bool
87 NetworkMonitorImplRtnl::isEnumerating() const
88 {
89  return m_isEnumeratingLinks || m_isEnumeratingAddresses;
90 }
91 
92 void
93 NetworkMonitorImplRtnl::initSocket()
94 {
95  NDN_LOG_TRACE("creating netlink socket");
96 
97  int fd = ::socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE);
98  if (fd < 0) {
99  BOOST_THROW_EXCEPTION(Error(std::string("Cannot create netlink socket (") +
100  std::strerror(errno) + ")"));
101  }
102  m_socket->assign(fd);
103 
104  sockaddr_nl addr{};
105  addr.nl_family = AF_NETLINK;
106  addr.nl_groups = RTMGRP_LINK | RTMGRP_NOTIFY |
107  RTMGRP_IPV4_IFADDR | RTMGRP_IPV4_ROUTE |
108  RTMGRP_IPV6_IFADDR | RTMGRP_IPV6_ROUTE;
109  if (::bind(fd, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) < 0) {
110  BOOST_THROW_EXCEPTION(Error(std::string("Cannot bind netlink socket (") +
111  std::strerror(errno) + ")"));
112  }
113 
114  // find out what pid has been assigned to us
115  socklen_t len = sizeof(addr);
116  if (::getsockname(fd, reinterpret_cast<sockaddr*>(&addr), &len) < 0) {
117  BOOST_THROW_EXCEPTION(Error(std::string("Cannot obtain netlink socket address (") +
118  std::strerror(errno) + ")"));
119  }
120  if (len != sizeof(addr)) {
121  BOOST_THROW_EXCEPTION(Error("Wrong address length (" + to_string(len) + ")"));
122  }
123  if (addr.nl_family != AF_NETLINK) {
124  BOOST_THROW_EXCEPTION(Error("Wrong address family (" + to_string(addr.nl_family) + ")"));
125  }
126  m_pid = addr.nl_pid;
127  NDN_LOG_TRACE("our pid is " << m_pid);
128 }
129 
130 void
131 NetworkMonitorImplRtnl::sendDumpRequest(uint16_t nlmsgType)
132 {
133  auto request = make_shared<RtnlRequest>();
134  request->nlh.nlmsg_len = sizeof(RtnlRequest);
135  request->nlh.nlmsg_type = nlmsgType;
136  request->nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
137  request->nlh.nlmsg_seq = ++m_sequenceNo;
138  request->nlh.nlmsg_pid = m_pid;
139  request->ifi.ifi_family = AF_UNSPEC;
140  request->rta.rta_type = IFLA_EXT_MASK;
141  request->rta.rta_len = RTA_LENGTH(sizeof(request->rtext));
142  request->rtext = 1 << 3; // RTEXT_FILTER_SKIP_STATS
143 
144  boost::asio::async_write(*m_socket, boost::asio::buffer(request.get(), sizeof(RtnlRequest)),
145  // capture 'request' to prevent its premature deallocation
146  [request] (const boost::system::error_code& error, size_t) {
147  if (error && error != boost::asio::error::operation_aborted) {
148  NDN_LOG_ERROR("write failed: " << error.message());
149  BOOST_THROW_EXCEPTION(Error("Failed to send netlink request (" + error.message() + ")"));
150  }
151  });
152 }
153 
154 static const char*
155 nlmsgTypeToString(uint16_t type)
156 {
157 #define NLMSG_STRINGIFY(x) case NLMSG_##x: return "<" #x ">"
158 #define RTM_STRINGIFY(x) case RTM_##x: return "<" #x ">"
159  switch (type) {
160  NLMSG_STRINGIFY(NOOP);
161  NLMSG_STRINGIFY(ERROR);
162  NLMSG_STRINGIFY(DONE);
163  NLMSG_STRINGIFY(OVERRUN);
164  RTM_STRINGIFY(NEWLINK);
165  RTM_STRINGIFY(DELLINK);
166  RTM_STRINGIFY(NEWADDR);
167  RTM_STRINGIFY(DELADDR);
168  RTM_STRINGIFY(NEWROUTE);
169  RTM_STRINGIFY(DELROUTE);
170  default:
171  return "";
172  }
173 #undef NLMSG_STRINGIFY
174 #undef RTM_STRINGIFY
175 }
176 
177 static InterfaceType
179 {
180  switch (type) {
181  case ARPHRD_ETHER:
183  case ARPHRD_LOOPBACK:
185  default:
186  return InterfaceType::UNKNOWN;
187  }
188 }
189 
190 static AddressFamily
192 {
193  switch (family) {
194  case AF_INET:
195  return AddressFamily::V4;
196  case AF_INET6:
197  return AddressFamily::V6;
198  default:
200  }
201 }
202 
203 static AddressScope
205 {
206  switch (scope) {
207  case RT_SCOPE_NOWHERE:
208  return AddressScope::NOWHERE;
209  case RT_SCOPE_HOST:
210  return AddressScope::HOST;
211  case RT_SCOPE_LINK:
212  return AddressScope::LINK;
213  default:
214  return AddressScope::GLOBAL;
215  }
216 }
217 
218 void
219 NetworkMonitorImplRtnl::asyncRead()
220 {
221  m_socket->async_read_some(boost::asio::buffer(m_buffer),
222  bind(&NetworkMonitorImplRtnl::handleRead, this, _1, _2, m_socket));
223 }
224 
225 void
226 NetworkMonitorImplRtnl::handleRead(const boost::system::error_code& error, size_t nBytesRead,
227  const shared_ptr<boost::asio::posix::stream_descriptor>& socket)
228 {
229  if (!socket->is_open() ||
231  // socket was closed, ignore the error
232  NDN_LOG_TRACE("socket closed or operation aborted");
233  return;
234  }
235  if (error) {
236  NDN_LOG_ERROR("read failed: " << error.message());
237  BOOST_THROW_EXCEPTION(Error("Netlink socket read failed (" + error.message() + ")"));
238  }
239 
240  NDN_LOG_TRACE("read " << nBytesRead << " bytes from netlink socket");
241 
242  const nlmsghdr* nlh = reinterpret_cast<const nlmsghdr*>(m_buffer.data());
243  if (!isEnumerating() || (nlh->nlmsg_seq == m_sequenceNo && nlh->nlmsg_pid == m_pid)) {
244  parseNetlinkMessage(nlh, nBytesRead);
245  }
246  else {
247  NDN_LOG_TRACE("seq/pid mismatch, ignoring");
248  }
249 
250  asyncRead();
251 }
252 
253 void
254 NetworkMonitorImplRtnl::parseNetlinkMessage(const nlmsghdr* nlh, size_t len)
255 {
256  while (NLMSG_OK(nlh, len)) {
257  NDN_LOG_TRACE("parsing " << (nlh->nlmsg_flags & NLM_F_MULTI ? "multi-part " : "") <<
258  "message type=" << nlh->nlmsg_type << nlmsgTypeToString(nlh->nlmsg_type) <<
259  " len=" << nlh->nlmsg_len <<
260  " seq=" << nlh->nlmsg_seq <<
261  " pid=" << nlh->nlmsg_pid);
262 
263  if (nlh->nlmsg_flags & NLM_F_DUMP_INTR) {
264  NDN_LOG_ERROR("netlink dump was interrupted");
265  // TODO: technically we should retry the dump...
266  break;
267  }
268 
269  if (nlh->nlmsg_type == NLMSG_DONE)
270  break;
271 
272  switch (nlh->nlmsg_type) {
273  case RTM_NEWLINK:
274  case RTM_DELLINK:
275  parseLinkMessage(nlh, reinterpret_cast<const ifinfomsg*>(NLMSG_DATA(nlh)));
276  if (!isEnumerating())
277  this->emitSignal(onNetworkStateChanged); // backward compat
278  break;
279 
280  case RTM_NEWADDR:
281  case RTM_DELADDR:
282  parseAddressMessage(nlh, reinterpret_cast<const ifaddrmsg*>(NLMSG_DATA(nlh)));
283  if (!isEnumerating())
284  this->emitSignal(onNetworkStateChanged); // backward compat
285  break;
286 
287  case RTM_NEWROUTE:
288  case RTM_DELROUTE:
289  parseRouteMessage(nlh, reinterpret_cast<const rtmsg*>(NLMSG_DATA(nlh)));
290  if (!isEnumerating())
291  this->emitSignal(onNetworkStateChanged); // backward compat
292  break;
293 
294  case NLMSG_ERROR: {
295  const nlmsgerr* err = reinterpret_cast<const nlmsgerr*>(NLMSG_DATA(nlh));
296  if (nlh->nlmsg_len < NLMSG_LENGTH(sizeof(nlmsgerr)))
297  NDN_LOG_ERROR("truncated NLMSG_ERROR");
298  else if (err->error == 0)
299  // an error code of zero indicates an ACK message, not an error
300  NDN_LOG_TRACE("ACK");
301  else
302  NDN_LOG_ERROR("NLMSG_ERROR: " << std::strerror(std::abs(err->error)));
303  break;
304  }
305  }
306 
307  nlh = NLMSG_NEXT(nlh, len);
308  }
309 
310  if (nlh->nlmsg_type == NLMSG_DONE && m_isEnumeratingLinks) {
311  // links enumeration complete, now request all the addresses
312  m_isEnumeratingLinks = false;
313  NDN_LOG_TRACE("enumerating addresses");
314  sendDumpRequest(RTM_GETADDR);
315  m_isEnumeratingAddresses = true;
316  }
317  else if (nlh->nlmsg_type == NLMSG_DONE && m_isEnumeratingAddresses) {
318  // links and addresses enumeration complete
319  m_isEnumeratingAddresses = false;
320  // TODO: enumerate routes
321  NDN_LOG_DEBUG("enumeration complete");
323  }
324 }
325 
326 void
327 NetworkMonitorImplRtnl::parseLinkMessage(const nlmsghdr* nlh, const ifinfomsg* ifi)
328 {
329  if (ifiTypeToInterfaceType(ifi->ifi_type) == InterfaceType::UNKNOWN) {
330  NDN_LOG_DEBUG("unhandled interface type " << ifi->ifi_type);
331  return;
332  }
333 
334  shared_ptr<NetworkInterface> interface;
335  auto it = m_interfaces.find(ifi->ifi_index);
336  if (it != m_interfaces.end()) {
337  interface = it->second;
338  BOOST_ASSERT(interface != nullptr);
339  BOOST_ASSERT(interface->getIndex() == ifi->ifi_index);
340  }
341 
342  if (nlh->nlmsg_type == RTM_DELLINK) {
343  if (interface != nullptr) {
344  NDN_LOG_DEBUG("removing interface " << interface->getName());
345  m_interfaces.erase(it);
346  this->emitSignal(onInterfaceRemoved, interface);
347  }
348  return;
349  }
350 
351  if (interface == nullptr) {
352  interface = makeNetworkInterface();
353  interface->setIndex(ifi->ifi_index);
354  }
355  interface->setType(ifiTypeToInterfaceType(ifi->ifi_type));
356  interface->setFlags(ifi->ifi_flags);
357 
358  const rtattr* rta = reinterpret_cast<const rtattr*>(IFLA_RTA(ifi));
359  size_t rtaTotalLen = IFLA_PAYLOAD(nlh);
360  uint8_t operState = linux_if::OPER_STATE_UNKNOWN;
361 
362  while (RTA_OK(rta, rtaTotalLen)) {
363  size_t attrLen = RTA_PAYLOAD(rta);
364 
365  switch (rta->rta_type) {
366  case IFLA_ADDRESS:
367  if (attrLen == ethernet::ADDR_LEN) {
368  ethernet::Address addr(reinterpret_cast<const uint8_t*>(RTA_DATA(rta)));
369  interface->setEthernetAddress(addr);
370  }
371  break;
372 
373  case IFLA_BROADCAST:
374  if (attrLen == ethernet::ADDR_LEN) {
375  ethernet::Address addr(reinterpret_cast<const uint8_t*>(RTA_DATA(rta)));
376  interface->setEthernetBroadcastAddress(addr);
377  }
378  break;
379 
380  case IFLA_IFNAME: {
381  auto attrData = reinterpret_cast<const char*>(RTA_DATA(rta));
382  if (::strnlen(attrData, attrLen) <= attrLen)
383  interface->setName(attrData);
384  break;
385  }
386 
387  case IFLA_MTU:
388  if (attrLen == sizeof(uint32_t))
389  interface->setMtu(*(reinterpret_cast<const uint32_t*>(RTA_DATA(rta))));
390  break;
391 
392  case IFLA_OPERSTATE:
393  if (attrLen == sizeof(uint8_t))
394  operState = *(reinterpret_cast<const uint8_t*>RTA_DATA(rta));
395  break;
396  }
397 
398  rta = RTA_NEXT(rta, rtaTotalLen);
399  }
400 
401  updateInterfaceState(*interface, operState);
402 
403  if (it == m_interfaces.end()) {
404  NDN_LOG_DEBUG("adding interface " << interface->getName());
405  m_interfaces[interface->getIndex()] = interface;
406  this->emitSignal(onInterfaceAdded, interface);
407  }
408 }
409 
410 void
411 NetworkMonitorImplRtnl::parseAddressMessage(const nlmsghdr* nlh, const ifaddrmsg* ifa)
412 {
413  auto it = m_interfaces.find(ifa->ifa_index);
414  if (it == m_interfaces.end()) {
415  // unknown interface, ignore message
416  NDN_LOG_TRACE("unknown interface index " << ifa->ifa_index);
417  return;
418  }
419  auto interface = it->second;
420  BOOST_ASSERT(interface != nullptr);
421 
422  namespace ip = boost::asio::ip;
423  ip::address ipAddr, broadcastAddr;
424  uint32_t flags = ifa->ifa_flags; // will be overridden by IFA_FLAGS if the attribute is present
425 
426  const rtattr* rta = reinterpret_cast<const rtattr*>(IFA_RTA(ifa));
427  size_t rtaTotalLen = IFA_PAYLOAD(nlh);
428 
429  while (RTA_OK(rta, rtaTotalLen)) {
430  auto attrData = reinterpret_cast<const unsigned char*>(RTA_DATA(rta));
431  size_t attrLen = RTA_PAYLOAD(rta);
432 
433  switch (rta->rta_type) {
434  case IFA_LOCAL:
435  if (ifa->ifa_family == AF_INET && attrLen == sizeof(ip::address_v4::bytes_type)) {
436  ip::address_v4::bytes_type bytes;
437  std::copy_n(attrData, bytes.size(), bytes.begin());
438  ipAddr = ip::address_v4(bytes);
439  }
440  break;
441 
442  case IFA_ADDRESS:
443  if (ifa->ifa_family == AF_INET6 && attrLen == sizeof(ip::address_v6::bytes_type)) {
444  ip::address_v6::bytes_type bytes;
445  std::copy_n(attrData, bytes.size(), bytes.begin());
446  ip::address_v6 v6Addr(bytes);
447  if (v6Addr.is_link_local())
448  v6Addr.scope_id(ifa->ifa_index);
449  ipAddr = v6Addr;
450  }
451  break;
452 
453  case IFA_BROADCAST:
454  if (ifa->ifa_family == AF_INET && attrLen == sizeof(ip::address_v4::bytes_type)) {
455  ip::address_v4::bytes_type bytes;
456  std::copy_n(attrData, bytes.size(), bytes.begin());
457  broadcastAddr = ip::address_v4(bytes);
458  }
459  break;
460 
461 #ifdef NDN_CXX_HAVE_IFA_FLAGS
462  case IFA_FLAGS:
463  if (attrLen == sizeof(uint32_t))
464  flags = *(reinterpret_cast<const uint32_t*>(attrData));
465  break;
466 #endif // NDN_CXX_HAVE_IFA_FLAGS
467  }
468 
469  rta = RTA_NEXT(rta, rtaTotalLen);
470  }
471 
472  NetworkAddress address(ifaFamilyToAddressFamily(ifa->ifa_family),
473  ipAddr,
474  broadcastAddr,
475  ifa->ifa_prefixlen,
476  ifaScopeToAddressScope(ifa->ifa_scope),
477  flags);
478  BOOST_ASSERT(address.getFamily() != AddressFamily::UNSPECIFIED);
479 
480  if (nlh->nlmsg_type == RTM_NEWADDR)
481  interface->addNetworkAddress(address);
482  else if (nlh->nlmsg_type == RTM_DELADDR)
483  interface->removeNetworkAddress(address);
484 }
485 
486 void
487 NetworkMonitorImplRtnl::parseRouteMessage(const nlmsghdr* nlh, const rtmsg* rtm)
488 {
489  // TODO
490 }
491 
492 void
493 NetworkMonitorImplRtnl::updateInterfaceState(NetworkInterface& interface, uint8_t operState)
494 {
495  if (operState == linux_if::OPER_STATE_UP) {
497  }
498  else if (operState == linux_if::OPER_STATE_DORMANT) {
500  }
501  else {
502  // fallback to flags
503  auto flags = interface.getFlags();
504  if ((flags & linux_if::FLAG_LOWER_UP) && !(flags & linux_if::FLAG_DORMANT))
506  else if (flags & IFF_UP)
508  else
509  interface.setState(InterfaceState::DOWN);
510  }
511 }
512 
513 } // namespace net
514 } // namespace ndn
interface is administratively down
static AddressScope ifaScopeToAddressScope(uint8_t scope)
Copyright (c) 2011-2015 Regents of the University of California.
interface can be used to send and receive packets
Copyright (c) 2013-2017 Regents of the University of California.
interface is administratively up but has no carrier
void setState(InterfaceState state)
#define RTM_STRINGIFY(x)
static shared_ptr< NetworkInterface > makeNetworkInterface()
const size_t ADDR_LEN
Octets in one Ethernet address.
Definition: ethernet.hpp:41
static const char * nlmsgTypeToString(uint16_t type)
static AddressFamily ifaFamilyToAddressFamily(uint8_t family)
#define NLMSG_STRINGIFY(x)
#define emitSignal(...)
(implementation detail)
Definition: emit.hpp:76
InterfaceType
Indicates the hardware type of a network interface.
constexpr duration< Rep, Period > abs(duration< Rep, Period > d)
Definition: time.hpp:53
static InterfaceType ifiTypeToInterfaceType(uint16_t type)
shared_ptr< const NetworkInterface > getNetworkInterface(const std::string &ifname) const final
util::Signal< NetworkMonitorImpl > onEnumerationCompleted
Represents one network interface attached to the host.
#define NDN_LOG_INIT(name)
declare a log module
Definition: logger.hpp:32
Stores one IP address supported by a network interface.
#define NDN_LOG_DEBUG(expression)
Definition: logger.hpp:35
util::Signal< NetworkMonitorImpl, shared_ptr< const NetworkInterface > > onInterfaceRemoved
NetworkMonitorImplRtnl(boost::asio::io_service &io)
initialize netlink socket and start enumerating interfaces
util::Signal< NetworkMonitorImpl, shared_ptr< const NetworkInterface > > onInterfaceAdded
interface has a carrier but it cannot send or receive normal user traffic yet
AddressFamily getFamily() const
Returns the address family.
represents an Ethernet hardware address
Definition: ethernet.hpp:52
uint32_t getFlags() const
Returns a bitset of platform-specific flags enabled on the interface.
#define NDN_LOG_ERROR(expression)
Definition: logger.hpp:38
std::string to_string(const V &v)
Definition: backports.hpp:84
util::Signal< NetworkMonitorImpl > onNetworkStateChanged
#define NDN_LOG_TRACE(expression)
Definition: logger.hpp:34
Catch-all error for socket component errors that don&#39;t fit in other categories.
Definition: base.hpp:83
std::vector< shared_ptr< const NetworkInterface > > listNetworkInterfaces() const final