blob: b08422081f2bf0c3356266488dea3ea4c5108af4 [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 Shi406deb52017-01-05 20:07:44 +000054
55#ifdef HAVE_UNIX_SOCKETS
56 m_factories["unix"] = make_shared<UnixStreamFactory>();
57#endif // HAVE_UNIX_SOCKETS
Junxiao Shib8590312016-12-29 21:22:25 +000058}
59
60std::set<const ProtocolFactory*>
61FaceSystem::listProtocolFactories() const
62{
63 std::set<const ProtocolFactory*> factories;
Junxiao Shi38b24c72017-01-05 02:59:31 +000064 for (const auto& p : m_factoryByScheme) {
Junxiao Shib8590312016-12-29 21:22:25 +000065 factories.insert(p.second.get());
66 }
67 return factories;
68}
69
70ProtocolFactory*
Junxiao Shi38b24c72017-01-05 02:59:31 +000071FaceSystem::getFactoryById(const std::string& id)
Junxiao Shib8590312016-12-29 21:22:25 +000072{
Junxiao Shi38b24c72017-01-05 02:59:31 +000073 auto found = m_factories.find(id);
Junxiao Shib8590312016-12-29 21:22:25 +000074 return found == m_factories.end() ? nullptr : found->second.get();
75}
76
Junxiao Shi38b24c72017-01-05 02:59:31 +000077ProtocolFactory*
78FaceSystem::getFactoryByScheme(const std::string& scheme)
79{
80 auto found = m_factoryByScheme.find(scheme);
81 return found == m_factoryByScheme.end() ? nullptr : found->second.get();
82}
83
Junxiao Shib8590312016-12-29 21:22:25 +000084void
85FaceSystem::setConfigFile(ConfigFile& configFile)
86{
87 configFile.addSectionHandler("face_system", bind(&FaceSystem::processConfig, this, _1, _2, _3));
88}
89
90void
91FaceSystem::processConfig(const ConfigSection& configSection, bool isDryRun, const std::string& filename)
92{
Junxiao Shi38b24c72017-01-05 02:59:31 +000093 ConfigContext context;
94 context.isDryRun = isDryRun;
95 context.addFace = bind(&FaceTable::add, &m_faceTable, _1);
96 context.m_nicList = listNetworkInterfaces();
97
98 // process sections in protocol factories
99 for (const auto& pair : m_factories) {
100 const std::string& sectionName = pair.first;
101 shared_ptr<ProtocolFactory> factory = pair.second;
102
103 std::set<std::string> oldProvidedSchemes = factory->getProvidedSchemes();
104 factory->processConfig(configSection.get_child_optional(sectionName), context);
105
106 if (!isDryRun) {
107 for (const std::string& scheme : factory->getProvidedSchemes()) {
108 m_factoryByScheme[scheme] = factory;
109 oldProvidedSchemes.erase(scheme);
110 }
111 for (const std::string& scheme : oldProvidedSchemes) {
112 m_factoryByScheme.erase(scheme);
113 }
114 }
115 }
116
117 // process other sections
Junxiao Shib8590312016-12-29 21:22:25 +0000118 std::set<std::string> seenSections;
Junxiao Shi38b24c72017-01-05 02:59:31 +0000119 for (const auto& pair : configSection) {
120 const std::string& sectionName = pair.first;
121 const ConfigSection& subSection = pair.second;
Junxiao Shib8590312016-12-29 21:22:25 +0000122
Junxiao Shi38b24c72017-01-05 02:59:31 +0000123 if (!seenSections.insert(sectionName).second) {
124 BOOST_THROW_EXCEPTION(ConfigFile::Error("Duplicate section face_system." + sectionName));
Junxiao Shib8590312016-12-29 21:22:25 +0000125 }
126
Junxiao Shi38b24c72017-01-05 02:59:31 +0000127 if (m_factories.count(sectionName) > 0) {
128 continue;
Junxiao Shib8590312016-12-29 21:22:25 +0000129 }
Junxiao Shi38b24c72017-01-05 02:59:31 +0000130
131 ///\todo #3521 nicfaces
132
133 ///\todo #3904 process these in protocol factory
Junxiao Shi406deb52017-01-05 20:07:44 +0000134 if (sectionName == "udp") {
Junxiao Shi38b24c72017-01-05 02:59:31 +0000135 processSectionUdp(subSection, isDryRun, context.m_nicList);
Junxiao Shib8590312016-12-29 21:22:25 +0000136 }
Junxiao Shi38b24c72017-01-05 02:59:31 +0000137 else if (sectionName == "ether") {
138 processSectionEther(subSection, isDryRun, context.m_nicList);
Junxiao Shib8590312016-12-29 21:22:25 +0000139 }
Junxiao Shi38b24c72017-01-05 02:59:31 +0000140 else if (sectionName == "websocket") {
141 processSectionWebSocket(subSection, isDryRun);
Junxiao Shib8590312016-12-29 21:22:25 +0000142 }
143 else {
Junxiao Shi38b24c72017-01-05 02:59:31 +0000144 BOOST_THROW_EXCEPTION(ConfigFile::Error("Unrecognized option face_system." + sectionName));
Junxiao Shib8590312016-12-29 21:22:25 +0000145 }
146 }
147}
148
149void
Junxiao Shib8590312016-12-29 21:22:25 +0000150FaceSystem::processSectionUdp(const ConfigSection& configSection, bool isDryRun,
151 const std::vector<NetworkInterfaceInfo>& nicList)
152{
153 // ; the udp section contains settings of UDP faces and channels
154 // udp
155 // {
156 // port 6363 ; UDP unicast port number
157 // idle_timeout 600 ; idle time (seconds) before closing a UDP unicast face
158 // keep_alive_interval 25 ; interval (seconds) between keep-alive refreshes
159
160 // ; NFD creates one UDP multicast face per NIC
161 // mcast yes ; set to 'no' to disable UDP multicast, default 'yes'
162 // mcast_port 56363 ; UDP multicast port number
163 // mcast_group 224.0.23.170 ; UDP multicast group (IPv4 only)
164 // }
165
166 uint16_t port = 6363;
167 bool enableV4 = true;
168 bool enableV6 = true;
169 size_t timeout = 600;
170 size_t keepAliveInterval = 25;
171 bool useMcast = true;
172 auto mcastGroup = boost::asio::ip::address_v4::from_string("224.0.23.170");
173 uint16_t mcastPort = 56363;
174
175 for (const auto& i : configSection) {
176 if (i.first == "port") {
177 port = ConfigFile::parseNumber<uint16_t>(i, "udp");
178 NFD_LOG_TRACE("UDP unicast port set to " << port);
179 }
180 else if (i.first == "enable_v4") {
181 enableV4 = ConfigFile::parseYesNo(i, "udp");
182 }
183 else if (i.first == "enable_v6") {
184 enableV6 = ConfigFile::parseYesNo(i, "udp");
185 }
186 else if (i.first == "idle_timeout") {
187 try {
188 timeout = i.second.get_value<size_t>();
189 }
190 catch (const boost::property_tree::ptree_bad_data&) {
191 BOOST_THROW_EXCEPTION(ConfigFile::Error("Invalid value for option \"" +
192 i.first + "\" in \"udp\" section"));
193 }
194 }
195 else if (i.first == "keep_alive_interval") {
196 try {
197 keepAliveInterval = i.second.get_value<size_t>();
198 /// \todo Make use of keepAliveInterval
199 (void)(keepAliveInterval);
200 }
201 catch (const boost::property_tree::ptree_bad_data&) {
202 BOOST_THROW_EXCEPTION(ConfigFile::Error("Invalid value for option \"" +
203 i.first + "\" in \"udp\" section"));
204 }
205 }
206 else if (i.first == "mcast") {
207 useMcast = ConfigFile::parseYesNo(i, "udp");
208 }
209 else if (i.first == "mcast_port") {
210 mcastPort = ConfigFile::parseNumber<uint16_t>(i, "udp");
211 NFD_LOG_TRACE("UDP multicast port set to " << mcastPort);
212 }
213 else if (i.first == "mcast_group") {
214 boost::system::error_code ec;
215 mcastGroup = boost::asio::ip::address_v4::from_string(i.second.get_value<std::string>(), ec);
216 if (ec) {
217 BOOST_THROW_EXCEPTION(ConfigFile::Error("Invalid value for option \"" +
218 i.first + "\" in \"udp\" section"));
219 }
220 NFD_LOG_TRACE("UDP multicast group set to " << mcastGroup);
221 }
222 else {
223 BOOST_THROW_EXCEPTION(ConfigFile::Error("Unrecognized option \"" +
224 i.first + "\" in \"udp\" section"));
225 }
226 }
227
228 if (!enableV4 && !enableV6) {
229 BOOST_THROW_EXCEPTION(ConfigFile::Error("IPv4 and IPv6 UDP channels have been disabled."
230 " Remove \"udp\" section to disable UDP channels or"
231 " re-enable at least one channel type."));
232 }
233 else if (useMcast && !enableV4) {
234 BOOST_THROW_EXCEPTION(ConfigFile::Error("IPv4 multicast requested, but IPv4 channels"
235 " have been disabled (conflicting configuration options set)"));
236 }
237
238 if (!isDryRun) {
239 shared_ptr<UdpFactory> factory;
240 bool isReload = false;
Junxiao Shi38b24c72017-01-05 02:59:31 +0000241 if (m_factoryByScheme.count("udp") > 0) {
Junxiao Shib8590312016-12-29 21:22:25 +0000242 isReload = true;
Junxiao Shi38b24c72017-01-05 02:59:31 +0000243 factory = static_pointer_cast<UdpFactory>(m_factoryByScheme["udp"]);
Junxiao Shib8590312016-12-29 21:22:25 +0000244 }
245 else {
246 factory = make_shared<UdpFactory>();
Junxiao Shi38b24c72017-01-05 02:59:31 +0000247 m_factoryByScheme.emplace("udp", factory);
Junxiao Shib8590312016-12-29 21:22:25 +0000248 }
249
250 if (!isReload && enableV4) {
251 udp::Endpoint endpoint(boost::asio::ip::udp::v4(), port);
252 shared_ptr<UdpChannel> v4Channel = factory->createChannel(endpoint, time::seconds(timeout));
253 v4Channel->listen(bind(&FaceTable::add, &m_faceTable, _1), nullptr);
254
Junxiao Shi38b24c72017-01-05 02:59:31 +0000255 m_factoryByScheme.emplace("udp4", factory);
Junxiao Shib8590312016-12-29 21:22:25 +0000256 }
257
258 if (!isReload && enableV6) {
259 udp::Endpoint endpoint(boost::asio::ip::udp::v6(), port);
260 shared_ptr<UdpChannel> v6Channel = factory->createChannel(endpoint, time::seconds(timeout));
261 v6Channel->listen(bind(&FaceTable::add, &m_faceTable, _1), nullptr);
262
Junxiao Shi38b24c72017-01-05 02:59:31 +0000263 m_factoryByScheme.emplace("udp6", factory);
Junxiao Shib8590312016-12-29 21:22:25 +0000264 }
265
266 std::set<shared_ptr<Face>> multicastFacesToRemove;
267 for (const auto& i : factory->getMulticastFaces()) {
268 multicastFacesToRemove.insert(i.second);
269 }
270
271 if (useMcast && enableV4) {
272 std::vector<NetworkInterfaceInfo> ipv4MulticastInterfaces;
273 for (const auto& nic : nicList) {
274 if (nic.isUp() && nic.isMulticastCapable() && !nic.ipv4Addresses.empty()) {
275 ipv4MulticastInterfaces.push_back(nic);
276 }
277 }
278
279 bool isNicNameNecessary = false;
280#if defined(__linux__)
281 if (ipv4MulticastInterfaces.size() > 1) {
282 // On Linux if we have more than one MulticastUdpFace
283 // we need to specify the name of the interface
284 isNicNameNecessary = true;
285 }
286#endif
287
288 udp::Endpoint mcastEndpoint(mcastGroup, mcastPort);
289 for (const auto& nic : ipv4MulticastInterfaces) {
290 udp::Endpoint localEndpoint(nic.ipv4Addresses[0], mcastPort);
291 auto newFace = factory->createMulticastFace(localEndpoint, mcastEndpoint,
292 isNicNameNecessary ? nic.name : "");
293 m_faceTable.add(newFace);
294 multicastFacesToRemove.erase(newFace);
295 }
296 }
297
298 for (const auto& face : multicastFacesToRemove) {
299 face->close();
300 }
301 }
302}
303
304void
305FaceSystem::processSectionEther(const ConfigSection& configSection, bool isDryRun,
306 const std::vector<NetworkInterfaceInfo>& nicList)
307{
308 // ; the ether section contains settings of Ethernet faces and channels
309 // ether
310 // {
311 // ; NFD creates one Ethernet multicast face per NIC
312 // mcast yes ; set to 'no' to disable Ethernet multicast, default 'yes'
313 // mcast_group 01:00:5E:00:17:AA ; Ethernet multicast group
314 // }
315
316#if defined(HAVE_LIBPCAP)
317 NetworkInterfacePredicate nicPredicate;
318 bool useMcast = true;
319 ethernet::Address mcastGroup(ethernet::getDefaultMulticastAddress());
320
321 for (const auto& i : configSection) {
322 if (i.first == "mcast") {
323 useMcast = ConfigFile::parseYesNo(i, "ether");
324 }
325 else if (i.first == "mcast_group") {
326 mcastGroup = ethernet::Address::fromString(i.second.get_value<std::string>());
327 if (mcastGroup.isNull()) {
328 BOOST_THROW_EXCEPTION(ConfigFile::Error("Invalid value for option \"" +
329 i.first + "\" in \"ether\" section"));
330 }
331 NFD_LOG_TRACE("Ethernet multicast group set to " << mcastGroup);
332 }
333 else if (i.first == "whitelist") {
334 nicPredicate.parseWhitelist(i.second);
335 }
336 else if (i.first == "blacklist") {
337 nicPredicate.parseBlacklist(i.second);
338 }
339 else {
340 BOOST_THROW_EXCEPTION(ConfigFile::Error("Unrecognized option \"" +
341 i.first + "\" in \"ether\" section"));
342 }
343 }
344
345 if (!isDryRun) {
346 shared_ptr<EthernetFactory> factory;
Junxiao Shi38b24c72017-01-05 02:59:31 +0000347 if (m_factoryByScheme.count("ether") > 0) {
348 factory = static_pointer_cast<EthernetFactory>(m_factoryByScheme["ether"]);
Junxiao Shib8590312016-12-29 21:22:25 +0000349 }
350 else {
351 factory = make_shared<EthernetFactory>();
Junxiao Shi38b24c72017-01-05 02:59:31 +0000352 m_factoryByScheme.emplace("ether", factory);
Junxiao Shib8590312016-12-29 21:22:25 +0000353 }
354
355 std::set<shared_ptr<Face>> multicastFacesToRemove;
356 for (const auto& i : factory->getMulticastFaces()) {
357 multicastFacesToRemove.insert(i.second);
358 }
359
360 if (useMcast) {
361 for (const auto& nic : nicList) {
362 if (nic.isUp() && nic.isMulticastCapable() && nicPredicate(nic)) {
363 try {
364 auto newFace = factory->createMulticastFace(nic, mcastGroup);
365 m_faceTable.add(newFace);
366 multicastFacesToRemove.erase(newFace);
367 }
368 catch (const EthernetFactory::Error& factoryError) {
369 NFD_LOG_ERROR(factoryError.what() << ", continuing");
370 }
371 catch (const EthernetTransport::Error& faceError) {
372 NFD_LOG_ERROR(faceError.what() << ", continuing");
373 }
374 }
375 }
376 }
377
378 for (const auto& face : multicastFacesToRemove) {
379 face->close();
380 }
381 }
382#else
383 BOOST_THROW_EXCEPTION(ConfigFile::Error("NFD was compiled without libpcap, cannot process \"ether\" section"));
384#endif // HAVE_LIBPCAP
385}
386
387void
388FaceSystem::processSectionWebSocket(const ConfigSection& configSection, bool isDryRun)
389{
390 // ; the websocket section contains settings of WebSocket faces and channels
391 // websocket
392 // {
393 // listen yes ; set to 'no' to disable WebSocket listener, default 'yes'
394 // port 9696 ; WebSocket listener port number
395 // enable_v4 yes ; set to 'no' to disable listening on IPv4 socket, default 'yes'
396 // enable_v6 yes ; set to 'no' to disable listening on IPv6 socket, default 'yes'
397 // }
398
399#if defined(HAVE_WEBSOCKET)
400 uint16_t port = 9696;
401 bool needToListen = true;
402 bool enableV4 = true;
403 bool enableV6 = true;
404
405 for (const auto& i : configSection) {
406 if (i.first == "port") {
407 port = ConfigFile::parseNumber<uint16_t>(i, "websocket");
408 NFD_LOG_TRACE("WebSocket port set to " << port);
409 }
410 else if (i.first == "listen") {
411 needToListen = ConfigFile::parseYesNo(i, "websocket");
412 }
413 else if (i.first == "enable_v4") {
414 enableV4 = ConfigFile::parseYesNo(i, "websocket");
415 }
416 else if (i.first == "enable_v6") {
417 enableV6 = ConfigFile::parseYesNo(i, "websocket");
418 }
419 else {
420 BOOST_THROW_EXCEPTION(ConfigFile::Error("Unrecognized option \"" +
421 i.first + "\" in \"websocket\" section"));
422 }
423 }
424
425 if (!enableV4 && !enableV6) {
426 BOOST_THROW_EXCEPTION(ConfigFile::Error("IPv4 and IPv6 WebSocket channels have been disabled."
427 " Remove \"websocket\" section to disable WebSocket channels or"
428 " re-enable at least one channel type."));
429 }
430
431 if (!enableV4 && enableV6) {
432 BOOST_THROW_EXCEPTION(ConfigFile::Error("NFD does not allow pure IPv6 WebSocket channel."));
433 }
434
435 if (!isDryRun) {
Junxiao Shi38b24c72017-01-05 02:59:31 +0000436 if (m_factoryByScheme.count("websocket") > 0) {
Junxiao Shib8590312016-12-29 21:22:25 +0000437 return;
438 }
439
440 auto factory = make_shared<WebSocketFactory>();
Junxiao Shi38b24c72017-01-05 02:59:31 +0000441 m_factoryByScheme.emplace("websocket", factory);
Junxiao Shib8590312016-12-29 21:22:25 +0000442
443 shared_ptr<WebSocketChannel> channel;
444
445 if (enableV6 && enableV4) {
446 websocket::Endpoint endpoint(boost::asio::ip::address_v6::any(), port);
447 channel = factory->createChannel(endpoint);
448
Junxiao Shi38b24c72017-01-05 02:59:31 +0000449 m_factoryByScheme.emplace("websocket46", factory);
Junxiao Shib8590312016-12-29 21:22:25 +0000450 }
451 else if (enableV4) {
452 websocket::Endpoint endpoint(boost::asio::ip::address_v4::any(), port);
453 channel = factory->createChannel(endpoint);
454
Junxiao Shi38b24c72017-01-05 02:59:31 +0000455 m_factoryByScheme.emplace("websocket4", factory);
Junxiao Shib8590312016-12-29 21:22:25 +0000456 }
457
458 if (channel && needToListen) {
459 channel->listen(bind(&FaceTable::add, &m_faceTable, _1));
460 }
461 }
462#else
463 BOOST_THROW_EXCEPTION(ConfigFile::Error("NFD was compiled without WebSocket, "
464 "cannot process \"websocket\" section"));
465#endif // HAVE_WEBSOCKET
466}
467
468} // namespace face
469} // namespace nfd