blob: e404322489e46b0734edb3297be554ad7515fe60 [file] [log] [blame]
Davide Pesavento50b92262018-07-11 12:28:31 -04001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/*
3 * Copyright (c) 2013-2018 Regents of the University of California.
4 *
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 * @author Davide Pesavento <davide.pesavento@lip6.fr>
22 */
23
24#include "network-monitor-impl-netlink.hpp"
25#include "linux-if-constants.hpp"
Davide Pesaventoa0b2a2c2018-07-19 01:01:06 -040026#include "netlink-message.hpp"
Davide Pesavento50b92262018-07-11 12:28:31 -040027#include "../network-address.hpp"
28#include "../network-interface.hpp"
29#include "../../util/logger.hpp"
Davide Pesavento50b92262018-07-11 12:28:31 -040030
31#include <linux/if_addr.h>
32#include <linux/if_link.h>
33#include <net/if_arp.h>
Davide Pesavento50b92262018-07-11 12:28:31 -040034
35NDN_LOG_INIT(ndn.NetworkMonitor);
36
37namespace ndn {
38namespace net {
39
Davide Pesavento50b92262018-07-11 12:28:31 -040040NetworkMonitorImplNetlink::NetworkMonitorImplNetlink(boost::asio::io_service& io)
Davide Pesaventoa0b2a2c2018-07-19 01:01:06 -040041 : m_rtnlSocket(io)
Davide Pesavento10774992018-08-19 18:47:50 -040042 , m_genlSocket(io)
Davide Pesavento50b92262018-07-11 12:28:31 -040043 , m_isEnumeratingLinks(false)
44 , m_isEnumeratingAddresses(false)
45{
Davide Pesaventoa0b2a2c2018-07-19 01:01:06 -040046 m_rtnlSocket.open();
Davide Pesavento10774992018-08-19 18:47:50 -040047
Davide Pesavento50b92262018-07-11 12:28:31 -040048 for (auto group : {RTNLGRP_LINK,
49 RTNLGRP_IPV4_IFADDR, RTNLGRP_IPV4_ROUTE,
50 RTNLGRP_IPV6_IFADDR, RTNLGRP_IPV6_ROUTE}) {
Davide Pesaventoa0b2a2c2018-07-19 01:01:06 -040051 m_rtnlSocket.joinGroup(group);
Davide Pesavento50b92262018-07-11 12:28:31 -040052 }
Davide Pesaventodc5bb962018-08-12 23:40:38 -040053 m_rtnlSocket.registerNotificationCallback([this] (const auto& msg) { this->parseRtnlMessage(msg); });
Davide Pesavento50b92262018-07-11 12:28:31 -040054
55 NDN_LOG_TRACE("enumerating links");
Davide Pesaventodc5bb962018-08-12 23:40:38 -040056 m_rtnlSocket.sendDumpRequest(RTM_GETLINK,
57 [this] (const auto& msg) { this->parseRtnlMessage(msg); });
Davide Pesavento50b92262018-07-11 12:28:31 -040058 m_isEnumeratingLinks = true;
59}
60
Davide Pesavento50b92262018-07-11 12:28:31 -040061shared_ptr<const NetworkInterface>
62NetworkMonitorImplNetlink::getNetworkInterface(const std::string& ifname) const
63{
64 for (const auto& e : m_interfaces) {
65 if (e.second->getName() == ifname)
66 return e.second;
67 }
68 return nullptr;
69}
70
71std::vector<shared_ptr<const NetworkInterface>>
72NetworkMonitorImplNetlink::listNetworkInterfaces() const
73{
74 std::vector<shared_ptr<const NetworkInterface>> v;
75 v.reserve(m_interfaces.size());
76
77 for (const auto& e : m_interfaces) {
78 v.push_back(e.second);
79 }
80 return v;
81}
82
83bool
84NetworkMonitorImplNetlink::isEnumerating() const
85{
86 return m_isEnumeratingLinks || m_isEnumeratingAddresses;
87}
88
89void
Davide Pesaventoa0b2a2c2018-07-19 01:01:06 -040090NetworkMonitorImplNetlink::parseRtnlMessage(const NetlinkMessage& nlmsg)
Davide Pesavento50b92262018-07-11 12:28:31 -040091{
92 switch (nlmsg->nlmsg_type) {
93 case RTM_NEWLINK:
94 case RTM_DELLINK:
95 parseLinkMessage(nlmsg);
96 if (!isEnumerating())
97 this->emitSignal(onNetworkStateChanged); // backward compat
98 break;
99
100 case RTM_NEWADDR:
101 case RTM_DELADDR:
102 parseAddressMessage(nlmsg);
103 if (!isEnumerating())
104 this->emitSignal(onNetworkStateChanged); // backward compat
105 break;
106
107 case RTM_NEWROUTE:
108 case RTM_DELROUTE:
109 parseRouteMessage(nlmsg);
110 if (!isEnumerating())
111 this->emitSignal(onNetworkStateChanged); // backward compat
112 break;
113
Davide Pesaventoa0b2a2c2018-07-19 01:01:06 -0400114 case NLMSG_DONE:
115 if (m_isEnumeratingLinks) {
116 // links enumeration complete, now request all the addresses
117 m_isEnumeratingLinks = false;
118 NDN_LOG_TRACE("enumerating addresses");
Davide Pesaventodc5bb962018-08-12 23:40:38 -0400119 m_rtnlSocket.sendDumpRequest(RTM_GETADDR,
120 [this] (const auto& msg) { this->parseRtnlMessage(msg); });
Davide Pesaventoa0b2a2c2018-07-19 01:01:06 -0400121 m_isEnumeratingAddresses = true;
122 }
123 else if (m_isEnumeratingAddresses) {
124 // links and addresses enumeration complete
125 m_isEnumeratingAddresses = false;
126 // TODO: enumerate routes
127 NDN_LOG_DEBUG("enumeration complete");
128 this->emitSignal(onEnumerationCompleted);
129 }
130 break;
131
Davide Pesavento50b92262018-07-11 12:28:31 -0400132 case NLMSG_ERROR:
133 parseErrorMessage(nlmsg);
134 break;
135 }
136}
137
138static InterfaceType
139ifiTypeToInterfaceType(uint16_t type)
140{
141 switch (type) {
142 case ARPHRD_ETHER:
143 return InterfaceType::ETHERNET;
144 case ARPHRD_LOOPBACK:
145 return InterfaceType::LOOPBACK;
146 default:
147 return InterfaceType::UNKNOWN;
148 }
149}
150
151static AddressFamily
152ifaFamilyToAddressFamily(uint8_t family)
153{
154 switch (family) {
155 case AF_INET:
156 return AddressFamily::V4;
157 case AF_INET6:
158 return AddressFamily::V6;
159 default:
160 return AddressFamily::UNSPECIFIED;
161 }
162}
163
164static AddressScope
165ifaScopeToAddressScope(uint8_t scope)
166{
167 switch (scope) {
168 case RT_SCOPE_NOWHERE:
169 return AddressScope::NOWHERE;
170 case RT_SCOPE_HOST:
171 return AddressScope::HOST;
172 case RT_SCOPE_LINK:
173 return AddressScope::LINK;
174 default:
175 return AddressScope::GLOBAL;
176 }
177}
178
Davide Pesaventoa0b2a2c2018-07-19 01:01:06 -0400179static void
180updateInterfaceState(NetworkInterface& interface, uint8_t operState)
181{
182 if (operState == linux_if::OPER_STATE_UP) {
183 interface.setState(InterfaceState::RUNNING);
184 }
185 else if (operState == linux_if::OPER_STATE_DORMANT) {
186 interface.setState(InterfaceState::DORMANT);
187 }
188 else {
189 // fallback to flags
190 auto flags = interface.getFlags();
191 if ((flags & linux_if::FLAG_LOWER_UP) && !(flags & linux_if::FLAG_DORMANT))
192 interface.setState(InterfaceState::RUNNING);
193 else if (flags & IFF_UP)
194 interface.setState(InterfaceState::NO_CARRIER);
195 else
196 interface.setState(InterfaceState::DOWN);
197 }
198}
199
Davide Pesavento50b92262018-07-11 12:28:31 -0400200void
201NetworkMonitorImplNetlink::parseLinkMessage(const NetlinkMessage& nlmsg)
202{
203 const ifinfomsg* ifi = nlmsg.getPayload<ifinfomsg>();
204 if (ifi == nullptr) {
205 NDN_LOG_WARN("malformed ifinfomsg");
206 return;
207 }
208
209 if (ifiTypeToInterfaceType(ifi->ifi_type) == InterfaceType::UNKNOWN) {
210 NDN_LOG_DEBUG("unhandled interface type " << ifi->ifi_type);
211 return;
212 }
213
214 shared_ptr<NetworkInterface> interface;
215 auto it = m_interfaces.find(ifi->ifi_index);
216 if (it != m_interfaces.end()) {
217 interface = it->second;
218 BOOST_ASSERT(interface != nullptr);
219 BOOST_ASSERT(interface->getIndex() == ifi->ifi_index);
220 }
221
222 if (nlmsg->nlmsg_type == RTM_DELLINK) {
223 if (interface != nullptr) {
224 NDN_LOG_DEBUG("removing interface " << interface->getName());
225 m_interfaces.erase(it);
226 this->emitSignal(onInterfaceRemoved, interface);
227 }
228 return;
229 }
230
231 if (interface == nullptr) {
232 interface = makeNetworkInterface();
233 interface->setIndex(ifi->ifi_index);
234 }
235 interface->setType(ifiTypeToInterfaceType(ifi->ifi_type));
236 interface->setFlags(ifi->ifi_flags);
237
238 auto attrs = nlmsg.getAttributes<rtattr>(ifi);
239 NDN_LOG_TRACE("message contains " << attrs.size() << " attributes");
240
241 auto address = attrs.getAttributeByType<ethernet::Address>(IFLA_ADDRESS);
242 if (address)
243 interface->setEthernetAddress(*address);
244
245 auto broadcast = attrs.getAttributeByType<ethernet::Address>(IFLA_BROADCAST);
246 if (broadcast)
247 interface->setEthernetBroadcastAddress(*broadcast);
248
249 auto name = attrs.getAttributeByType<std::string>(IFLA_IFNAME);
250 if (name)
251 interface->setName(*name);
252
253 auto mtu = attrs.getAttributeByType<uint32_t>(IFLA_MTU);
254 if (mtu)
255 interface->setMtu(*mtu);
256
257 auto state = attrs.getAttributeByType<uint8_t>(IFLA_OPERSTATE);
258 updateInterfaceState(*interface, state ? *state : linux_if::OPER_STATE_UNKNOWN);
259
260 if (it == m_interfaces.end()) {
261 NDN_LOG_DEBUG("adding interface " << interface->getName());
262 m_interfaces[interface->getIndex()] = interface;
263 this->emitSignal(onInterfaceAdded, interface);
264 }
265}
266
267void
268NetworkMonitorImplNetlink::parseAddressMessage(const NetlinkMessage& nlmsg)
269{
270 const ifaddrmsg* ifa = nlmsg.getPayload<ifaddrmsg>();
271 if (ifa == nullptr) {
272 NDN_LOG_WARN("malformed ifaddrmsg");
273 return;
274 }
275
276 auto it = m_interfaces.find(ifa->ifa_index);
277 if (it == m_interfaces.end()) {
278 // unknown interface, ignore message
279 NDN_LOG_TRACE("unknown interface index " << ifa->ifa_index);
280 return;
281 }
282 auto interface = it->second;
283 BOOST_ASSERT(interface != nullptr);
284
285 auto attrs = nlmsg.getAttributes<rtattr>(ifa);
286 NDN_LOG_TRACE("message contains " << attrs.size() << " attributes");
287
288 namespace ip = boost::asio::ip;
289 ip::address ipAddr, broadcastAddr;
290 if (ifa->ifa_family == AF_INET) {
291 auto v4 = attrs.getAttributeByType<ip::address_v4>(IFA_LOCAL);
292 if (v4)
293 ipAddr = *v4;
294
295 v4 = attrs.getAttributeByType<ip::address_v4>(IFA_BROADCAST);
296 if (v4)
297 broadcastAddr = *v4;
298 }
299 else if (ifa->ifa_family == AF_INET6) {
300 auto v6 = attrs.getAttributeByType<ip::address_v6>(IFA_ADDRESS);
301 if (v6) {
302 if (v6->is_link_local())
303 v6->scope_id(ifa->ifa_index);
304
305 ipAddr = *v6;
306 }
307 }
308
309 uint32_t flags = ifa->ifa_flags; // overwritten by IFA_FLAGS if supported and present
310#ifdef NDN_CXX_HAVE_IFA_FLAGS
311 auto extFlags = attrs.getAttributeByType<uint32_t>(IFA_FLAGS);
312 if (extFlags)
313 flags = *extFlags;
314#endif // NDN_CXX_HAVE_IFA_FLAGS
315
316 NetworkAddress address(ifaFamilyToAddressFamily(ifa->ifa_family),
317 ipAddr,
318 broadcastAddr,
319 ifa->ifa_prefixlen,
320 ifaScopeToAddressScope(ifa->ifa_scope),
321 flags);
322 BOOST_ASSERT(address.getFamily() != AddressFamily::UNSPECIFIED);
323
324 if (nlmsg->nlmsg_type == RTM_NEWADDR)
325 interface->addNetworkAddress(address);
326 else if (nlmsg->nlmsg_type == RTM_DELADDR)
327 interface->removeNetworkAddress(address);
328}
329
330void
331NetworkMonitorImplNetlink::parseRouteMessage(const NetlinkMessage& nlmsg)
332{
333 // TODO
334}
335
336void
337NetworkMonitorImplNetlink::parseErrorMessage(const NetlinkMessage& nlmsg)
338{
339 const nlmsgerr* err = nlmsg.getPayload<nlmsgerr>();
340 if (err == nullptr) {
341 NDN_LOG_WARN("malformed nlmsgerr");
342 return;
343 }
344
345 if (err->error == 0) {
346 // an error code of zero indicates an ACK message, not an error
347 NDN_LOG_TRACE("ACK");
348 return;
349 }
350
351 NDN_LOG_ERROR("NLMSG_ERROR: " << std::strerror(std::abs(err->error)));
352
353#ifdef NDN_CXX_HAVE_NETLINK_EXT_ACK
354 if (!(nlmsg->nlmsg_flags & NLM_F_ACK_TLVS))
355 return;
356
357 size_t errLen = NLMSG_LENGTH(sizeof(nlmsgerr));
358 if (!(nlmsg->nlmsg_flags & NLM_F_CAPPED))
359 errLen += err->msg.nlmsg_len - NLMSG_HDRLEN; // don't count the inner nlmsghdr twice
360
361 if (nlmsg->nlmsg_len <= errLen)
362 return;
363
364 auto nla = reinterpret_cast<const nlattr*>(reinterpret_cast<const uint8_t*>(&*nlmsg) + errLen);
365 auto attrs = NetlinkMessageAttributes<nlattr>(nla, nlmsg->nlmsg_len - errLen);
366 auto msg = attrs.getAttributeByType<std::string>(NLMSGERR_ATTR_MSG);
Davide Pesaventoa0b2a2c2018-07-19 01:01:06 -0400367 if (msg && !msg->empty())
Davide Pesavento50b92262018-07-11 12:28:31 -0400368 NDN_LOG_ERROR("kernel message: " << *msg);
369#endif // NDN_CXX_HAVE_NETLINK_EXT_ACK
370}
371
Davide Pesavento50b92262018-07-11 12:28:31 -0400372} // namespace net
373} // namespace ndn