blob: 6fa3cc349afebeb27f380f0d8ce69c8d09055913 [file] [log] [blame]
wubf975eac2019-08-19 19:41:01 -07001// Copyright (c) 2019 The Chromium 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
5#include "net/third_party/quiche/src/quic/qbone/bonnet/tun_device.h"
6
7#include <fcntl.h>
8#include <linux/if_tun.h>
9#include <net/if.h>
10#include <sys/ioctl.h>
11#include <sys/socket.h>
12
13#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
14#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
15#include "net/third_party/quiche/src/quic/qbone/platform/kernel_interface.h"
16
17namespace quic {
18
19const char kTapTunDevicePath[] = "/dev/net/tun";
20const int kInvalidFd = -1;
21
22TunDevice::TunDevice(const string& interface_name,
23 int mtu,
24 bool persist,
QUICHE team65db4b02019-12-12 15:14:08 -080025 bool setup_tun,
wubf975eac2019-08-19 19:41:01 -070026 KernelInterface* kernel)
27 : interface_name_(interface_name),
28 mtu_(mtu),
29 persist_(persist),
QUICHE team65db4b02019-12-12 15:14:08 -080030 setup_tun_(setup_tun),
wubf975eac2019-08-19 19:41:01 -070031 file_descriptor_(kInvalidFd),
32 kernel_(*kernel) {}
33
34TunDevice::~TunDevice() {
QUICHE team133f4882019-11-04 19:25:52 -080035 if (!persist_) {
36 Down();
37 }
wubf975eac2019-08-19 19:41:01 -070038 CleanUpFileDescriptor();
39}
40
41bool TunDevice::Init() {
42 if (interface_name_.empty() || interface_name_.size() >= IFNAMSIZ) {
43 QUIC_BUG << "interface_name must be nonempty and shorter than " << IFNAMSIZ;
44 return false;
45 }
46
47 if (!OpenDevice()) {
48 return false;
49 }
50
51 if (!ConfigureInterface()) {
52 return false;
53 }
54
55 return true;
56}
57
58// TODO(pengg): might be better to use netlink socket, once we have a library to
59// use
60bool TunDevice::Up() {
QUICHE team65db4b02019-12-12 15:14:08 -080061 if (setup_tun_ && !is_interface_up_) {
wubf975eac2019-08-19 19:41:01 -070062 struct ifreq if_request;
63 memset(&if_request, 0, sizeof(if_request));
64 // copy does not zero-terminate the result string, but we've memset the
65 // entire struct.
66 interface_name_.copy(if_request.ifr_name, IFNAMSIZ);
67 if_request.ifr_flags = IFF_UP;
68
69 is_interface_up_ =
70 NetdeviceIoctl(SIOCSIFFLAGS, reinterpret_cast<void*>(&if_request));
71 return is_interface_up_;
72 } else {
73 return true;
74 }
75}
76
77// TODO(pengg): might be better to use netlink socket, once we have a library to
78// use
79bool TunDevice::Down() {
QUICHE team65db4b02019-12-12 15:14:08 -080080 if (setup_tun_ && is_interface_up_) {
wubf975eac2019-08-19 19:41:01 -070081 struct ifreq if_request;
82 memset(&if_request, 0, sizeof(if_request));
83 // copy does not zero-terminate the result string, but we've memset the
84 // entire struct.
85 interface_name_.copy(if_request.ifr_name, IFNAMSIZ);
86 if_request.ifr_flags = 0;
87
88 is_interface_up_ =
89 !NetdeviceIoctl(SIOCSIFFLAGS, reinterpret_cast<void*>(&if_request));
90 return !is_interface_up_;
91 } else {
92 return true;
93 }
94}
95
96int TunDevice::GetFileDescriptor() const {
97 return file_descriptor_;
98}
99
100bool TunDevice::OpenDevice() {
101 struct ifreq if_request;
102 memset(&if_request, 0, sizeof(if_request));
103 // copy does not zero-terminate the result string, but we've memset the entire
104 // struct.
105 interface_name_.copy(if_request.ifr_name, IFNAMSIZ);
106
107 // Always set IFF_MULTI_QUEUE since a persistent device does not allow this
108 // flag to be flipped when re-opening it. The only way to flip this flag is to
109 // destroy the device and create a new one, but that deletes any existing
110 // routing associated with the interface, which makes the meaning of the
111 // 'persist' bit ambiguous.
112 if_request.ifr_flags = IFF_TUN | IFF_MULTI_QUEUE | IFF_NO_PI;
113
114 // TODO(pengg): port MakeCleanup to quic/platform? This makes the call to
115 // CleanUpFileDescriptor nicer and less error-prone.
116 // When the device is running with IFF_MULTI_QUEUE set, each call to open will
117 // create a queue which can be used to read/write packets from/to the device.
118 int fd = kernel_.open(kTapTunDevicePath, O_RDWR);
119 if (fd < 0) {
120 QUIC_PLOG(WARNING) << "Failed to open " << kTapTunDevicePath;
121 CleanUpFileDescriptor();
122 return false;
123 }
124 file_descriptor_ = fd;
125 if (!CheckFeatures(fd)) {
126 CleanUpFileDescriptor();
127 return false;
128 }
129
130 if (kernel_.ioctl(fd, TUNSETIFF, reinterpret_cast<void*>(&if_request)) != 0) {
131 QUIC_PLOG(WARNING) << "Failed to TUNSETIFF on fd(" << fd << ")";
132 CleanUpFileDescriptor();
133 return false;
134 }
135
136 if (kernel_.ioctl(
137 fd, TUNSETPERSIST,
138 persist_ ? reinterpret_cast<void*>(&if_request) : nullptr) != 0) {
139 QUIC_PLOG(WARNING) << "Failed to TUNSETPERSIST on fd(" << fd << ")";
140 CleanUpFileDescriptor();
141 return false;
142 }
143
144 return true;
145}
146
147// TODO(pengg): might be better to use netlink socket, once we have a library to
148// use
149bool TunDevice::ConfigureInterface() {
QUICHE team65db4b02019-12-12 15:14:08 -0800150 if (!setup_tun_) {
151 return true;
152 }
153
wubf975eac2019-08-19 19:41:01 -0700154 struct ifreq if_request;
155 memset(&if_request, 0, sizeof(if_request));
156 // copy does not zero-terminate the result string, but we've memset the entire
157 // struct.
158 interface_name_.copy(if_request.ifr_name, IFNAMSIZ);
159 if_request.ifr_mtu = mtu_;
160
161 if (!NetdeviceIoctl(SIOCSIFMTU, reinterpret_cast<void*>(&if_request))) {
162 CleanUpFileDescriptor();
163 return false;
164 }
165
166 return true;
167}
168
169bool TunDevice::CheckFeatures(int tun_device_fd) {
170 unsigned int actual_features;
171 if (kernel_.ioctl(tun_device_fd, TUNGETFEATURES, &actual_features) != 0) {
172 QUIC_PLOG(WARNING) << "Failed to TUNGETFEATURES";
173 return false;
174 }
175 unsigned int required_features = IFF_TUN | IFF_NO_PI;
176 if ((required_features & actual_features) != required_features) {
177 QUIC_LOG(WARNING)
178 << "Required feature does not exist. required_features: 0x" << std::hex
179 << required_features << " vs actual_features: 0x" << std::hex
180 << actual_features;
181 return false;
182 }
183 return true;
184}
185
186bool TunDevice::NetdeviceIoctl(int request, void* argp) {
187 int fd = kernel_.socket(AF_INET6, SOCK_DGRAM, 0);
188 if (fd < 0) {
189 QUIC_PLOG(WARNING) << "Failed to create AF_INET6 socket.";
190 return false;
191 }
192
193 if (kernel_.ioctl(fd, request, argp) != 0) {
194 QUIC_PLOG(WARNING) << "Failed ioctl request: " << request;
195 kernel_.close(fd);
196 return false;
197 }
198 kernel_.close(fd);
199 return true;
200}
201
202void TunDevice::CleanUpFileDescriptor() {
203 if (file_descriptor_ != kInvalidFd) {
204 kernel_.close(file_descriptor_);
205 file_descriptor_ = kInvalidFd;
206 }
207}
208
209} // namespace quic