Skip to content

Commit

Permalink
[antlir2_vm][net] Add option to dump traffic to a pcap file
Browse files Browse the repository at this point in the history
Summary:
Traffic to/from a qemu netdev can be dumped to a pcap file using the
option:
```
-object filter-dump,id=id,netdev=dev[,file=filename][,maxlen=len][,position=head|tail|id=<id>][,insert=behind|before]
Dump the network traffic on netdev dev to the file specified by filename. At most len bytes (64k by default) per packet are stored. The file format is libpcap, so it can be analyzed with tools such as tcpdump or Wireshark.
```

This diff adds the `dump_file` field to `VirtualNic` struct and a setter.
When dump_file is `None`, no traffic is being dumped. When `Some(path)`,
traffic is dumped to `path`.

Test Plan: buck2 test and upcoming diffs

Reviewed By: wujj123456

Differential Revision: D63498734

fbshipit-source-id: bbcdd602550dbd230418229ef8f8688c943b5f30
  • Loading branch information
chantra authored and facebook-github-bot committed Sep 30, 2024
1 parent ee99f03 commit b3d0e2d
Showing 1 changed file with 69 additions and 2 deletions.
71 changes: 69 additions & 2 deletions antlir/antlir2/antlir2_vm/src/net.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use std::ffi::OsString;
use std::net::Ipv6Addr;
use std::ops::Index;
use std::ops::IndexMut;
use std::path::PathBuf;
use std::process::Command;

use thiserror::Error;
Expand All @@ -27,6 +28,8 @@ pub(crate) struct VirtualNIC {
id: usize,
/// Max Combined Channels of the NIC. Multi-queue is disabled when set to 1
max_combined_channels: usize,
/// Dump interface traffic to this file. This is not supported for multi-queue NICs.
dump_file: Option<PathBuf>,
}

#[derive(Error, Debug)]
Expand All @@ -35,6 +38,8 @@ pub(crate) enum VirtualNICError {
IPCmdExecError(#[from] std::io::Error),
#[error("Error from command: `{0}` ")]
IPCmdReturnError(String),
#[error("Traffic is not dumpable: `{0}` ")]
TrafficDumpingNotSupported(String),
}

type Result<T> = std::result::Result<T, VirtualNICError>;
Expand All @@ -46,6 +51,7 @@ impl VirtualNIC {
Self {
id,
max_combined_channels,
dump_file: None,
}
}

Expand Down Expand Up @@ -92,6 +98,18 @@ impl VirtualNIC {
})
}

/// Set the file to dump interface traffic to.
/// Set to None to disable dumping traffic.
pub(crate) fn try_dump_file(&mut self, path: Option<PathBuf>) -> Result<&mut Self> {
if self.max_combined_channels > 1 {
return Err(VirtualNICError::TrafficDumpingNotSupported(
"Can not dump traffic for multi-queue NIC: https://fburl.com/dmblggwc".into(),
));
}
self.dump_file = path;
Ok(self)
}

/// Host side IP address. It's fd00:<id>::1.
fn host_ipv6_addr(&self) -> Ipv6Addr {
let mut ip: u128 = 0xfd00 << (128 - 16);
Expand All @@ -116,7 +134,7 @@ impl VirtualNIC {

impl QemuDevice for VirtualNIC {
fn qemu_args(&self) -> Vec<OsString> {
[
let mut vec: Vec<_> = [
"-netdev",
&format!(
"tap,id={dev_id},ifname={dev_name},script=no,downscript=no,queues={queues}",
Expand All @@ -141,7 +159,24 @@ impl QemuDevice for VirtualNIC {
]
.iter()
.map(|x| x.into())
.collect()
.collect();
if let Some(path) = &self.dump_file {
vec.extend(
[
"-object",
&format!(
"filter-dump,id=dump0,netdev={},file={}",
self.dev_id(),
path.to_string_lossy()
),
]
.iter()
.map(|x| x.into())
.collect::<Vec<_>>(),
);
}

vec
}
}

Expand Down Expand Up @@ -219,6 +254,38 @@ mod test {
)
}

#[test]
fn test_qemu_args_with_dump_file() {
let dump_file = PathBuf::from("/tmp/dump");
let mut nic = VirtualNIC::new(0, 1);
nic.try_dump_file(Some(dump_file.clone())).unwrap();

assert_eq!(
nic.qemu_args().join(OsStr::new(" ")),
format!(
"-netdev tap,id=net0,ifname=vm0,script=no,downscript=no,queues=1 \
-device virtio-net-pci,netdev=net0,mac=00:00:00:00:00:01,mq=off,vectors=4 \
-object filter-dump,id=dump0,netdev=net0,file={}",
dump_file.to_string_lossy()
)
.as_str()
)
}

#[test]
// This test is to make sure that the dump file is not added to the qemu args when it's not supported (multi-queue nics)
fn test_qemu_args_with_dump_file_not_supported() {
let dump_file = PathBuf::from("/tmp/dump");
let mut nic = VirtualNIC::new(0, 2);
assert!(nic.try_dump_file(Some(dump_file.clone())).is_err());

assert_eq!(
nic.qemu_args().join(OsStr::new(" ")),
"-netdev tap,id=net0,ifname=vm0,script=no,downscript=no,queues=2 \
-device virtio-net-pci,netdev=net0,mac=00:00:00:00:00:01,mq=on,vectors=6"
)
}

#[test]
fn test_qemu_multiqueue_args() {
assert_eq!(
Expand Down

0 comments on commit b3d0e2d

Please sign in to comment.