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