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 NDN_LOG_INIT(ndn.NetworkMonitor);
39 
40 namespace ndn {
41 namespace net {
42 
44  : m_rtnlSocket(io)
45  , m_genlSocket(io)
46  , m_isEnumeratingLinks(false)
47  , m_isEnumeratingAddresses(false)
48 {
49  m_rtnlSocket.open();
50 
51  for (auto group : {RTNLGRP_LINK,
52  RTNLGRP_IPV4_IFADDR, RTNLGRP_IPV4_ROUTE,
53  RTNLGRP_IPV6_IFADDR, RTNLGRP_IPV6_ROUTE}) {
54  m_rtnlSocket.joinGroup(group);
55  }
56  m_rtnlSocket.registerNotificationCallback([this] (const auto& msg) { this->parseRtnlMessage(msg); });
57 
58  NDN_LOG_TRACE("enumerating links");
59  m_rtnlSocket.sendDumpRequest(RTM_GETLINK,
60  [this] (const auto& msg) { this->parseRtnlMessage(msg); });
61  m_isEnumeratingLinks = true;
62 }
63 
64 shared_ptr<const NetworkInterface>
65 NetworkMonitorImplNetlink::getNetworkInterface(const std::string& ifname) const
66 {
67  for (const auto& interface : m_interfaces | boost::adaptors::map_values) {
68  if (interface->getName() == ifname)
69  return interface;
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  boost::push_back(v, m_interfaces | boost::adaptors::map_values);
80  return v;
81 }
82 
83 bool
84 NetworkMonitorImplNetlink::isEnumerating() const
85 {
86  return m_isEnumeratingLinks || m_isEnumeratingAddresses;
87 }
88 
89 void
90 NetworkMonitorImplNetlink::parseRtnlMessage(const NetlinkMessage& nlmsg)
91 {
92  switch (nlmsg->nlmsg_type) {
93  case RTM_NEWLINK:
94  case RTM_DELLINK:
95  parseLinkMessage(nlmsg);
96  if (!isEnumerating())
97  this->emitSignal(onNetworkStateChanged); // backward compat
98  break;
99 
100  case RTM_NEWADDR:
101  case RTM_DELADDR:
102  parseAddressMessage(nlmsg);
103  if (!isEnumerating())
104  this->emitSignal(onNetworkStateChanged); // backward compat
105  break;
106 
107  case RTM_NEWROUTE:
108  case RTM_DELROUTE:
109  parseRouteMessage(nlmsg);
110  if (!isEnumerating())
111  this->emitSignal(onNetworkStateChanged); // backward compat
112  break;
113 
114  case NLMSG_DONE:
115  if (m_isEnumeratingLinks) {
116  // links enumeration complete, now request all the addresses
117  m_isEnumeratingLinks = false;
118  NDN_LOG_TRACE("enumerating addresses");
119  m_rtnlSocket.sendDumpRequest(RTM_GETADDR,
120  [this] (const auto& msg) { this->parseRtnlMessage(msg); });
121  m_isEnumeratingAddresses = true;
122  }
123  else if (m_isEnumeratingAddresses) {
124  // links and addresses enumeration complete
125  m_isEnumeratingAddresses = false;
126  // TODO: enumerate routes
127  NDN_LOG_DEBUG("enumeration complete");
129  }
130  break;
131 
132  case NLMSG_ERROR:
133  parseErrorMessage(nlmsg);
134  break;
135  }
136 }
137 
138 static InterfaceType
140 {
141  switch (type) {
142  case ARPHRD_ETHER:
144  case ARPHRD_LOOPBACK:
146  default:
147  return InterfaceType::UNKNOWN;
148  }
149 }
150 
151 static AddressFamily
153 {
154  switch (family) {
155  case AF_INET:
156  return AddressFamily::V4;
157  case AF_INET6:
158  return AddressFamily::V6;
159  default:
161  }
162 }
163 
164 static AddressScope
166 {
167  switch (scope) {
168  case RT_SCOPE_NOWHERE:
169  return AddressScope::NOWHERE;
170  case RT_SCOPE_HOST:
171  return AddressScope::HOST;
172  case RT_SCOPE_LINK:
173  return AddressScope::LINK;
174  default:
175  return AddressScope::GLOBAL;
176  }
177 }
178 
179 static void
180 updateInterfaceState(NetworkInterface& interface, uint8_t operState)
181 {
182  if (operState == linux_if::OPER_STATE_UP) {
184  }
185  else if (operState == linux_if::OPER_STATE_DORMANT) {
187  }
188  else {
189  // fallback to flags
190  auto flags = interface.getFlags();
191  if ((flags & linux_if::FLAG_LOWER_UP) && !(flags & linux_if::FLAG_DORMANT))
193  else if (flags & IFF_UP)
195  else
196  interface.setState(InterfaceState::DOWN);
197  }
198 }
199 
200 void
201 NetworkMonitorImplNetlink::parseLinkMessage(const NetlinkMessage& nlmsg)
202 {
203  const ifinfomsg* ifi = nlmsg.getPayload<ifinfomsg>();
204  if (ifi == nullptr) {
205  NDN_LOG_WARN("malformed ifinfomsg");
206  return;
207  }
208 
209  if (ifiTypeToInterfaceType(ifi->ifi_type) == InterfaceType::UNKNOWN) {
210  NDN_LOG_DEBUG("unhandled interface type " << ifi->ifi_type);
211  return;
212  }
213 
214  shared_ptr<NetworkInterface> interface;
215  auto it = m_interfaces.find(ifi->ifi_index);
216  if (it != m_interfaces.end()) {
217  interface = it->second;
218  BOOST_ASSERT(interface != nullptr);
219  BOOST_ASSERT(interface->getIndex() == ifi->ifi_index);
220  }
221 
222  if (nlmsg->nlmsg_type == RTM_DELLINK) {
223  if (interface != nullptr) {
224  NDN_LOG_DEBUG("removing interface " << interface->getName());
225  m_interfaces.erase(it);
226  this->emitSignal(onInterfaceRemoved, interface);
227  }
228  return;
229  }
230 
231  if (interface == nullptr) {
232  interface = makeNetworkInterface();
233  interface->setIndex(ifi->ifi_index);
234  }
235  interface->setType(ifiTypeToInterfaceType(ifi->ifi_type));
236  interface->setFlags(ifi->ifi_flags);
237 
238  auto attrs = nlmsg.getAttributes<rtattr>(ifi);
239  NDN_LOG_TRACE("message contains " << attrs.size() << " attributes");
240 
241  auto address = attrs.getAttributeByType<ethernet::Address>(IFLA_ADDRESS);
242  if (address)
243  interface->setEthernetAddress(*address);
244 
245  auto broadcast = attrs.getAttributeByType<ethernet::Address>(IFLA_BROADCAST);
246  if (broadcast)
247  interface->setEthernetBroadcastAddress(*broadcast);
248 
249  auto name = attrs.getAttributeByType<std::string>(IFLA_IFNAME);
250  if (name)
251  interface->setName(*name);
252 
253  auto mtu = attrs.getAttributeByType<uint32_t>(IFLA_MTU);
254  if (mtu)
255  interface->setMtu(*mtu);
256 
257  auto state = attrs.getAttributeByType<uint8_t>(IFLA_OPERSTATE);
258  updateInterfaceState(*interface, state ? *state : linux_if::OPER_STATE_UNKNOWN);
259 
260  if (it == m_interfaces.end()) {
261  NDN_LOG_DEBUG("adding interface " << interface->getName());
262  m_interfaces[interface->getIndex()] = interface;
263  this->emitSignal(onInterfaceAdded, interface);
264  }
265 }
266 
267 void
268 NetworkMonitorImplNetlink::parseAddressMessage(const NetlinkMessage& nlmsg)
269 {
270  const ifaddrmsg* ifa = nlmsg.getPayload<ifaddrmsg>();
271  if (ifa == nullptr) {
272  NDN_LOG_WARN("malformed ifaddrmsg");
273  return;
274  }
275 
276  auto it = m_interfaces.find(ifa->ifa_index);
277  if (it == m_interfaces.end()) {
278  // unknown interface, ignore message
279  NDN_LOG_TRACE("unknown interface index " << ifa->ifa_index);
280  return;
281  }
282  auto interface = it->second;
283  BOOST_ASSERT(interface != nullptr);
284 
285  auto attrs = nlmsg.getAttributes<rtattr>(ifa);
286  NDN_LOG_TRACE("message contains " << attrs.size() << " attributes");
287 
288  namespace ip = boost::asio::ip;
289  ip::address ipAddr, broadcastAddr;
290  if (ifa->ifa_family == AF_INET) {
291  auto v4 = attrs.getAttributeByType<ip::address_v4>(IFA_LOCAL);
292  if (v4)
293  ipAddr = *v4;
294 
295  v4 = attrs.getAttributeByType<ip::address_v4>(IFA_BROADCAST);
296  if (v4)
297  broadcastAddr = *v4;
298  }
299  else if (ifa->ifa_family == AF_INET6) {
300  auto v6 = attrs.getAttributeByType<ip::address_v6>(IFA_ADDRESS);
301  if (v6) {
302  if (v6->is_link_local())
303  v6->scope_id(ifa->ifa_index);
304 
305  ipAddr = *v6;
306  }
307  }
308 
309  uint32_t flags = ifa->ifa_flags; // overwritten by IFA_FLAGS if supported and present
310 #ifdef NDN_CXX_HAVE_IFA_FLAGS
311  auto extFlags = attrs.getAttributeByType<uint32_t>(IFA_FLAGS);
312  if (extFlags)
313  flags = *extFlags;
314 #endif // NDN_CXX_HAVE_IFA_FLAGS
315 
316  NetworkAddress address(ifaFamilyToAddressFamily(ifa->ifa_family),
317  ipAddr,
318  broadcastAddr,
319  ifa->ifa_prefixlen,
320  ifaScopeToAddressScope(ifa->ifa_scope),
321  flags);
322  BOOST_ASSERT(address.getFamily() != AddressFamily::UNSPECIFIED);
323 
324  if (nlmsg->nlmsg_type == RTM_NEWADDR)
325  interface->addNetworkAddress(address);
326  else if (nlmsg->nlmsg_type == RTM_DELADDR)
327  interface->removeNetworkAddress(address);
328 }
329 
330 void
331 NetworkMonitorImplNetlink::parseRouteMessage(const NetlinkMessage& nlmsg)
332 {
333  // TODO
334 }
335 
336 void
337 NetworkMonitorImplNetlink::parseErrorMessage(const NetlinkMessage& nlmsg)
338 {
339  const nlmsgerr* err = nlmsg.getPayload<nlmsgerr>();
340  if (err == nullptr) {
341  NDN_LOG_WARN("malformed nlmsgerr");
342  return;
343  }
344 
345  if (err->error == 0) {
346  // an error code of zero indicates an ACK message, not an error
347  NDN_LOG_TRACE("ACK");
348  return;
349  }
350 
351  NDN_LOG_ERROR("NLMSG_ERROR: " << std::strerror(std::abs(err->error)));
352 
353 #ifdef NDN_CXX_HAVE_NETLINK_EXT_ACK
354  if (!(nlmsg->nlmsg_flags & NLM_F_ACK_TLVS))
355  return;
356 
357  size_t errLen = NLMSG_LENGTH(sizeof(nlmsgerr));
358  if (!(nlmsg->nlmsg_flags & NLM_F_CAPPED))
359  errLen += err->msg.nlmsg_len - NLMSG_HDRLEN; // don't count the inner nlmsghdr twice
360 
361  if (nlmsg->nlmsg_len <= errLen)
362  return;
363 
364  auto nla = reinterpret_cast<const nlattr*>(reinterpret_cast<const uint8_t*>(&*nlmsg) + errLen);
365  auto attrs = NetlinkMessageAttributes<nlattr>(nla, nlmsg->nlmsg_len - errLen);
366  auto msg = attrs.getAttributeByType<std::string>(NLMSGERR_ATTR_MSG);
367  if (msg && !msg->empty())
368  NDN_LOG_ERROR("kernel message: " << *msg);
369 #endif // NDN_CXX_HAVE_NETLINK_EXT_ACK
370 }
371 
372 } // namespace net
373 } // 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
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:110
static AddressFamily ifaFamilyToAddressFamily(uint8_t family)
#define NDN_LOG_DEBUG(expression)
Definition: logger.hpp:108
#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:50
static InterfaceType ifiTypeToInterfaceType(uint16_t type)
util::Signal< NetworkMonitorImpl > onEnumerationCompleted
#define NDN_LOG_ERROR(expression)
Definition: logger.hpp:111
Represents one network interface attached to the host.
util::Signal< NetworkMonitorImpl, shared_ptr< const NetworkInterface > > onInterfaceRemoved
void sendDumpRequest(uint16_t nlmsgType, MessageCallback cb)
#define NDN_LOG_TRACE(expression)
Definition: logger.hpp:107
util::Signal< NetworkMonitorImpl, shared_ptr< const NetworkInterface > > onInterfaceAdded
interface has a carrier but it cannot send or receive normal user traffic yet
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:90
util::Signal< NetworkMonitorImpl > onNetworkStateChanged