Skip to content

Commit 2846eb3

Browse files
committed
feat(network): support packet capture file creation
Allows creating packet capture files in the pcap format.
1 parent 6ef84d7 commit 2846eb3

4 files changed

Lines changed: 77 additions & 18 deletions

File tree

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ mman = []
5050
mmap = ["mman"] # Deprecated in favor of mman
5151
newlib = []
5252
nostd = []
53+
pcap = ["net", "smoltcp", "virtio-fs"]
5354
pci = ["virtio?/pci"]
5455
rtl8139 = ["net", "pci", "volatile/derive", "endian-num"]
5556
semihosting = ["dep:semihosting"]

src/executor/device.rs

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@ use smoltcp::socket::dns;
1414
use smoltcp::wire::{EthernetAddress, HardwareAddress};
1515
#[cfg(not(feature = "dhcpv4"))]
1616
use smoltcp::wire::{IpCidr, Ipv4Address, Ipv4Cidr};
17+
#[cfg(feature = "pcap")]
18+
use {
19+
crate::fs::File,
20+
embedded_io::Write,
21+
smoltcp::phy::{PcapMode, PcapSink, PcapWriter},
22+
};
1723

1824
use super::network::{NetworkInterface, NetworkState};
1925
use crate::arch;
@@ -42,8 +48,7 @@ impl<'a> NetworkInterface<'a> {
4248
feature = "rtl8139",
4349
feature = "virtio-net",
4450
))] {
45-
#[cfg_attr(feature = "trace", expect(unused_mut))]
46-
let Some(mut device) = NETWORK_DEVICE.lock().take() else {
51+
let Some(device) = NETWORK_DEVICE.lock().take() else {
4752
return NetworkState::InitializationFailed;
4853
};
4954
} else {
@@ -54,8 +59,26 @@ impl<'a> NetworkInterface<'a> {
5459

5560
let mac = device.get_mac_address();
5661

57-
#[cfg(feature = "trace")]
58-
let mut device = Tracer::new(device, |timestamp, printer| trace!("{timestamp} {printer}"));
62+
cfg_if! {
63+
if #[cfg(feature = "trace")] {
64+
let mut device =
65+
Tracer::new(device, |timestamp, printer| trace!("{timestamp} {printer}"));
66+
} else if #[cfg(feature = "pcap")] {
67+
let mut device = PcapWriter::new(
68+
device,
69+
FileSink(
70+
File::create(
71+
format!("/root/{}.pcap", crate::executor::network::now().secs())
72+
.as_str(),
73+
)
74+
.unwrap(),
75+
),
76+
PcapMode::Both,
77+
);
78+
} else {
79+
let mut device = device;
80+
}
81+
}
5982

6083
let ethernet_addr = EthernetAddress(mac);
6184
let hardware_addr = HardwareAddress::Ethernet(ethernet_addr);
@@ -130,3 +153,21 @@ impl<'a> NetworkInterface<'a> {
130153
}))
131154
}
132155
}
156+
157+
#[cfg(feature = "pcap")]
158+
pub(in crate::executor) struct FileSink(File);
159+
160+
#[cfg(feature = "pcap")]
161+
impl PcapSink for FileSink {
162+
fn write(&mut self, data: &[u8]) {
163+
if let Some(err) = self.0.write(data).err() {
164+
error!("Error while writing to the packet capture: {err}");
165+
}
166+
}
167+
168+
fn flush(&mut self) {
169+
if let Some(err) = self.0.flush().err() {
170+
error!("Error while flushing the packet capture: {err}");
171+
}
172+
}
173+
}

src/executor/network.rs

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,10 @@ pub(crate) enum NetworkState<'a> {
4545
feature = "virtio-net",
4646
))]
4747
pub(crate) fn network_handler() {
48-
NIC.lock().as_nic_mut().unwrap().handle_interrupt();
48+
// It is possible for us to receive interrupts before we are done with initializing the network interface.
49+
if let Ok(nic) = NIC.lock().as_nic_mut() {
50+
nic.handle_interrupt();
51+
}
4952
}
5053

5154
impl<'a> NetworkState<'a> {
@@ -63,13 +66,20 @@ static LOCAL_ENDPOINT: AtomicU16 = AtomicU16::new(0);
6366
pub(crate) static NIC: InterruptTicketMutex<NetworkState<'_>> =
6467
InterruptTicketMutex::new(NetworkState::Missing);
6568

69+
cfg_if::cfg_if! {
70+
if #[cfg(feature = "trace")] {
71+
type InterfaceDevice = smoltcp::phy::Tracer<NetworkDevice>;
72+
} else if #[cfg(feature = "pcap")] {
73+
type InterfaceDevice = smoltcp::phy::PcapWriter<NetworkDevice, crate::executor::device::FileSink>;
74+
} else {
75+
type InterfaceDevice = NetworkDevice;
76+
}
77+
}
78+
6679
pub(crate) struct NetworkInterface<'a> {
6780
pub(super) iface: smoltcp::iface::Interface,
6881
pub(super) sockets: SocketSet<'a>,
69-
#[cfg(feature = "trace")]
70-
pub(super) device: smoltcp::phy::Tracer<NetworkDevice>,
71-
#[cfg(not(feature = "trace"))]
72-
pub(super) device: NetworkDevice,
82+
pub(super) device: InterfaceDevice,
7383
#[cfg(feature = "dhcpv4")]
7484
pub(super) dhcp_handle: SocketHandle,
7585
#[cfg(feature = "dns")]
@@ -344,16 +354,22 @@ impl<'a> NetworkInterface<'a> {
344354
feature = "virtio-net",
345355
))]
346356
fn handle_interrupt(&mut self) {
347-
#[cfg(feature = "trace")]
348-
self.device.get_mut().handle_interrupt();
349-
#[cfg(not(feature = "trace"))]
350-
self.device.handle_interrupt();
357+
cfg_if::cfg_if! {
358+
if #[cfg(any(feature = "trace", feature="pcap"))] {
359+
self.device.get_mut().handle_interrupt();
360+
} else {
361+
self.device.handle_interrupt();
362+
}
363+
}
351364
}
352365

353366
pub(crate) fn set_polling_mode(&mut self, value: bool) {
354-
#[cfg(feature = "trace")]
355-
self.device.get_mut().set_polling_mode(value);
356-
#[cfg(not(feature = "trace"))]
357-
self.device.set_polling_mode(value);
367+
cfg_if::cfg_if! {
368+
if #[cfg(any(feature = "trace", feature="pcap"))] {
369+
self.device.get_mut().set_polling_mode(value);
370+
} else {
371+
self.device.set_polling_mode(value);
372+
}
373+
}
358374
}
359375
}

src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,10 +139,11 @@ extern "C" fn initd(_arg: usize) {
139139

140140
// Initialize Drivers
141141
drivers::init();
142+
// The filesystem needs to be initialized before network to allow writing packet captures to a file.
143+
fs::init();
142144
crate::executor::init();
143145

144146
syscalls::init();
145-
fs::init();
146147
#[cfg(feature = "shell")]
147148
shell::init();
148149

0 commit comments

Comments
 (0)