net: NetworkMonitorImplOsx: minimize number of calls to getifaddrs()
Change-Id: I952d68bea780d2a237bffee29adea00dbf8d128a
Refs: #3817
diff --git a/src/net/detail/network-monitor-impl-osx.cpp b/src/net/detail/network-monitor-impl-osx.cpp
index 16b32bb..34d53ed 100644
--- a/src/net/detail/network-monitor-impl-osx.cpp
+++ b/src/net/detail/network-monitor-impl-osx.cpp
@@ -74,6 +74,34 @@
NDN_LOG_INIT(ndn.NetworkMonitor);
+class IfAddrs : noncopyable
+{
+public:
+ IfAddrs()
+ {
+ if (::getifaddrs(&m_ifaList) < 0) {
+ BOOST_THROW_EXCEPTION(NetworkMonitorImplOsx::Error(std::string("getifaddrs() failed: ") +
+ strerror(errno)));
+ }
+ }
+
+ ~IfAddrs()
+ {
+ if (m_ifaList != nullptr) {
+ ::freeifaddrs(m_ifaList);
+ }
+ }
+
+ ifaddrs*
+ get() const noexcept
+ {
+ return m_ifaList;
+ }
+
+private:
+ ifaddrs* m_ifaList = nullptr;
+};
+
NetworkMonitorImplOsx::NetworkMonitorImplOsx(boost::asio::io_service& io)
: m_scheduler(io)
, m_cfLoopEvent(m_scheduler)
@@ -97,8 +125,6 @@
nullptr, // object to observe
CFNotificationSuspensionBehaviorDeliverImmediately);
- io.post([this] { enumerateInterfaces(); });
-
CFRunLoopAddSource(CFRunLoopGetCurrent(), m_loopSource.get(), kCFRunLoopDefaultMode);
// Notifications from SystemConfiguration:
@@ -117,6 +143,8 @@
// CFArrayAppendValue(patterns, CFSTR("State:/Network/Global/IPv4"));
SCDynamicStoreSetNotificationKeys(m_scStore.get(), nullptr, patterns);
+
+ io.post([this] { enumerateInterfaces(); });
}
NetworkMonitorImplOsx::~NetworkMonitorImplOsx()
@@ -160,40 +188,19 @@
NetworkMonitorImplOsx::scheduleCfLoop()
{
// poll each second for new events
- m_cfLoopEvent = m_scheduler.scheduleEvent(time::seconds(1), [this] { pollCfLoop(); });
-}
-
-void
-NetworkMonitorImplOsx::pollCfLoop()
-{
- // this should dispatch ready events and exit
- CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, true);
-
- scheduleCfLoop();
-}
-
-void
-NetworkMonitorImplOsx::addNewInterface(const std::string& ifName)
-{
- shared_ptr<NetworkInterface> interface = makeNetworkInterface();
- 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));
- this->emitSignal(onInterfaceAdded, interface);
+ m_cfLoopEvent = m_scheduler.scheduleEvent(time::seconds(1), [this] {
+ // this should dispatch ready events and exit
+ CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, true);
+ scheduleCfLoop();
+ });
}
void
NetworkMonitorImplOsx::enumerateInterfaces()
{
+ IfAddrs ifaList;
for (const auto& ifName : getInterfaceNames()) {
- addNewInterface(ifName);
+ addNewInterface(ifName, ifaList);
}
this->emitSignal(onEnumerationCompleted);
}
@@ -219,7 +226,7 @@
}
std::set<std::string>
-NetworkMonitorImplOsx::getInterfaceNames()
+NetworkMonitorImplOsx::getInterfaceNames() const
{
CFReleaser<CFDictionaryRef> dict =
(CFDictionaryRef)SCDynamicStoreCopyValue(m_scStore.get(), CFSTR("State:/Network/Interface"));
@@ -234,11 +241,29 @@
return ifNames;
}
+void
+NetworkMonitorImplOsx::addNewInterface(const std::string& ifName, const IfAddrs& ifaList)
+{
+ shared_ptr<NetworkInterface> interface = makeNetworkInterface();
+ interface->setName(ifName);
+ interface->setState(getInterfaceState(interface->getName()));
+ updateInterfaceInfo(*interface, ifaList);
+
+ if (interface->getType() == InterfaceType::UNKNOWN) {
+ NDN_LOG_DEBUG("ignoring " << ifName << " due to unhandled interface type");
+ return;
+ }
+
+ NDN_LOG_DEBUG("adding interface " << interface->getName());
+ m_interfaces[interface->getName()] = interface;
+ this->emitSignal(onInterfaceAdded, interface);
+}
+
InterfaceState
-NetworkMonitorImplOsx::getInterfaceState(const std::string& ifName)
+NetworkMonitorImplOsx::getInterfaceState(const std::string& ifName) const
{
CFReleaser<CFStringRef> linkName =
- CFStringCreateWithCString(nullptr, ("State:/Network/Interface/" + ifName + "/Link").c_str(),
+ CFStringCreateWithCString(nullptr, ("State:/Network/Interface/" + ifName + "/Link").data(),
kCFStringEncodingASCII);
CFReleaser<CFDictionaryRef> dict = (CFDictionaryRef)SCDynamicStoreCopyValue(m_scStore.get(), linkName.get());
@@ -254,6 +279,20 @@
return CFBooleanGetValue(isActive) ? InterfaceState::RUNNING : InterfaceState::DOWN;
}
+size_t
+NetworkMonitorImplOsx::getInterfaceMtu(const std::string& ifName)
+{
+ ifreq ifr{};
+ std::strncpy(ifr.ifr_name, ifName.data(), 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 MTU of " << ifName << ": " << std::strerror(errno));
+ return ethernet::MAX_DATA_LEN;
+}
+
template<typename AddressBytes>
static uint8_t
computePrefixLength(const AddressBytes& mask)
@@ -269,14 +308,9 @@
}
void
-NetworkMonitorImplOsx::updateInterfaceInfo(NetworkInterface& netif)
+NetworkMonitorImplOsx::updateInterfaceInfo(NetworkInterface& netif, const IfAddrs& ifaList)
{
- 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) {
+ for (ifaddrs* ifa = ifaList.get(); ifa != nullptr; ifa = ifa->ifa_next) {
if (ifa->ifa_name != netif.getName())
continue;
@@ -327,7 +361,7 @@
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());
+ NDN_LOG_TRACE(netif.getName() << " has Ethernet address " << netif.getEthernetAddress());
}
else if (sdl->sdl_type == IFT_LOOP) {
netif.setType(InterfaceType::LOOPBACK);
@@ -347,7 +381,6 @@
ip::address_v4::bytes_type bytes;
std::copy_n(reinterpret_cast<const unsigned char*>(&sin->sin_addr), bytes.size(), bytes.begin());
broadcastAddr = ip::address_v4(bytes);
- NDN_LOG_TRACE(netif.getName() << ": set IPv4 broadcast address " << broadcastAddr);
}
}
@@ -362,22 +395,6 @@
netif.addNetworkAddress(NetworkAddress(addrFamily, ipAddr, broadcastAddr, prefixLength, scope, 0));
}
-
- ::freeifaddrs(ifa_list);
-}
-
-size_t
-NetworkMonitorImplOsx::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
@@ -389,6 +406,8 @@
void
NetworkMonitorImplOsx::onConfigChanged(CFArrayRef changedKeys)
{
+ IfAddrs ifaList;
+
size_t count = CFArrayGetCount(changedKeys);
for (size_t i = 0; i != count; ++i) {
std::string keyName = convertToStdString((CFStringRef)CFArrayGetValueAtIndex(changedKeys, i));
@@ -397,7 +416,7 @@
auto ifIt = m_interfaces.find(ifName);
if (ifIt == m_interfaces.end()) {
- addNewInterface(ifName);
+ addNewInterface(ifName, ifaList);
return;
}
@@ -412,27 +431,23 @@
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);
+ NDN_LOG_TRACE(ifName << " status changed from " << netif.getState() << " to " << newState);
netif.setState(newState);
}
if (key.at(-1).toUri() == "IPv4" || key.at(-1).toUri() == "IPv6") {
shared_ptr<NetworkInterface> updatedInterface = makeNetworkInterface();
updatedInterface->setName(ifName);
- updateInterfaceInfo(*updatedInterface);
+ updateInterfaceInfo(*updatedInterface, ifaList);
if (updatedInterface->getType() == InterfaceType::UNKNOWN) {
- // somehow, type of interface changed to unknown
- NDN_LOG_DEBUG("Removing " << ifName << " because it changed to unhandled interface type");
+ NDN_LOG_DEBUG(ifName << " type changed to unknown");
removeInterface();
return;
}
@@ -442,17 +457,14 @@
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);
}
diff --git a/src/net/detail/network-monitor-impl-osx.hpp b/src/net/detail/network-monitor-impl-osx.hpp
index 8de354c..7339153 100644
--- a/src/net/detail/network-monitor-impl-osx.hpp
+++ b/src/net/detail/network-monitor-impl-osx.hpp
@@ -1,5 +1,5 @@
/* -*- 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).
@@ -41,6 +41,8 @@
namespace ndn {
namespace net {
+class IfAddrs;
+
class NetworkMonitorImplOsx : public NetworkMonitorImpl
{
public:
@@ -65,38 +67,33 @@
std::vector<shared_ptr<const NetworkInterface>>
listNetworkInterfaces() const final;
+private:
static void
- afterNotificationCenterEvent(CFNotificationCenterRef center,
- void* observer,
- CFStringRef name,
- const void* object,
+ 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);
+ getInterfaceNames() const;
void
- updateInterfaceInfo(NetworkInterface& netif);
+ addNewInterface(const std::string& ifName, const IfAddrs& ifaList);
+
+ InterfaceState
+ getInterfaceState(const std::string& ifName) const;
size_t
getInterfaceMtu(const std::string& ifName);
+ void
+ updateInterfaceInfo(NetworkInterface& netif, const IfAddrs& ifaList);
+
static void
onConfigChanged(SCDynamicStoreRef store, CFArrayRef changedKeys, void* context);