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