gfe-relnote: Default-initialize QUIC BBRv2 loss event threshold for exiting STARTUP from a flag. Protected by --gfe2_reloadable_flag_quic_default_to_bbr_v2.

PiperOrigin-RevId: 264298542
Change-Id: I304ab19e4820dec51d3f8ef53762a393f6b175fd
diff --git a/quic/qbone/bonnet/tun_device.cc b/quic/qbone/bonnet/tun_device.cc
new file mode 100644
index 0000000..6c0a8a5
--- /dev/null
+++ b/quic/qbone/bonnet/tun_device.cc
@@ -0,0 +1,201 @@
+// Copyright (c) 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/qbone/bonnet/tun_device.h"
+
+#include <fcntl.h>
+#include <linux/if_tun.h>
+#include <net/if.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/qbone/platform/kernel_interface.h"
+
+namespace quic {
+
+const char kTapTunDevicePath[] = "/dev/net/tun";
+const int kInvalidFd = -1;
+
+TunDevice::TunDevice(const string& interface_name,
+                     int mtu,
+                     bool persist,
+                     KernelInterface* kernel)
+    : interface_name_(interface_name),
+      mtu_(mtu),
+      persist_(persist),
+      file_descriptor_(kInvalidFd),
+      kernel_(*kernel) {}
+
+TunDevice::~TunDevice() {
+  Down();
+  CleanUpFileDescriptor();
+}
+
+bool TunDevice::Init() {
+  if (interface_name_.empty() || interface_name_.size() >= IFNAMSIZ) {
+    QUIC_BUG << "interface_name must be nonempty and shorter than " << IFNAMSIZ;
+    return false;
+  }
+
+  if (!OpenDevice()) {
+    return false;
+  }
+
+  if (!ConfigureInterface()) {
+    return false;
+  }
+
+  return true;
+}
+
+// TODO(pengg): might be better to use netlink socket, once we have a library to
+// use
+bool TunDevice::Up() {
+  if (!is_interface_up_) {
+    struct ifreq if_request;
+    memset(&if_request, 0, sizeof(if_request));
+    // copy does not zero-terminate the result string, but we've memset the
+    // entire struct.
+    interface_name_.copy(if_request.ifr_name, IFNAMSIZ);
+    if_request.ifr_flags = IFF_UP;
+
+    is_interface_up_ =
+        NetdeviceIoctl(SIOCSIFFLAGS, reinterpret_cast<void*>(&if_request));
+    return is_interface_up_;
+  } else {
+    return true;
+  }
+}
+
+// TODO(pengg): might be better to use netlink socket, once we have a library to
+// use
+bool TunDevice::Down() {
+  if (is_interface_up_) {
+    struct ifreq if_request;
+    memset(&if_request, 0, sizeof(if_request));
+    // copy does not zero-terminate the result string, but we've memset the
+    // entire struct.
+    interface_name_.copy(if_request.ifr_name, IFNAMSIZ);
+    if_request.ifr_flags = 0;
+
+    is_interface_up_ =
+        !NetdeviceIoctl(SIOCSIFFLAGS, reinterpret_cast<void*>(&if_request));
+    return !is_interface_up_;
+  } else {
+    return true;
+  }
+}
+
+int TunDevice::GetFileDescriptor() const {
+  return file_descriptor_;
+}
+
+bool TunDevice::OpenDevice() {
+  struct ifreq if_request;
+  memset(&if_request, 0, sizeof(if_request));
+  // copy does not zero-terminate the result string, but we've memset the entire
+  // struct.
+  interface_name_.copy(if_request.ifr_name, IFNAMSIZ);
+
+  // Always set IFF_MULTI_QUEUE since a persistent device does not allow this
+  // flag to be flipped when re-opening it. The only way to flip this flag is to
+  // destroy the device and create a new one, but that deletes any existing
+  // routing associated with the interface, which makes the meaning of the
+  // 'persist' bit ambiguous.
+  if_request.ifr_flags = IFF_TUN | IFF_MULTI_QUEUE | IFF_NO_PI;
+
+  // TODO(pengg): port MakeCleanup to quic/platform? This makes the call to
+  // CleanUpFileDescriptor nicer and less error-prone.
+  // When the device is running with IFF_MULTI_QUEUE set, each call to open will
+  // create a queue which can be used to read/write packets from/to the device.
+  int fd = kernel_.open(kTapTunDevicePath, O_RDWR);
+  if (fd < 0) {
+    QUIC_PLOG(WARNING) << "Failed to open " << kTapTunDevicePath;
+    CleanUpFileDescriptor();
+    return false;
+  }
+  file_descriptor_ = fd;
+  if (!CheckFeatures(fd)) {
+    CleanUpFileDescriptor();
+    return false;
+  }
+
+  if (kernel_.ioctl(fd, TUNSETIFF, reinterpret_cast<void*>(&if_request)) != 0) {
+    QUIC_PLOG(WARNING) << "Failed to TUNSETIFF on fd(" << fd << ")";
+    CleanUpFileDescriptor();
+    return false;
+  }
+
+  if (kernel_.ioctl(
+          fd, TUNSETPERSIST,
+          persist_ ? reinterpret_cast<void*>(&if_request) : nullptr) != 0) {
+    QUIC_PLOG(WARNING) << "Failed to TUNSETPERSIST on fd(" << fd << ")";
+    CleanUpFileDescriptor();
+    return false;
+  }
+
+  return true;
+}
+
+// TODO(pengg): might be better to use netlink socket, once we have a library to
+// use
+bool TunDevice::ConfigureInterface() {
+  struct ifreq if_request;
+  memset(&if_request, 0, sizeof(if_request));
+  // copy does not zero-terminate the result string, but we've memset the entire
+  // struct.
+  interface_name_.copy(if_request.ifr_name, IFNAMSIZ);
+  if_request.ifr_mtu = mtu_;
+
+  if (!NetdeviceIoctl(SIOCSIFMTU, reinterpret_cast<void*>(&if_request))) {
+    CleanUpFileDescriptor();
+    return false;
+  }
+
+  return true;
+}
+
+bool TunDevice::CheckFeatures(int tun_device_fd) {
+  unsigned int actual_features;
+  if (kernel_.ioctl(tun_device_fd, TUNGETFEATURES, &actual_features) != 0) {
+    QUIC_PLOG(WARNING) << "Failed to TUNGETFEATURES";
+    return false;
+  }
+  unsigned int required_features = IFF_TUN | IFF_NO_PI;
+  if ((required_features & actual_features) != required_features) {
+    QUIC_LOG(WARNING)
+        << "Required feature does not exist. required_features: 0x" << std::hex
+        << required_features << " vs actual_features: 0x" << std::hex
+        << actual_features;
+    return false;
+  }
+  return true;
+}
+
+bool TunDevice::NetdeviceIoctl(int request, void* argp) {
+  int fd = kernel_.socket(AF_INET6, SOCK_DGRAM, 0);
+  if (fd < 0) {
+    QUIC_PLOG(WARNING) << "Failed to create AF_INET6 socket.";
+    return false;
+  }
+
+  if (kernel_.ioctl(fd, request, argp) != 0) {
+    QUIC_PLOG(WARNING) << "Failed ioctl request: " << request;
+    kernel_.close(fd);
+    return false;
+  }
+  kernel_.close(fd);
+  return true;
+}
+
+void TunDevice::CleanUpFileDescriptor() {
+  if (file_descriptor_ != kInvalidFd) {
+    kernel_.close(file_descriptor_);
+    file_descriptor_ = kInvalidFd;
+  }
+}
+
+}  // namespace quic