blob: 0d4a308a591fb5449bb65a932509cdaccfd35900 [file] [log] [blame]
Junxiao Shib8590312016-12-29 21:22:25 +00001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
3 * Copyright (c) 2014-2016, Regents of the University of California,
4 * 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"
28#include "core/network-interface.hpp"
29#include "core/network-interface-predicate.hpp"
30#include "fw/face-table.hpp"
31
32// ProtocolFactory includes, sorted alphabetically
33#ifdef HAVE_LIBPCAP
34#include "ethernet-factory.hpp"
35#include "ethernet-transport.hpp"
36#endif // HAVE_LIBPCAP
37#include "tcp-factory.hpp"
38#include "udp-factory.hpp"
39#ifdef HAVE_UNIX_SOCKETS
40#include "unix-stream-factory.hpp"
41#endif // HAVE_UNIX_SOCKETS
42#ifdef HAVE_WEBSOCKET
43#include "websocket-factory.hpp"
44#endif // HAVE_WEBSOCKET
45
46namespace nfd {
47namespace face {
48
49NFD_LOG_INIT("FaceSystem");
50
51FaceSystem::FaceSystem(FaceTable& faceTable)
52 : m_faceTable(faceTable)
53{
54}
55
56std::set<const ProtocolFactory*>
57FaceSystem::listProtocolFactories() const
58{
59 std::set<const ProtocolFactory*> factories;
60 for (const auto& p : m_factories) {
61 factories.insert(p.second.get());
62 }
63 return factories;
64}
65
66ProtocolFactory*
67FaceSystem::getProtocolFactory(const std::string& scheme)
68{
69 auto found = m_factories.find(scheme);
70 return found == m_factories.end() ? nullptr : found->second.get();
71}
72
73void
74FaceSystem::setConfigFile(ConfigFile& configFile)
75{
76 configFile.addSectionHandler("face_system", bind(&FaceSystem::processConfig, this, _1, _2, _3));
77}
78
79void
80FaceSystem::processConfig(const ConfigSection& configSection, bool isDryRun, const std::string& filename)
81{
82 std::set<std::string> seenSections;
83 auto nicList = listNetworkInterfaces();
84
85 for (const auto& item : configSection) {
86 if (!seenSections.insert(item.first).second) {
87 BOOST_THROW_EXCEPTION(ConfigFile::Error("Duplicate \"" + item.first + "\" section"));
88 }
89
90 if (item.first == "unix") {
91 processSectionUnix(item.second, isDryRun);
92 }
93 else if (item.first == "tcp") {
94 processSectionTcp(item.second, isDryRun);
95 }
96 else if (item.first == "udp") {
97 processSectionUdp(item.second, isDryRun, nicList);
98 }
99 else if (item.first == "ether") {
100 processSectionEther(item.second, isDryRun, nicList);
101 }
102 else if (item.first == "websocket") {
103 processSectionWebSocket(item.second, isDryRun);
104 }
105 else {
106 BOOST_THROW_EXCEPTION(ConfigFile::Error("Unrecognized option \"" + item.first + "\""));
107 }
108 }
109}
110
111void
112FaceSystem::processSectionUnix(const ConfigSection& configSection, bool isDryRun)
113{
114 // ; the unix section contains settings of Unix stream faces and channels
115 // unix
116 // {
117 // path /var/run/nfd.sock ; Unix stream listener path
118 // }
119
120#if defined(HAVE_UNIX_SOCKETS)
121 std::string path = "/var/run/nfd.sock";
122
123 for (const auto& i : configSection) {
124 if (i.first == "path") {
125 path = i.second.get_value<std::string>();
126 }
127 else {
128 BOOST_THROW_EXCEPTION(ConfigFile::Error("Unrecognized option \"" +
129 i.first + "\" in \"unix\" section"));
130 }
131 }
132
133 if (!isDryRun) {
134 if (m_factories.count("unix") > 0) {
135 return;
136 }
137
138 auto factory = make_shared<UnixStreamFactory>();
139 m_factories.emplace("unix", factory);
140
141 auto channel = factory->createChannel(path);
142 channel->listen(bind(&FaceTable::add, &m_faceTable, _1), nullptr);
143 }
144#else
145 BOOST_THROW_EXCEPTION(ConfigFile::Error("NFD was compiled without Unix sockets support, "
146 "cannot process \"unix\" section"));
147#endif // HAVE_UNIX_SOCKETS
148}
149
150void
151FaceSystem::processSectionTcp(const ConfigSection& configSection, bool isDryRun)
152{
153 // ; the tcp section contains settings of TCP faces and channels
154 // tcp
155 // {
156 // listen yes ; set to 'no' to disable TCP listener, default 'yes'
157 // port 6363 ; TCP listener port number
158 // }
159
160 uint16_t port = 6363;
161 bool needToListen = true;
162 bool enableV4 = true;
163 bool enableV6 = true;
164
165 for (const auto& i : configSection) {
166 if (i.first == "port") {
167 port = ConfigFile::parseNumber<uint16_t>(i, "tcp");
168 NFD_LOG_TRACE("TCP port set to " << port);
169 }
170 else if (i.first == "listen") {
171 needToListen = ConfigFile::parseYesNo(i, "tcp");
172 }
173 else if (i.first == "enable_v4") {
174 enableV4 = ConfigFile::parseYesNo(i, "tcp");
175 }
176 else if (i.first == "enable_v6") {
177 enableV6 = ConfigFile::parseYesNo(i, "tcp");
178 }
179 else {
180 BOOST_THROW_EXCEPTION(ConfigFile::Error("Unrecognized option \"" +
181 i.first + "\" in \"tcp\" section"));
182 }
183 }
184
185 if (!enableV4 && !enableV6) {
186 BOOST_THROW_EXCEPTION(ConfigFile::Error("IPv4 and IPv6 TCP channels have been disabled."
187 " Remove \"tcp\" section to disable TCP channels or"
188 " re-enable at least one channel type."));
189 }
190
191 if (!isDryRun) {
192 if (m_factories.count("tcp") > 0) {
193 return;
194 }
195
196 auto factory = make_shared<TcpFactory>();
197 m_factories.emplace("tcp", factory);
198
199 if (enableV4) {
200 tcp::Endpoint endpoint(boost::asio::ip::tcp::v4(), port);
201 shared_ptr<TcpChannel> v4Channel = factory->createChannel(endpoint);
202 if (needToListen) {
203 v4Channel->listen(bind(&FaceTable::add, &m_faceTable, _1), nullptr);
204 }
205
206 m_factories.emplace("tcp4", factory);
207 }
208
209 if (enableV6) {
210 tcp::Endpoint endpoint(boost::asio::ip::tcp::v6(), port);
211 shared_ptr<TcpChannel> v6Channel = factory->createChannel(endpoint);
212 if (needToListen) {
213 v6Channel->listen(bind(&FaceTable::add, &m_faceTable, _1), nullptr);
214 }
215
216 m_factories.emplace("tcp6", factory);
217 }
218 }
219}
220
221void
222FaceSystem::processSectionUdp(const ConfigSection& configSection, bool isDryRun,
223 const std::vector<NetworkInterfaceInfo>& nicList)
224{
225 // ; the udp section contains settings of UDP faces and channels
226 // udp
227 // {
228 // port 6363 ; UDP unicast port number
229 // idle_timeout 600 ; idle time (seconds) before closing a UDP unicast face
230 // keep_alive_interval 25 ; interval (seconds) between keep-alive refreshes
231
232 // ; NFD creates one UDP multicast face per NIC
233 // mcast yes ; set to 'no' to disable UDP multicast, default 'yes'
234 // mcast_port 56363 ; UDP multicast port number
235 // mcast_group 224.0.23.170 ; UDP multicast group (IPv4 only)
236 // }
237
238 uint16_t port = 6363;
239 bool enableV4 = true;
240 bool enableV6 = true;
241 size_t timeout = 600;
242 size_t keepAliveInterval = 25;
243 bool useMcast = true;
244 auto mcastGroup = boost::asio::ip::address_v4::from_string("224.0.23.170");
245 uint16_t mcastPort = 56363;
246
247 for (const auto& i : configSection) {
248 if (i.first == "port") {
249 port = ConfigFile::parseNumber<uint16_t>(i, "udp");
250 NFD_LOG_TRACE("UDP unicast port set to " << port);
251 }
252 else if (i.first == "enable_v4") {
253 enableV4 = ConfigFile::parseYesNo(i, "udp");
254 }
255 else if (i.first == "enable_v6") {
256 enableV6 = ConfigFile::parseYesNo(i, "udp");
257 }
258 else if (i.first == "idle_timeout") {
259 try {
260 timeout = i.second.get_value<size_t>();
261 }
262 catch (const boost::property_tree::ptree_bad_data&) {
263 BOOST_THROW_EXCEPTION(ConfigFile::Error("Invalid value for option \"" +
264 i.first + "\" in \"udp\" section"));
265 }
266 }
267 else if (i.first == "keep_alive_interval") {
268 try {
269 keepAliveInterval = i.second.get_value<size_t>();
270 /// \todo Make use of keepAliveInterval
271 (void)(keepAliveInterval);
272 }
273 catch (const boost::property_tree::ptree_bad_data&) {
274 BOOST_THROW_EXCEPTION(ConfigFile::Error("Invalid value for option \"" +
275 i.first + "\" in \"udp\" section"));
276 }
277 }
278 else if (i.first == "mcast") {
279 useMcast = ConfigFile::parseYesNo(i, "udp");
280 }
281 else if (i.first == "mcast_port") {
282 mcastPort = ConfigFile::parseNumber<uint16_t>(i, "udp");
283 NFD_LOG_TRACE("UDP multicast port set to " << mcastPort);
284 }
285 else if (i.first == "mcast_group") {
286 boost::system::error_code ec;
287 mcastGroup = boost::asio::ip::address_v4::from_string(i.second.get_value<std::string>(), ec);
288 if (ec) {
289 BOOST_THROW_EXCEPTION(ConfigFile::Error("Invalid value for option \"" +
290 i.first + "\" in \"udp\" section"));
291 }
292 NFD_LOG_TRACE("UDP multicast group set to " << mcastGroup);
293 }
294 else {
295 BOOST_THROW_EXCEPTION(ConfigFile::Error("Unrecognized option \"" +
296 i.first + "\" in \"udp\" section"));
297 }
298 }
299
300 if (!enableV4 && !enableV6) {
301 BOOST_THROW_EXCEPTION(ConfigFile::Error("IPv4 and IPv6 UDP channels have been disabled."
302 " Remove \"udp\" section to disable UDP channels or"
303 " re-enable at least one channel type."));
304 }
305 else if (useMcast && !enableV4) {
306 BOOST_THROW_EXCEPTION(ConfigFile::Error("IPv4 multicast requested, but IPv4 channels"
307 " have been disabled (conflicting configuration options set)"));
308 }
309
310 if (!isDryRun) {
311 shared_ptr<UdpFactory> factory;
312 bool isReload = false;
313 if (m_factories.count("udp") > 0) {
314 isReload = true;
315 factory = static_pointer_cast<UdpFactory>(m_factories["udp"]);
316 }
317 else {
318 factory = make_shared<UdpFactory>();
319 m_factories.emplace("udp", factory);
320 }
321
322 if (!isReload && enableV4) {
323 udp::Endpoint endpoint(boost::asio::ip::udp::v4(), port);
324 shared_ptr<UdpChannel> v4Channel = factory->createChannel(endpoint, time::seconds(timeout));
325 v4Channel->listen(bind(&FaceTable::add, &m_faceTable, _1), nullptr);
326
327 m_factories.emplace("udp4", factory);
328 }
329
330 if (!isReload && enableV6) {
331 udp::Endpoint endpoint(boost::asio::ip::udp::v6(), port);
332 shared_ptr<UdpChannel> v6Channel = factory->createChannel(endpoint, time::seconds(timeout));
333 v6Channel->listen(bind(&FaceTable::add, &m_faceTable, _1), nullptr);
334
335 m_factories.emplace("udp6", factory);
336 }
337
338 std::set<shared_ptr<Face>> multicastFacesToRemove;
339 for (const auto& i : factory->getMulticastFaces()) {
340 multicastFacesToRemove.insert(i.second);
341 }
342
343 if (useMcast && enableV4) {
344 std::vector<NetworkInterfaceInfo> ipv4MulticastInterfaces;
345 for (const auto& nic : nicList) {
346 if (nic.isUp() && nic.isMulticastCapable() && !nic.ipv4Addresses.empty()) {
347 ipv4MulticastInterfaces.push_back(nic);
348 }
349 }
350
351 bool isNicNameNecessary = false;
352#if defined(__linux__)
353 if (ipv4MulticastInterfaces.size() > 1) {
354 // On Linux if we have more than one MulticastUdpFace
355 // we need to specify the name of the interface
356 isNicNameNecessary = true;
357 }
358#endif
359
360 udp::Endpoint mcastEndpoint(mcastGroup, mcastPort);
361 for (const auto& nic : ipv4MulticastInterfaces) {
362 udp::Endpoint localEndpoint(nic.ipv4Addresses[0], mcastPort);
363 auto newFace = factory->createMulticastFace(localEndpoint, mcastEndpoint,
364 isNicNameNecessary ? nic.name : "");
365 m_faceTable.add(newFace);
366 multicastFacesToRemove.erase(newFace);
367 }
368 }
369
370 for (const auto& face : multicastFacesToRemove) {
371 face->close();
372 }
373 }
374}
375
376void
377FaceSystem::processSectionEther(const ConfigSection& configSection, bool isDryRun,
378 const std::vector<NetworkInterfaceInfo>& nicList)
379{
380 // ; the ether section contains settings of Ethernet faces and channels
381 // ether
382 // {
383 // ; NFD creates one Ethernet multicast face per NIC
384 // mcast yes ; set to 'no' to disable Ethernet multicast, default 'yes'
385 // mcast_group 01:00:5E:00:17:AA ; Ethernet multicast group
386 // }
387
388#if defined(HAVE_LIBPCAP)
389 NetworkInterfacePredicate nicPredicate;
390 bool useMcast = true;
391 ethernet::Address mcastGroup(ethernet::getDefaultMulticastAddress());
392
393 for (const auto& i : configSection) {
394 if (i.first == "mcast") {
395 useMcast = ConfigFile::parseYesNo(i, "ether");
396 }
397 else if (i.first == "mcast_group") {
398 mcastGroup = ethernet::Address::fromString(i.second.get_value<std::string>());
399 if (mcastGroup.isNull()) {
400 BOOST_THROW_EXCEPTION(ConfigFile::Error("Invalid value for option \"" +
401 i.first + "\" in \"ether\" section"));
402 }
403 NFD_LOG_TRACE("Ethernet multicast group set to " << mcastGroup);
404 }
405 else if (i.first == "whitelist") {
406 nicPredicate.parseWhitelist(i.second);
407 }
408 else if (i.first == "blacklist") {
409 nicPredicate.parseBlacklist(i.second);
410 }
411 else {
412 BOOST_THROW_EXCEPTION(ConfigFile::Error("Unrecognized option \"" +
413 i.first + "\" in \"ether\" section"));
414 }
415 }
416
417 if (!isDryRun) {
418 shared_ptr<EthernetFactory> factory;
419 if (m_factories.count("ether") > 0) {
420 factory = static_pointer_cast<EthernetFactory>(m_factories["ether"]);
421 }
422 else {
423 factory = make_shared<EthernetFactory>();
424 m_factories.emplace("ether", factory);
425 }
426
427 std::set<shared_ptr<Face>> multicastFacesToRemove;
428 for (const auto& i : factory->getMulticastFaces()) {
429 multicastFacesToRemove.insert(i.second);
430 }
431
432 if (useMcast) {
433 for (const auto& nic : nicList) {
434 if (nic.isUp() && nic.isMulticastCapable() && nicPredicate(nic)) {
435 try {
436 auto newFace = factory->createMulticastFace(nic, mcastGroup);
437 m_faceTable.add(newFace);
438 multicastFacesToRemove.erase(newFace);
439 }
440 catch (const EthernetFactory::Error& factoryError) {
441 NFD_LOG_ERROR(factoryError.what() << ", continuing");
442 }
443 catch (const EthernetTransport::Error& faceError) {
444 NFD_LOG_ERROR(faceError.what() << ", continuing");
445 }
446 }
447 }
448 }
449
450 for (const auto& face : multicastFacesToRemove) {
451 face->close();
452 }
453 }
454#else
455 BOOST_THROW_EXCEPTION(ConfigFile::Error("NFD was compiled without libpcap, cannot process \"ether\" section"));
456#endif // HAVE_LIBPCAP
457}
458
459void
460FaceSystem::processSectionWebSocket(const ConfigSection& configSection, bool isDryRun)
461{
462 // ; the websocket section contains settings of WebSocket faces and channels
463 // websocket
464 // {
465 // listen yes ; set to 'no' to disable WebSocket listener, default 'yes'
466 // port 9696 ; WebSocket listener port number
467 // enable_v4 yes ; set to 'no' to disable listening on IPv4 socket, default 'yes'
468 // enable_v6 yes ; set to 'no' to disable listening on IPv6 socket, default 'yes'
469 // }
470
471#if defined(HAVE_WEBSOCKET)
472 uint16_t port = 9696;
473 bool needToListen = true;
474 bool enableV4 = true;
475 bool enableV6 = true;
476
477 for (const auto& i : configSection) {
478 if (i.first == "port") {
479 port = ConfigFile::parseNumber<uint16_t>(i, "websocket");
480 NFD_LOG_TRACE("WebSocket port set to " << port);
481 }
482 else if (i.first == "listen") {
483 needToListen = ConfigFile::parseYesNo(i, "websocket");
484 }
485 else if (i.first == "enable_v4") {
486 enableV4 = ConfigFile::parseYesNo(i, "websocket");
487 }
488 else if (i.first == "enable_v6") {
489 enableV6 = ConfigFile::parseYesNo(i, "websocket");
490 }
491 else {
492 BOOST_THROW_EXCEPTION(ConfigFile::Error("Unrecognized option \"" +
493 i.first + "\" in \"websocket\" section"));
494 }
495 }
496
497 if (!enableV4 && !enableV6) {
498 BOOST_THROW_EXCEPTION(ConfigFile::Error("IPv4 and IPv6 WebSocket channels have been disabled."
499 " Remove \"websocket\" section to disable WebSocket channels or"
500 " re-enable at least one channel type."));
501 }
502
503 if (!enableV4 && enableV6) {
504 BOOST_THROW_EXCEPTION(ConfigFile::Error("NFD does not allow pure IPv6 WebSocket channel."));
505 }
506
507 if (!isDryRun) {
508 if (m_factories.count("websocket") > 0) {
509 return;
510 }
511
512 auto factory = make_shared<WebSocketFactory>();
513 m_factories.emplace("websocket", factory);
514
515 shared_ptr<WebSocketChannel> channel;
516
517 if (enableV6 && enableV4) {
518 websocket::Endpoint endpoint(boost::asio::ip::address_v6::any(), port);
519 channel = factory->createChannel(endpoint);
520
521 m_factories.emplace("websocket46", factory);
522 }
523 else if (enableV4) {
524 websocket::Endpoint endpoint(boost::asio::ip::address_v4::any(), port);
525 channel = factory->createChannel(endpoint);
526
527 m_factories.emplace("websocket4", factory);
528 }
529
530 if (channel && needToListen) {
531 channel->listen(bind(&FaceTable::add, &m_faceTable, _1));
532 }
533 }
534#else
535 BOOST_THROW_EXCEPTION(ConfigFile::Error("NFD was compiled without WebSocket, "
536 "cannot process \"websocket\" section"));
537#endif // HAVE_WEBSOCKET
538}
539
540} // namespace face
541} // namespace nfd