61 #include <net/if_dl.h> 62 #include <net/if_types.h> 63 #include <netinet/in.h> 65 #include <boost/asio/io_service.hpp> 66 #include <boost/asio/ip/address.hpp> 67 #include <boost/asio/ip/udp.hpp> 68 #include <boost/range/adaptor/map.hpp> 69 #include <boost/range/algorithm_ext/push_back.hpp> 76 using detail::CFReleaser;
83 if (::getifaddrs(&m_ifaList) < 0) {
90 if (m_ifaList !=
nullptr) {
91 ::freeifaddrs(m_ifaList);
102 ifaddrs* m_ifaList =
nullptr;
107 , m_context{0,
this,
nullptr,
nullptr,
nullptr}
108 , m_scStore(SCDynamicStoreCreate(
nullptr, CFSTR(
"net.named-data.ndn-cxx.NetworkMonitor"),
109 &NetworkMonitorImplOsx::onConfigChanged, &m_context))
110 , m_loopSource(SCDynamicStoreCreateRunLoopSource(
nullptr, m_scStore.get(), 0))
111 , m_ioctlSocket(io, boost::asio::ip::udp::v4())
119 CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(),
120 static_cast<void*>(
this),
121 &NetworkMonitorImplOsx::afterNotificationCenterEvent,
122 CFSTR(
"com.apple.system.config.network_change"),
124 CFNotificationSuspensionBehaviorDeliverImmediately);
126 CFRunLoopAddSource(CFRunLoopGetCurrent(), m_loopSource.get(), kCFRunLoopDefaultMode);
137 auto patterns = CFArrayCreateMutable(
nullptr, 0, &kCFTypeArrayCallBacks);
138 CFArrayAppendValue(patterns, CFSTR(
"State:/Network/Interface/.*/Link"));
139 CFArrayAppendValue(patterns, CFSTR(
"State:/Network/Interface/.*/IPv4"));
140 CFArrayAppendValue(patterns, CFSTR(
"State:/Network/Interface/.*/IPv6"));
142 if (!SCDynamicStoreSetNotificationKeys(m_scStore.get(),
nullptr, patterns)) {
143 BOOST_THROW_EXCEPTION(Error(
"SCDynamicStoreSetNotificationKeys failed"));
146 io.post([
this] { enumerateInterfaces(); });
151 CFRunLoopRemoveSource(CFRunLoopGetCurrent(), m_loopSource.
get(), kCFRunLoopDefaultMode);
153 CFNotificationCenterRemoveEveryObserver(CFNotificationCenterGetDarwinNotifyCenter(),
154 static_cast<void*>(
this));
157 shared_ptr<const NetworkInterface>
160 auto it = m_interfaces.find(ifname);
161 return it == m_interfaces.end() ? nullptr : it->second;
164 std::vector<shared_ptr<const NetworkInterface>>
167 std::vector<shared_ptr<const NetworkInterface>> v;
168 v.reserve(m_interfaces.size());
169 boost::push_back(v, m_interfaces | boost::adaptors::map_values);
174 NetworkMonitorImplOsx::afterNotificationCenterEvent(CFNotificationCenterRef center,
178 CFDictionaryRef userInfo)
184 NetworkMonitorImplOsx::scheduleCfLoop()
189 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0,
true);
195 NetworkMonitorImplOsx::enumerateInterfaces()
198 for (
const auto& ifName : getInterfaceNames()) {
199 addNewInterface(ifName, ifaList);
204 std::set<std::string>
205 NetworkMonitorImplOsx::getInterfaceNames()
const 207 CFReleaser<CFDictionaryRef> dict =
208 (CFDictionaryRef)SCDynamicStoreCopyValue(m_scStore.
get(), CFSTR(
"State:/Network/Interface"));
209 if (dict.get() ==
nullptr) {
213 CFArrayRef interfaces = (CFArrayRef)CFDictionaryGetValue(dict.get(), CFSTR(
"Interfaces"));
214 if (interfaces ==
nullptr) {
218 std::set<std::string> ifNames;
219 size_t count = CFArrayGetCount(interfaces);
220 for (
size_t i = 0; i != count; ++i) {
221 auto ifName = (CFStringRef)CFArrayGetValueAtIndex(interfaces, i);
228 NetworkMonitorImplOsx::addNewInterface(
const std::string& ifName,
const IfAddrs& ifaList)
231 interface->setName(ifName);
232 interface->setState(getInterfaceState(*interface));
233 updateInterfaceInfo(*interface, ifaList);
236 NDN_LOG_DEBUG(
"ignoring " << ifName <<
" due to unhandled interface type");
241 m_interfaces[interface->getName()] = interface;
246 NetworkMonitorImplOsx::getInterfaceState(
const NetworkInterface& netif)
const 248 CFReleaser<CFStringRef> linkName =
251 CFReleaser<CFDictionaryRef> dict =
252 (CFDictionaryRef)SCDynamicStoreCopyValue(m_scStore.
get(), linkName.get());
253 if (dict.get() ==
nullptr) {
257 CFBooleanRef isActive = (CFBooleanRef)CFDictionaryGetValue(dict.get(), CFSTR(
"Active"));
258 if (isActive ==
nullptr) {
266 NetworkMonitorImplOsx::getInterfaceMtu(
const NetworkInterface& netif)
269 std::strncpy(ifr.ifr_name, netif.getName().data(),
sizeof(ifr.ifr_name) - 1);
271 if (::ioctl(m_ioctlSocket.native_handle(), SIOCGIFMTU, &ifr) == 0) {
272 return static_cast<size_t>(ifr.ifr_mtu);
275 NDN_LOG_WARN(
"failed to get MTU of " << netif.getName() <<
": " << std::strerror(errno));
279 template<
typename AddressBytes>
283 uint8_t prefixLength = 0;
284 for (
auto byte : mask) {
294 NetworkMonitorImplOsx::updateInterfaceInfo(NetworkInterface& netif,
const IfAddrs& ifaList)
296 BOOST_ASSERT(!netif.getName().empty());
298 netif.setMtu(getInterfaceMtu(netif));
300 for (ifaddrs* ifa = ifaList.get(); ifa !=
nullptr; ifa = ifa->ifa_next) {
301 if (ifa->ifa_name != netif.getName())
304 netif.setFlags(ifa->ifa_flags);
306 if (ifa->ifa_addr ==
nullptr)
309 namespace ip = boost::asio::ip;
311 ip::address ipAddr, broadcastAddr;
312 uint8_t prefixLength = 0;
314 switch (ifa->ifa_addr->sa_family) {
318 const sockaddr_in* sin = reinterpret_cast<sockaddr_in*>(ifa->ifa_addr);
319 ip::address_v4::bytes_type bytes;
320 std::copy_n(reinterpret_cast<const unsigned char*>(&sin->sin_addr), bytes.size(), bytes.begin());
321 ipAddr = ip::address_v4(bytes);
323 const sockaddr_in* sinMask = reinterpret_cast<sockaddr_in*>(ifa->ifa_netmask);
324 std::copy_n(reinterpret_cast<const unsigned char*>(&sinMask->sin_addr), bytes.size(), bytes.begin());
332 const sockaddr_in6* sin6 = reinterpret_cast<sockaddr_in6*>(ifa->ifa_addr);
333 ip::address_v6::bytes_type bytes;
334 std::copy_n(reinterpret_cast<const unsigned char*>(&sin6->sin6_addr), bytes.size(), bytes.begin());
335 ip::address_v6 v6Addr(bytes);
336 if (v6Addr.is_link_local())
337 v6Addr.scope_id(if_nametoindex(netif.getName().data()));
340 const sockaddr_in6* sinMask = reinterpret_cast<sockaddr_in6*>(ifa->ifa_netmask);
341 std::copy_n(reinterpret_cast<const unsigned char*>(&sinMask->sin6_addr), bytes.size(), bytes.begin());
347 const sockaddr_dl* sdl = reinterpret_cast<sockaddr_dl*>(ifa->ifa_addr);
348 netif.setIndex(sdl->sdl_index);
352 netif.setEthernetAddress(ethernet::Address(reinterpret_cast<uint8_t*>(LLADDR(sdl))));
353 NDN_LOG_TRACE(netif.getName() <<
" has Ethernet address " << netif.getEthernetAddress());
355 else if (sdl->sdl_type == IFT_LOOP) {
365 if (netif.canBroadcast()) {
369 const sockaddr_in* sin = reinterpret_cast<sockaddr_in*>(ifa->ifa_broadaddr);
370 ip::address_v4::bytes_type bytes;
371 std::copy_n(reinterpret_cast<const unsigned char*>(&sin->sin_addr), bytes.size(), bytes.begin());
372 broadcastAddr = ip::address_v4(bytes);
380 if (ipAddr.is_loopback()) {
383 else if ((ipAddr.is_v4() && (ipAddr.to_v4().to_ulong() & 0xFFFF0000) == 0xA9FE0000) ||
384 (ipAddr.is_v6() && ipAddr.to_v6().is_link_local())) {
388 netif.addNetworkAddress(NetworkAddress(addrFamily, ipAddr, broadcastAddr, prefixLength, scope, 0));
393 NetworkMonitorImplOsx::onConfigChanged(SCDynamicStoreRef m_scStore, CFArrayRef changedKeys,
void* context)
395 static_cast<NetworkMonitorImplOsx*>(context)->onConfigChanged(changedKeys);
399 NetworkMonitorImplOsx::onConfigChanged(CFArrayRef changedKeys)
403 size_t count = CFArrayGetCount(changedKeys);
404 for (
size_t i = 0; i != count; ++i) {
406 std::string ifName = key.at(-2).toUri();
408 auto ifIt = m_interfaces.find(ifName);
409 if (ifIt == m_interfaces.end()) {
410 addNewInterface(ifName, ifaList);
414 auto removeInterface = [&] {
416 shared_ptr<NetworkInterface> removedNetif = ifIt->second;
417 m_interfaces.erase(ifIt);
421 NetworkInterface& netif = *ifIt->second;
422 std::string changedItem = key.at(-1).toUri();
423 if (changedItem ==
"Link") {
424 auto newState = getInterfaceState(netif);
427 if (getInterfaceNames().count(ifName) == 0) {
432 NDN_LOG_TRACE(ifName <<
" status changed from " << netif.getState() <<
" to " << newState);
433 netif.setState(newState);
435 else if (changedItem ==
"IPv4" || changedItem ==
"IPv6") {
437 updatedNetif->setName(ifName);
438 updateInterfaceInfo(*updatedNetif, ifaList);
445 const auto& newAddrs = updatedNetif->getNetworkAddresses();
446 const auto& oldAddrs = netif.getNetworkAddresses();
447 std::set<NetworkAddress> added;
448 std::set<NetworkAddress> removed;
449 std::set_difference(newAddrs.begin(), newAddrs.end(),
450 oldAddrs.begin(), oldAddrs.end(), std::inserter(added, added.end()));
451 std::set_difference(oldAddrs.begin(), oldAddrs.end(),
452 newAddrs.begin(), newAddrs.end(), std::inserter(removed, removed.end()));
454 for (
const auto& addr : removed) {
455 netif.removeNetworkAddress(addr);
457 for (
const auto& addr : added) {
458 netif.addNetworkAddress(addr);
interface is administratively down
CFReleaser< CFStringRef > fromStdString(const std::string &str)
Create a CFString by copying characters from a std::string.
This file contains utilities to deal with Apple Core Foundation's CFString and related types.
Copyright (c) 2011-2015 Regents of the University of California.
std::string toStdString(CFStringRef cfStr)
Convert a CFString to a std::string.
interface is in an unknown state
interface can be used to send and receive packets
static shared_ptr< NetworkInterface > makeNetworkInterface()
const size_t ADDR_LEN
Octets in one Ethernet address.
#define NDN_LOG_WARN(expression)
NetworkMonitorImplOsx(boost::asio::io_service &io)
#define NDN_LOG_DEBUG(expression)
#define emitSignal(...)
(implementation detail)
InterfaceState
Indicates the state of a network interface.
EventId scheduleEvent(time::nanoseconds after, const EventCallback &callback)
Schedule a one-time event after the specified delay.
util::Signal< NetworkMonitorImpl > onEnumerationCompleted
Address getBroadcastAddress()
Returns the Ethernet broadcast address (ff:ff:ff:ff:ff:ff)
std::vector< shared_ptr< const NetworkInterface > > listNetworkInterfaces() const final
util::Signal< NetworkMonitorImpl, shared_ptr< const NetworkInterface > > onInterfaceRemoved
#define NDN_LOG_TRACE(expression)
util::Signal< NetworkMonitorImpl, shared_ptr< const NetworkInterface > > onInterfaceAdded
shared_ptr< const NetworkInterface > getNetworkInterface(const std::string &ifname) const final
ifaddrs * get() const noexcept
#define NDN_LOG_INIT(name)
declare a log module
util::Signal< NetworkMonitorImpl > onNetworkStateChanged
const size_t MAX_DATA_LEN
Max octets in Ethernet payload.
static uint8_t computePrefixLength(const AddressBytes &mask)