Configure the tap fd before passing it to crosvm

Crosvm doesn't enforce or check any configuration on the tap file
descriptors it gets for networking, so the required configuration
needs to be set before calling the VMM.
This was not a problem with qemu because it actually enforces the
configuration it requires.

Bug: 127708997
Test: run locally
Change-Id: I4979a1d9a60c4dfa6f62f2fec83968c447f8862a
diff --git a/common/libs/utils/network.cpp b/common/libs/utils/network.cpp
index d45196d..f4943ed 100644
--- a/common/libs/utils/network.cpp
+++ b/common/libs/utils/network.cpp
@@ -23,6 +23,23 @@
 #include "common/libs/glog/logging.h"
 
 namespace cvd {
+namespace {
+// This should be the size of virtio_net_hdr_v1, from linux/virtio_net.h, but
+// the version of that header that ships with android in Pie does not include
+// that struct (it was added in Q).
+// This is what that struct looks like:
+// struct virtio_net_hdr_v1 {
+// u8 flags;
+// u8 gso_type;
+// u16 hdr_len;
+// u16 gso_size;
+// u16 csum_start;
+// u16 csum_offset;
+// u16 num_buffers;
+// };
+static constexpr int SIZE_OF_VIRTIO_NET_HDR_V1 = 12;
+}  // namespace
+
 SharedFD OpenTapInterface(const std::string& interface_name) {
   constexpr auto TUNTAP_DEV = "/dev/net/tun";
 
@@ -45,6 +62,16 @@
     return cvd::SharedFD();
   }
 
+  // The interface's configuration may have been modified or just not set
+  // correctly on creation. While qemu checks this and enforces the right
+  // configuration, crosvm does not, so it needs to be set before it's passed to
+  // it.
+  tap_fd->Ioctl(TUNSETOFFLOAD,
+                reinterpret_cast<void*>(TUN_F_CSUM | TUN_F_UFO | TUN_F_TSO4 |
+                                        TUN_F_TSO6));
+  int len = SIZE_OF_VIRTIO_NET_HDR_V1;
+  tap_fd->Ioctl(TUNSETVNETHDRSZ, &len);
+
   return tap_fd;
 }
-}
+}  // namespace cvd