blob: c3404ae4446cffd76011c5c2c1094a949f66bf5c [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;
26} // namespace
27
28SocketForwarder::SocketForwarder(const std::string& name,
29 std::unique_ptr<Socket> sock0,
30 std::unique_ptr<Socket> sock1)
31 : base::SimpleThread(name),
32 sock0_(std::move(sock0)),
Garrick Evans7db0dda2019-05-20 11:50:06 +090033 sock1_(std::move(sock1)),
34 len0_(0),
35 len1_(0),
36 poll_(false),
37 done_(false) {
38 DCHECK(sock0_);
39 DCHECK(sock1_);
40}
Garrick Evans3cbac7c2019-04-18 15:31:31 +090041
42SocketForwarder::~SocketForwarder() {
Garrick Evans088cd0e2019-06-04 15:20:43 +090043 // Ensure the polling loop exits.
44 poll_ = false;
Garrick Evans3cbac7c2019-04-18 15:31:31 +090045 Join();
46}
47
Garrick Evans088cd0e2019-06-04 15:20:43 +090048bool SocketForwarder::IsRunning() const {
Garrick Evans7db0dda2019-05-20 11:50:06 +090049 return !done_;
50}
51
Garrick Evans3cbac7c2019-04-18 15:31:31 +090052void SocketForwarder::Run() {
53 LOG(INFO) << "Starting forwarder: " << *sock0_ << " <-> " << *sock1_;
54
55 // We need these sockets to be non-blocking.
56 if (fcntl(sock0_->fd(), F_SETFL,
57 fcntl(sock0_->fd(), F_GETFL, 0) | O_NONBLOCK) < 0 ||
58 fcntl(sock1_->fd(), F_SETFL,
59 fcntl(sock1_->fd(), F_GETFL, 0) | O_NONBLOCK) < 0) {
60 PLOG(ERROR) << "fcntl failed";
61 return;
62 }
63
Garrick Evans7db0dda2019-05-20 11:50:06 +090064 Poll();
Garrick Evans7db0dda2019-05-20 11:50:06 +090065
Garrick Evans088cd0e2019-06-04 15:20:43 +090066 LOG(INFO) << "Forwarder stopped: " << *sock0_ << " <-> " << *sock1_;
67 done_ = true;
Garrick Evans7db0dda2019-05-20 11:50:06 +090068 sock1_.reset();
69 sock0_.reset();
70}
71
Garrick Evans7db0dda2019-05-20 11:50:06 +090072void SocketForwarder::Poll() {
73 base::ScopedFD cfd(epoll_create1(0));
74 if (!cfd.is_valid()) {
Garrick Evans3cbac7c2019-04-18 15:31:31 +090075 PLOG(ERROR) << "epoll_create1 failed";
76 return;
77 }
78 struct epoll_event ev;
79 ev.events = EPOLLIN | EPOLLRDHUP;
80 ev.data.fd = sock0_->fd();
Garrick Evans7db0dda2019-05-20 11:50:06 +090081 if (epoll_ctl(cfd.get(), EPOLL_CTL_ADD, sock0_->fd(), &ev) == -1) {
Garrick Evans3cbac7c2019-04-18 15:31:31 +090082 PLOG(ERROR) << "epoll_ctl failed";
83 return;
84 }
85 ev.data.fd = sock1_->fd();
Garrick Evans7db0dda2019-05-20 11:50:06 +090086 if (epoll_ctl(cfd.get(), EPOLL_CTL_ADD, sock1_->fd(), &ev) == -1) {
Garrick Evans3cbac7c2019-04-18 15:31:31 +090087 PLOG(ERROR) << "epoll_ctl failed";
88 return;
89 }
90
Garrick Evans7db0dda2019-05-20 11:50:06 +090091 poll_ = true;
Garrick Evans3cbac7c2019-04-18 15:31:31 +090092 struct epoll_event events[kMaxEvents];
Garrick Evans7db0dda2019-05-20 11:50:06 +090093 while (poll_) {
94 int n = epoll_wait(cfd.get(), events, kMaxEvents, kWaitTimeoutMs);
Garrick Evans3cbac7c2019-04-18 15:31:31 +090095 if (n == -1) {
Lepton Wubbd23b32019-11-05 15:59:47 -080096 if (errno == EINTR) {
97 LOG(INFO) << "Resume epoll_wait from interruption.";
98 continue;
99 }
Garrick Evans3cbac7c2019-04-18 15:31:31 +0900100 PLOG(ERROR) << "epoll_wait failed";
Garrick Evans7db0dda2019-05-20 11:50:06 +0900101 return;
Garrick Evans3cbac7c2019-04-18 15:31:31 +0900102 }
103 for (int i = 0; i < n; ++i) {
Garrick Evans088cd0e2019-06-04 15:20:43 +0900104 if (!poll_ ||
105 !ProcessEvents(events[i].events, events[i].data.fd, cfd.get()))
Garrick Evans7db0dda2019-05-20 11:50:06 +0900106 return;
107 }
108 }
109}
Garrick Evans3cbac7c2019-04-18 15:31:31 +0900110
Garrick Evans7db0dda2019-05-20 11:50:06 +0900111bool SocketForwarder::ProcessEvents(uint32_t events, int efd, int cfd) {
112 if (events & EPOLLERR) {
Garrick Evans088cd0e2019-06-04 15:20:43 +0900113 int so_error;
114 socklen_t optlen = sizeof(so_error);
115 getsockopt(efd, SOL_SOCKET, SO_ERROR, &so_error, &optlen);
116 PLOG(WARNING) << "Socket error: (" << so_error << ") " << *sock0_ << " <-> "
117 << *sock1_;
Garrick Evans7db0dda2019-05-20 11:50:06 +0900118 return false;
119 }
120 if (events & (EPOLLHUP | EPOLLRDHUP)) {
121 LOG(INFO) << "Peer closed connection: " << *sock0_ << " <-> " << *sock1_;
122 return false;
123 }
Garrick Evans3cbac7c2019-04-18 15:31:31 +0900124
Garrick Evans7db0dda2019-05-20 11:50:06 +0900125 if (events & EPOLLOUT) {
126 Socket* dst;
127 char* buf;
128 ssize_t* len;
129 if (sock0_->fd() == efd) {
130 dst = sock0_.get();
131 buf = buf1_;
132 len = &len1_;
133 } else {
134 dst = sock1_.get();
135 buf = buf0_;
136 len = &len0_;
137 }
Garrick Evans3cbac7c2019-04-18 15:31:31 +0900138
Garrick Evans7db0dda2019-05-20 11:50:06 +0900139 ssize_t bytes = dst->SendTo(buf, *len);
140 if (bytes < 0)
141 return false;
Garrick Evans3cbac7c2019-04-18 15:31:31 +0900142
Garrick Evans7db0dda2019-05-20 11:50:06 +0900143 // Still unavailable.
144 if (bytes == 0)
145 return true;
Garrick Evans3cbac7c2019-04-18 15:31:31 +0900146
Garrick Evans7db0dda2019-05-20 11:50:06 +0900147 // Partial write.
148 if (bytes < *len)
149 memmove(&buf[0], &buf[bytes], *len - bytes);
150 *len -= bytes;
Garrick Evans3cbac7c2019-04-18 15:31:31 +0900151
Garrick Evans7db0dda2019-05-20 11:50:06 +0900152 if (*len == 0) {
153 struct epoll_event ev;
154 ev.events = EPOLLIN | EPOLLRDHUP;
155 ev.data.fd = dst->fd();
156 if (epoll_ctl(cfd, EPOLL_CTL_MOD, dst->fd(), &ev) == -1) {
157 PLOG(ERROR) << "epoll_ctl failed";
158 return false;
Garrick Evans3cbac7c2019-04-18 15:31:31 +0900159 }
160 }
161 }
162
Garrick Evans7db0dda2019-05-20 11:50:06 +0900163 if (events & EPOLLIN) {
164 Socket *src, *dst;
165 char* buf;
166 ssize_t* len;
167 if (sock0_->fd() == efd) {
168 src = sock0_.get();
169 dst = sock1_.get();
170 buf = buf0_;
171 len = &len0_;
172 } else {
173 src = sock1_.get();
174 dst = sock0_.get();
175 buf = buf1_;
176 len = &len1_;
177 }
178
179 // Skip the read if this buffer is still pending write: requires that
180 // epoll_wait is in level-triggered mode.
181 if (*len > 0)
182 return true;
183
184 *len = src->RecvFrom(buf, kBufSize);
185 if (*len < 0)
186 return false;
187
188 if (*len == 0)
189 return true;
190
191 ssize_t bytes = dst->SendTo(buf, *len);
192 if (bytes < 0)
193 return false;
194
195 if (bytes > 0) {
196 // Partial write.
197 if (bytes < *len)
198 memmove(&buf[0], &buf[bytes], *len - bytes);
199 *len -= bytes;
200 }
201
202 if (*len > 0) {
203 struct epoll_event ev;
204 ev.events = EPOLLOUT | EPOLLRDHUP;
205 ev.data.fd = dst->fd();
206 if (epoll_ctl(cfd, EPOLL_CTL_MOD, dst->fd(), &ev) == -1) {
207 PLOG(ERROR) << "epoll_ctl failed";
208 return false;
209 }
210 }
211 }
212
213 return true;
Garrick Evans3cbac7c2019-04-18 15:31:31 +0900214}
215
Garrick Evans3388a032020-03-24 11:25:55 +0900216} // namespace patchpanel