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