xref: /aosp_15_r20/external/cronet/net/third_party/quiche/src/quiche/quic/qbone/bonnet/tun_device.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // 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 "quiche/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 "absl/cleanup/cleanup.h"
14 #include "quiche/quic/platform/api/quic_bug_tracker.h"
15 #include "quiche/quic/platform/api/quic_logging.h"
16 #include "quiche/quic/qbone/platform/kernel_interface.h"
17 
18 ABSL_FLAG(std::string, qbone_client_tun_device_path, "/dev/net/tun",
19           "The path to the QBONE client's TUN device.");
20 
21 namespace quic {
22 
23 const int kInvalidFd = -1;
24 
TunTapDevice(const std::string & interface_name,int mtu,bool persist,bool setup_tun,bool is_tap,KernelInterface * kernel)25 TunTapDevice::TunTapDevice(const std::string& interface_name, int mtu,
26                            bool persist, bool setup_tun, bool is_tap,
27                            KernelInterface* kernel)
28     : interface_name_(interface_name),
29       mtu_(mtu),
30       persist_(persist),
31       setup_tun_(setup_tun),
32       is_tap_(is_tap),
33       file_descriptor_(kInvalidFd),
34       kernel_(*kernel) {}
35 
~TunTapDevice()36 TunTapDevice::~TunTapDevice() {
37   if (!persist_) {
38     Down();
39   }
40   CloseDevice();
41 }
42 
Init()43 bool TunTapDevice::Init() {
44   if (interface_name_.empty() || interface_name_.size() >= IFNAMSIZ) {
45     QUIC_BUG(quic_bug_10995_1)
46         << "interface_name must be nonempty and shorter than " << IFNAMSIZ;
47     return false;
48   }
49 
50   if (!OpenDevice()) {
51     return false;
52   }
53 
54   if (!ConfigureInterface()) {
55     return false;
56   }
57 
58   return true;
59 }
60 
61 // TODO(pengg): might be better to use netlink socket, once we have a library to
62 // use
Up()63 bool TunTapDevice::Up() {
64   if (!setup_tun_) {
65     return true;
66   }
67   struct ifreq if_request;
68   memset(&if_request, 0, sizeof(if_request));
69   // copy does not zero-terminate the result string, but we've memset the
70   // entire struct.
71   interface_name_.copy(if_request.ifr_name, IFNAMSIZ);
72   if_request.ifr_flags = IFF_UP;
73 
74   return NetdeviceIoctl(SIOCSIFFLAGS, reinterpret_cast<void*>(&if_request));
75 }
76 
77 // TODO(pengg): might be better to use netlink socket, once we have a library to
78 // use
Down()79 bool TunTapDevice::Down() {
80   if (!setup_tun_) {
81     return true;
82   }
83   struct ifreq if_request;
84   memset(&if_request, 0, sizeof(if_request));
85   // copy does not zero-terminate the result string, but we've memset the
86   // entire struct.
87   interface_name_.copy(if_request.ifr_name, IFNAMSIZ);
88   if_request.ifr_flags = 0;
89 
90   return NetdeviceIoctl(SIOCSIFFLAGS, reinterpret_cast<void*>(&if_request));
91 }
92 
GetFileDescriptor() const93 int TunTapDevice::GetFileDescriptor() const { return file_descriptor_; }
94 
OpenDevice()95 bool TunTapDevice::OpenDevice() {
96   if (file_descriptor_ != kInvalidFd) {
97     CloseDevice();
98   }
99 
100   struct ifreq if_request;
101   memset(&if_request, 0, sizeof(if_request));
102   // copy does not zero-terminate the result string, but we've memset the entire
103   // struct.
104   interface_name_.copy(if_request.ifr_name, IFNAMSIZ);
105 
106   // Always set IFF_MULTI_QUEUE since a persistent device does not allow this
107   // flag to be flipped when re-opening it. The only way to flip this flag is to
108   // destroy the device and create a new one, but that deletes any existing
109   // routing associated with the interface, which makes the meaning of the
110   // 'persist' bit ambiguous.
111   if_request.ifr_flags = IFF_MULTI_QUEUE | IFF_NO_PI;
112   if (is_tap_) {
113     if_request.ifr_flags |= IFF_TAP;
114   } else {
115     if_request.ifr_flags |= IFF_TUN;
116   }
117 
118   // When the device is running with IFF_MULTI_QUEUE set, each call to open will
119   // create a queue which can be used to read/write packets from/to the device.
120   bool successfully_opened = false;
121   auto cleanup = absl::MakeCleanup([this, &successfully_opened]() {
122     if (!successfully_opened) {
123       CloseDevice();
124     }
125   });
126 
127   const std::string tun_device_path =
128       absl::GetFlag(FLAGS_qbone_client_tun_device_path);
129   int fd = kernel_.open(tun_device_path.c_str(), O_RDWR);
130   if (fd < 0) {
131     QUIC_PLOG(WARNING) << "Failed to open " << tun_device_path;
132     return successfully_opened;
133   }
134   file_descriptor_ = fd;
135   if (!CheckFeatures(fd)) {
136     return successfully_opened;
137   }
138 
139   if (kernel_.ioctl(fd, TUNSETIFF, reinterpret_cast<void*>(&if_request)) != 0) {
140     QUIC_PLOG(WARNING) << "Failed to TUNSETIFF on fd(" << fd << ")";
141     return successfully_opened;
142   }
143 
144   if (kernel_.ioctl(
145           fd, TUNSETPERSIST,
146           persist_ ? reinterpret_cast<void*>(&if_request) : nullptr) != 0) {
147     QUIC_PLOG(WARNING) << "Failed to TUNSETPERSIST on fd(" << fd << ")";
148     return successfully_opened;
149   }
150 
151   successfully_opened = true;
152   return successfully_opened;
153 }
154 
155 // TODO(pengg): might be better to use netlink socket, once we have a library to
156 // use
ConfigureInterface()157 bool TunTapDevice::ConfigureInterface() {
158   if (!setup_tun_) {
159     return true;
160   }
161 
162   struct ifreq if_request;
163   memset(&if_request, 0, sizeof(if_request));
164   // copy does not zero-terminate the result string, but we've memset the entire
165   // struct.
166   interface_name_.copy(if_request.ifr_name, IFNAMSIZ);
167   if_request.ifr_mtu = mtu_;
168 
169   if (!NetdeviceIoctl(SIOCSIFMTU, reinterpret_cast<void*>(&if_request))) {
170     CloseDevice();
171     return false;
172   }
173 
174   return true;
175 }
176 
CheckFeatures(int tun_device_fd)177 bool TunTapDevice::CheckFeatures(int tun_device_fd) {
178   unsigned int actual_features;
179   if (kernel_.ioctl(tun_device_fd, TUNGETFEATURES, &actual_features) != 0) {
180     QUIC_PLOG(WARNING) << "Failed to TUNGETFEATURES";
181     return false;
182   }
183   unsigned int required_features = IFF_TUN | IFF_NO_PI;
184   if ((required_features & actual_features) != required_features) {
185     QUIC_LOG(WARNING)
186         << "Required feature does not exist. required_features: 0x" << std::hex
187         << required_features << " vs actual_features: 0x" << std::hex
188         << actual_features;
189     return false;
190   }
191   return true;
192 }
193 
NetdeviceIoctl(int request,void * argp)194 bool TunTapDevice::NetdeviceIoctl(int request, void* argp) {
195   int fd = kernel_.socket(AF_INET6, SOCK_DGRAM, 0);
196   if (fd < 0) {
197     QUIC_PLOG(WARNING) << "Failed to create AF_INET6 socket.";
198     return false;
199   }
200 
201   if (kernel_.ioctl(fd, request, argp) != 0) {
202     QUIC_PLOG(WARNING) << "Failed ioctl request: " << request;
203     kernel_.close(fd);
204     return false;
205   }
206   kernel_.close(fd);
207   return true;
208 }
209 
CloseDevice()210 void TunTapDevice::CloseDevice() {
211   if (file_descriptor_ != kInvalidFd) {
212     kernel_.close(file_descriptor_);
213     file_descriptor_ = kInvalidFd;
214   }
215 }
216 
217 }  // namespace quic
218