-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtun_adapter_linux.cpp
More file actions
167 lines (146 loc) · 4.08 KB
/
Copy pathtun_adapter_linux.cpp
File metadata and controls
167 lines (146 loc) · 4.08 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
// Linux TUN adapter using /dev/net/tun.
// Implements the TunAdapter interface declared in tun_adapter.h.
#include "tun_adapter.h"
#include <fcntl.h>
#include <linux/if_tun.h>
#include <net/if.h>
#include <poll.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <array>
#include <cerrno>
#include <cstring>
#include <string>
#include "log.h"
#include "util.h"
namespace {
constexpr int kReadTimeoutMs = 500;
constexpr size_t kMaxIpPacketSize = 65535;
constexpr const char* kTunDevPath = "/dev/net/tun";
} // namespace
class LinuxTunAdapter : public TunAdapter {
public:
~LinuxTunAdapter() override { Close(); }
bool Open(const Config& cfg) override {
fd_ = ::open(kTunDevPath, O_RDWR);
if (fd_ < 0) {
Log(LogLevel::Error,
std::string("Failed to open ") + kTunDevPath + ": "
+ std::strerror(errno));
return false;
}
struct ifreq ifr {};
ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
if (!cfg.adapter_name.empty()) {
if (cfg.adapter_name.size() >= IFNAMSIZ) {
Log(LogLevel::Error,
"adapter_name is too long (max " + std::to_string(IFNAMSIZ - 1)
+ " chars): " + cfg.adapter_name);
Close();
return false;
}
std::strncpy(ifr.ifr_name, cfg.adapter_name.c_str(), IFNAMSIZ - 1);
}
if (::ioctl(fd_, TUNSETIFF, &ifr) < 0) {
Log(LogLevel::Error,
std::string("TUNSETIFF failed: ") + std::strerror(errno));
Close();
return false;
}
Log(LogLevel::Info,
std::string("TUN device opened: ") + ifr.ifr_name);
if (cfg.auto_config_ipv4) {
if (!ConfigureTunIpv4(cfg)) {
Log(LogLevel::Error, "Failed to configure TUN IPv4.");
Close();
return false;
}
if (!ConfigureTunMtu(cfg)) {
Log(LogLevel::Error, "Failed to configure TUN MTU.");
Close();
return false;
}
} else {
Log(LogLevel::Info,
"auto_config_ipv4=false, skip TUN IP/MTU setup.");
}
return true;
}
void Close() override {
if (fd_ >= 0) {
::close(fd_);
fd_ = -1;
}
}
TunReadResult ReadPacket(TunReadPacket& packet) override {
ResetReadPacket(packet);
if (fd_ < 0) {
return TunReadResult::Closed;
}
struct pollfd pfd {};
pfd.fd = fd_;
pfd.events = POLLIN;
const int ret = ::poll(&pfd, 1, kReadTimeoutMs);
if (ret < 0) {
if (errno == EINTR) {
return TunReadResult::NoPacket;
}
Log(LogLevel::Error,
std::string("poll() on TUN fd failed: ") + std::strerror(errno));
return TunReadResult::Error;
}
if (ret == 0) {
return TunReadResult::NoPacket;
}
if ((pfd.revents & (POLLHUP | POLLNVAL)) != 0) {
return TunReadResult::Closed;
}
const ssize_t n = ::read(fd_, readBuffer_.data(), readBuffer_.size());
if (n < 0) {
// poll() can produce spurious POLLIN on some kernels; treat
// EAGAIN / EWOULDBLOCK as a soft timeout rather than a fatal error.
if (errno == EAGAIN || errno == EWOULDBLOCK) {
return TunReadResult::NoPacket;
}
Log(LogLevel::Error,
std::string("read() from TUN fd failed: ") + std::strerror(errno));
return TunReadResult::Error;
}
if (n == 0) {
return TunReadResult::Closed;
}
SetReadPacket(packet, readBuffer_.data(), static_cast<size_t>(n));
return TunReadResult::Packet;
}
TunWriteResult WritePacket(const uint8_t* data, size_t len) override {
if (fd_ < 0) {
return TunWriteResult::Closed;
}
const ssize_t n = ::write(fd_, data, len);
if (n < 0) {
if (errno == EBADF || errno == ENODEV || errno == EIO) {
return TunWriteResult::Closed;
}
Log(LogLevel::Error,
std::string("write() to TUN fd failed: ") + std::strerror(errno));
return TunWriteResult::Error;
}
// TUN writes are atomic (one write = one IP packet), but guard against
// the unexpected case of a partial write.
if (static_cast<size_t>(n) != len) {
Log(LogLevel::Error,
"write() to TUN fd: partial write, expected="
+ std::to_string(len) + " got=" + std::to_string(n));
return TunWriteResult::Error;
}
return TunWriteResult::Written;
}
protected:
void ReleaseReadPacket(const uint8_t*) override {}
private:
int fd_ = -1;
std::array<uint8_t, kMaxIpPacketSize> readBuffer_{};
};
TunAdapter* TunAdapter::Create() {
return new LinuxTunAdapter();
}