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-2021 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.value_or(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  if ((flags & IFA_F_TENTATIVE) && nlmsg->nlmsg_type == RTM_NEWADDR) {
367  // https://redmine.named-data.net/issues/5155#note-10
368  NDN_LOG_DEBUG(" ignoring tentative address " << ipAddr);
369  return;
370  }
371 
372  NetworkAddress address(ifaFamilyToAddressFamily(ifa->ifa_family),
373  ipAddr,
374  broadcastAddr,
375  ifa->ifa_prefixlen,
376  ifaScopeToAddressScope(ifa->ifa_scope),
377  flags);
378  BOOST_ASSERT(address.getFamily() != AddressFamily::UNSPECIFIED);
379 
380  if (nlmsg->nlmsg_type == RTM_NEWADDR)
381  interface->addNetworkAddress(address);
382  else if (nlmsg->nlmsg_type == RTM_DELADDR)
383  interface->removeNetworkAddress(address);
384 }
385 
386 void
387 NetworkMonitorImplNetlink::parseRouteMessage(const NetlinkMessage& nlmsg)
388 {
389  // TODO
390 }
391 
392 void
393 NetworkMonitorImplNetlink::parseDoneMessage(const NetlinkMessage& nlmsg)
394 {
395  const int* errcode = nlmsg.getPayload<int>();
396  if (errcode == nullptr) {
397  NDN_LOG_WARN("malformed NLMSG_DONE");
398  }
399  else {
400  if (*errcode != 0) {
401  NDN_LOG_ERROR("NLMSG_DONE err=" << *errcode << " " << std::strerror(std::abs(*errcode)));
402  }
403 #ifdef NDN_CXX_HAVE_NETLINK_EXT_ACK
404  if (nlmsg->nlmsg_flags & NLM_F_ACK_TLVS) {
405  parseExtAckAttributes(nlmsg.getAttributes<nlattr>(errcode), *errcode != 0);
406  }
407 #endif // NDN_CXX_HAVE_NETLINK_EXT_ACK
408  }
409 
410  switch (m_phase) {
411  case ENUMERATING_LINKS:
412  // links enumeration complete, now request all the addresses
413  enumerateAddrs();
414  break;
415  case ENUMERATING_ADDRS:
416  // links and addresses enumeration complete, now request all the routes
417  enumerateRoutes();
418  break;
419  default:
420  break;
421  }
422 }
423 
424 void
425 NetworkMonitorImplNetlink::parseErrorMessage(const NetlinkMessage& nlmsg)
426 {
427  const nlmsgerr* err = nlmsg.getPayload<nlmsgerr>();
428  if (err == nullptr) {
429  NDN_LOG_WARN("malformed NLMSG_ERROR");
430  return;
431  }
432 
433  if (err->error != 0)
434  NDN_LOG_ERROR("NLMSG_ERROR for seq=" << err->msg.nlmsg_seq
435  << " err=" << err->error << " " << std::strerror(std::abs(err->error)));
436 
437 #ifdef NDN_CXX_HAVE_NETLINK_EXT_ACK
438  if (!(nlmsg->nlmsg_flags & NLM_F_ACK_TLVS))
439  return;
440 
441  size_t errLen = NLMSG_LENGTH(sizeof(nlmsgerr));
442  if (!(nlmsg->nlmsg_flags & NLM_F_CAPPED))
443  errLen += err->msg.nlmsg_len - NLMSG_HDRLEN; // don't count the inner nlmsghdr twice
444 
445  if (nlmsg->nlmsg_len <= errLen)
446  return;
447 
448  auto nla = reinterpret_cast<const nlattr*>(reinterpret_cast<const uint8_t*>(&*nlmsg) + errLen);
449  auto attrs = NetlinkMessageAttributes<nlattr>(nla, nlmsg->nlmsg_len - errLen);
450  parseExtAckAttributes(attrs, err->error != 0);
451 #endif // NDN_CXX_HAVE_NETLINK_EXT_ACK
452 }
453 
454 } // namespace net
455 } // namespace ndn
interface is administratively down
const T * getPayload() const noexcept
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
interface is administratively up but has no carrier
void setState(InterfaceState state)
static shared_ptr< NetworkInterface > makeNetworkInterface()
static void updateInterfaceState(NetworkInterface &interface, uint8_t operState)
#define NDN_LOG_WARN(expression)
Definition: logger.hpp:101
static AddressFamily ifaFamilyToAddressFamily(uint8_t family)
#define NDN_LOG_DEBUG(expression)
Definition: logger.hpp:99
#define emitSignal(...)
(implementation detail)
Definition: emit.hpp:76
InterfaceType
Indicates the hardware type of a network interface.
AddressFamily getFamily() const
Returns the address family.
constexpr duration< Rep, Period > abs(duration< Rep, Period > d)
Definition: time.hpp:58
static InterfaceType ifiTypeToInterfaceType(uint16_t type)
util::Signal< NetworkMonitorImpl > onEnumerationCompleted
#define NDN_LOG_ERROR(expression)
Definition: logger.hpp:102
Represents one network interface attached to the host.
Stores one IP address supported by a network interface.
util::Signal< NetworkMonitorImpl, shared_ptr< const NetworkInterface > > onInterfaceRemoved
optional< U > getAttributeByType(uint16_t attrType) const
#define NDN_LOG_TRACE(expression)
Definition: logger.hpp:98
util::Signal< NetworkMonitorImpl, shared_ptr< const NetworkInterface > > onInterfaceAdded
interface has a carrier but it cannot send or receive normal user traffic yet
represents an Ethernet hardware address
Definition: ethernet.hpp:52
void registerNotificationCallback(MessageCallback cb)
uint32_t getFlags() const
Returns a bitset of platform-specific flags enabled on the interface.
#define NDN_LOG_INIT(name)
declare a log module
Definition: logger.hpp:81
util::Signal< NetworkMonitorImpl > onNetworkStateChanged
NetlinkMessageAttributes< AttributeT > getAttributes(const PayloadT *p) const noexcept