net: move network-related files to src/net
Some namespaces are changed:
* ndn::util::FaceUri is now ndn::FaceUri
* ndn::util::ethernet is now ndn::ethernet
* ndn::util::NetworkMonitor and related classes are now in ndn::net
refs #3940
Change-Id: Ia4754caef9726efc73f5a303cec46fc95c744d70
diff --git a/src/net/detail/link-type-helper-osx.mm b/src/net/detail/link-type-helper-osx.mm
new file mode 100644
index 0000000..c45fa04
--- /dev/null
+++ b/src/net/detail/link-type-helper-osx.mm
@@ -0,0 +1,65 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2017 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "link-type-helper.hpp"
+
+#ifndef NDN_CXX_HAVE_OSX_FRAMEWORKS
+#error "This file should not be compiled ..."
+#endif
+
+#import <Foundation/Foundation.h>
+#import <CoreWLAN/CoreWLAN.h>
+#import <CoreWLAN/CWInterface.h>
+#import <CoreWLAN/CWWiFiClient.h>
+
+namespace ndn {
+namespace net {
+namespace detail {
+
+ndn::nfd::LinkType
+getLinkType(const std::string& ifName)
+{
+ @autoreleasepool {
+ NSString* interfaceName = [NSString stringWithCString:ifName.c_str()
+ encoding:[NSString defaultCStringEncoding]];
+
+ CWWiFiClient* wifiInterface = [CWWiFiClient sharedWiFiClient];
+ if (wifiInterface == nullptr) {
+ return nfd::LINK_TYPE_NONE;
+ }
+
+ CWInterface* airport = [wifiInterface interfaceWithName:interfaceName];
+ if (airport == nullptr) {
+ return nfd::LINK_TYPE_NONE;
+ }
+
+ if ([airport interfaceMode] == kCWInterfaceModeIBSS) {
+ return nfd::LINK_TYPE_AD_HOC;
+ }
+ else {
+ return nfd::LINK_TYPE_MULTI_ACCESS;
+ }
+ }
+}
+
+} // namespace detail
+} // namespace net
+} // namespace ndn
diff --git a/src/net/detail/link-type-helper.cpp b/src/net/detail/link-type-helper.cpp
new file mode 100644
index 0000000..36d51de
--- /dev/null
+++ b/src/net/detail/link-type-helper.cpp
@@ -0,0 +1,43 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2017 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "link-type-helper.hpp"
+#include "ndn-cxx-config.hpp"
+
+#ifdef NDN_CXX_HAVE_OSX_FRAMEWORKS
+// implemented in link-type-helper-osx.mm
+#else
+
+namespace ndn {
+namespace net {
+namespace detail {
+
+ndn::nfd::LinkType
+getLinkType(const std::string& ifName)
+{
+ return nfd::LINK_TYPE_NONE;
+}
+
+} // namespace detail
+} // namespace net
+} // namespace ndn
+
+#endif // NDN_CXX_HAVE_OSX_FRAMEWORKS
diff --git a/src/net/detail/link-type-helper.hpp b/src/net/detail/link-type-helper.hpp
new file mode 100644
index 0000000..9512f31
--- /dev/null
+++ b/src/net/detail/link-type-helper.hpp
@@ -0,0 +1,41 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2017 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#ifndef NDN_NET_DETAIL_LINK_TYPE_HELPER_HPP
+#define NDN_NET_DETAIL_LINK_TYPE_HELPER_HPP
+
+#include "../../encoding/nfd-constants.hpp"
+
+namespace ndn {
+namespace net {
+namespace detail {
+
+/**
+ * @brief Obtain information about WiFi link type
+ */
+ndn::nfd::LinkType
+getLinkType(const std::string& ifName);
+
+} // namespace detail
+} // namespace net
+} // namespace ndn
+
+#endif // NDN_NET_DETAIL_LINK_TYPE_HELPER_HPP
diff --git a/src/net/detail/linux-if-constants.cpp b/src/net/detail/linux-if-constants.cpp
new file mode 100644
index 0000000..dbcf54b
--- /dev/null
+++ b/src/net/detail/linux-if-constants.cpp
@@ -0,0 +1,51 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2017 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ *
+ * @author Davide Pesavento <davide.pesavento@lip6.fr>
+ */
+
+#ifdef __linux__
+
+#include "linux-if-constants.hpp"
+
+#include <sys/socket.h>
+#include <linux/if.h>
+
+namespace ndn {
+namespace net {
+namespace linux_if {
+
+const uint32_t FLAG_LOWER_UP = IFF_LOWER_UP;
+const uint32_t FLAG_DORMANT = IFF_DORMANT;
+const uint32_t FLAG_ECHO = IFF_ECHO;
+
+const uint8_t OPER_STATE_UNKNOWN = IF_OPER_UNKNOWN;
+const uint8_t OPER_STATE_NOTPRESENT = IF_OPER_NOTPRESENT;
+const uint8_t OPER_STATE_DOWN = IF_OPER_DOWN;
+const uint8_t OPER_STATE_LOWERLAYERDOWN = IF_OPER_LOWERLAYERDOWN;
+const uint8_t OPER_STATE_TESTING = IF_OPER_TESTING;
+const uint8_t OPER_STATE_DORMANT = IF_OPER_DORMANT;
+const uint8_t OPER_STATE_UP = IF_OPER_UP;
+
+} // namespace linux_if
+} // namespace net
+} // namespace ndn
+
+#endif // __linux__
diff --git a/src/net/detail/linux-if-constants.hpp b/src/net/detail/linux-if-constants.hpp
new file mode 100644
index 0000000..0670e70
--- /dev/null
+++ b/src/net/detail/linux-if-constants.hpp
@@ -0,0 +1,58 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2017 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ *
+ * @author Davide Pesavento <davide.pesavento@lip6.fr>
+ */
+
+#ifndef NDN_NET_LINUX_IF_CONSTANTS_HPP
+#define NDN_NET_LINUX_IF_CONSTANTS_HPP
+#ifdef __linux__
+
+#include <cstdint>
+
+namespace ndn {
+namespace net {
+namespace linux_if {
+
+// linux/if.h and net/if.h cannot be (directly or indirectly) included in the
+// same translation unit because they contain duplicate declarations, therefore
+// we have to resort to this workaround when we need to include both linux/if.h
+// and any other headers that pull in net/if.h (e.g. boost/asio.hpp)
+
+// net_device_flags missing from <net/if.h>
+extern const uint32_t FLAG_LOWER_UP;
+extern const uint32_t FLAG_DORMANT;
+extern const uint32_t FLAG_ECHO;
+
+// RFC 2863 operational status
+extern const uint8_t OPER_STATE_UNKNOWN;
+extern const uint8_t OPER_STATE_NOTPRESENT;
+extern const uint8_t OPER_STATE_DOWN;
+extern const uint8_t OPER_STATE_LOWERLAYERDOWN;
+extern const uint8_t OPER_STATE_TESTING;
+extern const uint8_t OPER_STATE_DORMANT;
+extern const uint8_t OPER_STATE_UP;
+
+} // namespace linux_if
+} // namespace net
+} // namespace ndn
+
+#endif // __linux__
+#endif // NDN_NET_LINUX_IF_CONSTANTS_HPP
diff --git a/src/net/detail/network-monitor-impl-noop.hpp b/src/net/detail/network-monitor-impl-noop.hpp
new file mode 100644
index 0000000..f0c5fb0
--- /dev/null
+++ b/src/net/detail/network-monitor-impl-noop.hpp
@@ -0,0 +1,61 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2017 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ *
+ * @author Davide Pesavento <davide.pesavento@lip6.fr>
+ */
+
+#ifndef NDN_NET_NETWORK_MONITOR_IMPL_NOOP_HPP
+#define NDN_NET_NETWORK_MONITOR_IMPL_NOOP_HPP
+
+#include "../network-monitor.hpp"
+
+namespace ndn {
+namespace net {
+
+class NetworkMonitor::Impl
+{
+public:
+ Impl(NetworkMonitor& nm, boost::asio::io_service& io)
+ {
+ }
+
+ uint32_t
+ getCapabilities() const
+ {
+ return NetworkMonitor::CAP_NONE;
+ }
+
+ shared_ptr<NetworkInterface>
+ getNetworkInterface(const std::string&) const
+ {
+ return {};
+ }
+
+ std::vector<shared_ptr<NetworkInterface>>
+ listNetworkInterfaces() const
+ {
+ return {};
+ }
+};
+
+} // namespace net
+} // namespace ndn
+
+#endif // NDN_NET_NETWORK_MONITOR_IMPL_NOOP_HPP
diff --git a/src/net/detail/network-monitor-impl-osx.cpp b/src/net/detail/network-monitor-impl-osx.cpp
new file mode 100644
index 0000000..97f3e7c
--- /dev/null
+++ b/src/net/detail/network-monitor-impl-osx.cpp
@@ -0,0 +1,453 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2017 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ *
+ *
+ * Parts of this implementation is based on daemondo command of MacPorts
+ * (https://www.macports.org/):
+ *
+ * Copyright (c) 2005-2007 James Berry <jberry@macports.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of The MacPorts Project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "ndn-cxx-config.hpp"
+
+#include "network-monitor-impl-osx.hpp"
+#include "../../name.hpp"
+#include "../../util/logger.hpp"
+#include "../network-address.hpp"
+
+#include <ifaddrs.h> // for getifaddrs()
+#include <arpa/inet.h> // for inet_ntop()
+#include <netinet/in.h> // for struct sockaddr_in{,6}
+#include <net/if_dl.h> // for struct sockaddr_dl
+#include <net/if_types.h> // for IFT_* constants
+
+#include <boost/asio.hpp>
+
+namespace ndn {
+namespace net {
+
+NDN_LOG_INIT(ndn.NetworkMonitor);
+
+NetworkMonitor::Impl::Impl(NetworkMonitor& nm, boost::asio::io_service& io)
+ : m_nm(nm)
+ , m_scheduler(io)
+ , m_cfLoopEvent(m_scheduler)
+ , m_context{0, this, nullptr, nullptr, nullptr}
+ , m_scStore(SCDynamicStoreCreate(nullptr, CFSTR("net.named-data.ndn-cxx.NetworkMonitor"),
+ &Impl::onConfigChanged, &m_context))
+ , m_loopSource(SCDynamicStoreCreateRunLoopSource(nullptr, m_scStore.get(), 0))
+ , m_nullUdpSocket(io, boost::asio::ip::udp::v4())
+
+{
+ scheduleCfLoop();
+
+ // Notifications from Darwin Notify Center:
+ //
+ // com.apple.system.config.network_change
+ //
+ CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(),
+ static_cast<void*>(this),
+ &Impl::afterNotificationCenterEvent,
+ CFSTR("com.apple.system.config.network_change"),
+ nullptr, // object to observe
+ CFNotificationSuspensionBehaviorDeliverImmediately);
+
+ io.post([this] { enumerateInterfaces(); });
+
+ CFRunLoopAddSource(CFRunLoopGetCurrent(), m_loopSource.get(), kCFRunLoopDefaultMode);
+
+ // Notifications from SystemConfiguration:
+ //
+ // State:/Network/Interface/.*/Link
+ // State:/Network/Interface/.*/IPv4
+ // State:/Network/Interface/.*/IPv6
+ // State:/Network/Global/DNS
+ // State:/Network/Global/IPv4
+ //
+ auto patterns = CFArrayCreateMutable(nullptr, 0, &kCFTypeArrayCallBacks);
+ CFArrayAppendValue(patterns, CFSTR("State:/Network/Interface/.*/Link"));
+ CFArrayAppendValue(patterns, CFSTR("State:/Network/Interface/.*/IPv4"));
+ CFArrayAppendValue(patterns, CFSTR("State:/Network/Interface/.*/IPv6"));
+ // CFArrayAppendValue(patterns, CFSTR("State:/Network/Global/DNS"));
+ // CFArrayAppendValue(patterns, CFSTR("State:/Network/Global/IPv4"));
+
+ SCDynamicStoreSetNotificationKeys(m_scStore.get(), nullptr, patterns);
+}
+
+NetworkMonitor::Impl::~Impl()
+{
+ CFRunLoopRemoveSource(CFRunLoopGetCurrent(), m_loopSource.get(), kCFRunLoopDefaultMode);
+
+ CFNotificationCenterRemoveEveryObserver(CFNotificationCenterGetDarwinNotifyCenter(),
+ static_cast<void*>(this));
+}
+
+shared_ptr<NetworkInterface>
+NetworkMonitor::Impl::getNetworkInterface(const std::string& ifname) const
+{
+ auto it = m_interfaces.find(ifname);
+ if (it != m_interfaces.end()) {
+ return it->second;
+ }
+ else {
+ return nullptr;
+ }
+}
+
+std::vector<shared_ptr<NetworkInterface>>
+NetworkMonitor::Impl::listNetworkInterfaces() const
+{
+ std::vector<shared_ptr<NetworkInterface>> v;
+ v.reserve(m_interfaces.size());
+
+ for (const auto& e : m_interfaces) {
+ v.push_back(e.second);
+ }
+ return v;
+}
+
+void
+NetworkMonitor::Impl::afterNotificationCenterEvent(CFNotificationCenterRef center,
+ void* observer,
+ CFStringRef name,
+ const void* object,
+ CFDictionaryRef userInfo)
+{
+ static_cast<Impl*>(observer)->m_nm.onNetworkStateChanged();
+}
+
+void
+NetworkMonitor::Impl::scheduleCfLoop()
+{
+ // poll each second for new events
+ m_cfLoopEvent = m_scheduler.scheduleEvent(time::seconds(1), bind(&Impl::pollCfLoop, this));
+}
+
+void
+NetworkMonitor::Impl::pollCfLoop()
+{
+ // this should dispatch ready events and exit
+ CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, true);
+
+ scheduleCfLoop();
+}
+
+void
+NetworkMonitor::Impl::addNewInterface(const std::string& ifName)
+{
+ shared_ptr<NetworkInterface> interface(new NetworkInterface);
+
+ interface->setName(ifName);
+ interface->setState(getInterfaceState(interface->getName()));
+ updateInterfaceInfo(*interface);
+ if (interface->getType() == InterfaceType::UNKNOWN) {
+ NDN_LOG_DEBUG("ignoring " << ifName << " because it has unhandled interface type");
+ return;
+ }
+
+ NDN_LOG_DEBUG("adding interface " << interface->getName());
+ m_interfaces.insert(make_pair(interface->getName(), interface));
+ m_nm.onInterfaceAdded(interface);
+}
+
+void
+NetworkMonitor::Impl::enumerateInterfaces()
+{
+ for (const auto& ifName : getInterfaceNames()) {
+ addNewInterface(ifName);
+ }
+ m_nm.onEnumerationCompleted();
+}
+
+static std::string
+convertToStdString(CFStringRef cfString)
+{
+ const char* cStr = CFStringGetCStringPtr(cfString, kCFStringEncodingASCII);
+ if (cStr != nullptr) {
+ return cStr;
+ }
+
+ size_t stringSize = CFStringGetLength(cfString);
+ char* buffer = new char[stringSize + 1];
+ CFStringGetCString(cfString, buffer, sizeof(buffer), kCFStringEncodingASCII);
+ std::string retval = buffer;
+ delete [] buffer;
+ return retval;
+}
+
+std::set<std::string>
+NetworkMonitor::Impl::getInterfaceNames()
+{
+ CFReleaser<CFDictionaryRef> dict = (CFDictionaryRef)SCDynamicStoreCopyValue(m_scStore.get(), CFSTR("State:/Network/Interface"));
+ CFArrayRef interfaces = (CFArrayRef)CFDictionaryGetValue(dict.get(), CFSTR("Interfaces"));
+
+ std::set<std::string> ifNames;
+ size_t count = CFArrayGetCount(interfaces);
+ for (size_t i = 0; i != count; ++i) {
+ auto ifName = (CFStringRef)CFArrayGetValueAtIndex(interfaces, i);
+ ifNames.insert(convertToStdString(ifName));
+ }
+ return ifNames;
+}
+
+InterfaceState
+NetworkMonitor::Impl::getInterfaceState(const std::string& ifName)
+{
+ CFReleaser<CFStringRef> linkName = CFStringCreateWithCString(nullptr,
+ ("State:/Network/Interface/" + ifName + "/Link").c_str(),
+ kCFStringEncodingASCII);
+
+ CFReleaser<CFDictionaryRef> dict = (CFDictionaryRef)SCDynamicStoreCopyValue(m_scStore.get(), linkName.get());
+ if (dict.get() == nullptr) {
+ return InterfaceState::UNKNOWN;
+ }
+
+ CFBooleanRef isActive = (CFBooleanRef)CFDictionaryGetValue(dict.get(), CFSTR("Active"));
+ if (isActive == nullptr) {
+ return InterfaceState::UNKNOWN;
+ }
+
+ return CFBooleanGetValue(isActive) ? InterfaceState::RUNNING : InterfaceState::DOWN;
+}
+
+void
+NetworkMonitor::Impl::updateInterfaceInfo(NetworkInterface& netif)
+{
+ ifaddrs* ifa_list = nullptr;
+ if (::getifaddrs(&ifa_list) < 0) {
+ BOOST_THROW_EXCEPTION(Error(std::string("getifaddrs() failed: ") + strerror(errno)));
+ }
+
+ for (ifaddrs* ifa = ifa_list; ifa != nullptr; ifa = ifa->ifa_next) {
+ if (ifa->ifa_name != netif.getName()) {
+ continue;
+ }
+
+ netif.setFlags(ifa->ifa_flags);
+ netif.setMtu(getInterfaceMtu(netif.getName()));
+
+ if (ifa->ifa_addr == nullptr)
+ continue;
+
+ NetworkAddress address;
+
+ switch (ifa->ifa_addr->sa_family) {
+ case AF_INET: {
+ address.m_family = AddressFamily::V4;
+
+ const sockaddr_in* sin = reinterpret_cast<sockaddr_in*>(ifa->ifa_addr);
+ boost::asio::ip::address_v4::bytes_type bytes;
+ std::copy_n(reinterpret_cast<const unsigned char*>(&sin->sin_addr), bytes.size(), bytes.begin());
+ address.m_ip = boost::asio::ip::address_v4(bytes);
+
+ const sockaddr_in* sinMask = reinterpret_cast<sockaddr_in*>(ifa->ifa_netmask);
+ std::copy_n(reinterpret_cast<const unsigned char*>(&sinMask->sin_addr), bytes.size(), bytes.begin());
+ uint8_t mask = 0;
+ for (auto byte : bytes) {
+ while (byte != 0) {
+ ++mask;
+ byte <<= 1;
+ }
+ }
+ address.m_prefixLength = mask;
+ break;
+ }
+
+ case AF_INET6: {
+ address.m_family = AddressFamily::V6;
+
+ const sockaddr_in6* sin6 = reinterpret_cast<sockaddr_in6*>(ifa->ifa_addr);
+ boost::asio::ip::address_v6::bytes_type bytes;
+ std::copy_n(reinterpret_cast<const unsigned char*>(&sin6->sin6_addr), bytes.size(), bytes.begin());
+ address.m_ip = boost::asio::ip::address_v6(bytes);
+
+ const sockaddr_in6* sinMask = reinterpret_cast<sockaddr_in6*>(ifa->ifa_netmask);
+ std::copy_n(reinterpret_cast<const unsigned char*>(&sinMask->sin6_addr), bytes.size(), bytes.begin());
+ uint8_t mask = 0;
+ for (auto byte : bytes) {
+ while (byte != 0) {
+ ++mask;
+ byte <<= 1;
+ }
+ }
+ address.m_prefixLength = mask;
+ break;
+ }
+
+ case AF_LINK: {
+ const sockaddr_dl* sdl = reinterpret_cast<sockaddr_dl*>(ifa->ifa_addr);
+ netif.setIndex(sdl->sdl_index);
+ if (sdl->sdl_type == IFT_ETHER && sdl->sdl_alen == ethernet::ADDR_LEN) {
+ netif.setType(InterfaceType::ETHERNET);
+ netif.setEthernetAddress(ethernet::Address(reinterpret_cast<uint8_t*>(LLADDR(sdl))));
+ NDN_LOG_TRACE(netif.getName() << ": set Ethernet address " << netif.getEthernetAddress());
+ }
+ else if (sdl->sdl_type == IFT_LOOP) {
+ netif.setType(InterfaceType::LOOPBACK);
+ }
+ else {
+ netif.setType(InterfaceType::UNKNOWN);
+ }
+ break;
+ }
+
+ default:
+ continue;
+ }
+
+ if (netif.canBroadcast() && ifa->ifa_broadaddr != nullptr) {
+ const sockaddr_in* sin = reinterpret_cast<sockaddr_in*>(ifa->ifa_broadaddr);
+ boost::asio::ip::address_v4::bytes_type bytes;
+ std::copy_n(reinterpret_cast<const unsigned char*>(&sin->sin_addr), bytes.size(), bytes.begin());
+ address.m_broadcast = boost::asio::ip::address_v4(bytes);
+ NDN_LOG_TRACE(netif.getName() << ": set IPv4 broadcast address " << address.m_broadcast);
+ }
+
+ if (netif.canBroadcast()) {
+ netif.setEthernetBroadcastAddress(ethernet::getBroadcastAddress());
+ }
+
+ netif.addNetworkAddress(address);
+ }
+
+ ::freeifaddrs(ifa_list);
+}
+
+size_t
+NetworkMonitor::Impl::getInterfaceMtu(const std::string& ifName)
+{
+ ifreq ifr{};
+ std::strncpy(ifr.ifr_name, ifName.c_str(), sizeof(ifr.ifr_name) - 1);
+
+ if (::ioctl(m_nullUdpSocket.native_handle(), SIOCGIFMTU, &ifr) == 0) {
+ return static_cast<size_t>(ifr.ifr_mtu);
+ }
+
+ NDN_LOG_WARN("Failed to get interface MTU: " << std::strerror(errno));
+ return ethernet::MAX_DATA_LEN;
+}
+
+void
+NetworkMonitor::Impl::onConfigChanged(SCDynamicStoreRef m_scStore, CFArrayRef changedKeys, void* context)
+{
+ static_cast<Impl*>(context)->onConfigChanged(changedKeys);
+}
+
+void
+NetworkMonitor::Impl::onConfigChanged(CFArrayRef changedKeys)
+{
+ size_t count = CFArrayGetCount(changedKeys);
+ for (size_t i = 0; i != count; ++i) {
+ std::string keyName = convertToStdString((CFStringRef)CFArrayGetValueAtIndex(changedKeys, i));
+ Name key(keyName);
+ std::string ifName = key.at(-2).toUri();
+
+ auto ifIt = m_interfaces.find(ifName);
+ if (ifIt == m_interfaces.end()) {
+ addNewInterface(ifName);
+ return;
+ }
+
+ NetworkInterface& netif = *ifIt->second;
+
+ auto removeInterface = [&] {
+ NDN_LOG_DEBUG("removing interface " << ifName);
+ shared_ptr<NetworkInterface> removedInterface = ifIt->second;
+ m_interfaces.erase(ifIt);
+ m_nm.onInterfaceRemoved(removedInterface);
+ };
+
+ if (key.at(-1).toUri() == "Link") {
+ auto newState = getInterfaceState(ifName);
+
+ if (newState == InterfaceState::UNKNOWN) {
+ // check if it is really unknown or interface removed
+ if (getInterfaceNames().count(ifName) == 0) {
+ // newState = InterfaceState::DOWN;
+ removeInterface();
+ return;
+ }
+ }
+
+ NDN_LOG_TRACE("Status of " << ifName << " changed from " << netif.getState() << " to " << newState);
+ netif.setState(newState);
+ }
+
+ if (key.at(-1).toUri() == "IPv4" || key.at(-1).toUri() == "IPv6") {
+ NetworkInterface updatedInterface;
+ updatedInterface.setName(ifName);
+ updateInterfaceInfo(updatedInterface);
+ if (updatedInterface.getType() == InterfaceType::UNKNOWN) {
+ // somehow, type of interface changed to unknown
+ NDN_LOG_DEBUG("Removing " << ifName << " because it changed to unhandled interface type");
+ removeInterface();
+ return;
+ }
+
+ const auto& newAddrs = updatedInterface.getNetworkAddresses();
+ const auto& oldAddrs = netif.getNetworkAddresses();
+
+ std::set<NetworkAddress> added;
+ std::set<NetworkAddress> removed;
+
+ std::set_difference(newAddrs.begin(), newAddrs.end(),
+ oldAddrs.begin(), oldAddrs.end(), std::inserter(added, added.end()));
+
+ std::set_difference(oldAddrs.begin(), oldAddrs.end(),
+ newAddrs.begin(), newAddrs.end(), std::inserter(removed, removed.end()));
+
+ for (const auto& addr : removed) {
+ netif.removeNetworkAddress(addr);
+ }
+
+ for (const auto& addr : added) {
+ netif.addNetworkAddress(addr);
+ }
+ }
+ }
+}
+
+} // namespace net
+} // namespace ndn
diff --git a/src/net/detail/network-monitor-impl-osx.hpp b/src/net/detail/network-monitor-impl-osx.hpp
new file mode 100644
index 0000000..3aac23f
--- /dev/null
+++ b/src/net/detail/network-monitor-impl-osx.hpp
@@ -0,0 +1,120 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2017 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#ifndef NDN_NET_NETWORK_MONITOR_IMPL_OSX_HPP
+#define NDN_NET_NETWORK_MONITOR_IMPL_OSX_HPP
+
+#include "ndn-cxx-config.hpp"
+#include "../network-monitor.hpp"
+
+#ifndef NDN_CXX_HAVE_OSX_FRAMEWORKS
+#error "This file should not be compiled ..."
+#endif
+
+#include "../network-interface.hpp"
+#include "../../security/tpm/helper-osx.hpp"
+#include "../../util/scheduler.hpp"
+#include "../../util/scheduler-scoped-event-id.hpp"
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <SystemConfiguration/SystemConfiguration.h>
+
+#include <boost/asio/ip/udp.hpp>
+
+namespace ndn {
+namespace net {
+
+class NetworkMonitor::Impl
+{
+public:
+ Impl(NetworkMonitor& nm, boost::asio::io_service& io);
+
+ ~Impl();
+
+ uint32_t
+ getCapabilities() const
+ {
+ return NetworkMonitor::CAP_ENUM | NetworkMonitor::CAP_IF_ADD_REMOVE |
+ NetworkMonitor::CAP_STATE_CHANGE | NetworkMonitor::CAP_ADDR_ADD_REMOVE;
+ }
+
+ shared_ptr<NetworkInterface>
+ getNetworkInterface(const std::string& ifname) const;
+
+ std::vector<shared_ptr<NetworkInterface>>
+ listNetworkInterfaces() const;
+
+ static void
+ afterNotificationCenterEvent(CFNotificationCenterRef center,
+ void* observer,
+ CFStringRef name,
+ const void* object,
+ CFDictionaryRef userInfo);
+
+private:
+ void
+ scheduleCfLoop();
+
+ void
+ pollCfLoop();
+
+ void
+ addNewInterface(const std::string& ifName);
+
+ void
+ enumerateInterfaces();
+
+ std::set<std::string>
+ getInterfaceNames();
+
+ InterfaceState
+ getInterfaceState(const std::string& ifName);
+
+ void
+ updateInterfaceInfo(NetworkInterface& netif);
+
+ size_t
+ getInterfaceMtu(const std::string& ifName);
+
+ static void
+ onConfigChanged(SCDynamicStoreRef store, CFArrayRef changedKeys, void* context);
+
+ void
+ onConfigChanged(CFArrayRef changedKeys);
+
+private:
+ NetworkMonitor& m_nm;
+ std::map<std::string /*ifname*/, shared_ptr<NetworkInterface>> m_interfaces; ///< interface map
+
+ util::Scheduler m_scheduler;
+ util::scheduler::ScopedEventId m_cfLoopEvent;
+
+ SCDynamicStoreContext m_context;
+ CFReleaser<SCDynamicStoreRef> m_scStore;
+ CFReleaser<CFRunLoopSourceRef> m_loopSource;
+
+ boost::asio::ip::udp::socket m_nullUdpSocket;
+};
+
+} // namespace net
+} // namespace ndn
+
+#endif // NDN_NET_NETWORK_MONITOR_IMPL_OSX_HPP
diff --git a/src/net/detail/network-monitor-impl-rtnl.cpp b/src/net/detail/network-monitor-impl-rtnl.cpp
new file mode 100644
index 0000000..1a05986
--- /dev/null
+++ b/src/net/detail/network-monitor-impl-rtnl.cpp
@@ -0,0 +1,510 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2017 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ *
+ * @author Davide Pesavento <davide.pesavento@lip6.fr>
+ */
+
+#include "network-monitor-impl-rtnl.hpp"
+#include "linux-if-constants.hpp"
+#include "../network-address.hpp"
+#include "../network-interface.hpp"
+#include "../../util/logger.hpp"
+#include "../../util/time.hpp"
+
+#include <boost/asio/write.hpp>
+
+#include <cerrno>
+#include <cstdlib>
+#include <net/if_arp.h>
+#include <sys/socket.h>
+
+NDN_LOG_INIT(ndn.NetworkMonitor);
+
+namespace ndn {
+namespace net {
+
+NetworkMonitor::Impl::Impl(NetworkMonitor& nm, boost::asio::io_service& io)
+ : m_nm(nm)
+ , m_socket(make_shared<boost::asio::posix::stream_descriptor>(io))
+ , m_pid(0)
+ , m_sequenceNo(static_cast<uint32_t>(time::system_clock::now().time_since_epoch().count()))
+ , m_isEnumeratingLinks(false)
+ , m_isEnumeratingAddresses(false)
+{
+ initSocket();
+ asyncRead();
+
+ NDN_LOG_TRACE("enumerating links");
+ sendDumpRequest(RTM_GETLINK);
+ m_isEnumeratingLinks = true;
+}
+
+NetworkMonitor::Impl::~Impl()
+{
+ boost::system::error_code error;
+ m_socket->close(error);
+}
+
+shared_ptr<NetworkInterface>
+NetworkMonitor::Impl::getNetworkInterface(const std::string& ifname) const
+{
+ for (const auto& e : m_interfaces) {
+ if (e.second->getName() == ifname)
+ return e.second;
+ }
+ return nullptr;
+}
+
+std::vector<shared_ptr<NetworkInterface>>
+NetworkMonitor::Impl::listNetworkInterfaces() const
+{
+ std::vector<shared_ptr<NetworkInterface>> v;
+ v.reserve(m_interfaces.size());
+
+ for (const auto& e : m_interfaces) {
+ v.push_back(e.second);
+ }
+ return v;
+}
+
+bool
+NetworkMonitor::Impl::isEnumerating() const
+{
+ return m_isEnumeratingLinks || m_isEnumeratingAddresses;
+}
+
+void
+NetworkMonitor::Impl::initSocket()
+{
+ NDN_LOG_TRACE("creating netlink socket");
+
+ int fd = ::socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE);
+ if (fd < 0) {
+ BOOST_THROW_EXCEPTION(Error(std::string("Cannot create netlink socket (") +
+ std::strerror(errno) + ")"));
+ }
+ m_socket->assign(fd);
+
+ sockaddr_nl addr{};
+ addr.nl_family = AF_NETLINK;
+ addr.nl_groups = RTMGRP_LINK | RTMGRP_NOTIFY |
+ RTMGRP_IPV4_IFADDR | RTMGRP_IPV4_ROUTE |
+ RTMGRP_IPV6_IFADDR | RTMGRP_IPV6_ROUTE;
+ if (::bind(fd, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) < 0) {
+ BOOST_THROW_EXCEPTION(Error(std::string("Cannot bind netlink socket (") +
+ std::strerror(errno) + ")"));
+ }
+
+ // find out what pid has been assigned to us
+ socklen_t len = sizeof(addr);
+ if (::getsockname(fd, reinterpret_cast<sockaddr*>(&addr), &len) < 0) {
+ BOOST_THROW_EXCEPTION(Error(std::string("Cannot obtain netlink socket address (") +
+ std::strerror(errno) + ")"));
+ }
+ if (len != sizeof(addr)) {
+ BOOST_THROW_EXCEPTION(Error("Wrong address length (" + to_string(len) + ")"));
+ }
+ if (addr.nl_family != AF_NETLINK) {
+ BOOST_THROW_EXCEPTION(Error("Wrong address family (" + to_string(addr.nl_family) + ")"));
+ }
+ m_pid = addr.nl_pid;
+ NDN_LOG_TRACE("our pid is " << m_pid);
+}
+
+void
+NetworkMonitor::Impl::sendDumpRequest(uint16_t nlmsgType)
+{
+ auto request = make_shared<RtnlRequest>();
+ request->nlh.nlmsg_len = sizeof(RtnlRequest);
+ request->nlh.nlmsg_type = nlmsgType;
+ request->nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
+ request->nlh.nlmsg_seq = ++m_sequenceNo;
+ request->nlh.nlmsg_pid = m_pid;
+ request->ifi.ifi_family = AF_UNSPEC;
+ request->rta.rta_type = IFLA_EXT_MASK;
+ request->rta.rta_len = RTA_LENGTH(sizeof(request->rtext));
+ request->rtext = 1 << 3; // RTEXT_FILTER_SKIP_STATS
+
+ boost::asio::async_write(*m_socket, boost::asio::buffer(request.get(), sizeof(RtnlRequest)),
+ // capture 'request' to prevent its premature deallocation
+ [request] (const boost::system::error_code& error, size_t) {
+ if (error && error != boost::asio::error::operation_aborted) {
+ NDN_LOG_ERROR("write failed: " << error.message());
+ BOOST_THROW_EXCEPTION(Error("Failed to send netlink request (" + error.message() + ")"));
+ }
+ });
+}
+
+static const char*
+nlmsgTypeToString(uint16_t type)
+{
+#define NDN_NLMSG_STRING(x) case NLMSG_##x: return "<" #x ">"
+#define NDN_RTM_STRING(x) case RTM_##x: return "<" #x ">"
+ switch (type) {
+ NDN_NLMSG_STRING(NOOP);
+ NDN_NLMSG_STRING(ERROR);
+ NDN_NLMSG_STRING(DONE);
+ NDN_NLMSG_STRING(OVERRUN);
+ NDN_RTM_STRING(NEWLINK);
+ NDN_RTM_STRING(DELLINK);
+ NDN_RTM_STRING(NEWADDR);
+ NDN_RTM_STRING(DELADDR);
+ NDN_RTM_STRING(NEWROUTE);
+ NDN_RTM_STRING(DELROUTE);
+ default:
+ return "";
+ }
+#undef NDN_NLMSG_STRING
+#undef NDN_RTM_STRING
+}
+
+static InterfaceType
+ifiTypeToInterfaceType(uint16_t type)
+{
+ switch (type) {
+ case ARPHRD_ETHER:
+ return InterfaceType::ETHERNET;
+ case ARPHRD_LOOPBACK:
+ return InterfaceType::LOOPBACK;
+ default:
+ return InterfaceType::UNKNOWN;
+ }
+}
+
+static AddressFamily
+ifaFamilyToAddressFamily(uint8_t family)
+{
+ switch (family) {
+ case AF_INET:
+ return AddressFamily::V4;
+ case AF_INET6:
+ return AddressFamily::V6;
+ default:
+ return AddressFamily::UNSPECIFIED;
+ }
+}
+
+static AddressScope
+ifaScopeToAddressScope(uint8_t scope)
+{
+ switch (scope) {
+ case RT_SCOPE_NOWHERE:
+ return AddressScope::NOWHERE;
+ case RT_SCOPE_HOST:
+ return AddressScope::HOST;
+ case RT_SCOPE_LINK:
+ return AddressScope::LINK;
+ default:
+ return AddressScope::GLOBAL;
+ }
+}
+
+void
+NetworkMonitor::Impl::asyncRead()
+{
+ m_socket->async_read_some(boost::asio::buffer(m_buffer),
+ bind(&Impl::handleRead, this, _1, _2, m_socket));
+}
+
+void
+NetworkMonitor::Impl::handleRead(const boost::system::error_code& error, size_t nBytesRead,
+ const shared_ptr<boost::asio::posix::stream_descriptor>& socket)
+{
+ if (!socket->is_open() ||
+ error == boost::asio::error::operation_aborted) {
+ // socket was closed, ignore the error
+ NDN_LOG_TRACE("socket closed or operation aborted");
+ return;
+ }
+ if (error) {
+ NDN_LOG_ERROR("read failed: " << error.message());
+ BOOST_THROW_EXCEPTION(Error("Netlink socket read failed (" + error.message() + ")"));
+ }
+
+ NDN_LOG_TRACE("read " << nBytesRead << " bytes from netlink socket");
+
+ const nlmsghdr* nlh = reinterpret_cast<const nlmsghdr*>(m_buffer.data());
+ if (!isEnumerating() || (nlh->nlmsg_seq == m_sequenceNo && nlh->nlmsg_pid == m_pid)) {
+ parseNetlinkMessage(nlh, nBytesRead);
+ }
+ else {
+ NDN_LOG_TRACE("seq/pid mismatch, ignoring");
+ }
+
+ asyncRead();
+}
+
+void
+NetworkMonitor::Impl::parseNetlinkMessage(const nlmsghdr* nlh, size_t len)
+{
+ while (NLMSG_OK(nlh, len)) {
+ NDN_LOG_TRACE("parsing " << (nlh->nlmsg_flags & NLM_F_MULTI ? "multi-part " : "") <<
+ "message type=" << nlh->nlmsg_type << nlmsgTypeToString(nlh->nlmsg_type) <<
+ " len=" << nlh->nlmsg_len <<
+ " seq=" << nlh->nlmsg_seq <<
+ " pid=" << nlh->nlmsg_pid);
+
+ if (nlh->nlmsg_flags & NLM_F_DUMP_INTR) {
+ NDN_LOG_ERROR("netlink dump was interrupted");
+ // TODO: technically we should retry the dump...
+ break;
+ }
+
+ if (nlh->nlmsg_type == NLMSG_DONE)
+ break;
+
+ switch (nlh->nlmsg_type) {
+ case RTM_NEWLINK:
+ case RTM_DELLINK:
+ parseLinkMessage(nlh, reinterpret_cast<const ifinfomsg*>(NLMSG_DATA(nlh)));
+ if (!isEnumerating())
+ m_nm.onNetworkStateChanged(); // backward compat
+ break;
+
+ case RTM_NEWADDR:
+ case RTM_DELADDR:
+ parseAddressMessage(nlh, reinterpret_cast<const ifaddrmsg*>(NLMSG_DATA(nlh)));
+ if (!isEnumerating())
+ m_nm.onNetworkStateChanged(); // backward compat
+ break;
+
+ case RTM_NEWROUTE:
+ case RTM_DELROUTE:
+ parseRouteMessage(nlh, reinterpret_cast<const rtmsg*>(NLMSG_DATA(nlh)));
+ if (!isEnumerating())
+ m_nm.onNetworkStateChanged(); // backward compat
+ break;
+
+ case NLMSG_ERROR: {
+ const nlmsgerr* err = reinterpret_cast<const nlmsgerr*>(NLMSG_DATA(nlh));
+ if (nlh->nlmsg_len < NLMSG_LENGTH(sizeof(nlmsgerr)))
+ NDN_LOG_ERROR("truncated NLMSG_ERROR");
+ else if (err->error == 0)
+ // an error code of zero indicates an ACK message, not an error
+ NDN_LOG_TRACE("ACK");
+ else
+ NDN_LOG_ERROR("NLMSG_ERROR: " << std::strerror(std::abs(err->error)));
+ break;
+ }
+ }
+
+ nlh = NLMSG_NEXT(nlh, len);
+ }
+
+ if (nlh->nlmsg_type == NLMSG_DONE && m_isEnumeratingLinks) {
+ // links enumeration complete, now request all the addresses
+ m_isEnumeratingLinks = false;
+ NDN_LOG_TRACE("enumerating addresses");
+ sendDumpRequest(RTM_GETADDR);
+ m_isEnumeratingAddresses = true;
+ }
+ else if (nlh->nlmsg_type == NLMSG_DONE && m_isEnumeratingAddresses) {
+ // links and addresses enumeration complete
+ m_isEnumeratingAddresses = false;
+ // TODO: enumerate routes
+ NDN_LOG_DEBUG("enumeration complete");
+ m_nm.onEnumerationCompleted();
+ }
+}
+
+void
+NetworkMonitor::Impl::parseLinkMessage(const nlmsghdr* nlh, const ifinfomsg* ifi)
+{
+ if (ifiTypeToInterfaceType(ifi->ifi_type) == InterfaceType::UNKNOWN) {
+ NDN_LOG_DEBUG("unhandled interface type " << ifi->ifi_type);
+ return;
+ }
+
+ shared_ptr<NetworkInterface> interface;
+ auto it = m_interfaces.find(ifi->ifi_index);
+ if (it != m_interfaces.end()) {
+ interface = it->second;
+ BOOST_ASSERT(interface != nullptr);
+ BOOST_ASSERT(interface->getIndex() == ifi->ifi_index);
+ }
+
+ if (nlh->nlmsg_type == RTM_DELLINK) {
+ if (interface != nullptr) {
+ NDN_LOG_DEBUG("removing interface " << interface->getName());
+ m_interfaces.erase(it);
+ m_nm.onInterfaceRemoved(interface);
+ }
+ return;
+ }
+
+ if (interface == nullptr) {
+ // cannot use make_shared because NetworkInterface constructor is private
+ interface.reset(new NetworkInterface);
+ interface->setIndex(ifi->ifi_index);
+ }
+ interface->setType(ifiTypeToInterfaceType(ifi->ifi_type));
+ interface->setFlags(ifi->ifi_flags);
+
+ const rtattr* rta = reinterpret_cast<const rtattr*>(IFLA_RTA(ifi));
+ size_t rtaTotalLen = IFLA_PAYLOAD(nlh);
+ uint8_t operState = linux_if::OPER_STATE_UNKNOWN;
+
+ while (RTA_OK(rta, rtaTotalLen)) {
+ size_t attrLen = RTA_PAYLOAD(rta);
+
+ switch (rta->rta_type) {
+ case IFLA_ADDRESS:
+ if (attrLen == ethernet::ADDR_LEN) {
+ ethernet::Address addr(reinterpret_cast<const uint8_t*>(RTA_DATA(rta)));
+ interface->setEthernetAddress(addr);
+ }
+ break;
+
+ case IFLA_BROADCAST:
+ if (attrLen == ethernet::ADDR_LEN) {
+ ethernet::Address addr(reinterpret_cast<const uint8_t*>(RTA_DATA(rta)));
+ interface->setEthernetBroadcastAddress(addr);
+ }
+ break;
+
+ case IFLA_IFNAME: {
+ auto attrData = reinterpret_cast<const char*>(RTA_DATA(rta));
+ if (::strnlen(attrData, attrLen) <= attrLen)
+ interface->setName(attrData);
+ break;
+ }
+
+ case IFLA_MTU:
+ if (attrLen == sizeof(uint32_t))
+ interface->setMtu(*(reinterpret_cast<const uint32_t*>(RTA_DATA(rta))));
+ break;
+
+ case IFLA_OPERSTATE:
+ if (attrLen == sizeof(uint8_t))
+ operState = *(reinterpret_cast<const uint8_t*>RTA_DATA(rta));
+ break;
+ }
+
+ rta = RTA_NEXT(rta, rtaTotalLen);
+ }
+
+ updateInterfaceState(*interface, operState);
+
+ if (it == m_interfaces.end()) {
+ NDN_LOG_DEBUG("adding interface " << interface->getName());
+ m_interfaces[interface->getIndex()] = interface;
+ m_nm.onInterfaceAdded(interface);
+ }
+}
+
+void
+NetworkMonitor::Impl::parseAddressMessage(const nlmsghdr* nlh, const ifaddrmsg* ifa)
+{
+ auto it = m_interfaces.find(ifa->ifa_index);
+ if (it == m_interfaces.end()) {
+ // unknown interface, ignore message
+ NDN_LOG_TRACE("unknown interface index " << ifa->ifa_index);
+ return;
+ }
+ auto interface = it->second;
+ BOOST_ASSERT(interface != nullptr);
+
+ namespace ip = boost::asio::ip;
+
+ NetworkAddress address;
+ address.m_family = ifaFamilyToAddressFamily(ifa->ifa_family);
+ BOOST_ASSERT(address.m_family != AddressFamily::UNSPECIFIED);
+ address.m_prefixLength = ifa->ifa_prefixlen;
+ address.m_flags = ifa->ifa_flags; // will be overridden by IFA_FLAGS below, if the attribute is present
+ address.m_scope = ifaScopeToAddressScope(ifa->ifa_scope);
+
+ const rtattr* rta = reinterpret_cast<const rtattr*>(IFA_RTA(ifa));
+ size_t rtaTotalLen = IFA_PAYLOAD(nlh);
+
+ while (RTA_OK(rta, rtaTotalLen)) {
+ auto attrData = reinterpret_cast<const unsigned char*>(RTA_DATA(rta));
+ size_t attrLen = RTA_PAYLOAD(rta);
+
+ switch (rta->rta_type) {
+ case IFA_LOCAL:
+ if (ifa->ifa_family == AF_INET && attrLen == sizeof(ip::address_v4::bytes_type)) {
+ ip::address_v4::bytes_type bytes;
+ std::copy_n(attrData, bytes.size(), bytes.begin());
+ address.m_ip = ip::address_v4(bytes);
+ }
+ break;
+
+ case IFA_ADDRESS:
+ if (ifa->ifa_family == AF_INET6 && attrLen == sizeof(ip::address_v6::bytes_type)) {
+ ip::address_v6::bytes_type bytes;
+ std::copy_n(attrData, bytes.size(), bytes.begin());
+ address.m_ip = ip::address_v6(bytes);
+ }
+ break;
+
+ case IFA_BROADCAST:
+ if (ifa->ifa_family == AF_INET && attrLen == sizeof(ip::address_v4::bytes_type)) {
+ ip::address_v4::bytes_type bytes;
+ std::copy_n(attrData, bytes.size(), bytes.begin());
+ address.m_broadcast = ip::address_v4(bytes);
+ }
+ break;
+
+#ifdef NDN_CXX_HAVE_IFA_FLAGS
+ case IFA_FLAGS:
+ if (attrLen == sizeof(uint32_t))
+ address.m_flags = *(reinterpret_cast<const uint32_t*>(attrData));
+ break;
+#endif // NDN_CXX_HAVE_IFA_FLAGS
+ }
+
+ rta = RTA_NEXT(rta, rtaTotalLen);
+ }
+
+ if (nlh->nlmsg_type == RTM_NEWADDR)
+ interface->addNetworkAddress(address);
+ else if (nlh->nlmsg_type == RTM_DELADDR)
+ interface->removeNetworkAddress(address);
+}
+
+void
+NetworkMonitor::Impl::parseRouteMessage(const nlmsghdr* nlh, const rtmsg* rtm)
+{
+ // TODO
+}
+
+void
+NetworkMonitor::Impl::updateInterfaceState(NetworkInterface& interface, uint8_t operState)
+{
+ if (operState == linux_if::OPER_STATE_UP) {
+ interface.setState(InterfaceState::RUNNING);
+ }
+ else if (operState == linux_if::OPER_STATE_DORMANT) {
+ interface.setState(InterfaceState::DORMANT);
+ }
+ else {
+ // fallback to flags
+ auto flags = interface.getFlags();
+ if ((flags & linux_if::FLAG_LOWER_UP) && !(flags & linux_if::FLAG_DORMANT))
+ interface.setState(InterfaceState::RUNNING);
+ else if (flags & IFF_UP)
+ interface.setState(InterfaceState::NO_CARRIER);
+ else
+ interface.setState(InterfaceState::DOWN);
+ }
+}
+
+} // namespace net
+} // namespace ndn
diff --git a/src/net/detail/network-monitor-impl-rtnl.hpp b/src/net/detail/network-monitor-impl-rtnl.hpp
new file mode 100644
index 0000000..d43abce
--- /dev/null
+++ b/src/net/detail/network-monitor-impl-rtnl.hpp
@@ -0,0 +1,126 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2017 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ *
+ * @author Davide Pesavento <davide.pesavento@lip6.fr>
+ */
+
+#ifndef NDN_NET_NETWORK_MONITOR_IMPL_RTNL_HPP
+#define NDN_NET_NETWORK_MONITOR_IMPL_RTNL_HPP
+
+#include "ndn-cxx-config.hpp"
+#include "../network-monitor.hpp"
+
+#ifndef NDN_CXX_HAVE_RTNETLINK
+#error "This file should not be compiled ..."
+#endif
+
+#include <boost/asio/posix/stream_descriptor.hpp>
+
+#include <array>
+#include <map>
+
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <linux/if_addr.h>
+#include <linux/if_link.h>
+
+namespace ndn {
+namespace net {
+
+class NetworkMonitor::Impl
+{
+public:
+ /** \brief initialize netlink socket and start enumerating interfaces
+ */
+ Impl(NetworkMonitor& nm, boost::asio::io_service& io);
+
+ ~Impl();
+
+ uint32_t
+ getCapabilities() const
+ {
+ return NetworkMonitor::CAP_ENUM |
+ NetworkMonitor::CAP_IF_ADD_REMOVE |
+ NetworkMonitor::CAP_STATE_CHANGE |
+ NetworkMonitor::CAP_MTU_CHANGE |
+ NetworkMonitor::CAP_ADDR_ADD_REMOVE;
+ }
+
+ shared_ptr<NetworkInterface>
+ getNetworkInterface(const std::string& ifname) const;
+
+ std::vector<shared_ptr<NetworkInterface>>
+ listNetworkInterfaces() const;
+
+private:
+ struct RtnlRequest
+ {
+ nlmsghdr nlh;
+ ifinfomsg ifi;
+ rtattr rta __attribute__((aligned(NLMSG_ALIGNTO))); // rtattr has to be aligned
+ uint32_t rtext; // space for IFLA_EXT_MASK
+ };
+
+ bool
+ isEnumerating() const;
+
+ void
+ initSocket();
+
+ void
+ sendDumpRequest(uint16_t nlmsgType);
+
+ void
+ asyncRead();
+
+ void
+ handleRead(const boost::system::error_code& error, size_t nBytesReceived,
+ const shared_ptr<boost::asio::posix::stream_descriptor>& socket);
+
+ void
+ parseNetlinkMessage(const nlmsghdr* nlh, size_t len);
+
+ void
+ parseLinkMessage(const nlmsghdr* nlh, const ifinfomsg* ifi);
+
+ void
+ parseAddressMessage(const nlmsghdr* nlh, const ifaddrmsg* ifa);
+
+ void
+ parseRouteMessage(const nlmsghdr* nlh, const rtmsg* rtm);
+
+ static void
+ updateInterfaceState(NetworkInterface& interface, uint8_t operState);
+
+private:
+ NetworkMonitor& m_nm;
+ std::map<int /*ifindex*/, shared_ptr<NetworkInterface>> m_interfaces; ///< interface map
+ std::array<uint8_t, 16384> m_buffer; ///< holds netlink messages received from the kernel
+ shared_ptr<boost::asio::posix::stream_descriptor> m_socket; ///< the netlink socket
+ uint32_t m_pid; ///< our port ID (unicast address for netlink sockets)
+ uint32_t m_sequenceNo; ///< sequence number of the last netlink request sent to the kernel
+ bool m_isEnumeratingLinks; ///< true if a dump of all links is in progress
+ bool m_isEnumeratingAddresses; ///< true if a dump of all addresses is in progress
+};
+
+} // namespace net
+} // namespace ndn
+
+#endif // NDN_NET_NETWORK_MONITOR_IMPL_RTNL_HPP