blob: 8433a163b952ad4a75d547e088a64f8e0d376ead [file] [log] [blame]
Junxiao Shib8590312016-12-29 21:22:25 +00001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
Junxiao Shi38b24c72017-01-05 02:59:31 +00003 * Copyright (c) 2014-2017, Regents of the University of California,
Junxiao Shib8590312016-12-29 21:22:25 +00004 * Arizona Board of Regents,
5 * Colorado State University,
6 * University Pierre & Marie Curie, Sorbonne University,
7 * Washington University in St. Louis,
8 * Beijing Institute of Technology,
9 * The University of Memphis.
10 *
11 * This file is part of NFD (Named Data Networking Forwarding Daemon).
12 * See AUTHORS.md for complete list of NFD authors and contributors.
13 *
14 * NFD is free software: you can redistribute it and/or modify it under the terms
15 * of the GNU General Public License as published by the Free Software Foundation,
16 * either version 3 of the License, or (at your option) any later version.
17 *
18 * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
19 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
20 * PURPOSE. See the GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License along with
23 * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
24 */
25
26#include "face-system.hpp"
27#include "core/logger.hpp"
Junxiao Shib8590312016-12-29 21:22:25 +000028#include "fw/face-table.hpp"
29
30// ProtocolFactory includes, sorted alphabetically
31#ifdef HAVE_LIBPCAP
32#include "ethernet-factory.hpp"
33#include "ethernet-transport.hpp"
34#endif // HAVE_LIBPCAP
35#include "tcp-factory.hpp"
36#include "udp-factory.hpp"
37#ifdef HAVE_UNIX_SOCKETS
38#include "unix-stream-factory.hpp"
39#endif // HAVE_UNIX_SOCKETS
40#ifdef HAVE_WEBSOCKET
41#include "websocket-factory.hpp"
42#endif // HAVE_WEBSOCKET
43
44namespace nfd {
45namespace face {
46
47NFD_LOG_INIT("FaceSystem");
48
49FaceSystem::FaceSystem(FaceTable& faceTable)
50 : m_faceTable(faceTable)
51{
Junxiao Shi38b24c72017-01-05 02:59:31 +000052 ///\todo #3904 make a registry, and construct instances from registry
53 m_factories["tcp"] = make_shared<TcpFactory>();
Junxiao Shib8590312016-12-29 21:22:25 +000054}
55
56std::set<const ProtocolFactory*>
57FaceSystem::listProtocolFactories() const
58{
59 std::set<const ProtocolFactory*> factories;
Junxiao Shi38b24c72017-01-05 02:59:31 +000060 for (const auto& p : m_factoryByScheme) {
Junxiao Shib8590312016-12-29 21:22:25 +000061 factories.insert(p.second.get());
62 }
63 return factories;
64}
65
66ProtocolFactory*
Junxiao Shi38b24c72017-01-05 02:59:31 +000067FaceSystem::getFactoryById(const std::string& id)
Junxiao Shib8590312016-12-29 21:22:25 +000068{
Junxiao Shi38b24c72017-01-05 02:59:31 +000069 auto found = m_factories.find(id);
Junxiao Shib8590312016-12-29 21:22:25 +000070 return found == m_factories.end() ? nullptr : found->second.get();
71}
72
Junxiao Shi38b24c72017-01-05 02:59:31 +000073ProtocolFactory*
74FaceSystem::getFactoryByScheme(const std::string& scheme)
75{
76 auto found = m_factoryByScheme.find(scheme);
77 return found == m_factoryByScheme.end() ? nullptr : found->second.get();
78}
79
Junxiao Shib8590312016-12-29 21:22:25 +000080void
81FaceSystem::setConfigFile(ConfigFile& configFile)
82{
83 configFile.addSectionHandler("face_system", bind(&FaceSystem::processConfig, this, _1, _2, _3));
84}
85
86void
87FaceSystem::processConfig(const ConfigSection& configSection, bool isDryRun, const std::string& filename)
88{
Junxiao Shi38b24c72017-01-05 02:59:31 +000089 ConfigContext context;
90 context.isDryRun = isDryRun;
91 context.addFace = bind(&FaceTable::add, &m_faceTable, _1);
92 context.m_nicList = listNetworkInterfaces();
93
94 // process sections in protocol factories
95 for (const auto& pair : m_factories) {
96 const std::string& sectionName = pair.first;
97 shared_ptr<ProtocolFactory> factory = pair.second;
98
99 std::set<std::string> oldProvidedSchemes = factory->getProvidedSchemes();
100 factory->processConfig(configSection.get_child_optional(sectionName), context);
101
102 if (!isDryRun) {
103 for (const std::string& scheme : factory->getProvidedSchemes()) {
104 m_factoryByScheme[scheme] = factory;
105 oldProvidedSchemes.erase(scheme);
106 }
107 for (const std::string& scheme : oldProvidedSchemes) {
108 m_factoryByScheme.erase(scheme);
109 }
110 }
111 }
112
113 // process other sections
Junxiao Shib8590312016-12-29 21:22:25 +0000114 std::set<std::string> seenSections;
Junxiao Shi38b24c72017-01-05 02:59:31 +0000115 for (const auto& pair : configSection) {
116 const std::string& sectionName = pair.first;
117 const ConfigSection& subSection = pair.second;
Junxiao Shib8590312016-12-29 21:22:25 +0000118
Junxiao Shi38b24c72017-01-05 02:59:31 +0000119 if (!seenSections.insert(sectionName).second) {
120 BOOST_THROW_EXCEPTION(ConfigFile::Error("Duplicate section face_system." + sectionName));
Junxiao Shib8590312016-12-29 21:22:25 +0000121 }
122
Junxiao Shi38b24c72017-01-05 02:59:31 +0000123 if (m_factories.count(sectionName) > 0) {
124 continue;
Junxiao Shib8590312016-12-29 21:22:25 +0000125 }
Junxiao Shi38b24c72017-01-05 02:59:31 +0000126
127 ///\todo #3521 nicfaces
128
129 ///\todo #3904 process these in protocol factory
130 if (sectionName == "unix") {
131 processSectionUnix(subSection, isDryRun);
Junxiao Shib8590312016-12-29 21:22:25 +0000132 }
Junxiao Shi38b24c72017-01-05 02:59:31 +0000133 else if (sectionName == "udp") {
134 processSectionUdp(subSection, isDryRun, context.m_nicList);
Junxiao Shib8590312016-12-29 21:22:25 +0000135 }
Junxiao Shi38b24c72017-01-05 02:59:31 +0000136 else if (sectionName == "ether") {
137 processSectionEther(subSection, isDryRun, context.m_nicList);
Junxiao Shib8590312016-12-29 21:22:25 +0000138 }
Junxiao Shi38b24c72017-01-05 02:59:31 +0000139 else if (sectionName == "websocket") {
140 processSectionWebSocket(subSection, isDryRun);
Junxiao Shib8590312016-12-29 21:22:25 +0000141 }
142 else {
Junxiao Shi38b24c72017-01-05 02:59:31 +0000143 BOOST_THROW_EXCEPTION(ConfigFile::Error("Unrecognized option face_system." + sectionName));
Junxiao Shib8590312016-12-29 21:22:25 +0000144 }
145 }
146}
147
148void
149FaceSystem::processSectionUnix(const ConfigSection& configSection, bool isDryRun)
150{
151 // ; the unix section contains settings of Unix stream faces and channels
152 // unix
153 // {
154 // path /var/run/nfd.sock ; Unix stream listener path
155 // }
156
157#if defined(HAVE_UNIX_SOCKETS)
158 std::string path = "/var/run/nfd.sock";
159
160 for (const auto& i : configSection) {
161 if (i.first == "path") {
162 path = i.second.get_value<std::string>();
163 }
164 else {
165 BOOST_THROW_EXCEPTION(ConfigFile::Error("Unrecognized option \"" +
166 i.first + "\" in \"unix\" section"));
167 }
168 }
169
170 if (!isDryRun) {
Junxiao Shi38b24c72017-01-05 02:59:31 +0000171 if (m_factoryByScheme.count("unix") > 0) {
Junxiao Shib8590312016-12-29 21:22:25 +0000172 return;
173 }
174
175 auto factory = make_shared<UnixStreamFactory>();
Junxiao Shi38b24c72017-01-05 02:59:31 +0000176 m_factoryByScheme.emplace("unix", factory);
Junxiao Shib8590312016-12-29 21:22:25 +0000177
178 auto channel = factory->createChannel(path);
179 channel->listen(bind(&FaceTable::add, &m_faceTable, _1), nullptr);
180 }
181#else
182 BOOST_THROW_EXCEPTION(ConfigFile::Error("NFD was compiled without Unix sockets support, "
183 "cannot process \"unix\" section"));
184#endif // HAVE_UNIX_SOCKETS
185}
186
187void
Junxiao Shib8590312016-12-29 21:22:25 +0000188FaceSystem::processSectionUdp(const ConfigSection& configSection, bool isDryRun,
189 const std::vector<NetworkInterfaceInfo>& nicList)
190{
191 // ; the udp section contains settings of UDP faces and channels
192 // udp
193 // {
194 // port 6363 ; UDP unicast port number
195 // idle_timeout 600 ; idle time (seconds) before closing a UDP unicast face
196 // keep_alive_interval 25 ; interval (seconds) between keep-alive refreshes
197
198 // ; NFD creates one UDP multicast face per NIC
199 // mcast yes ; set to 'no' to disable UDP multicast, default 'yes'
200 // mcast_port 56363 ; UDP multicast port number
201 // mcast_group 224.0.23.170 ; UDP multicast group (IPv4 only)
202 // }
203
204 uint16_t port = 6363;
205 bool enableV4 = true;
206 bool enableV6 = true;
207 size_t timeout = 600;
208 size_t keepAliveInterval = 25;
209 bool useMcast = true;
210 auto mcastGroup = boost::asio::ip::address_v4::from_string("224.0.23.170");
211 uint16_t mcastPort = 56363;
212
213 for (const auto& i : configSection) {
214 if (i.first == "port") {
215 port = ConfigFile::parseNumber<uint16_t>(i, "udp");
216 NFD_LOG_TRACE("UDP unicast port set to " << port);
217 }
218 else if (i.first == "enable_v4") {
219 enableV4 = ConfigFile::parseYesNo(i, "udp");
220 }
221 else if (i.first == "enable_v6") {
222 enableV6 = ConfigFile::parseYesNo(i, "udp");
223 }
224 else if (i.first == "idle_timeout") {
225 try {
226 timeout = i.second.get_value<size_t>();
227 }
228 catch (const boost::property_tree::ptree_bad_data&) {
229 BOOST_THROW_EXCEPTION(ConfigFile::Error("Invalid value for option \"" +
230 i.first + "\" in \"udp\" section"));
231 }
232 }
233 else if (i.first == "keep_alive_interval") {
234 try {
235 keepAliveInterval = i.second.get_value<size_t>();
236 /// \todo Make use of keepAliveInterval
237 (void)(keepAliveInterval);
238 }
239 catch (const boost::property_tree::ptree_bad_data&) {
240 BOOST_THROW_EXCEPTION(ConfigFile::Error("Invalid value for option \"" +
241 i.first + "\" in \"udp\" section"));
242 }
243 }
244 else if (i.first == "mcast") {
245 useMcast = ConfigFile::parseYesNo(i, "udp");
246 }
247 else if (i.first == "mcast_port") {
248 mcastPort = ConfigFile::parseNumber<uint16_t>(i, "udp");
249 NFD_LOG_TRACE("UDP multicast port set to " << mcastPort);
250 }
251 else if (i.first == "mcast_group") {
252 boost::system::error_code ec;
253 mcastGroup = boost::asio::ip::address_v4::from_string(i.second.get_value<std::string>(), ec);
254 if (ec) {
255 BOOST_THROW_EXCEPTION(ConfigFile::Error("Invalid value for option \"" +
256 i.first + "\" in \"udp\" section"));
257 }
258 NFD_LOG_TRACE("UDP multicast group set to " << mcastGroup);
259 }
260 else {
261 BOOST_THROW_EXCEPTION(ConfigFile::Error("Unrecognized option \"" +
262 i.first + "\" in \"udp\" section"));
263 }
264 }
265
266 if (!enableV4 && !enableV6) {
267 BOOST_THROW_EXCEPTION(ConfigFile::Error("IPv4 and IPv6 UDP channels have been disabled."
268 " Remove \"udp\" section to disable UDP channels or"
269 " re-enable at least one channel type."));
270 }
271 else if (useMcast && !enableV4) {
272 BOOST_THROW_EXCEPTION(ConfigFile::Error("IPv4 multicast requested, but IPv4 channels"
273 " have been disabled (conflicting configuration options set)"));
274 }
275
276 if (!isDryRun) {
277 shared_ptr<UdpFactory> factory;
278 bool isReload = false;
Junxiao Shi38b24c72017-01-05 02:59:31 +0000279 if (m_factoryByScheme.count("udp") > 0) {
Junxiao Shib8590312016-12-29 21:22:25 +0000280 isReload = true;
Junxiao Shi38b24c72017-01-05 02:59:31 +0000281 factory = static_pointer_cast<UdpFactory>(m_factoryByScheme["udp"]);
Junxiao Shib8590312016-12-29 21:22:25 +0000282 }
283 else {
284 factory = make_shared<UdpFactory>();
Junxiao Shi38b24c72017-01-05 02:59:31 +0000285 m_factoryByScheme.emplace("udp", factory);
Junxiao Shib8590312016-12-29 21:22:25 +0000286 }
287
288 if (!isReload && enableV4) {
289 udp::Endpoint endpoint(boost::asio::ip::udp::v4(), port);
290 shared_ptr<UdpChannel> v4Channel = factory->createChannel(endpoint, time::seconds(timeout));
291 v4Channel->listen(bind(&FaceTable::add, &m_faceTable, _1), nullptr);
292
Junxiao Shi38b24c72017-01-05 02:59:31 +0000293 m_factoryByScheme.emplace("udp4", factory);
Junxiao Shib8590312016-12-29 21:22:25 +0000294 }
295
296 if (!isReload && enableV6) {
297 udp::Endpoint endpoint(boost::asio::ip::udp::v6(), port);
298 shared_ptr<UdpChannel> v6Channel = factory->createChannel(endpoint, time::seconds(timeout));
299 v6Channel->listen(bind(&FaceTable::add, &m_faceTable, _1), nullptr);
300
Junxiao Shi38b24c72017-01-05 02:59:31 +0000301 m_factoryByScheme.emplace("udp6", factory);
Junxiao Shib8590312016-12-29 21:22:25 +0000302 }
303
304 std::set<shared_ptr<Face>> multicastFacesToRemove;
305 for (const auto& i : factory->getMulticastFaces()) {
306 multicastFacesToRemove.insert(i.second);
307 }
308
309 if (useMcast && enableV4) {
310 std::vector<NetworkInterfaceInfo> ipv4MulticastInterfaces;
311 for (const auto& nic : nicList) {
312 if (nic.isUp() && nic.isMulticastCapable() && !nic.ipv4Addresses.empty()) {
313 ipv4MulticastInterfaces.push_back(nic);
314 }
315 }
316
317 bool isNicNameNecessary = false;
318#if defined(__linux__)
319 if (ipv4MulticastInterfaces.size() > 1) {
320 // On Linux if we have more than one MulticastUdpFace
321 // we need to specify the name of the interface
322 isNicNameNecessary = true;
323 }
324#endif
325
326 udp::Endpoint mcastEndpoint(mcastGroup, mcastPort);
327 for (const auto& nic : ipv4MulticastInterfaces) {
328 udp::Endpoint localEndpoint(nic.ipv4Addresses[0], mcastPort);
329 auto newFace = factory->createMulticastFace(localEndpoint, mcastEndpoint,
330 isNicNameNecessary ? nic.name : "");
331 m_faceTable.add(newFace);
332 multicastFacesToRemove.erase(newFace);
333 }
334 }
335
336 for (const auto& face : multicastFacesToRemove) {
337 face->close();
338 }
339 }
340}
341
342void
343FaceSystem::processSectionEther(const ConfigSection& configSection, bool isDryRun,
344 const std::vector<NetworkInterfaceInfo>& nicList)
345{
346 // ; the ether section contains settings of Ethernet faces and channels
347 // ether
348 // {
349 // ; NFD creates one Ethernet multicast face per NIC
350 // mcast yes ; set to 'no' to disable Ethernet multicast, default 'yes'
351 // mcast_group 01:00:5E:00:17:AA ; Ethernet multicast group
352 // }
353
354#if defined(HAVE_LIBPCAP)
355 NetworkInterfacePredicate nicPredicate;
356 bool useMcast = true;
357 ethernet::Address mcastGroup(ethernet::getDefaultMulticastAddress());
358
359 for (const auto& i : configSection) {
360 if (i.first == "mcast") {
361 useMcast = ConfigFile::parseYesNo(i, "ether");
362 }
363 else if (i.first == "mcast_group") {
364 mcastGroup = ethernet::Address::fromString(i.second.get_value<std::string>());
365 if (mcastGroup.isNull()) {
366 BOOST_THROW_EXCEPTION(ConfigFile::Error("Invalid value for option \"" +
367 i.first + "\" in \"ether\" section"));
368 }
369 NFD_LOG_TRACE("Ethernet multicast group set to " << mcastGroup);
370 }
371 else if (i.first == "whitelist") {
372 nicPredicate.parseWhitelist(i.second);
373 }
374 else if (i.first == "blacklist") {
375 nicPredicate.parseBlacklist(i.second);
376 }
377 else {
378 BOOST_THROW_EXCEPTION(ConfigFile::Error("Unrecognized option \"" +
379 i.first + "\" in \"ether\" section"));
380 }
381 }
382
383 if (!isDryRun) {
384 shared_ptr<EthernetFactory> factory;
Junxiao Shi38b24c72017-01-05 02:59:31 +0000385 if (m_factoryByScheme.count("ether") > 0) {
386 factory = static_pointer_cast<EthernetFactory>(m_factoryByScheme["ether"]);
Junxiao Shib8590312016-12-29 21:22:25 +0000387 }
388 else {
389 factory = make_shared<EthernetFactory>();
Junxiao Shi38b24c72017-01-05 02:59:31 +0000390 m_factoryByScheme.emplace("ether", factory);
Junxiao Shib8590312016-12-29 21:22:25 +0000391 }
392
393 std::set<shared_ptr<Face>> multicastFacesToRemove;
394 for (const auto& i : factory->getMulticastFaces()) {
395 multicastFacesToRemove.insert(i.second);
396 }
397
398 if (useMcast) {
399 for (const auto& nic : nicList) {
400 if (nic.isUp() && nic.isMulticastCapable() && nicPredicate(nic)) {
401 try {
402 auto newFace = factory->createMulticastFace(nic, mcastGroup);
403 m_faceTable.add(newFace);
404 multicastFacesToRemove.erase(newFace);
405 }
406 catch (const EthernetFactory::Error& factoryError) {
407 NFD_LOG_ERROR(factoryError.what() << ", continuing");
408 }
409 catch (const EthernetTransport::Error& faceError) {
410 NFD_LOG_ERROR(faceError.what() << ", continuing");
411 }
412 }
413 }
414 }
415
416 for (const auto& face : multicastFacesToRemove) {
417 face->close();
418 }
419 }
420#else
421 BOOST_THROW_EXCEPTION(ConfigFile::Error("NFD was compiled without libpcap, cannot process \"ether\" section"));
422#endif // HAVE_LIBPCAP
423}
424
425void
426FaceSystem::processSectionWebSocket(const ConfigSection& configSection, bool isDryRun)
427{
428 // ; the websocket section contains settings of WebSocket faces and channels
429 // websocket
430 // {
431 // listen yes ; set to 'no' to disable WebSocket listener, default 'yes'
432 // port 9696 ; WebSocket listener port number
433 // enable_v4 yes ; set to 'no' to disable listening on IPv4 socket, default 'yes'
434 // enable_v6 yes ; set to 'no' to disable listening on IPv6 socket, default 'yes'
435 // }
436
437#if defined(HAVE_WEBSOCKET)
438 uint16_t port = 9696;
439 bool needToListen = true;
440 bool enableV4 = true;
441 bool enableV6 = true;
442
443 for (const auto& i : configSection) {
444 if (i.first == "port") {
445 port = ConfigFile::parseNumber<uint16_t>(i, "websocket");
446 NFD_LOG_TRACE("WebSocket port set to " << port);
447 }
448 else if (i.first == "listen") {
449 needToListen = ConfigFile::parseYesNo(i, "websocket");
450 }
451 else if (i.first == "enable_v4") {
452 enableV4 = ConfigFile::parseYesNo(i, "websocket");
453 }
454 else if (i.first == "enable_v6") {
455 enableV6 = ConfigFile::parseYesNo(i, "websocket");
456 }
457 else {
458 BOOST_THROW_EXCEPTION(ConfigFile::Error("Unrecognized option \"" +
459 i.first + "\" in \"websocket\" section"));
460 }
461 }
462
463 if (!enableV4 && !enableV6) {
464 BOOST_THROW_EXCEPTION(ConfigFile::Error("IPv4 and IPv6 WebSocket channels have been disabled."
465 " Remove \"websocket\" section to disable WebSocket channels or"
466 " re-enable at least one channel type."));
467 }
468
469 if (!enableV4 && enableV6) {
470 BOOST_THROW_EXCEPTION(ConfigFile::Error("NFD does not allow pure IPv6 WebSocket channel."));
471 }
472
473 if (!isDryRun) {
Junxiao Shi38b24c72017-01-05 02:59:31 +0000474 if (m_factoryByScheme.count("websocket") > 0) {
Junxiao Shib8590312016-12-29 21:22:25 +0000475 return;
476 }
477
478 auto factory = make_shared<WebSocketFactory>();
Junxiao Shi38b24c72017-01-05 02:59:31 +0000479 m_factoryByScheme.emplace("websocket", factory);
Junxiao Shib8590312016-12-29 21:22:25 +0000480
481 shared_ptr<WebSocketChannel> channel;
482
483 if (enableV6 && enableV4) {
484 websocket::Endpoint endpoint(boost::asio::ip::address_v6::any(), port);
485 channel = factory->createChannel(endpoint);
486
Junxiao Shi38b24c72017-01-05 02:59:31 +0000487 m_factoryByScheme.emplace("websocket46", factory);
Junxiao Shib8590312016-12-29 21:22:25 +0000488 }
489 else if (enableV4) {
490 websocket::Endpoint endpoint(boost::asio::ip::address_v4::any(), port);
491 channel = factory->createChannel(endpoint);
492
Junxiao Shi38b24c72017-01-05 02:59:31 +0000493 m_factoryByScheme.emplace("websocket4", factory);
Junxiao Shib8590312016-12-29 21:22:25 +0000494 }
495
496 if (channel && needToListen) {
497 channel->listen(bind(&FaceTable::add, &m_faceTable, _1));
498 }
499 }
500#else
501 BOOST_THROW_EXCEPTION(ConfigFile::Error("NFD was compiled without WebSocket, "
502 "cannot process \"websocket\" section"));
503#endif // HAVE_WEBSOCKET
504}
505
506} // namespace face
507} // namespace nfd