1use crate::error::{DpdkError, DpdkResult};
7use crate::mbuf::{Mbuf, Mempool};
8use std::ptr;
9
10pub const DEFAULT_RX_DESC: u16 = 1024;
12pub const DEFAULT_TX_DESC: u16 = 1024;
13
14pub const DEFAULT_BURST_SIZE: u16 = 32;
16
17#[derive(Debug, Clone, Copy, Default)]
19pub struct RxOffload {
20 pub vlan_strip: bool,
22 pub ipv4_cksum: bool,
24 pub udp_cksum: bool,
26 pub tcp_cksum: bool,
28}
29
30impl RxOffload {
31 pub fn to_flags(&self) -> u64 {
33 let mut flags = 0u64;
34 if self.vlan_strip {
35 flags |= dpdk_sys::RTE_ETH_RX_OFFLOAD_VLAN_STRIP;
36 }
37 if self.ipv4_cksum {
38 flags |= dpdk_sys::RTE_ETH_RX_OFFLOAD_IPV4_CKSUM;
39 }
40 if self.udp_cksum {
41 flags |= dpdk_sys::RTE_ETH_RX_OFFLOAD_UDP_CKSUM;
42 }
43 if self.tcp_cksum {
44 flags |= dpdk_sys::RTE_ETH_RX_OFFLOAD_TCP_CKSUM;
45 }
46 flags
47 }
48}
49
50#[derive(Debug, Clone, Copy, Default)]
52pub struct TxOffload {
53 pub vlan_insert: bool,
55 pub ipv4_cksum: bool,
57 pub udp_cksum: bool,
59 pub tcp_cksum: bool,
61}
62
63impl TxOffload {
64 pub fn to_flags(&self) -> u64 {
66 let mut flags = 0u64;
67 if self.vlan_insert {
68 flags |= dpdk_sys::RTE_ETH_TX_OFFLOAD_VLAN_INSERT;
69 }
70 if self.ipv4_cksum {
71 flags |= dpdk_sys::RTE_ETH_TX_OFFLOAD_IPV4_CKSUM;
72 }
73 if self.udp_cksum {
74 flags |= dpdk_sys::RTE_ETH_TX_OFFLOAD_UDP_CKSUM;
75 }
76 if self.tcp_cksum {
77 flags |= dpdk_sys::RTE_ETH_TX_OFFLOAD_TCP_CKSUM;
78 }
79 flags
80 }
81}
82
83#[derive(Debug, Clone)]
85pub struct PortConfig {
86 pub nb_rx_queues: u16,
88 pub nb_tx_queues: u16,
90 pub nb_rx_desc: u16,
92 pub nb_tx_desc: u16,
94 pub promiscuous: bool,
96 pub mtu: u32,
98 pub rx_offload: RxOffload,
100 pub tx_offload: TxOffload,
102}
103
104impl Default for PortConfig {
105 fn default() -> Self {
106 Self {
107 nb_rx_queues: 1,
108 nb_tx_queues: 1,
109 nb_rx_desc: DEFAULT_RX_DESC,
110 nb_tx_desc: DEFAULT_TX_DESC,
111 promiscuous: true,
112 mtu: 0,
113 rx_offload: RxOffload::default(),
114 tx_offload: TxOffload::default(),
115 }
116 }
117}
118
119impl PortConfig {
120 pub fn new() -> Self {
122 Self::default()
123 }
124
125 pub fn with_queues(mut self, rx: u16, tx: u16) -> Self {
127 self.nb_rx_queues = rx;
128 self.nb_tx_queues = tx;
129 self
130 }
131
132 pub fn with_descriptors(mut self, rx: u16, tx: u16) -> Self {
134 self.nb_rx_desc = rx;
135 self.nb_tx_desc = tx;
136 self
137 }
138
139 pub fn with_promiscuous(mut self, enable: bool) -> Self {
141 self.promiscuous = enable;
142 self
143 }
144
145 pub fn with_mtu(mut self, mtu: u32) -> Self {
147 self.mtu = mtu;
148 self
149 }
150
151 pub fn with_rx_offload(mut self, offload: RxOffload) -> Self {
153 self.rx_offload = offload;
154 self
155 }
156
157 pub fn with_tx_offload(mut self, offload: TxOffload) -> Self {
159 self.tx_offload = offload;
160 self
161 }
162
163 pub fn with_checksum_offload(mut self) -> Self {
165 self.rx_offload = RxOffload {
166 ipv4_cksum: true,
167 udp_cksum: true,
168 tcp_cksum: true,
169 ..Default::default()
170 };
171 self.tx_offload = TxOffload {
172 ipv4_cksum: true,
173 udp_cksum: true,
174 tcp_cksum: true,
175 ..Default::default()
176 };
177 self
178 }
179}
180
181#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
183pub struct MacAddress(pub [u8; 6]);
184
185impl MacAddress {
186 pub fn new(bytes: [u8; 6]) -> Self {
188 Self(bytes)
189 }
190
191 pub fn broadcast() -> Self {
193 Self([0xff; 6])
194 }
195
196 pub fn zero() -> Self {
198 Self([0; 6])
199 }
200
201 pub fn as_bytes(&self) -> &[u8; 6] {
203 &self.0
204 }
205
206 pub fn octets(&self) -> [u8; 6] {
208 self.0
209 }
210
211 pub fn is_broadcast(&self) -> bool {
213 self.0 == [0xff; 6]
214 }
215
216 pub fn is_multicast(&self) -> bool {
218 (self.0[0] & 0x01) != 0
219 }
220
221 pub fn is_zero(&self) -> bool {
223 self.0 == [0; 6]
224 }
225}
226
227impl std::fmt::Display for MacAddress {
228 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
229 write!(
230 f,
231 "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
232 self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5]
233 )
234 }
235}
236
237#[derive(Debug, Clone, Copy, Default)]
239pub struct LinkStatus {
240 pub speed: u32,
242 pub full_duplex: bool,
244 pub autoneg: bool,
246 pub link_up: bool,
248}
249
250#[derive(Debug, Clone, Copy, Default)]
252pub struct PortStats {
253 pub rx_packets: u64,
255 pub tx_packets: u64,
257 pub rx_bytes: u64,
259 pub tx_bytes: u64,
261 pub rx_missed: u64,
263 pub rx_errors: u64,
265 pub tx_errors: u64,
267}
268
269#[derive(Debug, Clone, Copy, Default)]
271pub struct DeviceCapabilities {
272 pub rx_offload_capa: u64,
274 pub tx_offload_capa: u64,
276 pub max_rx_queues: u16,
278 pub max_tx_queues: u16,
280}
281
282impl DeviceCapabilities {
283 pub fn supports_rx_ipv4_cksum(&self) -> bool {
285 (self.rx_offload_capa & dpdk_sys::RTE_ETH_RX_OFFLOAD_IPV4_CKSUM) != 0
286 }
287
288 pub fn supports_rx_udp_cksum(&self) -> bool {
290 (self.rx_offload_capa & dpdk_sys::RTE_ETH_RX_OFFLOAD_UDP_CKSUM) != 0
291 }
292
293 pub fn supports_tx_ipv4_cksum(&self) -> bool {
295 (self.tx_offload_capa & dpdk_sys::RTE_ETH_TX_OFFLOAD_IPV4_CKSUM) != 0
296 }
297
298 pub fn supports_tx_udp_cksum(&self) -> bool {
300 (self.tx_offload_capa & dpdk_sys::RTE_ETH_TX_OFFLOAD_UDP_CKSUM) != 0
301 }
302
303 pub fn supports_vlan_strip(&self) -> bool {
305 (self.rx_offload_capa & dpdk_sys::RTE_ETH_RX_OFFLOAD_VLAN_STRIP) != 0
306 }
307
308 pub fn supports_vlan_insert(&self) -> bool {
310 (self.tx_offload_capa & dpdk_sys::RTE_ETH_TX_OFFLOAD_VLAN_INSERT) != 0
311 }
312}
313
314pub struct Port {
316 port_id: u16,
317 config: PortConfig,
318 started: bool,
319 mac_address: MacAddress,
320 capabilities: DeviceCapabilities,
322 active_rx_offload: u64,
324 active_tx_offload: u64,
325}
326
327impl Port {
328 pub fn count_available() -> u16 {
330 unsafe { dpdk_sys::rte_eth_dev_count_avail() }
331 }
332
333 pub fn is_valid(port_id: u16) -> bool {
335 port_id < Self::count_available()
336 }
337
338 pub fn init(port_id: u16, config: PortConfig, mempool: &Mempool) -> DpdkResult<Self> {
346 if !Self::is_valid(port_id) {
348 return Err(DpdkError::InvalidPortId(port_id));
349 }
350
351 let mut dev_info = dpdk_sys::rte_eth_dev_info::default();
353 let ret = unsafe { dpdk_sys::rte_eth_dev_info_get(port_id, &mut dev_info) };
354 if ret != 0 {
355 return Err(DpdkError::PortConfigFailed(ret));
356 }
357
358 let capabilities = DeviceCapabilities {
360 rx_offload_capa: dev_info.rx_offload_capa,
361 tx_offload_capa: dev_info.tx_offload_capa,
362 max_rx_queues: dev_info.max_rx_queues,
363 max_tx_queues: dev_info.max_tx_queues,
364 };
365
366 let nb_rx_queues = config.nb_rx_queues.min(dev_info.max_rx_queues);
368 let nb_tx_queues = config.nb_tx_queues.min(dev_info.max_tx_queues);
369
370 let requested_rx_offload = config.rx_offload.to_flags();
372 let requested_tx_offload = config.tx_offload.to_flags();
373 let active_rx_offload = requested_rx_offload & dev_info.rx_offload_capa;
374 let active_tx_offload = requested_tx_offload & dev_info.tx_offload_capa;
375
376 if active_rx_offload != requested_rx_offload {
378 let unsupported = requested_rx_offload & !dev_info.rx_offload_capa;
379 eprintln!("Warning: Some RX offloads not supported by device (flags: 0x{:x})", unsupported);
380 }
381 if active_tx_offload != requested_tx_offload {
382 let unsupported = requested_tx_offload & !dev_info.tx_offload_capa;
383 eprintln!("Warning: Some TX offloads not supported by device (flags: 0x{:x})", unsupported);
384 }
385
386 let eth_conf = dpdk_sys::rte_eth_conf {
388 rxmode: dpdk_sys::rte_eth_rxmode {
389 mtu: config.mtu,
390 offloads: active_rx_offload,
391 ..Default::default()
392 },
393 txmode: dpdk_sys::rte_eth_txmode {
394 offloads: active_tx_offload,
395 ..Default::default()
396 },
397 ..Default::default()
398 };
399
400 let ret = unsafe {
401 dpdk_sys::rte_eth_dev_configure(port_id, nb_rx_queues, nb_tx_queues, ð_conf)
402 };
403 if ret != 0 {
404 return Err(DpdkError::PortConfigFailed(ret));
405 }
406
407 let socket_id = unsafe { dpdk_sys::rte_socket_id() };
409
410 for queue_id in 0..nb_rx_queues {
412 let ret = unsafe {
413 dpdk_sys::rte_eth_rx_queue_setup(
414 port_id,
415 queue_id,
416 config.nb_rx_desc,
417 socket_id as u32,
418 ptr::null(),
419 mempool.as_raw(),
420 )
421 };
422 if ret != 0 {
423 return Err(DpdkError::PortConfigFailed(ret));
424 }
425 }
426
427 for queue_id in 0..nb_tx_queues {
429 let ret = unsafe {
430 dpdk_sys::rte_eth_tx_queue_setup(
431 port_id,
432 queue_id,
433 config.nb_tx_desc,
434 socket_id as u32,
435 ptr::null(),
436 )
437 };
438 if ret != 0 {
439 return Err(DpdkError::PortConfigFailed(ret));
440 }
441 }
442
443 let mut mac_addr = dpdk_sys::rte_ether_addr::default();
445 let ret = unsafe { dpdk_sys::rte_eth_macaddr_get(port_id, &mut mac_addr) };
446 if ret != 0 {
447 return Err(DpdkError::PortConfigFailed(ret));
448 }
449
450 let mac_address = MacAddress::new(mac_addr.addr_bytes);
451
452 Ok(Self {
453 port_id,
454 config: PortConfig {
455 nb_rx_queues,
456 nb_tx_queues,
457 ..config
458 },
459 started: false,
460 mac_address,
461 capabilities,
462 active_rx_offload,
463 active_tx_offload,
464 })
465 }
466
467 pub fn new(port_id: u16) -> DpdkResult<Self> {
469 if !Self::is_valid(port_id) {
470 return Err(DpdkError::InvalidPortId(port_id));
471 }
472
473 Ok(Self {
474 port_id,
475 config: PortConfig::default(),
476 started: false,
477 mac_address: MacAddress::default(),
478 capabilities: DeviceCapabilities::default(),
479 active_rx_offload: 0,
480 active_tx_offload: 0,
481 })
482 }
483
484 pub fn capabilities(&self) -> &DeviceCapabilities {
486 &self.capabilities
487 }
488
489 pub fn numa_node(&self) -> i32 {
495 unsafe { dpdk_sys::rte_eth_dev_socket_id(self.port_id) }
496 }
497
498 pub fn active_rx_offload(&self) -> u64 {
500 self.active_rx_offload
501 }
502
503 pub fn active_tx_offload(&self) -> u64 {
505 self.active_tx_offload
506 }
507
508 pub fn is_rx_offload_active(&self, offload: u64) -> bool {
510 (self.active_rx_offload & offload) != 0
511 }
512
513 pub fn is_tx_offload_active(&self, offload: u64) -> bool {
515 (self.active_tx_offload & offload) != 0
516 }
517
518 pub fn port_id(&self) -> u16 {
520 self.port_id
521 }
522
523 pub fn mac_address(&self) -> MacAddress {
525 self.mac_address
526 }
527
528 pub fn config(&self) -> &PortConfig {
530 &self.config
531 }
532
533 pub fn is_started(&self) -> bool {
535 self.started
536 }
537
538 pub fn start(&mut self) -> DpdkResult<()> {
540 if self.started {
541 return Ok(());
542 }
543
544 let ret = unsafe { dpdk_sys::rte_eth_dev_start(self.port_id) };
545 if ret != 0 {
546 return Err(DpdkError::PortConfigFailed(ret));
547 }
548
549 if self.config.promiscuous {
551 unsafe {
552 dpdk_sys::rte_eth_promiscuous_enable(self.port_id);
553 }
554 }
555
556 self.started = true;
557 Ok(())
558 }
559
560 pub fn stop(&mut self) -> DpdkResult<()> {
562 if !self.started {
563 return Ok(());
564 }
565
566 let ret = unsafe { dpdk_sys::rte_eth_dev_stop(self.port_id) };
567 if ret != 0 {
568 return Err(DpdkError::PortConfigFailed(ret));
569 }
570
571 self.started = false;
572 Ok(())
573 }
574
575 pub fn link_status(&self) -> LinkStatus {
577 let mut link = dpdk_sys::rte_eth_link::default();
578 unsafe {
579 dpdk_sys::rte_eth_link_get_nowait(self.port_id, &mut link);
580 }
581
582 LinkStatus {
583 speed: link.link_speed,
584 full_duplex: link.link_duplex() != 0,
585 autoneg: link.link_autoneg() != 0,
586 link_up: link.link_status() != 0,
587 }
588 }
589
590 pub fn stats(&self) -> DpdkResult<PortStats> {
592 let mut stats = dpdk_sys::rte_eth_stats::default();
593 let ret = unsafe { dpdk_sys::rte_eth_stats_get(self.port_id, &mut stats) };
594 if ret != 0 {
595 return Err(DpdkError::PortConfigFailed(ret));
596 }
597
598 Ok(PortStats {
599 rx_packets: stats.ipackets,
600 tx_packets: stats.opackets,
601 rx_bytes: stats.ibytes,
602 tx_bytes: stats.obytes,
603 rx_missed: stats.imissed,
604 rx_errors: stats.ierrors,
605 tx_errors: stats.oerrors,
606 })
607 }
608
609 pub fn reset_stats(&self) -> DpdkResult<()> {
611 let ret = unsafe { dpdk_sys::rte_eth_stats_reset(self.port_id) };
612 if ret != 0 {
613 return Err(DpdkError::PortConfigFailed(ret));
614 }
615 Ok(())
616 }
617
618 pub fn rx_burst(&self, queue_id: u16, max_packets: u16) -> DpdkResult<Vec<Mbuf>> {
623 if !self.started {
624 return Ok(Vec::new());
625 }
626
627 let mut rx_pkts: Vec<*mut dpdk_sys::rte_mbuf> = vec![ptr::null_mut(); max_packets as usize];
628
629 let nb_rx = unsafe {
630 dpdk_sys::rte_eth_rx_burst(
631 self.port_id,
632 queue_id,
633 rx_pkts.as_mut_ptr(),
634 max_packets,
635 )
636 };
637
638 let mut mbufs = Vec::with_capacity(nb_rx as usize);
639 for i in 0..nb_rx as usize {
640 if let Some(mbuf) = unsafe { Mbuf::from_raw(rx_pkts[i]) } {
641 mbufs.push(mbuf);
642 }
643 }
644
645 Ok(mbufs)
646 }
647
648 pub fn tx_burst(&self, queue_id: u16, packets: &mut Vec<Mbuf>) -> DpdkResult<u16> {
653 if !self.started || packets.is_empty() {
654 return Ok(0);
655 }
656
657 let mut tx_pkts: Vec<*mut dpdk_sys::rte_mbuf> = packets
659 .iter()
660 .map(|m| m.as_raw())
661 .collect();
662
663 let nb_tx = unsafe {
664 dpdk_sys::rte_eth_tx_burst(
665 self.port_id,
666 queue_id,
667 tx_pkts.as_mut_ptr(),
668 tx_pkts.len() as u16,
669 )
670 };
671
672 for mbuf in packets.drain(..nb_tx as usize) {
675 std::mem::forget(mbuf);
676 }
677
678 Ok(nb_tx)
679 }
680
681 pub fn set_promiscuous(&mut self, enable: bool) -> DpdkResult<()> {
690 let ret = if enable {
691 unsafe { dpdk_sys::rte_eth_promiscuous_enable(self.port_id) }
692 } else {
693 unsafe { dpdk_sys::rte_eth_promiscuous_disable(self.port_id) }
694 };
695
696 if ret != 0 {
697 return Err(DpdkError::PortConfigFailed(ret));
698 }
699
700 self.config.promiscuous = enable;
701 Ok(())
702 }
703
704 pub fn is_promiscuous(&self) -> bool {
706 unsafe { dpdk_sys::rte_eth_promiscuous_get(self.port_id) != 0 }
707 }
708
709 pub fn set_allmulticast(&self, enable: bool) -> DpdkResult<()> {
718 let ret = if enable {
719 unsafe { dpdk_sys::rte_eth_allmulticast_enable(self.port_id) }
720 } else {
721 unsafe { dpdk_sys::rte_eth_allmulticast_disable(self.port_id) }
722 };
723
724 if ret != 0 {
725 return Err(DpdkError::PortConfigFailed(ret));
726 }
727 Ok(())
728 }
729
730 pub fn is_allmulticast(&self) -> bool {
732 unsafe { dpdk_sys::rte_eth_allmulticast_get(self.port_id) != 0 }
733 }
734
735 pub fn set_multicast_addrs(&self, addrs: &[MacAddress]) -> DpdkResult<()> {
744 if addrs.is_empty() {
745 let ret = unsafe {
747 dpdk_sys::rte_eth_dev_set_mc_addr_list(
748 self.port_id,
749 ptr::null_mut(),
750 0,
751 )
752 };
753 if ret != 0 {
754 return Err(DpdkError::PortConfigFailed(ret));
755 }
756 return Ok(());
757 }
758
759 let mut mc_addrs: Vec<dpdk_sys::rte_ether_addr> = addrs
761 .iter()
762 .map(|mac| dpdk_sys::rte_ether_addr {
763 addr_bytes: mac.octets(),
764 })
765 .collect();
766
767 let ret = unsafe {
768 dpdk_sys::rte_eth_dev_set_mc_addr_list(
769 self.port_id,
770 mc_addrs.as_mut_ptr(),
771 mc_addrs.len() as u32,
772 )
773 };
774
775 if ret != 0 {
776 return Err(DpdkError::PortConfigFailed(ret));
777 }
778 Ok(())
779 }
780}
781
782impl Drop for Port {
783 fn drop(&mut self) {
784 if self.started {
785 let _ = self.stop();
786 }
787 unsafe {
788 dpdk_sys::rte_eth_dev_close(self.port_id);
789 }
790 }
791}
792
793#[cfg(test)]
798mod tests {
799 use super::*;
800
801 #[test]
802 fn test_mac_address_display() {
803 let mac = MacAddress::new([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]);
804 assert_eq!(mac.to_string(), "02:00:00:00:00:01");
805 }
806
807 #[test]
808 fn test_mac_address_broadcast() {
809 let broadcast = MacAddress::new([0xff, 0xff, 0xff, 0xff, 0xff, 0xff]);
810 assert!(broadcast.is_broadcast());
811 assert!(broadcast.is_multicast());
812
813 let unicast = MacAddress::new([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]);
814 assert!(!unicast.is_broadcast());
815 assert!(!unicast.is_multicast());
816 }
817
818 #[test]
819 fn test_mac_address_multicast() {
820 let multicast = MacAddress::new([0x01, 0x00, 0x5e, 0x00, 0x00, 0x01]);
822 assert!(multicast.is_multicast());
823 assert!(!multicast.is_broadcast());
824 }
825
826 #[test]
827 fn test_mac_address_zero() {
828 let zero = MacAddress::default();
829 assert!(zero.is_zero());
830
831 let non_zero = MacAddress::new([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]);
832 assert!(!non_zero.is_zero());
833 }
834
835 #[test]
836 fn test_port_config_default() {
837 let config = PortConfig::default();
838 assert_eq!(config.nb_rx_queues, 1);
839 assert_eq!(config.nb_tx_queues, 1);
840 assert_eq!(config.nb_rx_desc, DEFAULT_RX_DESC);
841 assert_eq!(config.nb_tx_desc, DEFAULT_TX_DESC);
842 assert!(config.promiscuous);
843 }
844
845 #[test]
846 fn test_link_status_default() {
847 let status = LinkStatus::default();
848 assert_eq!(status.speed, 0);
849 assert!(!status.full_duplex);
850 assert!(!status.link_up);
851 }
852
853 #[test]
854 fn test_port_stats_default() {
855 let stats = PortStats::default();
856 assert_eq!(stats.rx_packets, 0);
857 assert_eq!(stats.tx_packets, 0);
858 assert_eq!(stats.rx_bytes, 0);
859 assert_eq!(stats.tx_bytes, 0);
860 }
861
862 #[test]
863 fn test_port_count_available() {
864 let count = Port::count_available();
866 assert!(count >= 1);
867 }
868
869 #[test]
870 fn test_port_is_valid() {
871 assert!(Port::is_valid(0));
873 assert!(!Port::is_valid(100));
875 }
876
877 #[test]
878 fn test_port_new() {
879 let port = Port::new(0);
880 assert!(port.is_ok());
881 let port = port.unwrap();
882 assert_eq!(port.port_id(), 0);
883 assert!(!port.is_started());
884 }
885
886 #[test]
887 fn test_port_new_invalid() {
888 let port = Port::new(100);
889 assert!(port.is_err());
890 match port {
891 Err(DpdkError::InvalidPortId(id)) => assert_eq!(id, 100),
892 _ => panic!("Expected InvalidPortId error"),
893 }
894 }
895
896 #[test]
897 fn test_port_start_stop() {
898 let mut port = Port::new(0).unwrap();
899
900 assert!(port.start().is_ok());
902 assert!(port.is_started());
903
904 assert!(port.start().is_ok());
906
907 assert!(port.stop().is_ok());
909 assert!(!port.is_started());
910
911 assert!(port.stop().is_ok());
913 }
914
915 #[test]
916 fn test_port_link_status() {
917 let port = Port::new(0).unwrap();
918 let status = port.link_status();
919 assert!(status.link_up);
921 assert_eq!(status.speed, 10000);
922 }
923
924 #[test]
925 fn test_port_stats() {
926 let port = Port::new(0).unwrap();
927 let stats = port.stats();
928 assert!(stats.is_ok());
929 let stats = stats.unwrap();
930 assert_eq!(stats.rx_packets, 0);
931 assert_eq!(stats.tx_packets, 0);
932 }
933
934 #[test]
935 fn test_port_reset_stats() {
936 let port = Port::new(0).unwrap();
937 assert!(port.reset_stats().is_ok());
938 }
939
940 #[test]
941 fn test_port_rx_burst_not_started() {
942 let port = Port::new(0).unwrap();
943 let packets = port.rx_burst(0, 32);
944 assert!(packets.is_ok());
945 assert!(packets.unwrap().is_empty()); }
947
948 #[test]
949 fn test_port_tx_burst_not_started() {
950 let port = Port::new(0).unwrap();
951 let mut packets: Vec<Mbuf> = Vec::new();
952 let sent = port.tx_burst(0, &mut packets);
953 assert!(sent.is_ok());
954 assert_eq!(sent.unwrap(), 0); }
956}