tcp-factory.cpp
Go to the documentation of this file.
1 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2 /*
3  * Copyright (c) 2014-2023, 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 "tcp-factory.hpp"
27 
28 #include <boost/lexical_cast.hpp>
29 
30 namespace nfd::face {
31 
32 namespace ip = boost::asio::ip;
33 
34 NFD_LOG_INIT(TcpFactory);
36 
37 const std::string&
39 {
40  static std::string id("tcp");
41  return id;
42 }
43 
44 void
45 TcpFactory::doProcessConfig(OptionalConfigSection configSection,
47 {
48  // tcp
49  // {
50  // listen yes
51  // port 6363
52  // enable_v4 yes
53  // enable_v6 yes
54  // }
55 
56  m_wantCongestionMarking = context.generalConfig.wantCongestionMarking;
57 
58  if (!configSection) {
59  if (!context.isDryRun && !m_channels.empty()) {
60  NFD_LOG_WARN("Cannot disable TCP channels after initialization");
61  }
62  return;
63  }
64 
65  bool wantListen = true;
66  uint16_t port = 6363;
67  bool enableV4 = true;
68  bool enableV6 = true;
69  IpAddressPredicate local;
70  bool isLocalConfigured = false;
71 
72  for (const auto& pair : *configSection) {
73  const std::string& key = pair.first;
74 
75  if (key == "listen") {
76  wantListen = ConfigFile::parseYesNo(pair, "face_system.tcp");
77  }
78  else if (key == "port") {
79  port = ConfigFile::parseNumber<uint16_t>(pair, "face_system.tcp");
80  }
81  else if (key == "enable_v4") {
82  enableV4 = ConfigFile::parseYesNo(pair, "face_system.tcp");
83  }
84  else if (key == "enable_v6") {
85  enableV6 = ConfigFile::parseYesNo(pair, "face_system.tcp");
86  }
87  else if (key == "local") {
88  isLocalConfigured = true;
89  for (const auto& localPair : pair.second) {
90  const std::string& localKey = localPair.first;
91  if (localKey == "whitelist") {
92  local.parseWhitelist(localPair.second);
93  }
94  else if (localKey == "blacklist") {
95  local.parseBlacklist(localPair.second);
96  }
97  else {
98  NDN_THROW(ConfigFile::Error("Unrecognized option face_system.tcp.local." + localKey));
99  }
100  }
101  }
102  else {
103  NDN_THROW(ConfigFile::Error("Unrecognized option face_system.tcp." + key));
104  }
105  }
106  if (!isLocalConfigured) {
107  local.assign({{"subnet", "127.0.0.0/8"}, {"subnet", "::1/128"}}, {});
108  }
109 
110  if (!enableV4 && !enableV6) {
111  NDN_THROW(ConfigFile::Error(
112  "IPv4 and IPv6 TCP channels have been disabled. Remove face_system.tcp section to disable "
113  "TCP channels or enable at least one channel type."));
114  }
115 
116  if (context.isDryRun) {
117  return;
118  }
119 
120  providedSchemes.insert("tcp");
121 
122  if (enableV4) {
123  tcp::Endpoint endpoint(ip::tcp::v4(), port);
124  auto v4Channel = this->createChannel(endpoint);
125  if (wantListen && !v4Channel->isListening()) {
126  v4Channel->listen(this->addFace, nullptr);
127  }
128  providedSchemes.insert("tcp4");
129  }
130  else if (providedSchemes.count("tcp4") > 0) {
131  NFD_LOG_WARN("Cannot close tcp4 channel after its creation");
132  }
133 
134  if (enableV6) {
135  tcp::Endpoint endpoint(ip::tcp::v6(), port);
136  auto v6Channel = this->createChannel(endpoint);
137  if (wantListen && !v6Channel->isListening()) {
138  v6Channel->listen(this->addFace, nullptr);
139  }
140  providedSchemes.insert("tcp6");
141  }
142  else if (providedSchemes.count("tcp6") > 0) {
143  NFD_LOG_WARN("Cannot close tcp6 channel after its creation");
144  }
145 
146  m_local = std::move(local);
147 }
148 
149 void
150 TcpFactory::doCreateFace(const CreateFaceRequest& req,
151  const FaceCreatedCallback& onCreated,
152  const FaceCreationFailedCallback& onFailure)
153 {
154  if (req.localUri) {
155  NFD_LOG_TRACE("createFace: unsupported LocalUri");
156  onFailure(406, "Unicast TCP faces cannot be created with a LocalUri");
157  return;
158  }
159 
160  if (req.params.persistency == ndn::nfd::FACE_PERSISTENCY_ON_DEMAND) {
161  NFD_LOG_TRACE("createFace: unsupported FacePersistency");
162  onFailure(406, "Outgoing TCP faces do not support on-demand persistency");
163  return;
164  }
165 
166  tcp::Endpoint endpoint(ip::make_address(req.remoteUri.getHost()),
167  boost::lexical_cast<uint16_t>(req.remoteUri.getPort()));
168 
169  // a canonical tcp4/tcp6 FaceUri cannot have a multicast address
170  BOOST_ASSERT(!endpoint.address().is_multicast());
171 
172  if (req.params.wantLocalFields && !endpoint.address().is_loopback()) {
173  NFD_LOG_TRACE("createFace: cannot create non-local face with local fields enabled");
174  onFailure(406, "Local fields can only be enabled on faces with local scope");
175  return;
176  }
177 
178  if (req.params.mtu) {
179  NFD_LOG_TRACE("createFace: cannot create TCP face with overridden MTU");
180  onFailure(406, "TCP faces do not support MTU overrides");
181  return;
182  }
183 
184  // very simple logic for now
185  for (const auto& i : m_channels) {
186  if ((i.first.address().is_v4() && endpoint.address().is_v4()) ||
187  (i.first.address().is_v6() && endpoint.address().is_v6())) {
188  i.second->connect(endpoint, req.params, onCreated, onFailure);
189  return;
190  }
191  }
192 
193  NFD_LOG_TRACE("No channels available to connect to " << endpoint);
194  onFailure(504, "No channels available to connect");
195 }
196 
197 shared_ptr<TcpChannel>
199 {
200  auto it = m_channels.find(endpoint);
201  if (it != m_channels.end())
202  return it->second;
203 
204  auto channel = make_shared<TcpChannel>(endpoint, m_wantCongestionMarking, [this] (auto&&... args) {
205  return determineFaceScopeFromAddresses(std::forward<decltype(args)>(args)...);
206  });
207  m_channels[endpoint] = channel;
208  return channel;
209 }
210 
211 std::vector<shared_ptr<const Channel>>
212 TcpFactory::doGetChannels() const
213 {
214  return getChannelsFromMap(m_channels);
215 }
216 
217 ndn::nfd::FaceScope
218 TcpFactory::determineFaceScopeFromAddresses(const ip::address& localAddress,
219  const ip::address& remoteAddress) const
220 {
221  if (m_local(localAddress) && m_local(remoteAddress)) {
222  return ndn::nfd::FACE_SCOPE_LOCAL;
223  }
224  return ndn::nfd::FACE_SCOPE_NON_LOCAL;
225 }
226 
227 } // namespace nfd::face
static bool parseYesNo(const ConfigSection &node, const std::string &key, const std::string &sectionName)
Parse a config option that can be either "yes" or "no".
Definition: config-file.cpp:60
Context for processing a config section in ProtocolFactory.
static std::vector< shared_ptr< const Channel > > getChannelsFromMap(const ChannelMap &channelMap)
std::set< std::string > providedSchemes
FaceUri schemes provided by this protocol factory.
FaceCreatedCallback addFace
callback when a new face is created
Protocol factory for TCP over IPv4 and IPv6.
Definition: tcp-factory.hpp:39
shared_ptr< TcpChannel > createChannel(const tcp::Endpoint &localEndpoint)
Create TCP-based channel using tcp::Endpoint.
static const std::string & getId() noexcept
Definition: tcp-factory.cpp:38
#define NFD_LOG_INIT(name)
Definition: logger.hpp:31
#define NFD_LOG_WARN
Definition: logger.hpp:40
#define NFD_LOG_TRACE
Definition: logger.hpp:37
std::function< void(uint32_t status, const std::string &reason)> FaceCreationFailedCallback
Prototype for the callback that is invoked when a face fails to be created.
Definition: channel.hpp:94
std::function< void(const shared_ptr< Face > &)> FaceCreatedCallback
Prototype for the callback that is invoked when a face is created (in response to an incoming connect...
Definition: channel.hpp:90
boost::asio::ip::tcp::endpoint Endpoint
Definition: tcp-channel.hpp:38
boost::optional< const ConfigSection & > OptionalConfigSection
An optional configuration file section.
Definition: config-file.hpp:46
#define NFD_REGISTER_PROTOCOL_FACTORY(PF)
Registers a protocol factory.