blob: 6ce6afe3abdb99cb2ca3a1541867338a8b8197e4 [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"
Junxiao Shib8590312016-12-29 21:22:25 +000033#endif // HAVE_LIBPCAP
34#include "tcp-factory.hpp"
35#include "udp-factory.hpp"
36#ifdef HAVE_UNIX_SOCKETS
37#include "unix-stream-factory.hpp"
38#endif // HAVE_UNIX_SOCKETS
39#ifdef HAVE_WEBSOCKET
40#include "websocket-factory.hpp"
41#endif // HAVE_WEBSOCKET
42
43namespace nfd {
44namespace face {
45
46NFD_LOG_INIT("FaceSystem");
47
48FaceSystem::FaceSystem(FaceTable& faceTable)
49 : m_faceTable(faceTable)
50{
Junxiao Shi38b24c72017-01-05 02:59:31 +000051 ///\todo #3904 make a registry, and construct instances from registry
Junxiao Shi7003c602017-01-10 13:35:28 +000052
53#ifdef HAVE_LIBPCAP
54 m_factories["ether"] = make_shared<EthernetFactory>();
55#endif // HAVE_LIBPCAP
56
Junxiao Shi38b24c72017-01-05 02:59:31 +000057 m_factories["tcp"] = make_shared<TcpFactory>();
Junxiao Shi406deb52017-01-05 20:07:44 +000058
59#ifdef HAVE_UNIX_SOCKETS
60 m_factories["unix"] = make_shared<UnixStreamFactory>();
61#endif // HAVE_UNIX_SOCKETS
Junxiao Shib8590312016-12-29 21:22:25 +000062}
63
64std::set<const ProtocolFactory*>
65FaceSystem::listProtocolFactories() const
66{
67 std::set<const ProtocolFactory*> factories;
Junxiao Shi38b24c72017-01-05 02:59:31 +000068 for (const auto& p : m_factoryByScheme) {
Junxiao Shib8590312016-12-29 21:22:25 +000069 factories.insert(p.second.get());
70 }
71 return factories;
72}
73
74ProtocolFactory*
Junxiao Shi38b24c72017-01-05 02:59:31 +000075FaceSystem::getFactoryById(const std::string& id)
Junxiao Shib8590312016-12-29 21:22:25 +000076{
Junxiao Shi38b24c72017-01-05 02:59:31 +000077 auto found = m_factories.find(id);
Junxiao Shib8590312016-12-29 21:22:25 +000078 return found == m_factories.end() ? nullptr : found->second.get();
79}
80
Junxiao Shi38b24c72017-01-05 02:59:31 +000081ProtocolFactory*
82FaceSystem::getFactoryByScheme(const std::string& scheme)
83{
84 auto found = m_factoryByScheme.find(scheme);
85 return found == m_factoryByScheme.end() ? nullptr : found->second.get();
86}
87
Junxiao Shib8590312016-12-29 21:22:25 +000088void
89FaceSystem::setConfigFile(ConfigFile& configFile)
90{
91 configFile.addSectionHandler("face_system", bind(&FaceSystem::processConfig, this, _1, _2, _3));
92}
93
94void
95FaceSystem::processConfig(const ConfigSection& configSection, bool isDryRun, const std::string& filename)
96{
Junxiao Shi38b24c72017-01-05 02:59:31 +000097 ConfigContext context;
98 context.isDryRun = isDryRun;
99 context.addFace = bind(&FaceTable::add, &m_faceTable, _1);
Junxiao Shi7003c602017-01-10 13:35:28 +0000100 context.m_netifs = listNetworkInterfaces();
Junxiao Shi38b24c72017-01-05 02:59:31 +0000101
102 // process sections in protocol factories
103 for (const auto& pair : m_factories) {
104 const std::string& sectionName = pair.first;
105 shared_ptr<ProtocolFactory> factory = pair.second;
106
107 std::set<std::string> oldProvidedSchemes = factory->getProvidedSchemes();
108 factory->processConfig(configSection.get_child_optional(sectionName), context);
109
110 if (!isDryRun) {
111 for (const std::string& scheme : factory->getProvidedSchemes()) {
112 m_factoryByScheme[scheme] = factory;
113 oldProvidedSchemes.erase(scheme);
114 }
115 for (const std::string& scheme : oldProvidedSchemes) {
116 m_factoryByScheme.erase(scheme);
117 }
118 }
119 }
120
121 // process other sections
Junxiao Shib8590312016-12-29 21:22:25 +0000122 std::set<std::string> seenSections;
Junxiao Shi38b24c72017-01-05 02:59:31 +0000123 for (const auto& pair : configSection) {
124 const std::string& sectionName = pair.first;
125 const ConfigSection& subSection = pair.second;
Junxiao Shib8590312016-12-29 21:22:25 +0000126
Junxiao Shi38b24c72017-01-05 02:59:31 +0000127 if (!seenSections.insert(sectionName).second) {
128 BOOST_THROW_EXCEPTION(ConfigFile::Error("Duplicate section face_system." + sectionName));
Junxiao Shib8590312016-12-29 21:22:25 +0000129 }
130
Junxiao Shi38b24c72017-01-05 02:59:31 +0000131 if (m_factories.count(sectionName) > 0) {
132 continue;
Junxiao Shib8590312016-12-29 21:22:25 +0000133 }
Junxiao Shi38b24c72017-01-05 02:59:31 +0000134
135 ///\todo #3521 nicfaces
136
137 ///\todo #3904 process these in protocol factory
Junxiao Shi406deb52017-01-05 20:07:44 +0000138 if (sectionName == "udp") {
Junxiao Shi7003c602017-01-10 13:35:28 +0000139 processSectionUdp(subSection, isDryRun, context.m_netifs);
Junxiao Shib8590312016-12-29 21:22:25 +0000140 }
Junxiao Shi38b24c72017-01-05 02:59:31 +0000141 else if (sectionName == "websocket") {
142 processSectionWebSocket(subSection, isDryRun);
Junxiao Shib8590312016-12-29 21:22:25 +0000143 }
144 else {
Junxiao Shi38b24c72017-01-05 02:59:31 +0000145 BOOST_THROW_EXCEPTION(ConfigFile::Error("Unrecognized option face_system." + sectionName));
Junxiao Shib8590312016-12-29 21:22:25 +0000146 }
147 }
148}
149
150void
Junxiao Shib8590312016-12-29 21:22:25 +0000151FaceSystem::processSectionUdp(const ConfigSection& configSection, bool isDryRun,
152 const std::vector<NetworkInterfaceInfo>& nicList)
153{
154 // ; the udp section contains settings of UDP faces and channels
155 // udp
156 // {
157 // port 6363 ; UDP unicast port number
158 // idle_timeout 600 ; idle time (seconds) before closing a UDP unicast face
159 // keep_alive_interval 25 ; interval (seconds) between keep-alive refreshes
160
161 // ; NFD creates one UDP multicast face per NIC
162 // mcast yes ; set to 'no' to disable UDP multicast, default 'yes'
163 // mcast_port 56363 ; UDP multicast port number
164 // mcast_group 224.0.23.170 ; UDP multicast group (IPv4 only)
165 // }
166
167 uint16_t port = 6363;
168 bool enableV4 = true;
169 bool enableV6 = true;
170 size_t timeout = 600;
171 size_t keepAliveInterval = 25;
172 bool useMcast = true;
173 auto mcastGroup = boost::asio::ip::address_v4::from_string("224.0.23.170");
174 uint16_t mcastPort = 56363;
175
176 for (const auto& i : configSection) {
177 if (i.first == "port") {
178 port = ConfigFile::parseNumber<uint16_t>(i, "udp");
179 NFD_LOG_TRACE("UDP unicast port set to " << port);
180 }
181 else if (i.first == "enable_v4") {
182 enableV4 = ConfigFile::parseYesNo(i, "udp");
183 }
184 else if (i.first == "enable_v6") {
185 enableV6 = ConfigFile::parseYesNo(i, "udp");
186 }
187 else if (i.first == "idle_timeout") {
188 try {
189 timeout = i.second.get_value<size_t>();
190 }
191 catch (const boost::property_tree::ptree_bad_data&) {
192 BOOST_THROW_EXCEPTION(ConfigFile::Error("Invalid value for option \"" +
193 i.first + "\" in \"udp\" section"));
194 }
195 }
196 else if (i.first == "keep_alive_interval") {
197 try {
198 keepAliveInterval = i.second.get_value<size_t>();
199 /// \todo Make use of keepAliveInterval
200 (void)(keepAliveInterval);
201 }
202 catch (const boost::property_tree::ptree_bad_data&) {
203 BOOST_THROW_EXCEPTION(ConfigFile::Error("Invalid value for option \"" +
204 i.first + "\" in \"udp\" section"));
205 }
206 }
207 else if (i.first == "mcast") {
208 useMcast = ConfigFile::parseYesNo(i, "udp");
209 }
210 else if (i.first == "mcast_port") {
211 mcastPort = ConfigFile::parseNumber<uint16_t>(i, "udp");
212 NFD_LOG_TRACE("UDP multicast port set to " << mcastPort);
213 }
214 else if (i.first == "mcast_group") {
215 boost::system::error_code ec;
216 mcastGroup = boost::asio::ip::address_v4::from_string(i.second.get_value<std::string>(), ec);
217 if (ec) {
218 BOOST_THROW_EXCEPTION(ConfigFile::Error("Invalid value for option \"" +
219 i.first + "\" in \"udp\" section"));
220 }
221 NFD_LOG_TRACE("UDP multicast group set to " << mcastGroup);
222 }
223 else {
224 BOOST_THROW_EXCEPTION(ConfigFile::Error("Unrecognized option \"" +
225 i.first + "\" in \"udp\" section"));
226 }
227 }
228
229 if (!enableV4 && !enableV6) {
230 BOOST_THROW_EXCEPTION(ConfigFile::Error("IPv4 and IPv6 UDP channels have been disabled."
231 " Remove \"udp\" section to disable UDP channels or"
232 " re-enable at least one channel type."));
233 }
234 else if (useMcast && !enableV4) {
235 BOOST_THROW_EXCEPTION(ConfigFile::Error("IPv4 multicast requested, but IPv4 channels"
236 " have been disabled (conflicting configuration options set)"));
237 }
238
239 if (!isDryRun) {
240 shared_ptr<UdpFactory> factory;
241 bool isReload = false;
Junxiao Shi38b24c72017-01-05 02:59:31 +0000242 if (m_factoryByScheme.count("udp") > 0) {
Junxiao Shib8590312016-12-29 21:22:25 +0000243 isReload = true;
Junxiao Shi38b24c72017-01-05 02:59:31 +0000244 factory = static_pointer_cast<UdpFactory>(m_factoryByScheme["udp"]);
Junxiao Shib8590312016-12-29 21:22:25 +0000245 }
246 else {
247 factory = make_shared<UdpFactory>();
Junxiao Shi38b24c72017-01-05 02:59:31 +0000248 m_factoryByScheme.emplace("udp", factory);
Junxiao Shib8590312016-12-29 21:22:25 +0000249 }
250
251 if (!isReload && enableV4) {
252 udp::Endpoint endpoint(boost::asio::ip::udp::v4(), port);
253 shared_ptr<UdpChannel> v4Channel = factory->createChannel(endpoint, time::seconds(timeout));
254 v4Channel->listen(bind(&FaceTable::add, &m_faceTable, _1), nullptr);
255
Junxiao Shi38b24c72017-01-05 02:59:31 +0000256 m_factoryByScheme.emplace("udp4", factory);
Junxiao Shib8590312016-12-29 21:22:25 +0000257 }
258
259 if (!isReload && enableV6) {
260 udp::Endpoint endpoint(boost::asio::ip::udp::v6(), port);
261 shared_ptr<UdpChannel> v6Channel = factory->createChannel(endpoint, time::seconds(timeout));
262 v6Channel->listen(bind(&FaceTable::add, &m_faceTable, _1), nullptr);
263
Junxiao Shi38b24c72017-01-05 02:59:31 +0000264 m_factoryByScheme.emplace("udp6", factory);
Junxiao Shib8590312016-12-29 21:22:25 +0000265 }
266
267 std::set<shared_ptr<Face>> multicastFacesToRemove;
268 for (const auto& i : factory->getMulticastFaces()) {
269 multicastFacesToRemove.insert(i.second);
270 }
271
272 if (useMcast && enableV4) {
273 std::vector<NetworkInterfaceInfo> ipv4MulticastInterfaces;
274 for (const auto& nic : nicList) {
275 if (nic.isUp() && nic.isMulticastCapable() && !nic.ipv4Addresses.empty()) {
276 ipv4MulticastInterfaces.push_back(nic);
277 }
278 }
279
280 bool isNicNameNecessary = false;
281#if defined(__linux__)
282 if (ipv4MulticastInterfaces.size() > 1) {
283 // On Linux if we have more than one MulticastUdpFace
284 // we need to specify the name of the interface
285 isNicNameNecessary = true;
286 }
287#endif
288
289 udp::Endpoint mcastEndpoint(mcastGroup, mcastPort);
290 for (const auto& nic : ipv4MulticastInterfaces) {
291 udp::Endpoint localEndpoint(nic.ipv4Addresses[0], mcastPort);
292 auto newFace = factory->createMulticastFace(localEndpoint, mcastEndpoint,
293 isNicNameNecessary ? nic.name : "");
294 m_faceTable.add(newFace);
295 multicastFacesToRemove.erase(newFace);
296 }
297 }
298
299 for (const auto& face : multicastFacesToRemove) {
300 face->close();
301 }
302 }
303}
304
305void
Junxiao Shib8590312016-12-29 21:22:25 +0000306FaceSystem::processSectionWebSocket(const ConfigSection& configSection, bool isDryRun)
307{
308 // ; the websocket section contains settings of WebSocket faces and channels
309 // websocket
310 // {
311 // listen yes ; set to 'no' to disable WebSocket listener, default 'yes'
312 // port 9696 ; WebSocket listener port number
313 // enable_v4 yes ; set to 'no' to disable listening on IPv4 socket, default 'yes'
314 // enable_v6 yes ; set to 'no' to disable listening on IPv6 socket, default 'yes'
315 // }
316
317#if defined(HAVE_WEBSOCKET)
318 uint16_t port = 9696;
319 bool needToListen = true;
320 bool enableV4 = true;
321 bool enableV6 = true;
322
323 for (const auto& i : configSection) {
324 if (i.first == "port") {
325 port = ConfigFile::parseNumber<uint16_t>(i, "websocket");
326 NFD_LOG_TRACE("WebSocket port set to " << port);
327 }
328 else if (i.first == "listen") {
329 needToListen = ConfigFile::parseYesNo(i, "websocket");
330 }
331 else if (i.first == "enable_v4") {
332 enableV4 = ConfigFile::parseYesNo(i, "websocket");
333 }
334 else if (i.first == "enable_v6") {
335 enableV6 = ConfigFile::parseYesNo(i, "websocket");
336 }
337 else {
338 BOOST_THROW_EXCEPTION(ConfigFile::Error("Unrecognized option \"" +
339 i.first + "\" in \"websocket\" section"));
340 }
341 }
342
343 if (!enableV4 && !enableV6) {
344 BOOST_THROW_EXCEPTION(ConfigFile::Error("IPv4 and IPv6 WebSocket channels have been disabled."
345 " Remove \"websocket\" section to disable WebSocket channels or"
346 " re-enable at least one channel type."));
347 }
348
349 if (!enableV4 && enableV6) {
350 BOOST_THROW_EXCEPTION(ConfigFile::Error("NFD does not allow pure IPv6 WebSocket channel."));
351 }
352
353 if (!isDryRun) {
Junxiao Shi38b24c72017-01-05 02:59:31 +0000354 if (m_factoryByScheme.count("websocket") > 0) {
Junxiao Shib8590312016-12-29 21:22:25 +0000355 return;
356 }
357
358 auto factory = make_shared<WebSocketFactory>();
Junxiao Shi38b24c72017-01-05 02:59:31 +0000359 m_factoryByScheme.emplace("websocket", factory);
Junxiao Shib8590312016-12-29 21:22:25 +0000360
361 shared_ptr<WebSocketChannel> channel;
362
363 if (enableV6 && enableV4) {
364 websocket::Endpoint endpoint(boost::asio::ip::address_v6::any(), port);
365 channel = factory->createChannel(endpoint);
366
Junxiao Shi38b24c72017-01-05 02:59:31 +0000367 m_factoryByScheme.emplace("websocket46", factory);
Junxiao Shib8590312016-12-29 21:22:25 +0000368 }
369 else if (enableV4) {
370 websocket::Endpoint endpoint(boost::asio::ip::address_v4::any(), port);
371 channel = factory->createChannel(endpoint);
372
Junxiao Shi38b24c72017-01-05 02:59:31 +0000373 m_factoryByScheme.emplace("websocket4", factory);
Junxiao Shib8590312016-12-29 21:22:25 +0000374 }
375
376 if (channel && needToListen) {
377 channel->listen(bind(&FaceTable::add, &m_faceTable, _1));
378 }
379 }
380#else
381 BOOST_THROW_EXCEPTION(ConfigFile::Error("NFD was compiled without WebSocket, "
382 "cannot process \"websocket\" section"));
383#endif // HAVE_WEBSOCKET
384}
385
386} // namespace face
387} // namespace nfd