blob: 4824bab96b2315505e59f4abe8a8d03fe542d5bf [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.
20 *
21 *
22 * Parts of this implementation is based on daemondo command of MacPorts
23 * (https://www.macports.org/):
24 *
25 * Copyright (c) 2005-2007 James Berry <jberry@macports.org>
26 * All rights reserved.
27 *
28 * Redistribution and use in source and binary forms, with or without
29 * modification, are permitted provided that the following conditions
30 * are met:
31 * 1. Redistributions of source code must retain the above copyright
32 * notice, this list of conditions and the following disclaimer.
33 * 2. Redistributions in binary form must reproduce the above copyright
34 * notice, this list of conditions and the following disclaimer in the
35 * documentation and/or other materials provided with the distribution.
36 * 3. Neither the name of The MacPorts Project nor the names of its contributors
37 * may be used to endorse or promote products derived from this software
38 * without specific prior written permission.
39 *
40 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
41 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
42 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
43 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
44 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
45 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
46 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
47 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
48 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
49 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
50 * POSSIBILITY OF SUCH DAMAGE.
51 */
52
53#include "ndn-cxx-config.hpp"
54
Davide Pesavento9a8bae52016-02-24 20:33:08 +010055#include "network-monitor-impl-osx.hpp"
Alexander Afanasyev3b3355c2017-03-26 11:57:13 -050056#include "../../name.hpp"
57#include "../logger.hpp"
58#include "../network-address.hpp"
59
60#include <ifaddrs.h> // for getifaddrs()
61#include <arpa/inet.h> // for inet_ntop()
62#include <netinet/in.h> // for struct sockaddr_in{,6}
63#include <net/if_dl.h> // for struct sockaddr_dl
64#include <net/if_types.h> // for IFT_* constants
65
66#include <boost/asio.hpp>
Davide Pesavento9a8bae52016-02-24 20:33:08 +010067
68namespace ndn {
69namespace util {
70
Alexander Afanasyev3b3355c2017-03-26 11:57:13 -050071NDN_LOG_INIT(ndn.NetworkMonitor);
72
Davide Pesavento9a8bae52016-02-24 20:33:08 +010073NetworkMonitor::Impl::Impl(NetworkMonitor& nm, boost::asio::io_service& io)
74 : m_nm(nm)
75 , m_scheduler(io)
76 , m_cfLoopEvent(m_scheduler)
Alexander Afanasyev3b3355c2017-03-26 11:57:13 -050077 , m_context{0, this, nullptr, nullptr, nullptr}
78 , m_scStore(SCDynamicStoreCreate(nullptr, CFSTR("net.named-data.ndn-cxx.NetworkMonitor"),
79 &Impl::onConfigChanged, &m_context))
80 , m_loopSource(SCDynamicStoreCreateRunLoopSource(nullptr, m_scStore.get(), 0))
81 , m_nullUdpSocket(io, boost::asio::ip::udp::v4())
82
Davide Pesavento9a8bae52016-02-24 20:33:08 +010083{
84 scheduleCfLoop();
85
Alexander Afanasyev3b3355c2017-03-26 11:57:13 -050086 // Notifications from Darwin Notify Center:
Davide Pesavento9a8bae52016-02-24 20:33:08 +010087 //
88 // com.apple.system.config.network_change
89 //
90 CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(),
91 static_cast<void*>(this),
92 &Impl::afterNotificationCenterEvent,
93 CFSTR("com.apple.system.config.network_change"),
94 nullptr, // object to observe
95 CFNotificationSuspensionBehaviorDeliverImmediately);
Alexander Afanasyev3b3355c2017-03-26 11:57:13 -050096
97 io.post([this] { enumerateInterfaces(); });
98
99 CFRunLoopAddSource(CFRunLoopGetCurrent(), m_loopSource.get(), kCFRunLoopDefaultMode);
100
101 // Notifications from SystemConfiguration:
102 //
103 // State:/Network/Interface/.*/Link
104 // State:/Network/Interface/.*/IPv4
105 // State:/Network/Interface/.*/IPv6
106 // State:/Network/Global/DNS
107 // State:/Network/Global/IPv4
108 //
109 auto patterns = CFArrayCreateMutable(nullptr, 0, &kCFTypeArrayCallBacks);
110 CFArrayAppendValue(patterns, CFSTR("State:/Network/Interface/.*/Link"));
111 CFArrayAppendValue(patterns, CFSTR("State:/Network/Interface/.*/IPv4"));
112 CFArrayAppendValue(patterns, CFSTR("State:/Network/Interface/.*/IPv6"));
113 // CFArrayAppendValue(patterns, CFSTR("State:/Network/Global/DNS"));
114 // CFArrayAppendValue(patterns, CFSTR("State:/Network/Global/IPv4"));
115
116 SCDynamicStoreSetNotificationKeys(m_scStore.get(), nullptr, patterns);
Davide Pesavento9a8bae52016-02-24 20:33:08 +0100117}
118
119NetworkMonitor::Impl::~Impl()
120{
Alexander Afanasyev3b3355c2017-03-26 11:57:13 -0500121 CFRunLoopRemoveSource(CFRunLoopGetCurrent(), m_loopSource.get(), kCFRunLoopDefaultMode);
122
Davide Pesavento9a8bae52016-02-24 20:33:08 +0100123 CFNotificationCenterRemoveEveryObserver(CFNotificationCenterGetDarwinNotifyCenter(),
124 static_cast<void*>(this));
125}
126
Davide Pesavento2bf35a62017-04-02 00:41:06 -0400127shared_ptr<NetworkInterface>
128NetworkMonitor::Impl::getNetworkInterface(const std::string& ifname) const
129{
Alexander Afanasyev3b3355c2017-03-26 11:57:13 -0500130 auto it = m_interfaces.find(ifname);
131 if (it != m_interfaces.end()) {
132 return it->second;
133 }
134 else {
135 return nullptr;
136 }
Davide Pesavento2bf35a62017-04-02 00:41:06 -0400137}
138
139std::vector<shared_ptr<NetworkInterface>>
140NetworkMonitor::Impl::listNetworkInterfaces() const
141{
Alexander Afanasyev3b3355c2017-03-26 11:57:13 -0500142 std::vector<shared_ptr<NetworkInterface>> v;
143 v.reserve(m_interfaces.size());
144
145 for (const auto& e : m_interfaces) {
146 v.push_back(e.second);
147 }
148 return v;
Davide Pesavento2bf35a62017-04-02 00:41:06 -0400149}
150
Davide Pesavento9a8bae52016-02-24 20:33:08 +0100151void
152NetworkMonitor::Impl::afterNotificationCenterEvent(CFNotificationCenterRef center,
Davide Pesavento2bf35a62017-04-02 00:41:06 -0400153 void* observer,
Davide Pesavento9a8bae52016-02-24 20:33:08 +0100154 CFStringRef name,
Davide Pesavento2bf35a62017-04-02 00:41:06 -0400155 const void* object,
Davide Pesavento9a8bae52016-02-24 20:33:08 +0100156 CFDictionaryRef userInfo)
157{
158 static_cast<Impl*>(observer)->m_nm.onNetworkStateChanged();
159}
160
161void
162NetworkMonitor::Impl::scheduleCfLoop()
163{
164 // poll each second for new events
165 m_cfLoopEvent = m_scheduler.scheduleEvent(time::seconds(1), bind(&Impl::pollCfLoop, this));
166}
167
168void
169NetworkMonitor::Impl::pollCfLoop()
170{
171 // this should dispatch ready events and exit
172 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, true);
173
174 scheduleCfLoop();
175}
176
Alexander Afanasyev3b3355c2017-03-26 11:57:13 -0500177void
178NetworkMonitor::Impl::addNewInterface(const std::string& ifName)
179{
180 shared_ptr<NetworkInterface> interface(new NetworkInterface);
181
182 interface->setName(ifName);
183 interface->setState(getInterfaceState(interface->getName()));
184 updateInterfaceInfo(*interface);
185 if (interface->getType() == InterfaceType::UNKNOWN) {
186 NDN_LOG_DEBUG("ignoring " << ifName << " because it has unhandled interface type");
187 return;
188 }
189
190 NDN_LOG_DEBUG("adding interface " << interface->getName());
191 m_interfaces.insert(make_pair(interface->getName(), interface));
192 m_nm.onInterfaceAdded(interface);
193}
194
195void
196NetworkMonitor::Impl::enumerateInterfaces()
197{
198 for (const auto& ifName : getInterfaceNames()) {
199 addNewInterface(ifName);
200 }
201 m_nm.onEnumerationCompleted();
202}
203
204static std::string
205convertToStdString(CFStringRef cfString)
206{
207 const char* cStr = CFStringGetCStringPtr(cfString, kCFStringEncodingASCII);
208 if (cStr != nullptr) {
209 return cStr;
210 }
211
212 size_t stringSize = CFStringGetLength(cfString);
213 char* buffer = new char[stringSize + 1];
214 CFStringGetCString(cfString, buffer, sizeof(buffer), kCFStringEncodingASCII);
215 std::string retval = buffer;
216 delete [] buffer;
217 return retval;
218}
219
220std::set<std::string>
221NetworkMonitor::Impl::getInterfaceNames()
222{
223 CFReleaser<CFDictionaryRef> dict = (CFDictionaryRef)SCDynamicStoreCopyValue(m_scStore.get(), CFSTR("State:/Network/Interface"));
224 CFArrayRef interfaces = (CFArrayRef)CFDictionaryGetValue(dict.get(), CFSTR("Interfaces"));
225
226 std::set<std::string> ifNames;
227 size_t count = CFArrayGetCount(interfaces);
228 for (size_t i = 0; i != count; ++i) {
229 auto ifName = (CFStringRef)CFArrayGetValueAtIndex(interfaces, i);
230 ifNames.insert(convertToStdString(ifName));
231 }
232 return ifNames;
233}
234
235InterfaceState
236NetworkMonitor::Impl::getInterfaceState(const std::string& ifName)
237{
238 CFReleaser<CFStringRef> linkName = CFStringCreateWithCString(nullptr,
239 ("State:/Network/Interface/" + ifName + "/Link").c_str(),
240 kCFStringEncodingASCII);
241
242 CFReleaser<CFDictionaryRef> dict = (CFDictionaryRef)SCDynamicStoreCopyValue(m_scStore.get(), linkName.get());
243 if (dict.get() == nullptr) {
244 return InterfaceState::UNKNOWN;
245 }
246
247 CFBooleanRef isActive = (CFBooleanRef)CFDictionaryGetValue(dict.get(), CFSTR("Active"));
248 if (isActive == nullptr) {
249 return InterfaceState::UNKNOWN;
250 }
251
252 return CFBooleanGetValue(isActive) ? InterfaceState::RUNNING : InterfaceState::DOWN;
253}
254
255void
256NetworkMonitor::Impl::updateInterfaceInfo(NetworkInterface& netif)
257{
258 ifaddrs* ifa_list = nullptr;
259 if (::getifaddrs(&ifa_list) < 0) {
260 BOOST_THROW_EXCEPTION(Error(std::string("getifaddrs() failed: ") + strerror(errno)));
261 }
262
263 for (ifaddrs* ifa = ifa_list; ifa != nullptr; ifa = ifa->ifa_next) {
264 if (ifa->ifa_name != netif.getName()) {
265 continue;
266 }
267
268 netif.setFlags(ifa->ifa_flags);
269 netif.setMtu(getInterfaceMtu(netif.getName()));
270
271 if (ifa->ifa_addr == nullptr)
272 continue;
273
274 NetworkAddress address;
275
276 switch (ifa->ifa_addr->sa_family) {
277 case AF_INET: {
278 address.m_family = AddressFamily::V4;
279
280 const sockaddr_in* sin = reinterpret_cast<sockaddr_in*>(ifa->ifa_addr);
281 boost::asio::ip::address_v4::bytes_type bytes;
282 std::copy_n(reinterpret_cast<const unsigned char*>(&sin->sin_addr), bytes.size(), bytes.begin());
283 address.m_ip = boost::asio::ip::address_v4(bytes);
284
285 const sockaddr_in* sinMask = reinterpret_cast<sockaddr_in*>(ifa->ifa_netmask);
286 std::copy_n(reinterpret_cast<const unsigned char*>(&sinMask->sin_addr), bytes.size(), bytes.begin());
287 uint8_t mask = 0;
288 for (auto byte : bytes) {
289 while (byte != 0) {
290 ++mask;
291 byte <<= 1;
292 }
293 }
294 address.m_prefixLength = mask;
295 break;
296 }
297
298 case AF_INET6: {
299 address.m_family = AddressFamily::V6;
300
301 const sockaddr_in6* sin6 = reinterpret_cast<sockaddr_in6*>(ifa->ifa_addr);
302 boost::asio::ip::address_v6::bytes_type bytes;
303 std::copy_n(reinterpret_cast<const unsigned char*>(&sin6->sin6_addr), bytes.size(), bytes.begin());
304 address.m_ip = boost::asio::ip::address_v6(bytes);
305
306 const sockaddr_in6* sinMask = reinterpret_cast<sockaddr_in6*>(ifa->ifa_netmask);
307 std::copy_n(reinterpret_cast<const unsigned char*>(&sinMask->sin6_addr), bytes.size(), bytes.begin());
308 uint8_t mask = 0;
309 for (auto byte : bytes) {
310 while (byte != 0) {
311 ++mask;
312 byte <<= 1;
313 }
314 }
315 address.m_prefixLength = mask;
316 break;
317 }
318
319 case AF_LINK: {
320 const sockaddr_dl* sdl = reinterpret_cast<sockaddr_dl*>(ifa->ifa_addr);
321 netif.setIndex(sdl->sdl_index);
322 if (sdl->sdl_type == IFT_ETHER && sdl->sdl_alen == ethernet::ADDR_LEN) {
323 netif.setType(InterfaceType::ETHERNET);
324 netif.setEthernetAddress(ethernet::Address(reinterpret_cast<uint8_t*>(LLADDR(sdl))));
325 NDN_LOG_TRACE(netif.getName() << ": set Ethernet address " << netif.getEthernetAddress());
326 }
327 else if (sdl->sdl_type == IFT_LOOP) {
328 netif.setType(InterfaceType::LOOPBACK);
329 }
330 else {
331 netif.setType(InterfaceType::UNKNOWN);
332 }
333 break;
334 }
335
336 default:
337 continue;
338 }
339
340 if (netif.canBroadcast() && ifa->ifa_broadaddr != nullptr) {
341 const sockaddr_in* sin = reinterpret_cast<sockaddr_in*>(ifa->ifa_broadaddr);
342 boost::asio::ip::address_v4::bytes_type bytes;
343 std::copy_n(reinterpret_cast<const unsigned char*>(&sin->sin_addr), bytes.size(), bytes.begin());
344 address.m_broadcast = boost::asio::ip::address_v4(bytes);
345 NDN_LOG_TRACE(netif.getName() << ": set IPv4 broadcast address " << address.m_broadcast);
346 }
347
348 if (netif.canBroadcast()) {
349 netif.setEthernetBroadcastAddress(ethernet::getBroadcastAddress());
350 }
351
352 netif.addNetworkAddress(address);
353 }
354
355 ::freeifaddrs(ifa_list);
356}
357
358size_t
359NetworkMonitor::Impl::getInterfaceMtu(const std::string& ifName)
360{
361 ifreq ifr{};
362 std::strncpy(ifr.ifr_name, ifName.c_str(), sizeof(ifr.ifr_name) - 1);
363
364 if (::ioctl(m_nullUdpSocket.native_handle(), SIOCGIFMTU, &ifr) == 0) {
365 return static_cast<size_t>(ifr.ifr_mtu);
366 }
367
368 NDN_LOG_WARN("Failed to get interface MTU: " << std::strerror(errno));
369 return ethernet::MAX_DATA_LEN;
370}
371
372void
373NetworkMonitor::Impl::onConfigChanged(SCDynamicStoreRef m_scStore, CFArrayRef changedKeys, void* context)
374{
375 static_cast<Impl*>(context)->onConfigChanged(changedKeys);
376}
377
378void
379NetworkMonitor::Impl::onConfigChanged(CFArrayRef changedKeys)
380{
381 size_t count = CFArrayGetCount(changedKeys);
382 for (size_t i = 0; i != count; ++i) {
383 std::string keyName = convertToStdString((CFStringRef)CFArrayGetValueAtIndex(changedKeys, i));
384 Name key(keyName);
385 std::string ifName = key.at(-2).toUri();
386
387 auto ifIt = m_interfaces.find(ifName);
388 if (ifIt == m_interfaces.end()) {
389 addNewInterface(ifName);
390 return;
391 }
392
393 NetworkInterface& netif = *ifIt->second;
394
395 auto removeInterface = [&] {
396 NDN_LOG_DEBUG("removing interface " << ifName);
397 shared_ptr<NetworkInterface> removedInterface = ifIt->second;
398 m_interfaces.erase(ifIt);
399 m_nm.onInterfaceRemoved(removedInterface);
400 };
401
402 if (key.at(-1).toUri() == "Link") {
403 auto newState = getInterfaceState(ifName);
404
405 if (newState == InterfaceState::UNKNOWN) {
406 // check if it is really unknown or interface removed
407 if (getInterfaceNames().count(ifName) == 0) {
408 // newState = InterfaceState::DOWN;
409 removeInterface();
410 return;
411 }
412 }
413
414 NDN_LOG_TRACE("Status of " << ifName << " changed from " << netif.getState() << " to " << newState);
415 netif.setState(newState);
416 }
417
418 if (key.at(-1).toUri() == "IPv4" || key.at(-1).toUri() == "IPv6") {
419 NetworkInterface updatedInterface;
420 updatedInterface.setName(ifName);
421 updateInterfaceInfo(updatedInterface);
422 if (updatedInterface.getType() == InterfaceType::UNKNOWN) {
423 // somehow, type of interface changed to unknown
424 NDN_LOG_DEBUG("Removing " << ifName << " because it changed to unhandled interface type");
425 removeInterface();
426 return;
427 }
428
429 const auto& newAddrs = updatedInterface.getNetworkAddresses();
430 const auto& oldAddrs = netif.getNetworkAddresses();
431
432 std::set<NetworkAddress> added;
433 std::set<NetworkAddress> removed;
434
435 std::set_difference(newAddrs.begin(), newAddrs.end(),
436 oldAddrs.begin(), oldAddrs.end(), std::inserter(added, added.end()));
437
438 std::set_difference(oldAddrs.begin(), oldAddrs.end(),
439 newAddrs.begin(), newAddrs.end(), std::inserter(removed, removed.end()));
440
441 for (const auto& addr : removed) {
442 netif.removeNetworkAddress(addr);
443 }
444
445 for (const auto& addr : added) {
446 netif.addNetworkAddress(addr);
447 }
448 }
449 }
450}
451
Davide Pesavento9a8bae52016-02-24 20:33:08 +0100452} // namespace util
453} // namespace ndn