blob: 3c149b73b4019209cdf60ee5f5716d0b243ed0e8 [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>
18#include <base/logging.h>
19#include <base/time/time.h>
20
Garrick Evans3388a032020-03-24 11:25:55 +090021namespace patchpanel {
Garrick Evans3cbac7c2019-04-18 15:31:31 +090022namespace {
Garrick Evans7db0dda2019-05-20 11:50:06 +090023constexpr int kWaitTimeoutMs = 1000;
Garrick Evans3cbac7c2019-04-18 15:31:31 +090024// Maximum number of epoll events to process per wait.
25constexpr int kMaxEvents = 4;
Andreea Costinas456ee5b2020-09-08 15:11:43 +020026
27std::ostream& operator<<(std::ostream& stream,
28 const struct epoll_event& event) {
29 stream << "{ fd: " << event.data.fd << ", events: 0x" << std::hex
30 << event.events << "}";
31 return stream;
32}
33
34bool SetPollEvents(Socket* socket, int cfd, uint32_t events) {
35 struct epoll_event ev;
36 ev.events = events;
37 ev.data.fd = socket->fd();
38 if (epoll_ctl(cfd, EPOLL_CTL_MOD, socket->fd(), &ev) == -1) {
39 PLOG(ERROR) << "epoll_ctl(" << ev << ") failed";
40 return false;
41 }
42 return true;
43}
44
Garrick Evans3cbac7c2019-04-18 15:31:31 +090045} // namespace
46
47SocketForwarder::SocketForwarder(const std::string& name,
48 std::unique_ptr<Socket> sock0,
49 std::unique_ptr<Socket> sock1)
50 : base::SimpleThread(name),
51 sock0_(std::move(sock0)),
Garrick Evans7db0dda2019-05-20 11:50:06 +090052 sock1_(std::move(sock1)),
53 len0_(0),
54 len1_(0),
Andreea Costinas456ee5b2020-09-08 15:11:43 +020055 eof_(-1),
Garrick Evans7db0dda2019-05-20 11:50:06 +090056 poll_(false),
57 done_(false) {
58 DCHECK(sock0_);
59 DCHECK(sock1_);
60}
Garrick Evans3cbac7c2019-04-18 15:31:31 +090061
62SocketForwarder::~SocketForwarder() {
Garrick Evans088cd0e2019-06-04 15:20:43 +090063 // Ensure the polling loop exits.
64 poll_ = false;
Garrick Evans3cbac7c2019-04-18 15:31:31 +090065 Join();
66}
67
Garrick Evans088cd0e2019-06-04 15:20:43 +090068bool SocketForwarder::IsRunning() const {
Garrick Evans7db0dda2019-05-20 11:50:06 +090069 return !done_;
70}
71
Andreea Costinas456ee5b2020-09-08 15:11:43 +020072void SocketForwarder::SetStopQuitClosureForTesting(base::OnceClosure closure) {
73 stop_quit_closure_for_testing_ = std::move(closure);
74}
75
Garrick Evans3cbac7c2019-04-18 15:31:31 +090076void SocketForwarder::Run() {
77 LOG(INFO) << "Starting forwarder: " << *sock0_ << " <-> " << *sock1_;
78
79 // We need these sockets to be non-blocking.
80 if (fcntl(sock0_->fd(), F_SETFL,
81 fcntl(sock0_->fd(), F_GETFL, 0) | O_NONBLOCK) < 0 ||
82 fcntl(sock1_->fd(), F_SETFL,
83 fcntl(sock1_->fd(), F_GETFL, 0) | O_NONBLOCK) < 0) {
84 PLOG(ERROR) << "fcntl failed";
Andreea Costinas456ee5b2020-09-08 15:11:43 +020085 if (stop_quit_closure_for_testing_)
86 std::move(stop_quit_closure_for_testing_).Run();
Garrick Evans3cbac7c2019-04-18 15:31:31 +090087 return;
88 }
89
Garrick Evans7db0dda2019-05-20 11:50:06 +090090 Poll();
Garrick Evans7db0dda2019-05-20 11:50:06 +090091
Garrick Evans088cd0e2019-06-04 15:20:43 +090092 LOG(INFO) << "Forwarder stopped: " << *sock0_ << " <-> " << *sock1_;
93 done_ = true;
Garrick Evans7db0dda2019-05-20 11:50:06 +090094 sock1_.reset();
95 sock0_.reset();
Andreea Costinas456ee5b2020-09-08 15:11:43 +020096 if (stop_quit_closure_for_testing_)
97 std::move(stop_quit_closure_for_testing_).Run();
Garrick Evans7db0dda2019-05-20 11:50:06 +090098}
99
Garrick Evans7db0dda2019-05-20 11:50:06 +0900100void SocketForwarder::Poll() {
101 base::ScopedFD cfd(epoll_create1(0));
102 if (!cfd.is_valid()) {
Garrick Evans3cbac7c2019-04-18 15:31:31 +0900103 PLOG(ERROR) << "epoll_create1 failed";
104 return;
105 }
106 struct epoll_event ev;
Andreea Costinas456ee5b2020-09-08 15:11:43 +0200107 ev.events = EPOLLIN;
Garrick Evans3cbac7c2019-04-18 15:31:31 +0900108 ev.data.fd = sock0_->fd();
Garrick Evans7db0dda2019-05-20 11:50:06 +0900109 if (epoll_ctl(cfd.get(), EPOLL_CTL_ADD, sock0_->fd(), &ev) == -1) {
Garrick Evans3cbac7c2019-04-18 15:31:31 +0900110 PLOG(ERROR) << "epoll_ctl failed";
111 return;
112 }
113 ev.data.fd = sock1_->fd();
Garrick Evans7db0dda2019-05-20 11:50:06 +0900114 if (epoll_ctl(cfd.get(), EPOLL_CTL_ADD, sock1_->fd(), &ev) == -1) {
Garrick Evans3cbac7c2019-04-18 15:31:31 +0900115 PLOG(ERROR) << "epoll_ctl failed";
116 return;
117 }
118
Garrick Evans7db0dda2019-05-20 11:50:06 +0900119 poll_ = true;
Garrick Evans3cbac7c2019-04-18 15:31:31 +0900120 struct epoll_event events[kMaxEvents];
Garrick Evans7db0dda2019-05-20 11:50:06 +0900121 while (poll_) {
122 int n = epoll_wait(cfd.get(), events, kMaxEvents, kWaitTimeoutMs);
Garrick Evans3cbac7c2019-04-18 15:31:31 +0900123 if (n == -1) {
Lepton Wubbd23b32019-11-05 15:59:47 -0800124 if (errno == EINTR) {
125 LOG(INFO) << "Resume epoll_wait from interruption.";
126 continue;
127 }
Garrick Evans3cbac7c2019-04-18 15:31:31 +0900128 PLOG(ERROR) << "epoll_wait failed";
Garrick Evans7db0dda2019-05-20 11:50:06 +0900129 return;
Garrick Evans3cbac7c2019-04-18 15:31:31 +0900130 }
131 for (int i = 0; i < n; ++i) {
Garrick Evans088cd0e2019-06-04 15:20:43 +0900132 if (!poll_ ||
133 !ProcessEvents(events[i].events, events[i].data.fd, cfd.get()))
Garrick Evans7db0dda2019-05-20 11:50:06 +0900134 return;
135 }
136 }
137}
Garrick Evans3cbac7c2019-04-18 15:31:31 +0900138
Garrick Evans7db0dda2019-05-20 11:50:06 +0900139bool SocketForwarder::ProcessEvents(uint32_t events, int efd, int cfd) {
140 if (events & EPOLLERR) {
Garrick Evans088cd0e2019-06-04 15:20:43 +0900141 int so_error;
142 socklen_t optlen = sizeof(so_error);
143 getsockopt(efd, SOL_SOCKET, SO_ERROR, &so_error, &optlen);
144 PLOG(WARNING) << "Socket error: (" << so_error << ") " << *sock0_ << " <-> "
145 << *sock1_;
Garrick Evans7db0dda2019-05-20 11:50:06 +0900146 return false;
147 }
Garrick Evans3cbac7c2019-04-18 15:31:31 +0900148
Garrick Evans7db0dda2019-05-20 11:50:06 +0900149 if (events & EPOLLOUT) {
150 Socket* dst;
151 char* buf;
152 ssize_t* len;
153 if (sock0_->fd() == efd) {
154 dst = sock0_.get();
155 buf = buf1_;
156 len = &len1_;
157 } else {
158 dst = sock1_.get();
159 buf = buf0_;
160 len = &len0_;
161 }
Garrick Evans3cbac7c2019-04-18 15:31:31 +0900162
Garrick Evans7db0dda2019-05-20 11:50:06 +0900163 ssize_t bytes = dst->SendTo(buf, *len);
164 if (bytes < 0)
165 return false;
Garrick Evans3cbac7c2019-04-18 15:31:31 +0900166
Garrick Evans7db0dda2019-05-20 11:50:06 +0900167 // Still unavailable.
168 if (bytes == 0)
169 return true;
Garrick Evans3cbac7c2019-04-18 15:31:31 +0900170
Garrick Evans7db0dda2019-05-20 11:50:06 +0900171 // Partial write.
172 if (bytes < *len)
173 memmove(&buf[0], &buf[bytes], *len - bytes);
174 *len -= bytes;
Garrick Evans3cbac7c2019-04-18 15:31:31 +0900175
Andreea Costinas456ee5b2020-09-08 15:11:43 +0200176 // If all the buffered data was written to the socket and the peer socket is
177 // still open for writing, listen for read events on the socket.
178 if (*len == 0 && eof_ != dst->fd() && !SetPollEvents(dst, cfd, EPOLLIN))
179 return false;
Garrick Evans3cbac7c2019-04-18 15:31:31 +0900180 }
181
Andreea Costinas456ee5b2020-09-08 15:11:43 +0200182 Socket *src, *dst;
183 char* buf;
184 ssize_t* len;
185 if (sock0_->fd() == efd) {
186 src = sock0_.get();
187 dst = sock1_.get();
188 buf = buf0_;
189 len = &len0_;
190 } else {
191 src = sock1_.get();
192 dst = sock0_.get();
193 buf = buf1_;
194 len = &len1_;
195 }
196
197 // Skip the read if this buffer is still pending write: requires that
198 // epoll_wait is in level-triggered mode.
199 if (*len > 0)
200 return true;
201
Garrick Evans7db0dda2019-05-20 11:50:06 +0900202 if (events & EPOLLIN) {
Garrick Evans7db0dda2019-05-20 11:50:06 +0900203 *len = src->RecvFrom(buf, kBufSize);
204 if (*len < 0)
205 return false;
206
207 if (*len == 0)
Andreea Costinas456ee5b2020-09-08 15:11:43 +0200208 return HandleConnectionClosed(src, dst, cfd);
Garrick Evans7db0dda2019-05-20 11:50:06 +0900209
210 ssize_t bytes = dst->SendTo(buf, *len);
211 if (bytes < 0)
212 return false;
213
214 if (bytes > 0) {
215 // Partial write.
216 if (bytes < *len)
217 memmove(&buf[0], &buf[bytes], *len - bytes);
218 *len -= bytes;
219 }
220
Andreea Costinas456ee5b2020-09-08 15:11:43 +0200221 if (*len > 0 && !SetPollEvents(dst, cfd, EPOLLOUT))
222 return false;
Garrick Evans7db0dda2019-05-20 11:50:06 +0900223 }
224
Andreea Costinas456ee5b2020-09-08 15:11:43 +0200225 if (events & EPOLLHUP) {
226 LOG(INFO) << "Peer closed connection: " << *sock0_ << " <-> " << *sock1_;
227 return false;
228 }
Garrick Evans7db0dda2019-05-20 11:50:06 +0900229 return true;
Garrick Evans3cbac7c2019-04-18 15:31:31 +0900230}
231
Andreea Costinas456ee5b2020-09-08 15:11:43 +0200232bool SocketForwarder::HandleConnectionClosed(Socket* src,
233 Socket* dst,
234 int cfd) {
235 LOG(INFO) << "Peer closed connection: " << *src;
236 if (eof_ == dst->fd()) {
237 // Stop the forwarder since the other peer has already closed the
238 // connection.
239 LOG(INFO) << "Closed connection: " << *sock0_ << " <-> " << *sock1_;
240 return false;
241 }
242 // Stop listening for read ready events from |src|.
243 if (!SetPollEvents(src, cfd, 0))
244 return false;
245
246 // Propagate the shut down for writing to the other peer. This is safe
247 // to do since reading the EOF on |src| only happens if the buffer
248 // associated with the |src| socket if empty, so there's no outstanding
249 // data to be written to |dst|.
250 if (shutdown(dst->fd(), SHUT_WR) == -1) {
251 PLOG(ERROR) << "Shutting down " << *socket << " for writing failed";
252 return false;
253 }
254
255 eof_ = src->fd();
256 return true;
257}
Garrick Evans3388a032020-03-24 11:25:55 +0900258} // namespace patchpanel