Switch logging to use tracing instead of log

Tracing has support for intervals and a great os_log integration.
This commit is contained in:
Jett Chen 2023-08-27 11:43:17 +08:00
parent 60cfd95789
commit e643d9dd41
14 changed files with 297 additions and 8 deletions

181
Cargo.lock generated
View file

@ -125,6 +125,28 @@ version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
[[package]]
name = "bindgen"
version = "0.64.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4243e6031260db77ede97ad86c27e501d646a27ab57b59a574f725d98ab1fb4"
dependencies = [
"bitflags 1.3.2",
"cexpr",
"clang-sys",
"lazy_static",
"lazycell",
"log",
"peeking_take_while",
"proc-macro2",
"quote",
"regex",
"rustc-hash",
"shlex",
"syn 1.0.109",
"which",
]
[[package]]
name = "bindgen"
version = "0.65.1"
@ -179,6 +201,7 @@ checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1"
name = "burrow"
version = "0.1.0"
dependencies = [
"anyhow",
"caps",
"clap",
"env_logger",
@ -188,6 +211,11 @@ dependencies = [
"serde",
"serde_json",
"tokio",
"tracing",
"tracing-journald",
"tracing-log",
"tracing-oslog",
"tracing-subscriber",
"tun",
]
@ -902,6 +930,16 @@ version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0"
[[package]]
name = "lock_api"
version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16"
dependencies = [
"autocfg",
"scopeguard",
]
[[package]]
name = "log"
version = "0.4.19"
@ -1020,6 +1058,16 @@ dependencies = [
"minimal-lexical",
]
[[package]]
name = "nu-ansi-term"
version = "0.46.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
dependencies = [
"overload",
"winapi",
]
[[package]]
name = "object"
version = "0.31.1"
@ -1079,6 +1127,35 @@ dependencies = [
"vcpkg",
]
[[package]]
name = "overload"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
[[package]]
name = "parking_lot"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
dependencies = [
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"smallvec",
"windows-targets",
]
[[package]]
name = "password-hash"
version = "0.4.2"
@ -1283,6 +1360,12 @@ dependencies = [
"windows-sys 0.42.0",
]
[[package]]
name = "scopeguard"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "security-framework"
version = "2.9.1"
@ -1382,6 +1465,15 @@ dependencies = [
"digest",
]
[[package]]
name = "sharded-slab"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31"
dependencies = [
"lazy_static",
]
[[package]]
name = "shlex"
version = "1.1.0"
@ -1397,6 +1489,12 @@ dependencies = [
"autocfg",
]
[[package]]
name = "smallvec"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9"
[[package]]
name = "socket2"
version = "0.4.9"
@ -1506,6 +1604,16 @@ dependencies = [
"syn 2.0.22",
]
[[package]]
name = "thread_local"
version = "1.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152"
dependencies = [
"cfg-if",
"once_cell",
]
[[package]]
name = "time"
version = "0.3.22"
@ -1603,9 +1711,21 @@ checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8"
dependencies = [
"cfg-if",
"pin-project-lite",
"tracing-attributes",
"tracing-core",
]
[[package]]
name = "tracing-attributes"
version = "0.1.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.22",
]
[[package]]
name = "tracing-core"
version = "0.1.31"
@ -1613,6 +1733,58 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a"
dependencies = [
"once_cell",
"valuable",
]
[[package]]
name = "tracing-journald"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba316a74e8fc3c3896a850dba2375928a9fa171b085ecddfc7c054d39970f3fd"
dependencies = [
"libc",
"tracing-core",
"tracing-subscriber",
]
[[package]]
name = "tracing-log"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922"
dependencies = [
"lazy_static",
"log",
"tracing-core",
]
[[package]]
name = "tracing-oslog"
version = "0.1.2"
source = "git+https://github.com/Stormshield-robinc/tracing-oslog#c4d21a95e70cdd62b1cea04fc4f8be1c547cad6c"
dependencies = [
"bindgen 0.64.0",
"cc",
"cfg-if",
"fnv",
"once_cell",
"parking_lot",
"tracing-core",
"tracing-subscriber",
]
[[package]]
name = "tracing-subscriber"
version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77"
dependencies = [
"nu-ansi-term",
"sharded-slab",
"smallvec",
"thread_local",
"tracing-core",
"tracing-log",
]
[[package]]
@ -1626,7 +1798,7 @@ name = "tun"
version = "0.1.0"
dependencies = [
"anyhow",
"bindgen",
"bindgen 0.65.1",
"byteorder",
"fehler",
"futures",
@ -1641,6 +1813,7 @@ dependencies = [
"ssri",
"tempfile",
"tokio",
"tracing",
"widestring",
"windows",
"zip",
@ -1705,6 +1878,12 @@ dependencies = [
"serde",
]
[[package]]
name = "valuable"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
[[package]]
name = "vcpkg"
version = "0.2.15"

View file

@ -7,9 +7,15 @@ edition = "2021"
crate-type = ["lib", "staticlib"]
[dependencies]
tokio = { version = "1.21", features = ["rt", "sync", "io-util", "macros"] }
anyhow = "1.0"
tokio = { version = "1.21", features = ["rt", "macros", "sync", "io-util"] }
tun = { version = "0.1", path = "../tun", features = ["serde"] }
clap = { version = "4.3.2", features = ["derive"] }
tracing = "0.1"
tracing-log = "0.1"
tracing-journald = "0.3"
tracing-oslog = {git = "https://github.com/Stormshield-robinc/tracing-oslog"}
tracing-subscriber = "0.3"
env_logger = "0.10"
log = "0.4"
serde = { version = "1", features = ["derive"] }

View file

@ -1,5 +1,8 @@
use tracing::instrument;
// Check capabilities on Linux
#[cfg(target_os = "linux")]
#[instrument]
pub fn ensure_root() {
use caps::{has_cap, CapSet, Capability};
@ -19,6 +22,7 @@ pub fn ensure_root() {
// Check for root user on macOS
#[cfg(target_vendor = "apple")]
#[instrument]
pub fn ensure_root() {
use nix::unistd::Uid;
@ -30,6 +34,7 @@ pub fn ensure_root() {
}
#[cfg(target_family = "windows")]
#[instrument]
pub fn ensure_root() {
todo!()
}

View file

@ -1,3 +1,4 @@
#![deny(missing_debug_implementations)]
pub mod ensureroot;
#[cfg(any(target_os = "linux", target_vendor = "apple"))]

View file

@ -1,8 +1,14 @@
use anyhow::Context;
use std::mem;
#[cfg(any(target_os = "linux", target_vendor = "apple"))]
use std::os::fd::FromRawFd;
use clap::{Args, Parser, Subcommand};
use tracing::instrument;
use tracing_log::LogTracer;
use tracing_oslog::OsLogger;
use tracing_subscriber::{prelude::*, FmtSubscriber};
use tokio::io::Result;
#[cfg(any(target_os = "linux", target_vendor = "apple"))]
use burrow::retrieve;
@ -58,10 +64,21 @@ async fn try_start() -> Result<()> {
}
#[cfg(any(target_os = "linux", target_vendor = "apple"))]
#[instrument]
async fn try_retrieve() -> Result<()> {
LogTracer::init().context("Failed to initialize LogTracer").unwrap();
if cfg!(target_os = "linux") || cfg!(target_vendor = "apple") {
let maybe_layer = system_log().unwrap();
if let Some(layer) = maybe_layer {
let logger = layer.with_subscriber(FmtSubscriber::new());
tracing::subscriber::set_global_default(logger).context("Failed to set the global tracing subscriber").unwrap();
}
}
burrow::ensureroot::ensure_root();
let iface2 = retrieve();
println!("{}", iface2);
tracing::info!("{}", iface2);
Ok(())
}
@ -89,17 +106,17 @@ async fn try_stop() -> Result<()> {
#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<()> {
println!("Platform: {}", std::env::consts::OS);
tracing::info!("Platform: {}", std::env::consts::OS);
let cli = Cli::parse();
match &cli.command {
Commands::Start(..) => {
try_start().await.unwrap();
println!("FINISHED");
tracing::info!("FINISHED");
}
Commands::Retrieve(..) => {
try_retrieve().await.unwrap();
println!("FINISHED");
tracing::info!("FINISHED");
}
Commands::Stop => {
try_stop().await.unwrap();
@ -109,3 +126,20 @@ async fn main() -> Result<()> {
Ok(())
}
#[cfg(target_os = "linux")]
fn system_log() -> anyhow::Result<Option<tracing_journald::Layer>> {
let maybe_journald = tracing_journald::layer();
match maybe_journald {
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
tracing::trace!("journald not found");
Ok(None)
},
_ => Ok(Some(maybe_journald?))
}
}
#[cfg(target_vendor = "apple")]
fn system_log() -> anyhow::Result<Option<OsLogger>> {
Ok(Some(OsLogger::new("com.hackclub.burrow", "default")))
}

View file

@ -10,6 +10,7 @@ nix = { version = "0.26", features = ["ioctl"] }
socket2 = "0.4"
tokio = { version = "1.28", features = [] }
byteorder = "1.4"
tracing = "0.1"
log = "0.4"
serde = { version = "1", features = ["derive"], optional = true }

View file

@ -1,3 +1,5 @@
#![deny(missing_debug_implementations)]
#[cfg(target_os = "windows")]
#[path = "windows/mod.rs"]
mod imp;

View file

@ -1,17 +1,21 @@
use std::io;
use tokio::io::unix::AsyncFd;
use tracing::instrument;
#[derive(Debug)]
pub struct TunInterface {
inner: AsyncFd<crate::TunInterface>,
}
impl TunInterface {
#[instrument]
pub fn new(tun: crate::TunInterface) -> io::Result<Self> {
Ok(Self {
inner: AsyncFd::new(tun)?,
})
}
#[instrument]
pub async fn write(&self, buf: &[u8]) -> io::Result<usize> {
loop {
let mut guard = self.inner.writable().await?;
@ -22,6 +26,7 @@ impl TunInterface {
}
}
#[instrument]
pub async fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
loop {
let mut guard = self.inner.readable_mut().await?;

View file

@ -1,12 +1,13 @@
use byteorder::{ByteOrder, NetworkEndian};
use fehler::throws;
use libc::{c_char, iovec, writev, AF_INET, AF_INET6};
use log::info;
use tracing::info;
use socket2::{Domain, SockAddr, Socket, Type};
use std::io::IoSlice;
use std::net::{Ipv4Addr, SocketAddrV4};
use std::os::fd::{AsRawFd, RawFd};
use std::{io::Error, mem};
use tracing::instrument;
mod kern_control;
mod sys;
@ -23,16 +24,19 @@ pub struct TunInterface {
impl TunInterface {
#[throws]
#[instrument]
pub fn new() -> TunInterface {
Self::new_with_options(TunOptions::new())?
}
#[throws]
#[instrument]
pub fn new_with_options(_: TunOptions) -> TunInterface {
TunInterface::connect(0)?
}
#[throws]
#[instrument]
fn connect(index: u32) -> TunInterface {
use socket2::{Domain, Protocol, Socket, Type};
@ -48,6 +52,7 @@ impl TunInterface {
}
#[throws]
#[instrument]
pub fn name(&self) -> String {
let mut buf = [0 as c_char; sys::IFNAMSIZ];
let mut len = buf.len() as sys::socklen_t;
@ -62,6 +67,7 @@ impl TunInterface {
}
#[throws]
#[instrument]
fn ifreq(&self) -> sys::ifreq {
let mut iff: sys::ifreq = unsafe { mem::zeroed() };
iff.ifr_name = string_to_ifname(&self.name()?);
@ -69,6 +75,7 @@ impl TunInterface {
}
#[throws]
#[instrument]
pub fn set_ipv4_addr(&self, addr: Ipv4Addr) {
let addr = SockAddr::from(SocketAddrV4::new(addr, 0));
let mut iff = self.ifreq()?;
@ -78,6 +85,7 @@ impl TunInterface {
}
#[throws]
#[instrument]
pub fn ipv4_addr(&self) -> Ipv4Addr {
let mut iff = self.ifreq()?;
self.perform(|fd| unsafe { sys::if_get_addr(fd, &mut iff) })?;
@ -87,11 +95,15 @@ impl TunInterface {
#[throws]
fn perform<R>(&self, perform: impl FnOnce(RawFd) -> Result<R, nix::Error>) -> R {
let span = tracing::info_span!("perform", fd = self.as_raw_fd());
let _enter = span.enter();
let socket = Socket::new(Domain::IPV4, Type::DGRAM, None)?;
perform(socket.as_raw_fd())?
}
#[throws]
#[instrument]
pub fn mtu(&self) -> i32 {
let mut iff = self.ifreq()?;
self.perform(|fd| unsafe { sys::if_get_mtu(fd, &mut iff) })?;
@ -101,6 +113,7 @@ impl TunInterface {
}
#[throws]
#[instrument]
pub fn set_mtu(&self, mtu: i32) {
let mut iff = self.ifreq()?;
iff.ifr_ifru.ifru_mtu = mtu;
@ -109,6 +122,7 @@ impl TunInterface {
}
#[throws]
#[instrument]
pub fn netmask(&self) -> Ipv4Addr {
let mut iff = self.ifreq()?;
self.perform(|fd| unsafe { sys::if_get_netmask(fd, &mut iff) })?;
@ -120,6 +134,7 @@ impl TunInterface {
}
#[throws]
#[instrument]
pub fn set_netmask(&self, addr: Ipv4Addr) {
let addr = SockAddr::from(SocketAddrV4::new(addr, 0));
let mut iff = self.ifreq()?;
@ -133,6 +148,7 @@ impl TunInterface {
}
#[throws]
#[instrument]
pub fn send(&self, buf: &[u8]) -> usize {
use std::io::ErrorKind;
let proto = match buf[0] >> 4 {

View file

@ -8,7 +8,7 @@ use std::net::{Ipv4Addr, Ipv6Addr, SocketAddrV4};
use std::os::fd::RawFd;
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd};
use log::info;
use tracing::{info, instrument};
use libc::in6_ifreq;
@ -23,11 +23,13 @@ pub struct TunInterface {
impl TunInterface {
#[throws]
#[instrument]
pub fn new() -> TunInterface {
Self::new_with_options(TunOptions::new())?
}
#[throws]
#[instrument]
pub(crate) fn new_with_options(options: TunOptions) -> TunInterface {
let file = OpenOptions::new()
.read(true)
@ -59,6 +61,7 @@ impl TunInterface {
}
#[throws]
#[instrument]
pub fn name(&self) -> String {
let mut iff = unsafe { mem::zeroed() };
unsafe { sys::tun_get_iff(self.socket.as_raw_fd(), &mut iff)? };
@ -66,6 +69,7 @@ impl TunInterface {
}
#[throws]
#[instrument]
fn ifreq(&self) -> sys::ifreq {
let mut iff: sys::ifreq = unsafe { mem::zeroed() };
iff.ifr_name = string_to_ifname(&self.name()?);
@ -73,6 +77,7 @@ impl TunInterface {
}
#[throws]
#[instrument]
fn in6_ifreq(&self) -> in6_ifreq {
let mut iff: in6_ifreq = unsafe { mem::zeroed() };
iff.ifr6_ifindex = self.index()?;
@ -80,6 +85,7 @@ impl TunInterface {
}
#[throws]
#[instrument]
pub fn index(&self) -> i32 {
let mut iff = self.ifreq()?;
self.perform(|fd| unsafe { sys::if_get_index(fd, &mut iff) })?;
@ -87,6 +93,7 @@ impl TunInterface {
}
#[throws]
#[instrument]
pub fn set_ipv4_addr(&self, addr: Ipv4Addr) {
let addr = SockAddr::from(SocketAddrV4::new(addr, 0));
let mut iff = self.ifreq()?;
@ -96,6 +103,7 @@ impl TunInterface {
}
#[throws]
#[instrument]
pub fn ipv4_addr(&self) -> Ipv4Addr {
let mut iff = self.ifreq()?;
self.perform(|fd| unsafe { sys::if_get_addr(fd, &mut iff) })?;
@ -104,6 +112,7 @@ impl TunInterface {
}
#[throws]
#[instrument]
pub fn set_broadcast_addr(&self, addr: Ipv4Addr) {
let addr = SockAddr::from(SocketAddrV4::new(addr, 0));
let mut iff = self.ifreq()?;
@ -117,6 +126,7 @@ impl TunInterface {
}
#[throws]
#[instrument]
pub fn broadcast_addr(&self) -> Ipv4Addr {
let mut iff = self.ifreq()?;
self.perform(|fd| unsafe { sys::if_get_brdaddr(fd, &mut iff) })?;
@ -126,6 +136,7 @@ impl TunInterface {
}
#[throws]
#[instrument]
pub fn set_ipv6_addr(&self, addr: Ipv6Addr) {
let mut iff = self.in6_ifreq()?;
iff.ifr6_addr.s6_addr = addr.octets();
@ -134,6 +145,7 @@ impl TunInterface {
}
#[throws]
#[instrument]
pub fn set_mtu(&self, mtu: i32) {
let mut iff = self.ifreq()?;
iff.ifr_ifru.ifru_mtu = mtu;
@ -142,6 +154,7 @@ impl TunInterface {
}
#[throws]
#[instrument]
pub fn mtu(&self) -> i32 {
let mut iff = self.ifreq()?;
self.perform(|fd| unsafe { sys::if_get_mtu(fd, &mut iff) })?;
@ -151,6 +164,7 @@ impl TunInterface {
}
#[throws]
#[instrument]
pub fn set_netmask(&self, addr: Ipv4Addr) {
let addr = SockAddr::from(SocketAddrV4::new(addr, 0));
@ -167,6 +181,7 @@ impl TunInterface {
}
#[throws]
#[instrument]
pub fn netmask(&self) -> Ipv4Addr {
let mut iff = self.ifreq()?;
self.perform(|fd| unsafe { sys::if_get_netmask(fd, &mut iff) })?;
@ -179,17 +194,24 @@ impl TunInterface {
#[throws]
fn perform<R>(&self, perform: impl FnOnce(RawFd) -> Result<R, nix::Error>) -> R {
let span = tracing::info_span!("perform");
let _enter = span.enter();
let socket = Socket::new(Domain::IPV4, Type::DGRAM, None)?;
perform(socket.as_raw_fd())?
}
#[throws]
fn perform6<R>(&self, perform: impl FnOnce(RawFd) -> Result<R, nix::Error>) -> R {
let span = tracing::info_span!("perform");
let _enter = span.enter();
let socket = Socket::new(Domain::IPV6, Type::DGRAM, None)?;
perform(socket.as_raw_fd())?
}
#[throws]
#[instrument]
pub fn send(&self, buf: &[u8]) -> usize {
self.socket.send(buf)?
}

View file

@ -2,6 +2,7 @@ use std::{
io::{Error, Read},
os::fd::{AsRawFd, FromRawFd, IntoRawFd, RawFd},
};
use tracing::instrument;
use super::TunOptions;
@ -41,11 +42,13 @@ impl IntoRawFd for TunInterface {
impl TunInterface {
#[throws]
#[instrument]
pub fn recv(&mut self, buf: &mut [u8]) -> usize {
self.socket.read(buf)?
}
}
#[instrument]
pub fn ifname_to_string(buf: [libc::c_char; libc::IFNAMSIZ]) -> String {
// TODO: Switch to `CStr::from_bytes_until_nul` when stabilized
unsafe {
@ -56,6 +59,7 @@ pub fn ifname_to_string(buf: [libc::c_char; libc::IFNAMSIZ]) -> String {
}
}
#[instrument]
pub fn string_to_ifname(name: &str) -> [libc::c_char; libc::IFNAMSIZ] {
let mut buf = [0 as libc::c_char; libc::IFNAMSIZ];
let len = name.len().min(buf.len());

View file

@ -5,15 +5,18 @@ use std::{
mem::MaybeUninit,
os::unix::io::{AsRawFd, IntoRawFd, RawFd},
};
use tracing::instrument;
use crate::TunInterface;
#[derive(Debug)]
pub struct TunQueue {
socket: socket2::Socket,
}
impl TunQueue {
#[throws]
#[instrument]
pub fn recv(&self, buf: &mut [MaybeUninit<u8>]) -> usize {
self.socket.recv(buf)?
}

View file

@ -1,3 +1,4 @@
use std::fmt::Debug;
use fehler::throws;
use std::io::Error;
use std::ptr;
@ -14,6 +15,15 @@ pub struct TunInterface {
name: String,
}
impl Debug for TunInterface {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("TunInterface")
.field("handle", &"SYS_WINTUN_ADAPTER_HANDLE".to_string())
.field("name", &self.name)
.finish()
}
}
impl TunInterface {
#[throws]
pub fn new() -> TunInterface {

View file

@ -1 +1,2 @@
#[derive(Debug)]
pub struct TunQueue;