blob: c95f69313829389fd64b49db7e00130e392e4090 [file] [log] [blame]
Yukai Tu16aabbc2015-10-06 05:08:42 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
Eric Newberryb49313d2017-12-24 20:22:27 -07002/*
3 * Copyright (c) 2014-2018, Regents of the University of California,
Yukai Tu16aabbc2015-10-06 05:08:42 -07004 * 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-transport.hpp"
27
Eric Newberryb49313d2017-12-24 20:22:27 -070028#if defined(__linux__)
29#include <linux/sockios.h>
30#include <sys/ioctl.h>
31#endif
32
Yukai Tu16aabbc2015-10-06 05:08:42 -070033namespace nfd {
34namespace face {
35
Weiwei Liudcdf6212016-08-31 14:34:22 -070036NFD_LOG_INCLASS_TEMPLATE_SPECIALIZATION_DEFINE(StreamTransport, TcpTransport::protocol, "TcpTransport");
37
38time::milliseconds TcpTransport::s_initialReconnectWait = time::seconds(1);
39time::milliseconds TcpTransport::s_maxReconnectWait = time::minutes(5);
40float TcpTransport::s_reconnectWaitMultiplier = 2.0f;
Yukai Tu16aabbc2015-10-06 05:08:42 -070041
42TcpTransport::TcpTransport(protocol::socket&& socket, ndn::nfd::FacePersistency persistency)
43 : StreamTransport(std::move(socket))
Weiwei Liudcdf6212016-08-31 14:34:22 -070044 , m_remoteEndpoint(m_socket.remote_endpoint())
45 , m_nextReconnectWait(s_initialReconnectWait)
Yukai Tu16aabbc2015-10-06 05:08:42 -070046{
47 this->setLocalUri(FaceUri(m_socket.local_endpoint()));
48 this->setRemoteUri(FaceUri(m_socket.remote_endpoint()));
49
50 if (m_socket.local_endpoint().address().is_loopback() &&
51 m_socket.remote_endpoint().address().is_loopback())
52 this->setScope(ndn::nfd::FACE_SCOPE_LOCAL);
53 else
54 this->setScope(ndn::nfd::FACE_SCOPE_NON_LOCAL);
55
56 this->setPersistency(persistency);
57 this->setLinkType(ndn::nfd::LINK_TYPE_POINT_TO_POINT);
58 this->setMtu(MTU_UNLIMITED);
59
60 NFD_LOG_FACE_INFO("Creating transport");
61}
62
Eric Newberryb49313d2017-12-24 20:22:27 -070063ssize_t
64TcpTransport::getSendQueueLength()
65{
66 int queueLength = getSendQueueBytes();
67
68 // We want to obtain the amount of "not sent" bytes instead of the amount of "not sent" + "not
69 // acked" bytes. On Linux, we use SIOCOUTQNSD for this reason. However, macOS does not provide an
70 // efficient mechanism to obtain this value (SO_NWRITE includes both "not sent" and "not acked").
71#if defined(__linux__)
72 int nsd;
73 if (ioctl(m_socket.native_handle(), SIOCOUTQNSD, &nsd) < 0) {
74 NFD_LOG_FACE_WARN("Failed to obtain send queue length from socket: " << std::strerror(errno));
75 }
76 else if (nsd > 0) {
77 NFD_LOG_FACE_TRACE("SIOCOUTQNSD=" << nsd);
78 queueLength += nsd;
79 }
80#endif
81
82 return queueLength;
83}
84
Yanbiao Li32dab972016-11-27 12:26:09 +080085bool
86TcpTransport::canChangePersistencyToImpl(ndn::nfd::FacePersistency newPersistency) const
Yukai Tu16aabbc2015-10-06 05:08:42 -070087{
Yanbiao Li32dab972016-11-27 12:26:09 +080088 return true;
89}
90
91void
92TcpTransport::afterChangePersistency(ndn::nfd::FacePersistency oldPersistency)
93{
94 // if persistency was changed from permanent to any other value
95 if (oldPersistency == ndn::nfd::FACE_PERSISTENCY_PERMANENT) {
Weiwei Liudcdf6212016-08-31 14:34:22 -070096 if (this->getState() == TransportState::DOWN) {
97 // non-permanent transport cannot be in DOWN state, so fail hard
98 this->setState(TransportState::FAILED);
99 doClose();
100 }
Yukai Tu16aabbc2015-10-06 05:08:42 -0700101 }
102}
103
Weiwei Liudcdf6212016-08-31 14:34:22 -0700104void
105TcpTransport::handleError(const boost::system::error_code& error)
106{
107 if (this->getPersistency() == ndn::nfd::FACE_PERSISTENCY_PERMANENT) {
108 NFD_LOG_FACE_TRACE("TCP socket error: " << error.message());
109 this->setState(TransportState::DOWN);
110
111 // cancel all outstanding operations
112 boost::system::error_code error;
113 m_socket.cancel(error);
114
115 // do this asynchronously because there could be some callbacks still pending
116 getGlobalIoService().post([this] { reconnect(); });
117 }
118 else {
119 StreamTransport::handleError(error);
120 }
121}
122
123void
124TcpTransport::reconnect()
125{
126 NFD_LOG_FACE_TRACE(__func__);
127
128 if (getState() == TransportState::CLOSING ||
129 getState() == TransportState::FAILED ||
130 getState() == TransportState::CLOSED) {
131 // transport is shutting down, don't attempt to reconnect
132 return;
133 }
134
135 BOOST_ASSERT(getPersistency() == ndn::nfd::FACE_PERSISTENCY_PERMANENT);
136 BOOST_ASSERT(getState() == TransportState::DOWN);
137
138 // recreate the socket
139 m_socket = protocol::socket(m_socket.get_io_service());
140 this->resetReceiveBuffer();
141 this->resetSendQueue();
142
143 m_reconnectEvent = scheduler::schedule(m_nextReconnectWait,
144 [this] { handleReconnectTimeout(); });
145 m_socket.async_connect(m_remoteEndpoint,
146 [this] (const boost::system::error_code& error) { handleReconnect(error); });
147}
148
149void
150TcpTransport::handleReconnect(const boost::system::error_code& error)
151{
152 if (getState() == TransportState::CLOSING ||
153 getState() == TransportState::FAILED ||
154 getState() == TransportState::CLOSED ||
155 error == boost::asio::error::operation_aborted) {
156 // transport is shutting down, abort the reconnection attempt and ignore any errors
157 return;
158 }
159
160 if (error) {
161 NFD_LOG_FACE_TRACE("Reconnection attempt failed: " << error.message());
162 return;
163 }
164
165 m_reconnectEvent.cancel();
166 m_nextReconnectWait = s_initialReconnectWait;
167
168 this->setLocalUri(FaceUri(m_socket.local_endpoint()));
169 NFD_LOG_FACE_TRACE("TCP connection reestablished");
170 this->setState(TransportState::UP);
171 this->startReceive();
172}
173
174void
175TcpTransport::handleReconnectTimeout()
176{
177 // abort the reconnection attempt
178 boost::system::error_code error;
179 m_socket.close(error);
180
181 // exponentially back off the reconnection timer
182 m_nextReconnectWait =
183 std::min(time::duration_cast<time::milliseconds>(m_nextReconnectWait * s_reconnectWaitMultiplier),
184 s_maxReconnectWait);
185
186 // do this asynchronously because there could be some callbacks still pending
187 getGlobalIoService().post([this] { reconnect(); });
188}
189
190void
191TcpTransport::doClose()
192{
193 m_reconnectEvent.cancel();
194 StreamTransport::doClose();
195}
196
Yukai Tu16aabbc2015-10-06 05:08:42 -0700197} // namespace face
198} // namespace nfd