blob: 1a05986e957213637634b6ef8d869e97919dfd21 [file] [log] [blame]
Davide Pesavento9a8bae52016-02-24 20:33:08 +01001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
Davide Pesavento2bf35a62017-04-02 00:41:06 -04003 * Copyright (c) 2013-2017 Regents of the University of California.
Davide Pesavento9a8bae52016-02-24 20:33:08 +01004 *
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.
Davide Pesavento2bf35a62017-04-02 00:41:06 -040020 *
21 * @author Davide Pesavento <davide.pesavento@lip6.fr>
Davide Pesavento9a8bae52016-02-24 20:33:08 +010022 */
23
Davide Pesavento9a8bae52016-02-24 20:33:08 +010024#include "network-monitor-impl-rtnl.hpp"
Davide Pesavento2bf35a62017-04-02 00:41:06 -040025#include "linux-if-constants.hpp"
Davide Pesavento2bf35a62017-04-02 00:41:06 -040026#include "../network-address.hpp"
27#include "../network-interface.hpp"
Junxiao Shi25467942017-06-30 02:53:14 +000028#include "../../util/logger.hpp"
29#include "../../util/time.hpp"
Davide Pesavento9a8bae52016-02-24 20:33:08 +010030
Davide Pesavento2bf35a62017-04-02 00:41:06 -040031#include <boost/asio/write.hpp>
Davide Pesavento9a8bae52016-02-24 20:33:08 +010032
33#include <cerrno>
Davide Pesavento2bf35a62017-04-02 00:41:06 -040034#include <cstdlib>
35#include <net/if_arp.h>
36#include <sys/socket.h>
37
38NDN_LOG_INIT(ndn.NetworkMonitor);
Davide Pesavento9a8bae52016-02-24 20:33:08 +010039
40namespace ndn {
Junxiao Shi25467942017-06-30 02:53:14 +000041namespace net {
Davide Pesavento9a8bae52016-02-24 20:33:08 +010042
43NetworkMonitor::Impl::Impl(NetworkMonitor& nm, boost::asio::io_service& io)
44 : m_nm(nm)
Davide Pesavento2bf35a62017-04-02 00:41:06 -040045 , 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)
Davide Pesavento9a8bae52016-02-24 20:33:08 +010050{
Davide Pesavento2bf35a62017-04-02 00:41:06 -040051 initSocket();
52 asyncRead();
Davide Pesavento9a8bae52016-02-24 20:33:08 +010053
Davide Pesavento2bf35a62017-04-02 00:41:06 -040054 NDN_LOG_TRACE("enumerating links");
55 sendDumpRequest(RTM_GETLINK);
56 m_isEnumeratingLinks = true;
57}
Davide Pesavento9a8bae52016-02-24 20:33:08 +010058
Davide Pesavento2bf35a62017-04-02 00:41:06 -040059NetworkMonitor::Impl::~Impl()
60{
61 boost::system::error_code error;
62 m_socket->close(error);
63}
64
65shared_ptr<NetworkInterface>
66NetworkMonitor::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;
Davide Pesavento9a8bae52016-02-24 20:33:08 +010071 }
Davide Pesavento2bf35a62017-04-02 00:41:06 -040072 return nullptr;
73}
Davide Pesavento9a8bae52016-02-24 20:33:08 +010074
Davide Pesavento2bf35a62017-04-02 00:41:06 -040075std::vector<shared_ptr<NetworkInterface>>
76NetworkMonitor::Impl::listNetworkInterfaces() const
77{
78 std::vector<shared_ptr<NetworkInterface>> v;
79 v.reserve(m_interfaces.size());
Davide Pesavento9a8bae52016-02-24 20:33:08 +010080
Davide Pesavento2bf35a62017-04-02 00:41:06 -040081 for (const auto& e : m_interfaces) {
82 v.push_back(e.second);
83 }
84 return v;
85}
86
87bool
88NetworkMonitor::Impl::isEnumerating() const
89{
90 return m_isEnumeratingLinks || m_isEnumeratingAddresses;
Davide Pesavento9a8bae52016-02-24 20:33:08 +010091}
92
93void
Davide Pesavento2bf35a62017-04-02 00:41:06 -040094NetworkMonitor::Impl::initSocket()
Davide Pesavento9a8bae52016-02-24 20:33:08 +010095{
Davide Pesavento2bf35a62017-04-02 00:41:06 -040096 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
131void
132NetworkMonitor::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
155static const char*
156nlmsgTypeToString(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);
162 NDN_NLMSG_STRING(ERROR);
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
178static InterfaceType
179ifiTypeToInterfaceType(uint16_t type)
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
191static AddressFamily
192ifaFamilyToAddressFamily(uint8_t family)
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
204static AddressScope
205ifaScopeToAddressScope(uint8_t scope)
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
219void
220NetworkMonitor::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
226void
227NetworkMonitor::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 }
Davide Pesavento9a8bae52016-02-24 20:33:08 +0100236 if (error) {
Davide Pesavento2bf35a62017-04-02 00:41:06 -0400237 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
254void
255NetworkMonitor::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
327void
328NetworkMonitor::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);
Davide Pesavento9a8bae52016-02-24 20:33:08 +0100332 return;
333 }
334
Davide Pesavento2bf35a62017-04-02 00:41:06 -0400335 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);
Davide Pesavento9a8bae52016-02-24 20:33:08 +0100341 }
342
Davide Pesavento2bf35a62017-04-02 00:41:06 -0400343 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
412void
413NetworkMonitor::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
482void
483NetworkMonitor::Impl::parseRouteMessage(const nlmsghdr* nlh, const rtmsg* rtm)
484{
485 // TODO
486}
487
488void
489NetworkMonitor::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 }
Davide Pesavento9a8bae52016-02-24 20:33:08 +0100507}
508
Junxiao Shi25467942017-06-30 02:53:14 +0000509} // namespace net
Davide Pesavento9a8bae52016-02-24 20:33:08 +0100510} // namespace ndn