1use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr};
9use std::sync::Arc;
10
11use smoltcp::iface::{Config, Interface, SocketSet};
12use smoltcp::time::Instant;
13use std::sync::atomic::Ordering;
14
15use smoltcp::wire::{
16 EthernetAddress, EthernetFrame, EthernetProtocol, HardwareAddress, Icmpv4Packet, Icmpv4Repr,
17 Icmpv6Packet, Icmpv6Repr, IpAddress, IpCidr, IpProtocol, Ipv4Packet, Ipv4Repr, Ipv6Packet,
18 Ipv6Repr, TcpPacket, UdpPacket,
19};
20
21use crate::config::{DnsConfig, PublishedPort};
22use crate::conn::ConnectionTracker;
23use crate::device::SmoltcpDevice;
24use crate::dns::interceptor::DnsInterceptor;
25use crate::icmp_relay::IcmpRelay;
26use crate::policy::{NetworkPolicy, Protocol};
27use crate::proxy;
28use crate::publisher::PortPublisher;
29use crate::shared::SharedState;
30use crate::tls::{proxy as tls_proxy, state::TlsState};
31use crate::udp_relay::UdpRelay;
32
33pub enum FrameAction {
44 TcpSyn { src: SocketAddr, dst: SocketAddr },
47
48 UdpRelay { src: SocketAddr, dst: SocketAddr },
51
52 Dns,
54
55 Passthrough,
58}
59
60pub struct PollLoopConfig {
63 pub gateway_mac: [u8; 6],
65 pub guest_mac: [u8; 6],
67 pub gateway_ipv4: Ipv4Addr,
69 pub guest_ipv4: Ipv4Addr,
71 pub gateway_ipv6: Ipv6Addr,
73 pub mtu: usize,
75}
76
77pub fn classify_frame(frame: &[u8]) -> FrameAction {
87 let Ok(eth) = EthernetFrame::new_checked(frame) else {
88 return FrameAction::Passthrough;
89 };
90
91 match eth.ethertype() {
92 EthernetProtocol::Ipv4 => classify_ipv4(eth.payload()),
93 EthernetProtocol::Ipv6 => classify_ipv6(eth.payload()),
94 _ => FrameAction::Passthrough, }
96}
97
98pub fn create_interface(device: &mut SmoltcpDevice, config: &PollLoopConfig) -> Interface {
105 let hw_addr = HardwareAddress::Ethernet(EthernetAddress(config.gateway_mac));
106 let iface_config = Config::new(hw_addr);
107 let mut iface = Interface::new(iface_config, device, smoltcp_now());
108
109 iface.update_ip_addrs(|addrs| {
111 addrs
112 .push(IpCidr::new(
113 IpAddress::Ipv4(config.gateway_ipv4),
114 30,
116 ))
117 .expect("failed to add gateway IPv4 address");
118 addrs
119 .push(IpCidr::new(IpAddress::Ipv6(config.gateway_ipv6), 64))
120 .expect("failed to add gateway IPv6 address");
121 });
122
123 iface
125 .routes_mut()
126 .add_default_ipv4_route(config.gateway_ipv4)
127 .expect("failed to add default IPv4 route");
128 iface
129 .routes_mut()
130 .add_default_ipv6_route(config.gateway_ipv6)
131 .expect("failed to add default IPv6 route");
132
133 iface.set_any_ip(true);
135
136 iface
137}
138
139#[allow(clippy::too_many_arguments)]
153pub fn smoltcp_poll_loop(
154 shared: Arc<SharedState>,
155 config: PollLoopConfig,
156 network_policy: NetworkPolicy,
157 dns_config: DnsConfig,
158 tls_state: Option<Arc<TlsState>>,
159 published_ports: Vec<PublishedPort>,
160 max_connections: Option<usize>,
161 tokio_handle: tokio::runtime::Handle,
162) {
163 let mut device = SmoltcpDevice::new(shared.clone(), config.mtu);
164 let mut iface = create_interface(&mut device, &config);
165 let mut sockets = SocketSet::new(vec![]);
166 let mut conn_tracker = ConnectionTracker::new(max_connections);
167
168 let mut dns_interceptor =
169 DnsInterceptor::new(&mut sockets, dns_config, shared.clone(), &tokio_handle);
170 let mut port_publisher = PortPublisher::new(&published_ports, config.guest_ipv4, &tokio_handle);
171 let mut udp_relay = UdpRelay::new(
172 shared.clone(),
173 config.gateway_mac,
174 config.guest_mac,
175 tokio_handle.clone(),
176 );
177 let icmp_relay = IcmpRelay::new(
178 shared.clone(),
179 config.gateway_mac,
180 config.guest_mac,
181 tokio_handle.clone(),
182 );
183
184 let mut last_cleanup = std::time::Instant::now();
186
187 let mut poll_fds = [
189 libc::pollfd {
190 fd: shared.tx_wake.as_raw_fd(),
191 events: libc::POLLIN,
192 revents: 0,
193 },
194 libc::pollfd {
195 fd: shared.proxy_wake.as_raw_fd(),
196 events: libc::POLLIN,
197 revents: 0,
198 },
199 ];
200
201 loop {
202 let now = smoltcp_now();
203
204 while let Some(frame) = device.stage_next_frame() {
206 if handle_gateway_icmp_echo(frame, &config, &shared) {
207 device.drop_staged_frame();
208 continue;
209 }
210
211 if icmp_relay.relay_outbound_if_echo(frame, &config, &network_policy) {
212 device.drop_staged_frame();
213 continue;
214 }
215
216 match classify_frame(frame) {
217 FrameAction::TcpSyn { src, dst } => {
218 if network_policy
220 .evaluate_egress(dst, Protocol::Tcp)
221 .is_allow()
222 && !conn_tracker.has_socket_for(&src, &dst)
223 {
224 conn_tracker.create_tcp_socket(src, dst, &mut sockets);
225 }
226 iface.poll_ingress_single(now, &mut device, &mut sockets);
229 }
230
231 FrameAction::UdpRelay { src, dst } => {
232 if let Some(ref tls) = tls_state
235 && tls.config.intercepted_ports.contains(&dst.port())
236 && tls.config.block_quic_on_intercept
237 {
238 device.drop_staged_frame();
239 continue;
240 }
241
242 if network_policy.evaluate_egress(dst, Protocol::Udp).is_deny() {
244 device.drop_staged_frame();
245 continue;
246 }
247
248 udp_relay.relay_outbound(frame, src, dst);
249 device.drop_staged_frame();
250 }
251
252 FrameAction::Dns | FrameAction::Passthrough => {
253 iface.poll_ingress_single(now, &mut device, &mut sockets);
255 }
256 }
257 }
258
259 loop {
263 let result = iface.poll_egress(now, &mut device, &mut sockets);
264 if matches!(result, smoltcp::iface::PollResult::None) {
265 break;
266 }
267 }
268 iface.poll_maintenance(now);
269
270 if device.frames_emitted.swap(false, Ordering::Relaxed) {
273 shared.rx_wake.wake();
274 }
275
276 conn_tracker.relay_data(&mut sockets);
281 dns_interceptor.process(&mut sockets);
282
283 port_publisher.accept_inbound(&mut iface, &mut sockets, &shared, &tokio_handle);
285 port_publisher.relay_data(&mut sockets);
286
287 let new_conns = conn_tracker.take_new_connections(&mut sockets);
289 for conn in new_conns {
290 if let Some(ref tls_state) = tls_state
291 && tls_state
292 .config
293 .intercepted_ports
294 .contains(&conn.dst.port())
295 {
296 tls_proxy::spawn_tls_proxy(
298 &tokio_handle,
299 conn.dst,
300 conn.from_smoltcp,
301 conn.to_smoltcp,
302 shared.clone(),
303 tls_state.clone(),
304 );
305 continue;
306 }
307 proxy::spawn_tcp_proxy(
309 &tokio_handle,
310 conn.dst,
311 conn.from_smoltcp,
312 conn.to_smoltcp,
313 shared.clone(),
314 );
315 }
316
317 if last_cleanup.elapsed() >= std::time::Duration::from_secs(1) {
320 conn_tracker.cleanup_closed(&mut sockets);
321 port_publisher.cleanup_closed(&mut sockets);
322 udp_relay.cleanup_expired();
323 last_cleanup = std::time::Instant::now();
324 }
325
326 loop {
329 let result = iface.poll_egress(now, &mut device, &mut sockets);
330 if matches!(result, smoltcp::iface::PollResult::None) {
331 break;
332 }
333 }
334
335 if device.frames_emitted.swap(false, Ordering::Relaxed) {
337 shared.rx_wake.wake();
338 }
339
340 let timeout_ms = iface
341 .poll_delay(now, &sockets)
342 .map(|d| d.total_millis().min(i32::MAX as u64) as i32)
343 .unwrap_or(100); unsafe {
347 libc::poll(
348 poll_fds.as_mut_ptr(),
349 poll_fds.len() as libc::nfds_t,
350 timeout_ms,
351 );
352 }
353
354 if poll_fds[0].revents & libc::POLLIN != 0 {
356 shared.tx_wake.drain();
357 }
358 if poll_fds[1].revents & libc::POLLIN != 0 {
359 shared.proxy_wake.drain();
360 }
361 }
362}
363
364fn smoltcp_now() -> Instant {
374 static EPOCH: std::sync::OnceLock<std::time::Instant> = std::sync::OnceLock::new();
375 let epoch = EPOCH.get_or_init(std::time::Instant::now);
376 let elapsed = epoch.elapsed();
377 Instant::from_millis(elapsed.as_millis() as i64)
378}
379
380fn handle_gateway_icmp_echo(frame: &[u8], config: &PollLoopConfig, shared: &SharedState) -> bool {
387 let Ok(eth) = EthernetFrame::new_checked(frame) else {
388 return false;
389 };
390
391 let reply = match eth.ethertype() {
392 EthernetProtocol::Ipv4 => gateway_icmpv4_echo_reply(ð, config),
393 EthernetProtocol::Ipv6 => gateway_icmpv6_echo_reply(ð, config),
394 _ => None,
395 };
396 let Some(reply) = reply else {
397 return false;
398 };
399
400 let reply_len = reply.len();
401 if shared.rx_ring.push(reply).is_ok() {
402 shared.add_rx_bytes(reply_len);
403 shared.rx_wake.wake();
404 }
405
406 true
407}
408
409fn gateway_icmpv4_echo_reply(
411 eth: &EthernetFrame<&[u8]>,
412 config: &PollLoopConfig,
413) -> Option<Vec<u8>> {
414 let ipv4 = Ipv4Packet::new_checked(eth.payload()).ok()?;
415 if ipv4.dst_addr() != config.gateway_ipv4 || ipv4.next_header() != IpProtocol::Icmp {
416 return None;
417 }
418
419 let icmp = Icmpv4Packet::new_checked(ipv4.payload()).ok()?;
420 let Icmpv4Repr::EchoRequest {
421 ident,
422 seq_no,
423 data,
424 } = Icmpv4Repr::parse(&icmp, &smoltcp::phy::ChecksumCapabilities::default()).ok()?
425 else {
426 return None;
427 };
428
429 let ipv4_repr = Ipv4Repr {
430 src_addr: config.gateway_ipv4,
431 dst_addr: ipv4.src_addr(),
432 next_header: IpProtocol::Icmp,
433 payload_len: 8 + data.len(),
434 hop_limit: 64,
435 };
436 let icmp_repr = Icmpv4Repr::EchoReply {
437 ident,
438 seq_no,
439 data,
440 };
441 let mut reply = vec![0u8; 14 + ipv4_repr.buffer_len() + icmp_repr.buffer_len()];
442
443 let mut reply_eth = EthernetFrame::new_unchecked(&mut reply);
444 reply_eth.set_src_addr(EthernetAddress(config.gateway_mac));
445 reply_eth.set_dst_addr(eth.src_addr());
446 reply_eth.set_ethertype(EthernetProtocol::Ipv4);
447
448 ipv4_repr.emit(
449 &mut Ipv4Packet::new_unchecked(&mut reply[14..34]),
450 &smoltcp::phy::ChecksumCapabilities::default(),
451 );
452 icmp_repr.emit(
453 &mut Icmpv4Packet::new_unchecked(&mut reply[34..]),
454 &smoltcp::phy::ChecksumCapabilities::default(),
455 );
456
457 Some(reply)
458}
459
460fn gateway_icmpv6_echo_reply(
462 eth: &EthernetFrame<&[u8]>,
463 config: &PollLoopConfig,
464) -> Option<Vec<u8>> {
465 let ipv6 = Ipv6Packet::new_checked(eth.payload()).ok()?;
466 if ipv6.dst_addr() != config.gateway_ipv6 || ipv6.next_header() != IpProtocol::Icmpv6 {
467 return None;
468 }
469
470 let icmp = Icmpv6Packet::new_checked(ipv6.payload()).ok()?;
471 let Icmpv6Repr::EchoRequest {
472 ident,
473 seq_no,
474 data,
475 } = Icmpv6Repr::parse(
476 &ipv6.src_addr(),
477 &ipv6.dst_addr(),
478 &icmp,
479 &smoltcp::phy::ChecksumCapabilities::default(),
480 )
481 .ok()?
482 else {
483 return None;
484 };
485
486 let ipv6_repr = Ipv6Repr {
487 src_addr: config.gateway_ipv6,
488 dst_addr: ipv6.src_addr(),
489 next_header: IpProtocol::Icmpv6,
490 payload_len: icmp_repr_buffer_len_v6(data),
491 hop_limit: 64,
492 };
493 let icmp_repr = Icmpv6Repr::EchoReply {
494 ident,
495 seq_no,
496 data,
497 };
498 let ipv6_hdr_len = 40;
499 let mut reply = vec![0u8; 14 + ipv6_hdr_len + icmp_repr.buffer_len()];
500
501 let mut reply_eth = EthernetFrame::new_unchecked(&mut reply);
502 reply_eth.set_src_addr(EthernetAddress(config.gateway_mac));
503 reply_eth.set_dst_addr(eth.src_addr());
504 reply_eth.set_ethertype(EthernetProtocol::Ipv6);
505
506 ipv6_repr.emit(&mut Ipv6Packet::new_unchecked(&mut reply[14..54]));
507 icmp_repr.emit(
508 &config.gateway_ipv6,
509 &ipv6.src_addr(),
510 &mut Icmpv6Packet::new_unchecked(&mut reply[54..]),
511 &smoltcp::phy::ChecksumCapabilities::default(),
512 );
513
514 Some(reply)
515}
516
517fn icmp_repr_buffer_len_v6(data: &[u8]) -> usize {
518 Icmpv6Repr::EchoReply {
519 ident: 0,
520 seq_no: 0,
521 data,
522 }
523 .buffer_len()
524}
525
526fn classify_ipv4(payload: &[u8]) -> FrameAction {
528 let Ok(ipv4) = Ipv4Packet::new_checked(payload) else {
529 return FrameAction::Passthrough;
530 };
531 classify_transport(
532 ipv4.next_header(),
533 ipv4.src_addr().into(),
534 ipv4.dst_addr().into(),
535 ipv4.payload(),
536 )
537}
538
539fn classify_ipv6(payload: &[u8]) -> FrameAction {
541 let Ok(ipv6) = Ipv6Packet::new_checked(payload) else {
542 return FrameAction::Passthrough;
543 };
544 classify_transport(
545 ipv6.next_header(),
546 ipv6.src_addr().into(),
547 ipv6.dst_addr().into(),
548 ipv6.payload(),
549 )
550}
551
552fn classify_transport(
554 protocol: IpProtocol,
555 src_ip: std::net::IpAddr,
556 dst_ip: std::net::IpAddr,
557 transport_payload: &[u8],
558) -> FrameAction {
559 match protocol {
560 IpProtocol::Tcp => {
561 let Ok(tcp) = TcpPacket::new_checked(transport_payload) else {
562 return FrameAction::Passthrough;
563 };
564 if tcp.syn() && !tcp.ack() {
565 FrameAction::TcpSyn {
566 src: SocketAddr::new(src_ip, tcp.src_port()),
567 dst: SocketAddr::new(dst_ip, tcp.dst_port()),
568 }
569 } else {
570 FrameAction::Passthrough
571 }
572 }
573 IpProtocol::Udp => {
574 let Ok(udp) = UdpPacket::new_checked(transport_payload) else {
575 return FrameAction::Passthrough;
576 };
577 if udp.dst_port() == 53 {
578 FrameAction::Dns
579 } else {
580 FrameAction::UdpRelay {
581 src: SocketAddr::new(src_ip, udp.src_port()),
582 dst: SocketAddr::new(dst_ip, udp.dst_port()),
583 }
584 }
585 }
586 _ => FrameAction::Passthrough, }
588}
589
590#[cfg(test)]
595mod tests {
596 use super::*;
597 use std::sync::Arc;
598
599 use smoltcp::phy::ChecksumCapabilities;
600 use smoltcp::wire::{
601 ArpOperation, ArpPacket, ArpRepr, EthernetRepr, Icmpv4Packet, Icmpv4Repr, Ipv4Repr,
602 };
603
604 use crate::device::SmoltcpDevice;
605 use crate::shared::SharedState;
606
607 fn build_tcp_syn_frame(
609 src_ip: [u8; 4],
610 dst_ip: [u8; 4],
611 src_port: u16,
612 dst_port: u16,
613 ) -> Vec<u8> {
614 let mut frame = vec![0u8; 14 + 20 + 20]; frame[12] = 0x08; frame[13] = 0x00;
619
620 let ip = &mut frame[14..34];
622 ip[0] = 0x45; let total_len = 40u16; ip[2..4].copy_from_slice(&total_len.to_be_bytes());
625 ip[6] = 0x40; ip[8] = 64; ip[9] = 6; ip[12..16].copy_from_slice(&src_ip);
629 ip[16..20].copy_from_slice(&dst_ip);
630
631 let tcp = &mut frame[34..54];
633 tcp[0..2].copy_from_slice(&src_port.to_be_bytes());
634 tcp[2..4].copy_from_slice(&dst_port.to_be_bytes());
635 tcp[12] = 0x50; tcp[13] = 0x02; frame
639 }
640
641 fn build_udp_frame(src_ip: [u8; 4], dst_ip: [u8; 4], src_port: u16, dst_port: u16) -> Vec<u8> {
643 let mut frame = vec![0u8; 14 + 20 + 8]; frame[12] = 0x08;
647 frame[13] = 0x00;
648
649 let ip = &mut frame[14..34];
651 ip[0] = 0x45;
652 let total_len = 28u16; ip[2..4].copy_from_slice(&total_len.to_be_bytes());
654 ip[8] = 64;
655 ip[9] = 17; ip[12..16].copy_from_slice(&src_ip);
657 ip[16..20].copy_from_slice(&dst_ip);
658
659 let udp = &mut frame[34..42];
661 udp[0..2].copy_from_slice(&src_port.to_be_bytes());
662 udp[2..4].copy_from_slice(&dst_port.to_be_bytes());
663 let udp_len = 8u16;
664 udp[4..6].copy_from_slice(&udp_len.to_be_bytes());
665
666 frame
667 }
668
669 fn build_icmpv4_echo_frame(
671 src_mac: [u8; 6],
672 dst_mac: [u8; 6],
673 src_ip: [u8; 4],
674 dst_ip: [u8; 4],
675 ident: u16,
676 seq_no: u16,
677 data: &[u8],
678 ) -> Vec<u8> {
679 let ipv4_repr = Ipv4Repr {
680 src_addr: Ipv4Addr::from(src_ip).into(),
681 dst_addr: Ipv4Addr::from(dst_ip).into(),
682 next_header: IpProtocol::Icmp,
683 payload_len: 8 + data.len(),
684 hop_limit: 64,
685 };
686 let icmp_repr = Icmpv4Repr::EchoRequest {
687 ident,
688 seq_no,
689 data,
690 };
691 let frame_len = 14 + ipv4_repr.buffer_len() + icmp_repr.buffer_len();
692 let mut frame = vec![0u8; frame_len];
693
694 let mut eth_frame = EthernetFrame::new_unchecked(&mut frame);
695 EthernetRepr {
696 src_addr: EthernetAddress(src_mac),
697 dst_addr: EthernetAddress(dst_mac),
698 ethertype: EthernetProtocol::Ipv4,
699 }
700 .emit(&mut eth_frame);
701
702 ipv4_repr.emit(
703 &mut Ipv4Packet::new_unchecked(&mut frame[14..34]),
704 &ChecksumCapabilities::default(),
705 );
706 icmp_repr.emit(
707 &mut Icmpv4Packet::new_unchecked(&mut frame[34..]),
708 &ChecksumCapabilities::default(),
709 );
710
711 frame
712 }
713
714 fn build_arp_request_frame(src_mac: [u8; 6], src_ip: [u8; 4], target_ip: [u8; 4]) -> Vec<u8> {
716 let mut frame = vec![0u8; 14 + 28];
717
718 let mut eth_frame = EthernetFrame::new_unchecked(&mut frame);
719 EthernetRepr {
720 src_addr: EthernetAddress(src_mac),
721 dst_addr: EthernetAddress([0xff; 6]),
722 ethertype: EthernetProtocol::Arp,
723 }
724 .emit(&mut eth_frame);
725
726 ArpRepr::EthernetIpv4 {
727 operation: ArpOperation::Request,
728 source_hardware_addr: EthernetAddress(src_mac),
729 source_protocol_addr: Ipv4Addr::from(src_ip).into(),
730 target_hardware_addr: EthernetAddress([0x00; 6]),
731 target_protocol_addr: Ipv4Addr::from(target_ip).into(),
732 }
733 .emit(&mut ArpPacket::new_unchecked(&mut frame[14..]));
734
735 frame
736 }
737
738 #[test]
739 fn classify_tcp_syn() {
740 let frame = build_tcp_syn_frame([10, 0, 0, 2], [93, 184, 216, 34], 54321, 443);
741 match classify_frame(&frame) {
742 FrameAction::TcpSyn { src, dst } => {
743 assert_eq!(
744 src,
745 SocketAddr::new(Ipv4Addr::new(10, 0, 0, 2).into(), 54321)
746 );
747 assert_eq!(
748 dst,
749 SocketAddr::new(Ipv4Addr::new(93, 184, 216, 34).into(), 443)
750 );
751 }
752 _ => panic!("expected TcpSyn"),
753 }
754 }
755
756 #[test]
757 fn classify_tcp_ack_is_passthrough() {
758 let mut frame = build_tcp_syn_frame([10, 0, 0, 2], [93, 184, 216, 34], 54321, 443);
759 frame[34 + 13] = 0x10; assert!(matches!(classify_frame(&frame), FrameAction::Passthrough));
762 }
763
764 #[test]
765 fn classify_udp_dns() {
766 let frame = build_udp_frame([10, 0, 0, 2], [10, 0, 0, 1], 12345, 53);
767 assert!(matches!(classify_frame(&frame), FrameAction::Dns));
768 }
769
770 #[test]
771 fn classify_udp_non_dns() {
772 let frame = build_udp_frame([10, 0, 0, 2], [8, 8, 8, 8], 12345, 443);
773 match classify_frame(&frame) {
774 FrameAction::UdpRelay { src, dst } => {
775 assert_eq!(src.port(), 12345);
776 assert_eq!(dst.port(), 443);
777 }
778 _ => panic!("expected UdpRelay"),
779 }
780 }
781
782 #[test]
783 fn classify_arp_is_passthrough() {
784 let mut frame = vec![0u8; 42]; frame[12] = 0x08;
786 frame[13] = 0x06; assert!(matches!(classify_frame(&frame), FrameAction::Passthrough));
788 }
789
790 #[test]
791 fn classify_garbage_is_passthrough() {
792 assert!(matches!(classify_frame(&[]), FrameAction::Passthrough));
793 assert!(matches!(classify_frame(&[0; 5]), FrameAction::Passthrough));
794 }
795
796 #[test]
797 fn gateway_replies_to_icmp_echo_requests() {
798 fn drive_one_frame(
799 device: &mut SmoltcpDevice,
800 iface: &mut Interface,
801 sockets: &mut SocketSet<'_>,
802 shared: &Arc<SharedState>,
803 poll_config: &PollLoopConfig,
804 now: Instant,
805 ) {
806 let frame = device.stage_next_frame().expect("expected staged frame");
807 if handle_gateway_icmp_echo(frame, poll_config, shared) {
808 device.drop_staged_frame();
809 return;
810 }
811 let _ = iface.poll_ingress_single(now, device, sockets);
812 let _ = iface.poll_egress(now, device, sockets);
813 }
814
815 let shared = Arc::new(SharedState::new(4));
816 let poll_config = PollLoopConfig {
817 gateway_mac: [0x02, 0x00, 0x00, 0x00, 0x00, 0x01],
818 guest_mac: [0x02, 0x00, 0x00, 0x00, 0x00, 0x02],
819 gateway_ipv4: Ipv4Addr::new(100, 96, 0, 1),
820 guest_ipv4: Ipv4Addr::new(100, 96, 0, 2),
821 gateway_ipv6: Ipv6Addr::LOCALHOST,
822 mtu: 1500,
823 };
824 let mut device = SmoltcpDevice::new(shared.clone(), poll_config.mtu);
825 let mut iface = create_interface(&mut device, &poll_config);
826 let mut sockets = SocketSet::new(vec![]);
827 let now = smoltcp_now();
828
829 shared
832 .tx_ring
833 .push(build_arp_request_frame(
834 poll_config.guest_mac,
835 poll_config.guest_ipv4.octets(),
836 poll_config.gateway_ipv4.octets(),
837 ))
838 .unwrap();
839 shared
840 .tx_ring
841 .push(build_icmpv4_echo_frame(
842 poll_config.guest_mac,
843 poll_config.gateway_mac,
844 poll_config.guest_ipv4.octets(),
845 poll_config.gateway_ipv4.octets(),
846 0x1234,
847 0xABCD,
848 b"ping",
849 ))
850 .unwrap();
851
852 drive_one_frame(
853 &mut device,
854 &mut iface,
855 &mut sockets,
856 &shared,
857 &poll_config,
858 now,
859 );
860 let _ = shared.rx_ring.pop().expect("expected ARP reply");
861
862 drive_one_frame(
863 &mut device,
864 &mut iface,
865 &mut sockets,
866 &shared,
867 &poll_config,
868 now,
869 );
870
871 let reply = shared.rx_ring.pop().expect("expected ICMP echo reply");
872 let eth = EthernetFrame::new_checked(&reply).expect("valid ethernet frame");
873 assert_eq!(eth.src_addr(), EthernetAddress(poll_config.gateway_mac));
874 assert_eq!(eth.dst_addr(), EthernetAddress(poll_config.guest_mac));
875 assert_eq!(eth.ethertype(), EthernetProtocol::Ipv4);
876
877 let ipv4 = Ipv4Packet::new_checked(eth.payload()).expect("valid IPv4 packet");
878 assert_eq!(Ipv4Addr::from(ipv4.src_addr()), poll_config.gateway_ipv4);
879 assert_eq!(Ipv4Addr::from(ipv4.dst_addr()), poll_config.guest_ipv4);
880 assert_eq!(ipv4.next_header(), IpProtocol::Icmp);
881
882 let icmp = Icmpv4Packet::new_checked(ipv4.payload()).expect("valid ICMP packet");
883 let icmp_repr = Icmpv4Repr::parse(&icmp, &ChecksumCapabilities::default())
884 .expect("valid ICMP echo reply");
885 assert_eq!(
886 icmp_repr,
887 Icmpv4Repr::EchoReply {
888 ident: 0x1234,
889 seq_no: 0xABCD,
890 data: b"ping",
891 }
892 );
893 }
894
895 #[test]
896 fn external_icmp_echo_requests_are_not_answered_locally() {
897 fn drive_one_frame(
898 device: &mut SmoltcpDevice,
899 iface: &mut Interface,
900 sockets: &mut SocketSet<'_>,
901 shared: &Arc<SharedState>,
902 poll_config: &PollLoopConfig,
903 now: Instant,
904 ) {
905 let frame = device.stage_next_frame().expect("expected staged frame");
906 if handle_gateway_icmp_echo(frame, poll_config, shared) {
907 device.drop_staged_frame();
908 return;
909 }
910 let _ = iface.poll_ingress_single(now, device, sockets);
911 let _ = iface.poll_egress(now, device, sockets);
912 }
913
914 let shared = Arc::new(SharedState::new(4));
915 let poll_config = PollLoopConfig {
916 gateway_mac: [0x02, 0x00, 0x00, 0x00, 0x00, 0x01],
917 guest_mac: [0x02, 0x00, 0x00, 0x00, 0x00, 0x02],
918 gateway_ipv4: Ipv4Addr::new(100, 96, 0, 1),
919 guest_ipv4: Ipv4Addr::new(100, 96, 0, 2),
920 gateway_ipv6: Ipv6Addr::LOCALHOST,
921 mtu: 1500,
922 };
923 let mut device = SmoltcpDevice::new(shared.clone(), poll_config.mtu);
924 let mut iface = create_interface(&mut device, &poll_config);
925 let mut sockets = SocketSet::new(vec![]);
926 let now = smoltcp_now();
927
928 shared
929 .tx_ring
930 .push(build_arp_request_frame(
931 poll_config.guest_mac,
932 poll_config.guest_ipv4.octets(),
933 poll_config.gateway_ipv4.octets(),
934 ))
935 .unwrap();
936 shared
937 .tx_ring
938 .push(build_icmpv4_echo_frame(
939 poll_config.guest_mac,
940 poll_config.gateway_mac,
941 poll_config.guest_ipv4.octets(),
942 [142, 251, 216, 46],
943 0x1234,
944 0xABCD,
945 b"ping",
946 ))
947 .unwrap();
948
949 drive_one_frame(
950 &mut device,
951 &mut iface,
952 &mut sockets,
953 &shared,
954 &poll_config,
955 now,
956 );
957 let _ = shared.rx_ring.pop().expect("expected ARP reply");
958
959 drive_one_frame(
960 &mut device,
961 &mut iface,
962 &mut sockets,
963 &shared,
964 &poll_config,
965 now,
966 );
967 assert!(
968 shared.rx_ring.pop().is_none(),
969 "external ICMP should not be answered locally"
970 );
971 }
972}