NS-3 based Named Data Networking (NDN) simulator
ndnSIM 2.5: NDN, CCN, CCNx, content centric networks
API Documentation
network-monitor-impl-netlink.cpp
Go to the documentation of this file.
1 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2 /*
3  * Copyright (c) 2013-2019 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 
29 #include "ndn-cxx/util/logger.hpp"
30 
31 #include <linux/if_addr.h>
32 #include <linux/if_link.h>
33 #include <net/if_arp.h>
34 
35 #include <boost/range/adaptor/map.hpp>
36 #include <boost/range/algorithm_ext/push_back.hpp>
37 
38 #ifndef RTEXT_FILTER_SKIP_STATS
39 #define RTEXT_FILTER_SKIP_STATS (1 << 3)
40 #endif
41 
42 NDN_LOG_INIT(ndn.NetworkMonitor);
43 
44 namespace ndn {
45 namespace net {
46 
48  : m_rtnlSocket(io)
49  , m_genlSocket(io)
50 {
51  m_rtnlSocket.open();
52 
53  for (auto group : {RTNLGRP_LINK,
54  RTNLGRP_IPV4_IFADDR, RTNLGRP_IPV4_ROUTE,
55  RTNLGRP_IPV6_IFADDR, RTNLGRP_IPV6_ROUTE}) {
56  m_rtnlSocket.joinGroup(group);
57  }
58  m_rtnlSocket.registerNotificationCallback([this] (const auto& msg) { this->parseRtnlMessage(msg); });
59 
60  enumerateLinks();
61 }
62 
63 shared_ptr<const NetworkInterface>
64 NetworkMonitorImplNetlink::getNetworkInterface(const std::string& ifname) const
65 {
66  for (const auto& interface : m_interfaces | boost::adaptors::map_values) {
67  if (interface->getName() == ifname)
68  return interface;
69  }
70  return nullptr;
71 }
72 
73 std::vector<shared_ptr<const NetworkInterface>>
75 {
76  std::vector<shared_ptr<const NetworkInterface>> v;
77  v.reserve(m_interfaces.size());
78  boost::push_back(v, m_interfaces | boost::adaptors::map_values);
79  return v;
80 }
81 
82 void
83 NetworkMonitorImplNetlink::enumerateLinks()
84 {
85  NDN_LOG_TRACE("enumerating links");
86  m_phase = ENUMERATING_LINKS;
87 
88  struct IfInfoMessage
89  {
90  alignas(NLMSG_ALIGNTO) ifinfomsg ifi;
91  alignas(RTA_ALIGNTO) rtattr rta;
92  alignas(RTA_ALIGNTO) uint32_t rtext; // space for IFLA_EXT_MASK
93  };
94 
95  auto payload = make_shared<IfInfoMessage>();
96  payload->ifi.ifi_family = AF_UNSPEC;
97  payload->rta.rta_type = IFLA_EXT_MASK;
98  payload->rta.rta_len = RTA_LENGTH(sizeof(payload->rtext));
99  payload->rtext = RTEXT_FILTER_SKIP_STATS;
100 
101  m_rtnlSocket.sendDumpRequest(RTM_GETLINK, payload.get(), sizeof(IfInfoMessage),
102  // capture 'payload' to prevent its premature deallocation
103  [this, payload] (const auto& msg) { this->parseRtnlMessage(msg); });
104 }
105 
106 void
107 NetworkMonitorImplNetlink::enumerateAddrs()
108 {
109  NDN_LOG_TRACE("enumerating addresses");
110  m_phase = ENUMERATING_ADDRS;
111 
112  struct IfAddrMessage
113  {
114  alignas(NLMSG_ALIGNTO) ifaddrmsg ifa;
115  };
116 
117  auto payload = make_shared<IfAddrMessage>();
118  payload->ifa.ifa_family = AF_UNSPEC;
119 
120  m_rtnlSocket.sendDumpRequest(RTM_GETADDR, payload.get(), sizeof(IfAddrMessage),
121  // capture 'payload' to prevent its premature deallocation
122  [this, payload] (const auto& msg) { this->parseRtnlMessage(msg); });
123 }
124 
125 void
126 NetworkMonitorImplNetlink::enumerateRoutes()
127 {
128  // TODO: enumerate routes
129  //NDN_LOG_TRACE("enumerating routes");
130  //m_phase = ENUMERATING_ROUTES;
131 
132  NDN_LOG_DEBUG("enumeration complete");
133  m_phase = ENUMERATION_COMPLETE;
135 }
136 
137 void
138 NetworkMonitorImplNetlink::parseRtnlMessage(const NetlinkMessage& nlmsg)
139 {
140  switch (nlmsg->nlmsg_type) {
141  case RTM_NEWLINK:
142  case RTM_DELLINK:
143  parseLinkMessage(nlmsg);
144  if (m_phase == ENUMERATION_COMPLETE)
145  this->emitSignal(onNetworkStateChanged); // backward compat
146  break;
147 
148  case RTM_NEWADDR:
149  case RTM_DELADDR:
150  parseAddressMessage(nlmsg);
151  if (m_phase == ENUMERATION_COMPLETE)
152  this->emitSignal(onNetworkStateChanged); // backward compat
153  break;
154 
155  case RTM_NEWROUTE:
156  case RTM_DELROUTE:
157  parseRouteMessage(nlmsg);
158  if (m_phase == ENUMERATION_COMPLETE)
159  this->emitSignal(onNetworkStateChanged); // backward compat
160  break;
161 
162  case NLMSG_DONE:
163  parseDoneMessage(nlmsg);
164  break;
165 
166  case NLMSG_ERROR:
167  parseErrorMessage(nlmsg);
168  break;
169  }
170 }
171 
172 static InterfaceType
174 {
175  switch (type) {
176  case ARPHRD_ETHER:
178  case ARPHRD_LOOPBACK:
180  default:
181  return InterfaceType::UNKNOWN;
182  }
183 }
184 
185 static AddressFamily
187 {
188  switch (family) {
189  case AF_INET:
190  return AddressFamily::V4;
191  case AF_INET6:
192  return AddressFamily::V6;
193  default:
195  }
196 }
197 
198 static AddressScope
200 {
201  switch (scope) {
202  case RT_SCOPE_NOWHERE:
203  return AddressScope::NOWHERE;
204  case RT_SCOPE_HOST:
205  return AddressScope::HOST;
206  case RT_SCOPE_LINK:
207  return AddressScope::LINK;
208  default:
209  return AddressScope::GLOBAL;
210  }
211 }
212 
213 static void
214 updateInterfaceState(NetworkInterface& interface, uint8_t operState)
215 {
216  if (operState == linux_if::OPER_STATE_UP) {
218  }
219  else if (operState == linux_if::OPER_STATE_DORMANT) {
221  }
222  else {
223  // fallback to flags
224  auto flags = interface.getFlags();
225  if ((flags & linux_if::FLAG_LOWER_UP) && !(flags & linux_if::FLAG_DORMANT))
227  else if (flags & IFF_UP)
229  else
230  interface.setState(InterfaceState::DOWN);
231  }
232 }
233 
234 #ifdef NDN_CXX_HAVE_NETLINK_EXT_ACK
235 static void
236 parseExtAckAttributes(const NetlinkMessageAttributes<nlattr>& attrs, bool isError)
237 {
238  NDN_LOG_TRACE(" message contains " << attrs.size() << " attributes");
239 
240  auto msg = attrs.getAttributeByType<std::string>(NLMSGERR_ATTR_MSG);
241  if (msg && !msg->empty()) {
242  if (isError)
243  NDN_LOG_ERROR(" extended err: " << *msg);
244  else
245  NDN_LOG_DEBUG(" extended msg: " << *msg);
246  }
247 }
248 #endif // NDN_CXX_HAVE_NETLINK_EXT_ACK
249 
250 void
251 NetworkMonitorImplNetlink::parseLinkMessage(const NetlinkMessage& nlmsg)
252 {
253  const ifinfomsg* ifi = nlmsg.getPayload<ifinfomsg>();
254  if (ifi == nullptr) {
255  NDN_LOG_WARN("malformed ifinfomsg");
256  return;
257  }
258 
259  if (ifiTypeToInterfaceType(ifi->ifi_type) == InterfaceType::UNKNOWN) {
260  NDN_LOG_DEBUG(" unhandled interface type " << ifi->ifi_type);
261  return;
262  }
263 
264  shared_ptr<NetworkInterface> interface;
265  auto it = m_interfaces.find(ifi->ifi_index);
266  if (it != m_interfaces.end()) {
267  interface = it->second;
268  BOOST_ASSERT(interface != nullptr);
269  BOOST_ASSERT(interface->getIndex() == ifi->ifi_index);
270  }
271 
272  if (nlmsg->nlmsg_type == RTM_DELLINK) {
273  if (interface != nullptr) {
274  NDN_LOG_DEBUG(" removing interface " << interface->getName());
275  m_interfaces.erase(it);
276  this->emitSignal(onInterfaceRemoved, interface);
277  }
278  return;
279  }
280 
281  if (interface == nullptr) {
282  interface = makeNetworkInterface();
283  interface->setIndex(ifi->ifi_index);
284  }
285  interface->setType(ifiTypeToInterfaceType(ifi->ifi_type));
286  interface->setFlags(ifi->ifi_flags);
287 
288  auto attrs = nlmsg.getAttributes<rtattr>(ifi);
289  NDN_LOG_TRACE(" message contains " << attrs.size() << " attributes");
290 
291  auto address = attrs.getAttributeByType<ethernet::Address>(IFLA_ADDRESS);
292  if (address)
293  interface->setEthernetAddress(*address);
294 
295  auto broadcast = attrs.getAttributeByType<ethernet::Address>(IFLA_BROADCAST);
296  if (broadcast)
297  interface->setEthernetBroadcastAddress(*broadcast);
298 
299  auto name = attrs.getAttributeByType<std::string>(IFLA_IFNAME);
300  if (name)
301  interface->setName(*name);
302 
303  auto mtu = attrs.getAttributeByType<uint32_t>(IFLA_MTU);
304  if (mtu)
305  interface->setMtu(*mtu);
306 
307  auto state = attrs.getAttributeByType<uint8_t>(IFLA_OPERSTATE);
308  updateInterfaceState(*interface, state ? *state : linux_if::OPER_STATE_UNKNOWN);
309 
310  if (it == m_interfaces.end()) {
311  NDN_LOG_DEBUG(" adding interface " << interface->getName());
312  m_interfaces[interface->getIndex()] = interface;
313  this->emitSignal(onInterfaceAdded, interface);
314  }
315 }
316 
317 void
318 NetworkMonitorImplNetlink::parseAddressMessage(const NetlinkMessage& nlmsg)
319 {
320  const ifaddrmsg* ifa = nlmsg.getPayload<ifaddrmsg>();
321  if (ifa == nullptr) {
322  NDN_LOG_WARN("malformed ifaddrmsg");
323  return;
324  }
325 
326  auto it = m_interfaces.find(ifa->ifa_index);
327  if (it == m_interfaces.end()) {
328  // unknown interface, ignore message
329  NDN_LOG_TRACE(" unknown interface index " << ifa->ifa_index);
330  return;
331  }
332  auto interface = it->second;
333  BOOST_ASSERT(interface != nullptr);
334 
335  auto attrs = nlmsg.getAttributes<rtattr>(ifa);
336  NDN_LOG_TRACE(" message contains " << attrs.size() << " attributes");
337 
338  namespace ip = boost::asio::ip;
339  ip::address ipAddr, broadcastAddr;
340  if (ifa->ifa_family == AF_INET) {
341  auto v4 = attrs.getAttributeByType<ip::address_v4>(IFA_LOCAL);
342  if (v4)
343  ipAddr = *v4;
344 
345  v4 = attrs.getAttributeByType<ip::address_v4>(IFA_BROADCAST);
346  if (v4)
347  broadcastAddr = *v4;
348  }
349  else if (ifa->ifa_family == AF_INET6) {
350  auto v6 = attrs.getAttributeByType<ip::address_v6>(IFA_ADDRESS);
351  if (v6) {
352  if (v6->is_link_local())
353  v6->scope_id(ifa->ifa_index);
354 
355  ipAddr = *v6;
356  }
357  }
358 
359  uint32_t flags = ifa->ifa_flags; // overwritten by IFA_FLAGS if supported and present
360 #ifdef NDN_CXX_HAVE_IFA_FLAGS
361  auto extFlags = attrs.getAttributeByType<uint32_t>(IFA_FLAGS);
362  if (extFlags)
363  flags = *extFlags;
364 #endif // NDN_CXX_HAVE_IFA_FLAGS
365 
366  NetworkAddress address(ifaFamilyToAddressFamily(ifa->ifa_family),
367  ipAddr,
368  broadcastAddr,
369  ifa->ifa_prefixlen,
370  ifaScopeToAddressScope(ifa->ifa_scope),
371  flags);
372  BOOST_ASSERT(address.getFamily() != AddressFamily::UNSPECIFIED);
373 
374  if (nlmsg->nlmsg_type == RTM_NEWADDR)
375  interface->addNetworkAddress(address);
376  else if (nlmsg->nlmsg_type == RTM_DELADDR)
377  interface->removeNetworkAddress(address);
378 }
379 
380 void
381 NetworkMonitorImplNetlink::parseRouteMessage(const NetlinkMessage& nlmsg)
382 {
383  // TODO
384 }
385 
386 void
387 NetworkMonitorImplNetlink::parseDoneMessage(const NetlinkMessage& nlmsg)
388 {
389  const int* errcode = nlmsg.getPayload<int>();
390  if (errcode == nullptr) {
391  NDN_LOG_WARN("malformed NLMSG_DONE");
392  }
393  else {
394  if (*errcode != 0) {
395  NDN_LOG_ERROR("NLMSG_DONE err=" << *errcode << " " << std::strerror(std::abs(*errcode)));
396  }
397 #ifdef NDN_CXX_HAVE_NETLINK_EXT_ACK
398  if (nlmsg->nlmsg_flags & NLM_F_ACK_TLVS) {
399  parseExtAckAttributes(nlmsg.getAttributes<nlattr>(errcode), *errcode != 0);
400  }
401 #endif // NDN_CXX_HAVE_NETLINK_EXT_ACK
402  }
403 
404  switch (m_phase) {
405  case ENUMERATING_LINKS:
406  // links enumeration complete, now request all the addresses
407  enumerateAddrs();
408  break;
409  case ENUMERATING_ADDRS:
410  // links and addresses enumeration complete, now request all the routes
411  enumerateRoutes();
412  break;
413  default:
414  break;
415  }
416 }
417 
418 void
419 NetworkMonitorImplNetlink::parseErrorMessage(const NetlinkMessage& nlmsg)
420 {
421  const nlmsgerr* err = nlmsg.getPayload<nlmsgerr>();
422  if (err == nullptr) {
423  NDN_LOG_WARN("malformed NLMSG_ERROR");
424  return;
425  }
426 
427  if (err->error != 0)
428  NDN_LOG_ERROR("NLMSG_ERROR for seq=" << err->msg.nlmsg_seq
429  << " err=" << err->error << " " << std::strerror(std::abs(err->error)));
430 
431 #ifdef NDN_CXX_HAVE_NETLINK_EXT_ACK
432  if (!(nlmsg->nlmsg_flags & NLM_F_ACK_TLVS))
433  return;
434 
435  size_t errLen = NLMSG_LENGTH(sizeof(nlmsgerr));
436  if (!(nlmsg->nlmsg_flags & NLM_F_CAPPED))
437  errLen += err->msg.nlmsg_len - NLMSG_HDRLEN; // don't count the inner nlmsghdr twice
438 
439  if (nlmsg->nlmsg_len <= errLen)
440  return;
441 
442  auto nla = reinterpret_cast<const nlattr*>(reinterpret_cast<const uint8_t*>(&*nlmsg) + errLen);
443  auto attrs = NetlinkMessageAttributes<nlattr>(nla, nlmsg->nlmsg_len - errLen);
444  parseExtAckAttributes(attrs, err->error != 0);
445 #endif // NDN_CXX_HAVE_NETLINK_EXT_ACK
446 }
447 
448 } // namespace net
449 } // namespace ndn
NDN_LOG_INIT
#define NDN_LOG_INIT(name)
declare a log module
Definition: logger.hpp:81
ndn::net::NetworkInterface::setState
void setState(InterfaceState state)
Definition: network-interface.cpp:99
ndn::net::NetworkInterface
Represents one network interface attached to the host.
Definition: network-interface.hpp:70
ndn::net::NetlinkSocket::joinGroup
void joinGroup(int group)
Definition: netlink-socket.cpp:178
ndn::net::RtnlSocket::sendDumpRequest
void sendDumpRequest(uint16_t nlmsgType, const void *payload, size_t payloadLen, MessageCallback cb)
Definition: netlink-socket.cpp:363
ndn::net::InterfaceType::LOOPBACK
@ LOOPBACK
ndn::net::AddressScope::NOWHERE
@ NOWHERE
ndn::net::InterfaceState::NO_CARRIER
@ NO_CARRIER
interface is administratively up but has no carrier
ndn::net::AddressFamily::UNSPECIFIED
@ UNSPECIFIED
linux-if-constants.hpp
ndn::net::AddressScope::LINK
@ LINK
ndn::net::NetlinkSocket::registerNotificationCallback
void registerNotificationCallback(MessageCallback cb)
Definition: netlink-socket.cpp:188
network-address.hpp
ndn::net::NetworkMonitorImpl::onInterfaceRemoved
util::Signal< NetworkMonitorImpl, shared_ptr< const NetworkInterface > > onInterfaceRemoved
Definition: network-monitor.hpp:155
ndn::net::AddressFamily::V6
@ V6
ndn::net::NetworkMonitorImpl::onNetworkStateChanged
util::Signal< NetworkMonitorImpl > onNetworkStateChanged
Definition: network-monitor.hpp:156
ndn::net::AddressScope::HOST
@ HOST
ndn::net::RtnlSocket::open
void open()
Definition: netlink-socket.cpp:356
ndn::net::NetworkMonitorImpl::makeNetworkInterface
static shared_ptr< NetworkInterface > makeNetworkInterface()
Definition: network-monitor.cpp:91
ndn::net::InterfaceType::ETHERNET
@ ETHERNET
ndn::net::NetworkInterface::getFlags
uint32_t getFlags() const
Returns a bitset of platform-specific flags enabled on the interface.
Definition: network-interface.hpp:116
ndn::net::AddressScope::GLOBAL
@ GLOBAL
ndn::net::InterfaceType
InterfaceType
Indicates the hardware type of a network interface.
Definition: network-interface.hpp:38
ndn::net::AddressFamily
AddressFamily
Definition: network-address.hpp:34
ndn::net::NetworkMonitorImpl::onEnumerationCompleted
util::Signal< NetworkMonitorImpl > onEnumerationCompleted
Definition: network-monitor.hpp:153
logger.hpp
ndn::net::updateInterfaceState
static void updateInterfaceState(NetworkInterface &interface, uint8_t operState)
Definition: network-monitor-impl-netlink.cpp:214
NDN_LOG_WARN
#define NDN_LOG_WARN(expression)
Definition: logger.hpp:101
ndn::net::ifiTypeToInterfaceType
static InterfaceType ifiTypeToInterfaceType(uint16_t type)
Definition: network-monitor-impl-netlink.cpp:173
ndn::net::AddressFamily::V4
@ V4
ndn::net::ifaFamilyToAddressFamily
static AddressFamily ifaFamilyToAddressFamily(uint8_t family)
Definition: network-monitor-impl-netlink.cpp:186
ndn::net::AddressScope
AddressScope
Definition: network-address.hpp:40
ndn::net::ifaScopeToAddressScope
static AddressScope ifaScopeToAddressScope(uint8_t scope)
Definition: network-monitor-impl-netlink.cpp:199
NDN_LOG_DEBUG
#define NDN_LOG_DEBUG(expression)
Definition: logger.hpp:99
ndn::time::abs
constexpr duration< Rep, Period > abs(duration< Rep, Period > d)
Definition: time.hpp:50
ndn::name
Definition: name-component-types.hpp:33
ndn::net
Definition: link-type-helper.cpp:30
network-interface.hpp
ndn::net::InterfaceState::DORMANT
@ DORMANT
interface has a carrier but it cannot send or receive normal user traffic yet
NDN_LOG_TRACE
#define NDN_LOG_TRACE(expression)
Definition: logger.hpp:98
ndn::net::InterfaceState::DOWN
@ DOWN
interface is administratively down
emitSignal
#define emitSignal(...)
(implementation detail)
Definition: emit.hpp:76
ndn::net::NetworkMonitorImpl::onInterfaceAdded
util::Signal< NetworkMonitorImpl, shared_ptr< const NetworkInterface > > onInterfaceAdded
Definition: network-monitor.hpp:154
ndn::net::InterfaceState::RUNNING
@ RUNNING
interface can be used to send and receive packets
ndn::net::InterfaceType::UNKNOWN
@ UNKNOWN
ndn
Copyright (c) 2011-2015 Regents of the University of California.
Definition: ndn-strategy-choice-helper.hpp:34
NDN_LOG_ERROR
#define NDN_LOG_ERROR(expression)
Definition: logger.hpp:102