| /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ |
| /* |
| * Copyright (c) 2013-2018 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-netlink.hpp" |
| #include "linux-if-constants.hpp" |
| #include "netlink-message.hpp" |
| #include "../network-address.hpp" |
| #include "../network-interface.hpp" |
| #include "../../util/logger.hpp" |
| |
| #include <linux/if_addr.h> |
| #include <linux/if_link.h> |
| #include <net/if_arp.h> |
| |
| NDN_LOG_INIT(ndn.NetworkMonitor); |
| |
| namespace ndn { |
| namespace net { |
| |
| NetworkMonitorImplNetlink::NetworkMonitorImplNetlink(boost::asio::io_service& io) |
| : m_rtnlSocket(io) |
| , m_genlSocket(io) |
| , m_isEnumeratingLinks(false) |
| , m_isEnumeratingAddresses(false) |
| { |
| m_rtnlSocket.open(); |
| |
| for (auto group : {RTNLGRP_LINK, |
| RTNLGRP_IPV4_IFADDR, RTNLGRP_IPV4_ROUTE, |
| RTNLGRP_IPV6_IFADDR, RTNLGRP_IPV6_ROUTE}) { |
| m_rtnlSocket.joinGroup(group); |
| } |
| m_rtnlSocket.registerNotificationCallback([this] (const auto& msg) { this->parseRtnlMessage(msg); }); |
| |
| NDN_LOG_TRACE("enumerating links"); |
| m_rtnlSocket.sendDumpRequest(RTM_GETLINK, |
| [this] (const auto& msg) { this->parseRtnlMessage(msg); }); |
| m_isEnumeratingLinks = true; |
| } |
| |
| shared_ptr<const NetworkInterface> |
| NetworkMonitorImplNetlink::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<const NetworkInterface>> |
| NetworkMonitorImplNetlink::listNetworkInterfaces() const |
| { |
| std::vector<shared_ptr<const NetworkInterface>> v; |
| v.reserve(m_interfaces.size()); |
| |
| for (const auto& e : m_interfaces) { |
| v.push_back(e.second); |
| } |
| return v; |
| } |
| |
| bool |
| NetworkMonitorImplNetlink::isEnumerating() const |
| { |
| return m_isEnumeratingLinks || m_isEnumeratingAddresses; |
| } |
| |
| void |
| NetworkMonitorImplNetlink::parseRtnlMessage(const NetlinkMessage& nlmsg) |
| { |
| switch (nlmsg->nlmsg_type) { |
| case RTM_NEWLINK: |
| case RTM_DELLINK: |
| parseLinkMessage(nlmsg); |
| if (!isEnumerating()) |
| this->emitSignal(onNetworkStateChanged); // backward compat |
| break; |
| |
| case RTM_NEWADDR: |
| case RTM_DELADDR: |
| parseAddressMessage(nlmsg); |
| if (!isEnumerating()) |
| this->emitSignal(onNetworkStateChanged); // backward compat |
| break; |
| |
| case RTM_NEWROUTE: |
| case RTM_DELROUTE: |
| parseRouteMessage(nlmsg); |
| if (!isEnumerating()) |
| this->emitSignal(onNetworkStateChanged); // backward compat |
| break; |
| |
| case NLMSG_DONE: |
| if (m_isEnumeratingLinks) { |
| // links enumeration complete, now request all the addresses |
| m_isEnumeratingLinks = false; |
| NDN_LOG_TRACE("enumerating addresses"); |
| m_rtnlSocket.sendDumpRequest(RTM_GETADDR, |
| [this] (const auto& msg) { this->parseRtnlMessage(msg); }); |
| m_isEnumeratingAddresses = true; |
| } |
| else if (m_isEnumeratingAddresses) { |
| // links and addresses enumeration complete |
| m_isEnumeratingAddresses = false; |
| // TODO: enumerate routes |
| NDN_LOG_DEBUG("enumeration complete"); |
| this->emitSignal(onEnumerationCompleted); |
| } |
| break; |
| |
| case NLMSG_ERROR: |
| parseErrorMessage(nlmsg); |
| break; |
| } |
| } |
| |
| 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; |
| } |
| } |
| |
| static void |
| 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); |
| } |
| } |
| |
| void |
| NetworkMonitorImplNetlink::parseLinkMessage(const NetlinkMessage& nlmsg) |
| { |
| const ifinfomsg* ifi = nlmsg.getPayload<ifinfomsg>(); |
| if (ifi == nullptr) { |
| NDN_LOG_WARN("malformed ifinfomsg"); |
| return; |
| } |
| |
| 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 (nlmsg->nlmsg_type == RTM_DELLINK) { |
| if (interface != nullptr) { |
| NDN_LOG_DEBUG("removing interface " << interface->getName()); |
| m_interfaces.erase(it); |
| this->emitSignal(onInterfaceRemoved, interface); |
| } |
| return; |
| } |
| |
| if (interface == nullptr) { |
| interface = makeNetworkInterface(); |
| interface->setIndex(ifi->ifi_index); |
| } |
| interface->setType(ifiTypeToInterfaceType(ifi->ifi_type)); |
| interface->setFlags(ifi->ifi_flags); |
| |
| auto attrs = nlmsg.getAttributes<rtattr>(ifi); |
| NDN_LOG_TRACE("message contains " << attrs.size() << " attributes"); |
| |
| auto address = attrs.getAttributeByType<ethernet::Address>(IFLA_ADDRESS); |
| if (address) |
| interface->setEthernetAddress(*address); |
| |
| auto broadcast = attrs.getAttributeByType<ethernet::Address>(IFLA_BROADCAST); |
| if (broadcast) |
| interface->setEthernetBroadcastAddress(*broadcast); |
| |
| auto name = attrs.getAttributeByType<std::string>(IFLA_IFNAME); |
| if (name) |
| interface->setName(*name); |
| |
| auto mtu = attrs.getAttributeByType<uint32_t>(IFLA_MTU); |
| if (mtu) |
| interface->setMtu(*mtu); |
| |
| auto state = attrs.getAttributeByType<uint8_t>(IFLA_OPERSTATE); |
| updateInterfaceState(*interface, state ? *state : linux_if::OPER_STATE_UNKNOWN); |
| |
| if (it == m_interfaces.end()) { |
| NDN_LOG_DEBUG("adding interface " << interface->getName()); |
| m_interfaces[interface->getIndex()] = interface; |
| this->emitSignal(onInterfaceAdded, interface); |
| } |
| } |
| |
| void |
| NetworkMonitorImplNetlink::parseAddressMessage(const NetlinkMessage& nlmsg) |
| { |
| const ifaddrmsg* ifa = nlmsg.getPayload<ifaddrmsg>(); |
| if (ifa == nullptr) { |
| NDN_LOG_WARN("malformed ifaddrmsg"); |
| return; |
| } |
| |
| 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); |
| |
| auto attrs = nlmsg.getAttributes<rtattr>(ifa); |
| NDN_LOG_TRACE("message contains " << attrs.size() << " attributes"); |
| |
| namespace ip = boost::asio::ip; |
| ip::address ipAddr, broadcastAddr; |
| if (ifa->ifa_family == AF_INET) { |
| auto v4 = attrs.getAttributeByType<ip::address_v4>(IFA_LOCAL); |
| if (v4) |
| ipAddr = *v4; |
| |
| v4 = attrs.getAttributeByType<ip::address_v4>(IFA_BROADCAST); |
| if (v4) |
| broadcastAddr = *v4; |
| } |
| else if (ifa->ifa_family == AF_INET6) { |
| auto v6 = attrs.getAttributeByType<ip::address_v6>(IFA_ADDRESS); |
| if (v6) { |
| if (v6->is_link_local()) |
| v6->scope_id(ifa->ifa_index); |
| |
| ipAddr = *v6; |
| } |
| } |
| |
| uint32_t flags = ifa->ifa_flags; // overwritten by IFA_FLAGS if supported and present |
| #ifdef NDN_CXX_HAVE_IFA_FLAGS |
| auto extFlags = attrs.getAttributeByType<uint32_t>(IFA_FLAGS); |
| if (extFlags) |
| flags = *extFlags; |
| #endif // NDN_CXX_HAVE_IFA_FLAGS |
| |
| NetworkAddress address(ifaFamilyToAddressFamily(ifa->ifa_family), |
| ipAddr, |
| broadcastAddr, |
| ifa->ifa_prefixlen, |
| ifaScopeToAddressScope(ifa->ifa_scope), |
| flags); |
| BOOST_ASSERT(address.getFamily() != AddressFamily::UNSPECIFIED); |
| |
| if (nlmsg->nlmsg_type == RTM_NEWADDR) |
| interface->addNetworkAddress(address); |
| else if (nlmsg->nlmsg_type == RTM_DELADDR) |
| interface->removeNetworkAddress(address); |
| } |
| |
| void |
| NetworkMonitorImplNetlink::parseRouteMessage(const NetlinkMessage& nlmsg) |
| { |
| // TODO |
| } |
| |
| void |
| NetworkMonitorImplNetlink::parseErrorMessage(const NetlinkMessage& nlmsg) |
| { |
| const nlmsgerr* err = nlmsg.getPayload<nlmsgerr>(); |
| if (err == nullptr) { |
| NDN_LOG_WARN("malformed nlmsgerr"); |
| return; |
| } |
| |
| if (err->error == 0) { |
| // an error code of zero indicates an ACK message, not an error |
| NDN_LOG_TRACE("ACK"); |
| return; |
| } |
| |
| NDN_LOG_ERROR("NLMSG_ERROR: " << std::strerror(std::abs(err->error))); |
| |
| #ifdef NDN_CXX_HAVE_NETLINK_EXT_ACK |
| if (!(nlmsg->nlmsg_flags & NLM_F_ACK_TLVS)) |
| return; |
| |
| size_t errLen = NLMSG_LENGTH(sizeof(nlmsgerr)); |
| if (!(nlmsg->nlmsg_flags & NLM_F_CAPPED)) |
| errLen += err->msg.nlmsg_len - NLMSG_HDRLEN; // don't count the inner nlmsghdr twice |
| |
| if (nlmsg->nlmsg_len <= errLen) |
| return; |
| |
| auto nla = reinterpret_cast<const nlattr*>(reinterpret_cast<const uint8_t*>(&*nlmsg) + errLen); |
| auto attrs = NetlinkMessageAttributes<nlattr>(nla, nlmsg->nlmsg_len - errLen); |
| auto msg = attrs.getAttributeByType<std::string>(NLMSGERR_ATTR_MSG); |
| if (msg && !msg->empty()) |
| NDN_LOG_ERROR("kernel message: " << *msg); |
| #endif // NDN_CXX_HAVE_NETLINK_EXT_ACK |
| } |
| |
| } // namespace net |
| } // namespace ndn |