@@ -6,6 +6,8 @@ use smoltcp::iface::{Config, Interface, SocketSet};
66#[ cfg( feature = "net-trace" ) ]
77use smoltcp:: phy:: Tracer ;
88use smoltcp:: phy:: { Device , Medium } ;
9+ #[ cfg( feature = "write-pcap-file" ) ]
10+ use smoltcp:: phy:: { PcapMode , PcapWriter } ;
911#[ cfg( feature = "dhcpv4" ) ]
1012use smoltcp:: socket:: dhcpv4;
1113#[ cfg( all( feature = "dns" , not( feature = "dhcpv4" ) ) ) ]
@@ -16,6 +18,8 @@ use smoltcp::wire::{IpCidr, Ipv4Address, Ipv4Cidr};
1618
1719use super :: network:: { NetworkInterface , NetworkState } ;
1820use crate :: arch;
21+ #[ cfg( feature = "write-pcap-file" ) ]
22+ use crate :: drivers:: Driver ;
1923use crate :: drivers:: net:: NetworkDriver ;
2024
2125cfg_select ! {
@@ -42,19 +46,30 @@ impl<'a> NetworkInterface<'a> {
4246 feature = "rtl8139" ,
4347 feature = "virtio-net" ,
4448 ) => {
45- #[ cfg_attr( feature = "net-trace" , expect( unused_mut) ) ]
49+ #[ cfg_attr( any ( feature = "net-trace" , feature = "write-pcap-file" ) , expect( unused_mut) ) ]
4650 let Some ( mut device) = NETWORK_DEVICE . lock( ) . take( ) else {
4751 return NetworkState :: InitializationFailed ;
4852 } ;
4953 }
5054 _ => {
51- #[ cfg_attr( feature = "net-trace" , expect( unused_mut) ) ]
55+ #[ cfg_attr( any ( feature = "net-trace" , feature = "write-pcap-file" ) , expect( unused_mut) ) ]
5256 let mut device = LoopbackDriver :: new( ) ;
5357 }
5458 }
5559
5660 let mac = device. get_mac_address ( ) ;
5761
62+ #[ cfg_attr( feature = "net-trace" , expect( unused_mut) ) ]
63+ #[ cfg( feature = "write-pcap-file" ) ]
64+ let mut device = {
65+ let default_name = device. get_name ( ) ;
66+ PcapWriter :: new (
67+ device,
68+ pcap_writer:: FileSink :: new ( default_name) ,
69+ PcapMode :: Both ,
70+ )
71+ } ;
72+
5873 #[ cfg( feature = "net-trace" ) ]
5974 let mut device = Tracer :: new ( device, |timestamp, printer| trace ! ( "{timestamp} {printer}" ) ) ;
6075
@@ -132,3 +147,71 @@ impl<'a> NetworkInterface<'a> {
132147 } ) )
133148 }
134149}
150+
151+ #[ cfg( feature = "write-pcap-file" ) ]
152+ pub ( in crate :: executor) mod pcap_writer {
153+ use embedded_io:: Write ;
154+ use smoltcp:: phy:: PcapSink ;
155+
156+ use crate :: errno:: Errno ;
157+ use crate :: fs:: File ;
158+
159+ /// Sink for packet captures. If the file Option is None, the writes are ignored.
160+ /// This is useful when we fail to create the sink file at runtime.
161+ pub struct FileSink ( Option < File > ) ;
162+
163+ impl FileSink {
164+ pub ( super ) fn new ( default_name : & str ) -> Self {
165+ let file_name_base = option_env ! ( "HERMIT_PCAP_NAME" ) . unwrap_or ( default_name) ;
166+ let mut file_name = format ! ( "{file_name_base}.pcap" ) ;
167+ for i in 1 .. {
168+ let path = format ! ( "/root/{file_name}" ) ;
169+ match File :: create_new ( path. as_str ( ) ) {
170+ Ok ( file) => {
171+ info ! (
172+ "The packet capture will be written to a file called \" {file_name}\" under the mount point."
173+ ) ;
174+ return Self ( Some ( file) ) ;
175+ }
176+ Err ( Errno :: Exist ) => {
177+ file_name = format ! ( "{file_name_base} ({i}).pcap" ) ;
178+ }
179+ Err ( e) => {
180+ if e == Errno :: Noent {
181+ error ! ( "/root is not mounted. Are there any mount points for the VM?" ) ;
182+ }
183+ error ! (
184+ "Error {e:?} encountered while creating the pcap file. No pcap file will be written."
185+ ) ;
186+ break ;
187+ }
188+ }
189+ }
190+ Self ( None )
191+ }
192+ }
193+
194+ impl PcapSink for FileSink {
195+ fn write ( & mut self , data : & [ u8 ] ) {
196+ let Some ( file) = self . 0 . as_mut ( ) else {
197+ trace ! ( "No file to write packet capture." ) ;
198+ return ;
199+ } ;
200+
201+ if let Err ( err) = file. write ( data) {
202+ error ! ( "Error while writing to the pcap file: {err}" ) ;
203+ }
204+ }
205+
206+ fn flush ( & mut self ) {
207+ let Some ( file) = self . 0 . as_mut ( ) else {
208+ trace ! ( "No file to write packet capture." ) ;
209+ return ;
210+ } ;
211+
212+ if let Err ( err) = file. flush ( ) {
213+ error ! ( "Error while flushing the pcap file: {err}" ) ;
214+ }
215+ }
216+ }
217+ }
0 commit comments