blob: 1439c9011c6f56478c12f53efc2c8493d8815d15 [file] [log] [blame]
Garrick Evans3cbac7c2019-04-18 15:31:31 +09001// Copyright 2019 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Garrick Evans3388a032020-03-24 11:25:55 +09005#include "patchpanel/socket_forwarder.h"
Garrick Evans3cbac7c2019-04-18 15:31:31 +09006
7#include <arpa/inet.h>
8#include <fcntl.h>
9#include <netinet/ip.h>
10#include <string.h>
11#include <sys/epoll.h>
12#include <sys/socket.h>
13#include <sys/types.h>
14
15#include <utility>
16
17#include <base/bind.h>
Qijiang Fan713061e2021-03-08 15:45:12 +090018#include <base/check.h>
Garrick Evans3cbac7c2019-04-18 15:31:31 +090019#include <base/logging.h>
20#include <base/time/time.h>
21
Garrick Evans3388a032020-03-24 11:25:55 +090022namespace patchpanel {
Garrick Evans3cbac7c2019-04-18 15:31:31 +090023namespace {
Garrick Evans7db0dda2019-05-20 11:50:06 +090024constexpr int kWaitTimeoutMs = 1000;
Garrick Evans3cbac7c2019-04-18 15:31:31 +090025// Maximum number of epoll events to process per wait.
26constexpr int kMaxEvents = 4;
Andreea Costinas456ee5b2020-09-08 15:11:43 +020027
28std::ostream& operator<<(std::ostream& stream,
29 const struct epoll_event& event) {
30 stream << "{ fd: " << event.data.fd << ", events: 0x" << std::hex
31 << event.events << "}";
32 return stream;
33}
34
35bool SetPollEvents(Socket* socket, int cfd, uint32_t events) {
36 struct epoll_event ev;
37 ev.events = events;
38 ev.data.fd = socket->fd();
39 if (epoll_ctl(cfd, EPOLL_CTL_MOD, socket->fd(), &ev) == -1) {
40 PLOG(ERROR) << "epoll_ctl(" << ev << ") failed";
41 return false;
42 }
43 return true;
44}
45
Garrick Evans3cbac7c2019-04-18 15:31:31 +090046} // namespace
47
48SocketForwarder::SocketForwarder(const std::string& name,
49 std::unique_ptr<Socket> sock0,
50 std::unique_ptr<Socket> sock1)
51 : base::SimpleThread(name),
52 sock0_(std::move(sock0)),
Garrick Evans7db0dda2019-05-20 11:50:06 +090053 sock1_(std::move(sock1)),
54 len0_(0),
55 len1_(0),
Andreea Costinas456ee5b2020-09-08 15:11:43 +020056 eof_(-1),
Garrick Evans7db0dda2019-05-20 11:50:06 +090057 poll_(false),
58 done_(false) {
59 DCHECK(sock0_);
60 DCHECK(sock1_);
61}
Garrick Evans3cbac7c2019-04-18 15:31:31 +090062
63SocketForwarder::~SocketForwarder() {
Garrick Evans088cd0e2019-06-04 15:20:43 +090064 // Ensure the polling loop exits.
65 poll_ = false;
Garrick Evans3cbac7c2019-04-18 15:31:31 +090066 Join();
67}
68
Garrick Evans088cd0e2019-06-04 15:20:43 +090069bool SocketForwarder::IsRunning() const {
Garrick Evans7db0dda2019-05-20 11:50:06 +090070 return !done_;
71}
72
Andreea Costinas456ee5b2020-09-08 15:11:43 +020073void SocketForwarder::SetStopQuitClosureForTesting(base::OnceClosure closure) {
74 stop_quit_closure_for_testing_ = std::move(closure);
75}
76
Garrick Evans3cbac7c2019-04-18 15:31:31 +090077void SocketForwarder::Run() {
78 LOG(INFO) << "Starting forwarder: " << *sock0_ << " <-> " << *sock1_;
79
80 // We need these sockets to be non-blocking.
81 if (fcntl(sock0_->fd(), F_SETFL,
82 fcntl(sock0_->fd(), F_GETFL, 0) | O_NONBLOCK) < 0 ||
83 fcntl(sock1_->fd(), F_SETFL,
84 fcntl(sock1_->fd(), F_GETFL, 0) | O_NONBLOCK) < 0) {
85 PLOG(ERROR) << "fcntl failed";
Andreea Costinas456ee5b2020-09-08 15:11:43 +020086 if (stop_quit_closure_for_testing_)
87 std::move(stop_quit_closure_for_testing_).Run();
Garrick Evans3cbac7c2019-04-18 15:31:31 +090088 return;
89 }
90
Garrick Evans7db0dda2019-05-20 11:50:06 +090091 Poll();
Garrick Evans7db0dda2019-05-20 11:50:06 +090092
Garrick Evans088cd0e2019-06-04 15:20:43 +090093 LOG(INFO) << "Forwarder stopped: " << *sock0_ << " <-> " << *sock1_;
94 done_ = true;
Garrick Evans7db0dda2019-05-20 11:50:06 +090095 sock1_.reset();
96 sock0_.reset();
Andreea Costinas456ee5b2020-09-08 15:11:43 +020097 if (stop_quit_closure_for_testing_)
98 std::move(stop_quit_closure_for_testing_).Run();
Garrick Evans7db0dda2019-05-20 11:50:06 +090099}
100
Garrick Evans7db0dda2019-05-20 11:50:06 +0900101void SocketForwarder::Poll() {
102 base::ScopedFD cfd(epoll_create1(0));
103 if (!cfd.is_valid()) {
Garrick Evans3cbac7c2019-04-18 15:31:31 +0900104 PLOG(ERROR) << "epoll_create1 failed";
105 return;
106 }
107 struct epoll_event ev;
Andreea Costinas456ee5b2020-09-08 15:11:43 +0200108 ev.events = EPOLLIN;
Garrick Evans3cbac7c2019-04-18 15:31:31 +0900109 ev.data.fd = sock0_->fd();
Garrick Evans7db0dda2019-05-20 11:50:06 +0900110 if (epoll_ctl(cfd.get(), EPOLL_CTL_ADD, sock0_->fd(), &ev) == -1) {
Garrick Evans3cbac7c2019-04-18 15:31:31 +0900111 PLOG(ERROR) << "epoll_ctl failed";
112 return;
113 }
114 ev.data.fd = sock1_->fd();
Garrick Evans7db0dda2019-05-20 11:50:06 +0900115 if (epoll_ctl(cfd.get(), EPOLL_CTL_ADD, sock1_->fd(), &ev) == -1) {
Garrick Evans3cbac7c2019-04-18 15:31:31 +0900116 PLOG(ERROR) << "epoll_ctl failed";
117 return;
118 }
119
Garrick Evans7db0dda2019-05-20 11:50:06 +0900120 poll_ = true;
Garrick Evans3cbac7c2019-04-18 15:31:31 +0900121 struct epoll_event events[kMaxEvents];
Garrick Evans7db0dda2019-05-20 11:50:06 +0900122 while (poll_) {
123 int n = epoll_wait(cfd.get(), events, kMaxEvents, kWaitTimeoutMs);
Garrick Evans3cbac7c2019-04-18 15:31:31 +0900124 if (n == -1) {
Lepton Wubbd23b32019-11-05 15:59:47 -0800125 if (errno == EINTR) {
126 LOG(INFO) << "Resume epoll_wait from interruption.";
127 continue;
128 }
Garrick Evans3cbac7c2019-04-18 15:31:31 +0900129 PLOG(ERROR) << "epoll_wait failed";
Garrick Evans7db0dda2019-05-20 11:50:06 +0900130 return;
Garrick Evans3cbac7c2019-04-18 15:31:31 +0900131 }
132 for (int i = 0; i < n; ++i) {
Garrick Evans088cd0e2019-06-04 15:20:43 +0900133 if (!poll_ ||
134 !ProcessEvents(events[i].events, events[i].data.fd, cfd.get()))
Garrick Evans7db0dda2019-05-20 11:50:06 +0900135 return;
136 }
137 }
138}
Garrick Evans3cbac7c2019-04-18 15:31:31 +0900139
Garrick Evans7db0dda2019-05-20 11:50:06 +0900140bool SocketForwarder::ProcessEvents(uint32_t events, int efd, int cfd) {
141 if (events & EPOLLERR) {
Garrick Evans088cd0e2019-06-04 15:20:43 +0900142 int so_error;
143 socklen_t optlen = sizeof(so_error);
144 getsockopt(efd, SOL_SOCKET, SO_ERROR, &so_error, &optlen);
145 PLOG(WARNING) << "Socket error: (" << so_error << ") " << *sock0_ << " <-> "
146 << *sock1_;
Garrick Evans7db0dda2019-05-20 11:50:06 +0900147 return false;
148 }
Garrick Evans3cbac7c2019-04-18 15:31:31 +0900149
Garrick Evans7db0dda2019-05-20 11:50:06 +0900150 if (events & EPOLLOUT) {
151 Socket* dst;
152 char* buf;
153 ssize_t* len;
154 if (sock0_->fd() == efd) {
155 dst = sock0_.get();
156 buf = buf1_;
157 len = &len1_;
158 } else {
159 dst = sock1_.get();
160 buf = buf0_;
161 len = &len0_;
162 }
Garrick Evans3cbac7c2019-04-18 15:31:31 +0900163
Garrick Evans7db0dda2019-05-20 11:50:06 +0900164 ssize_t bytes = dst->SendTo(buf, *len);
165 if (bytes < 0)
166 return false;
Garrick Evans3cbac7c2019-04-18 15:31:31 +0900167
Garrick Evans7db0dda2019-05-20 11:50:06 +0900168 // Still unavailable.
169 if (bytes == 0)
170 return true;
Garrick Evans3cbac7c2019-04-18 15:31:31 +0900171
Garrick Evans7db0dda2019-05-20 11:50:06 +0900172 // Partial write.
173 if (bytes < *len)
174 memmove(&buf[0], &buf[bytes], *len - bytes);
175 *len -= bytes;
Garrick Evans3cbac7c2019-04-18 15:31:31 +0900176
Andreea Costinas456ee5b2020-09-08 15:11:43 +0200177 // If all the buffered data was written to the socket and the peer socket is
178 // still open for writing, listen for read events on the socket.
179 if (*len == 0 && eof_ != dst->fd() && !SetPollEvents(dst, cfd, EPOLLIN))
180 return false;
Garrick Evans3cbac7c2019-04-18 15:31:31 +0900181 }
182
Andreea Costinas456ee5b2020-09-08 15:11:43 +0200183 Socket *src, *dst;
184 char* buf;
185 ssize_t* len;
186 if (sock0_->fd() == efd) {
187 src = sock0_.get();
188 dst = sock1_.get();
189 buf = buf0_;
190 len = &len0_;
191 } else {
192 src = sock1_.get();
193 dst = sock0_.get();
194 buf = buf1_;
195 len = &len1_;
196 }
197
198 // Skip the read if this buffer is still pending write: requires that
199 // epoll_wait is in level-triggered mode.
200 if (*len > 0)
201 return true;
202
Garrick Evans7db0dda2019-05-20 11:50:06 +0900203 if (events & EPOLLIN) {
Garrick Evans7db0dda2019-05-20 11:50:06 +0900204 *len = src->RecvFrom(buf, kBufSize);
205 if (*len < 0)
206 return false;
207
208 if (*len == 0)
Andreea Costinas456ee5b2020-09-08 15:11:43 +0200209 return HandleConnectionClosed(src, dst, cfd);
Garrick Evans7db0dda2019-05-20 11:50:06 +0900210
211 ssize_t bytes = dst->SendTo(buf, *len);
212 if (bytes < 0)
213 return false;
214
215 if (bytes > 0) {
216 // Partial write.
217 if (bytes < *len)
218 memmove(&buf[0], &buf[bytes], *len - bytes);
219 *len -= bytes;
220 }
221
Andreea Costinas456ee5b2020-09-08 15:11:43 +0200222 if (*len > 0 && !SetPollEvents(dst, cfd, EPOLLOUT))
223 return false;
Garrick Evans7db0dda2019-05-20 11:50:06 +0900224 }
225
Andreea Costinas456ee5b2020-09-08 15:11:43 +0200226 if (events & EPOLLHUP) {
227 LOG(INFO) << "Peer closed connection: " << *sock0_ << " <-> " << *sock1_;
228 return false;
229 }
Garrick Evans7db0dda2019-05-20 11:50:06 +0900230 return true;
Garrick Evans3cbac7c2019-04-18 15:31:31 +0900231}
232
Andreea Costinas456ee5b2020-09-08 15:11:43 +0200233bool SocketForwarder::HandleConnectionClosed(Socket* src,
234 Socket* dst,
235 int cfd) {
236 LOG(INFO) << "Peer closed connection: " << *src;
237 if (eof_ == dst->fd()) {
238 // Stop the forwarder since the other peer has already closed the
239 // connection.
240 LOG(INFO) << "Closed connection: " << *sock0_ << " <-> " << *sock1_;
241 return false;
242 }
243 // Stop listening for read ready events from |src|.
244 if (!SetPollEvents(src, cfd, 0))
245 return false;
246
247 // Propagate the shut down for writing to the other peer. This is safe
248 // to do since reading the EOF on |src| only happens if the buffer
249 // associated with the |src| socket if empty, so there's no outstanding
250 // data to be written to |dst|.
251 if (shutdown(dst->fd(), SHUT_WR) == -1) {
252 PLOG(ERROR) << "Shutting down " << *socket << " for writing failed";
253 return false;
254 }
255
256 eof_ = src->fd();
257 return true;
258}
Garrick Evans3388a032020-03-24 11:25:55 +0900259} // namespace patchpanel