1use crate::device::VirtioNetworkDevice;
54use crate::dns;
55use crate::dns_relay::{self, DnsQuery, DnsResponse, DnsTransport};
56use crate::egress::EgressPolicy;
57use crate::icmp_relay;
58use crate::queues::NetworkFrameQueues;
59use crate::tcp_listeners::AcceptedTcpConnection;
60use crate::tcp_relay::{spawn_tcp_relay, TcpRelayTable};
61use crate::udp_relay;
62use crate::virtio_net_log;
63use smoltcp::iface::{
64 Config, Interface, PollIngressSingleResult, PollResult, SocketHandle, SocketSet,
65};
66use smoltcp::socket::raw::{
67 PacketBuffer as RawPacketBuffer, PacketMetadata as RawPacketMetadata, Socket as RawSocket,
68};
69use smoltcp::socket::tcp;
70use smoltcp::socket::udp::{PacketBuffer, PacketMetadata, Socket as UdpSocket, UdpMetadata};
71use smoltcp::time::Instant;
72use smoltcp::wire::{
73 EthernetAddress, EthernetFrame, EthernetProtocol, HardwareAddress, IpAddress, IpCidr,
74 IpListenEndpoint, IpProtocol, IpVersion, Ipv4Packet, Ipv6Packet, TcpPacket, UdpPacket,
75};
76use std::collections::HashMap;
77use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
78use std::sync::atomic::Ordering;
79use std::sync::mpsc::{Receiver, SyncSender, TryRecvError, TrySendError};
80use std::sync::Arc;
81use std::thread::{self, JoinHandle};
82use std::time::{Duration, Instant as StdInstant};
83
84const DNS_SOCKET_PORT: u16 = 53;
85const DNS_PACKET_SLOTS: usize = 8;
86const DNS_BUFFER_BYTES: usize = 2048;
87const DNS_TCP_LISTENERS: usize = 4;
93const DNS_TCP_RX_BYTES: usize = 4096;
94const DNS_TCP_TX_BYTES: usize = 8192;
95const DNS_TCP_MAX_MSG: usize = 4096;
99const DEFAULT_IDLE_TIMEOUT_MS: i32 = 100;
100const ICMP_PACKET_SLOTS: usize = 16;
102const ICMP_BUFFER_BYTES: usize = 32 * 1024;
104
105#[derive(Debug, Clone, Copy)]
111pub struct VirtioPollConfig {
112 pub gateway_mac: [u8; 6],
114 pub guest_mac: [u8; 6],
116 pub gateway_ipv4: Ipv4Addr,
118 pub guest_ipv4: Ipv4Addr,
120 pub gateway_ipv6: Ipv6Addr,
122 pub guest_ipv6: Ipv6Addr,
124 pub prefix_len6: u8,
126 pub upstream_dns: Ipv4Addr,
128 pub mtu: usize,
130}
131
132#[derive(Debug, Clone, Copy, PartialEq, Eq)]
133enum FrameAction {
134 TcpSyn {
135 source: SocketAddr,
136 destination: SocketAddr,
137 },
138 DnsQuery,
139 UdpFlow {
141 destination: SocketAddr,
142 },
143 Passthrough,
144}
145
146pub fn start_network_stack(
156 queues: Arc<NetworkFrameQueues>,
157 config: VirtioPollConfig,
158 tcp_receiver: Option<Receiver<AcceptedTcpConnection>>,
159 egress: EgressPolicy,
160) -> std::io::Result<JoinHandle<()>> {
161 virtio_net_log!(
162 "virtio-net: spawning poll thread guest_ip={} gateway_ip={} mtu={}",
163 config.guest_ipv4,
164 config.gateway_ipv4,
165 config.mtu
166 );
167 thread::Builder::new()
168 .name("smolvm-net-poll".into())
169 .spawn(move || run_network_stack(queues, config, tcp_receiver, egress))
170}
171
172fn run_network_stack(
173 queues: Arc<NetworkFrameQueues>,
174 config: VirtioPollConfig,
175 mut tcp_receiver: Option<Receiver<AcceptedTcpConnection>>,
176 egress: EgressPolicy,
177) {
178 virtio_net_log!(
192 "virtio-net: poll loop started guest_ip={} gateway_ip={}",
193 config.guest_ipv4,
194 config.gateway_ipv4
195 );
196 let clock = StdInstant::now();
197 let mut device = VirtioNetworkDevice::new(queues.clone(), config.mtu);
198 let mut interface = create_interface(&mut device, &config);
199 let mut sockets = SocketSet::new(vec![]);
200 let dns_socket_handle = add_dns_socket(&mut sockets);
201 let dns_tcp_handles = add_dns_tcp_sockets(&mut sockets);
202 let mut dns_tcp_conns: Vec<DnsTcpConn> = (0..dns_tcp_handles.len())
203 .map(|_| DnsTcpConn::default())
204 .collect();
205 let (icmp4_handle, icmp6_handle) = add_icmp_raw_sockets(&mut sockets);
206 let gateway_addrs = [
209 IpAddr::V4(config.gateway_ipv4),
210 IpAddr::V6(config.gateway_ipv6),
211 IpAddr::V6(link_local_from_mac(config.gateway_mac)),
212 ];
213 let relay_wake = Arc::new(queues.relay_wake.clone());
214 let mut relays = TcpRelayTable::new(None, egress.clone(), gateway_addrs.to_vec());
215 let mut udp_sockets = udp_relay::UdpSocketTable::new();
216 let udp_channels = {
217 let shutdown_queues = queues.clone();
218 udp_relay::start_udp_relay(
219 relay_wake.clone(),
220 Arc::new(move || shutdown_queues.is_shutting_down()),
221 )
222 };
223 let icmp_channels = {
224 let shutdown_queues = queues.clone();
225 icmp_relay::start_icmp_relay(
226 relay_wake.clone(),
227 Arc::new(move || shutdown_queues.is_shutting_down()),
228 )
229 };
230 let dns_channels = {
234 let shutdown_queues = queues.clone();
235 dns_relay::start_dns_relay(
236 relay_wake.clone(),
237 Arc::new(move || shutdown_queues.is_shutting_down()),
238 )
239 };
240 let mut dns_gateway = DnsGateway::new();
241
242 let poller = queues.guest_wake.poller().clone();
251 let mut events = polling::Events::new();
252
253 loop {
254 if queues.is_shutting_down() {
255 return;
256 }
257 let now = smoltcp_now(clock);
258
259 while let Some(frame) = device.stage_next_frame() {
260 match classify_guest_frame(frame, &gateway_addrs) {
266 FrameAction::TcpSyn {
267 source,
268 destination,
269 } => {
270 virtio_net_log!(
271 "virtio-net: guest TCP SYN source={} destination={}",
272 source,
273 destination
274 );
275 if !relays.has_socket_for(&source, &destination) {
276 relays.create_tcp_socket(source, destination, &mut sockets);
277 }
278 if matches!(
279 interface.poll_ingress_single(now, &mut device, &mut sockets),
280 PollIngressSingleResult::None
281 ) {
282 device.drop_staged_frame();
283 }
284 }
285 FrameAction::DnsQuery | FrameAction::Passthrough => {
286 if matches!(
287 interface.poll_ingress_single(now, &mut device, &mut sockets),
288 PollIngressSingleResult::None
289 ) {
290 device.drop_staged_frame();
291 }
292 }
293 FrameAction::UdpFlow { destination } => {
294 if udp_relay::should_relay_udp(destination, &egress)
297 && udp_sockets.ensure_socket(destination, &mut sockets)
298 {
299 if matches!(
300 interface.poll_ingress_single(now, &mut device, &mut sockets),
301 PollIngressSingleResult::None
302 ) {
303 device.drop_staged_frame();
304 }
305 } else {
306 device.drop_staged_frame();
307 }
308 }
309 }
310 }
311
312 relay_accepted_tcp_connection(
313 &mut tcp_receiver,
314 &mut relays,
315 &mut interface,
316 &mut sockets,
317 config.gateway_ipv4,
318 config.guest_ipv4,
319 );
320
321 flush_interface_egress(&mut interface, &mut device, &mut sockets, now);
324 interface.poll_maintenance(now);
325 wake_guest_if_needed(&queues, &device);
326
327 relays.relay_data(&mut sockets);
330 let mut woke_dns = false;
334 woke_dns |= dispatch_dns_udp(
335 dns_socket_handle,
336 &mut sockets,
337 &egress,
338 config.upstream_dns,
339 &mut dns_gateway,
340 &dns_channels.to_relay,
341 );
342 woke_dns |= process_dns_tcp(
343 &dns_tcp_handles,
344 &mut dns_tcp_conns,
345 &mut sockets,
346 &egress,
347 config.upstream_dns,
348 &mut dns_gateway,
349 &dns_channels.to_relay,
350 );
351 if woke_dns {
352 dns_channels.relay_thread_wake.wake();
353 }
354 deliver_dns_responses(
355 dns_socket_handle,
356 &dns_tcp_handles,
357 &mut dns_tcp_conns,
358 &mut sockets,
359 &egress,
360 &mut dns_gateway,
361 &dns_channels.from_relay,
362 );
363
364 if udp_sockets.drain_to_relay(&mut sockets, &udp_channels.to_relay) {
367 udp_channels.relay_thread_wake.wake();
368 }
369 udp_sockets.deliver_replies(&mut sockets, &udp_channels.from_relay);
370 udp_sockets.expire_idle(&mut sockets);
371
372 let mut woke_icmp = false;
376 woke_icmp |= drain_icmp_echo(
377 &mut sockets,
378 icmp4_handle,
379 false,
380 &egress,
381 &gateway_addrs,
382 &icmp_channels.to_relay,
383 );
384 woke_icmp |= drain_icmp_echo(
385 &mut sockets,
386 icmp6_handle,
387 true,
388 &egress,
389 &gateway_addrs,
390 &icmp_channels.to_relay,
391 );
392 if woke_icmp {
393 icmp_channels.relay_thread_wake.wake();
394 }
395 deliver_icmp_replies(
396 &mut sockets,
397 icmp4_handle,
398 icmp6_handle,
399 &icmp_channels.from_relay,
400 );
401
402 for connection in relays.take_new_connections(&mut sockets) {
405 spawn_tcp_relay(
406 connection.destination,
407 connection.relay_target,
408 connection.from_smoltcp,
409 connection.to_smoltcp,
410 relay_wake.clone(),
411 connection.exit_state,
412 );
413 }
414
415 relays.cleanup_closed(&mut sockets);
416
417 flush_interface_egress(&mut interface, &mut device, &mut sockets, now);
420 wake_guest_if_needed(&queues, &device);
421
422 let timeout = interface
423 .poll_delay(now, &sockets)
424 .map(|duration| Duration::from_millis(duration.total_millis().min(u32::MAX as u64)));
425 let timeout = match timeout {
426 Some(timeout) => Some(timeout),
427 None => Some(Duration::from_millis(DEFAULT_IDLE_TIMEOUT_MS as u64)),
428 };
429
430 events.clear();
434 let _ = poller.wait(&mut events, timeout);
435 }
436}
437
438fn create_interface(device: &mut VirtioNetworkDevice, config: &VirtioPollConfig) -> Interface {
439 let mut interface = Interface::new(
449 Config::new(HardwareAddress::Ethernet(EthernetAddress(
450 config.gateway_mac,
451 ))),
452 device,
453 Instant::ZERO,
454 );
455 interface.update_ip_addrs(|addresses| {
456 addresses
457 .push(IpCidr::new(IpAddress::Ipv4(config.gateway_ipv4), 30))
458 .expect("failed to add gateway IPv4 address");
459 addresses
460 .push(IpCidr::new(
461 IpAddress::Ipv6(config.gateway_ipv6),
462 config.prefix_len6,
463 ))
464 .expect("failed to add gateway IPv6 address");
465 addresses
469 .push(IpCidr::new(
470 IpAddress::Ipv6(link_local_from_mac(config.gateway_mac)),
471 64,
472 ))
473 .expect("failed to add gateway IPv6 link-local address");
474 });
475 interface
479 .routes_mut()
480 .add_default_ipv4_route(config.gateway_ipv4)
481 .expect("failed to add default IPv4 route");
482 interface
483 .routes_mut()
484 .add_default_ipv6_route(config.gateway_ipv6)
485 .expect("failed to add default IPv6 route");
486 interface.set_any_ip(true);
487 interface
488}
489
490fn link_local_from_mac(mac: [u8; 6]) -> Ipv6Addr {
493 Ipv6Addr::new(
494 0xfe80,
495 0,
496 0,
497 0,
498 u16::from_be_bytes([mac[0] ^ 0x02, mac[1]]),
499 u16::from_be_bytes([mac[2], 0xff]),
500 u16::from_be_bytes([0xfe, mac[3]]),
501 u16::from_be_bytes([mac[4], mac[5]]),
502 )
503}
504
505fn add_dns_socket(sockets: &mut SocketSet<'_>) -> SocketHandle {
516 let rx_meta = vec![PacketMetadata::EMPTY; DNS_PACKET_SLOTS];
517 let tx_meta = vec![PacketMetadata::EMPTY; DNS_PACKET_SLOTS];
518 let rx_buffer = PacketBuffer::new(rx_meta, vec![0u8; DNS_BUFFER_BYTES]);
519 let tx_buffer = PacketBuffer::new(tx_meta, vec![0u8; DNS_BUFFER_BYTES]);
520 let mut socket = UdpSocket::new(rx_buffer, tx_buffer);
521 socket
522 .bind(smoltcp::wire::IpListenEndpoint {
523 addr: None,
524 port: DNS_SOCKET_PORT,
525 })
526 .expect("failed to bind gateway DNS socket");
527 sockets.add(socket)
528}
529
530fn add_icmp_raw_sockets(sockets: &mut SocketSet<'_>) -> (SocketHandle, SocketHandle) {
538 fn raw_socket(version: IpVersion, protocol: IpProtocol) -> RawSocket<'static> {
539 let rx = RawPacketBuffer::new(
540 vec![RawPacketMetadata::EMPTY; ICMP_PACKET_SLOTS],
541 vec![0u8; ICMP_BUFFER_BYTES],
542 );
543 let tx = RawPacketBuffer::new(
544 vec![RawPacketMetadata::EMPTY; ICMP_PACKET_SLOTS],
545 vec![0u8; ICMP_BUFFER_BYTES],
546 );
547 RawSocket::new(Some(version), Some(protocol), rx, tx)
548 }
549
550 let v4 = sockets.add(raw_socket(IpVersion::Ipv4, IpProtocol::Icmp));
551 let v6 = sockets.add(raw_socket(IpVersion::Ipv6, IpProtocol::Icmpv6));
552 (v4, v6)
553}
554
555fn drain_icmp_echo(
560 sockets: &mut SocketSet<'_>,
561 handle: SocketHandle,
562 is_ipv6: bool,
563 egress: &EgressPolicy,
564 gateway_addrs: &[IpAddr],
565 to_relay: &SyncSender<icmp_relay::IcmpEcho>,
566) -> bool {
567 let mut echoes = Vec::new();
570 {
571 let socket = sockets.get_mut::<RawSocket>(handle);
572 while socket.can_recv() {
573 let Ok(packet) = socket.recv() else {
574 break;
575 };
576 let parsed = if is_ipv6 {
577 icmp_relay::parse_guest_echo_v6(packet)
578 } else {
579 icmp_relay::parse_guest_echo_v4(packet)
580 };
581 if let Some(echo) = parsed {
582 echoes.push(echo);
583 }
584 }
585 }
586
587 let mut woke = false;
589 let mut local_replies = Vec::new();
590 for echo in echoes {
591 if gateway_addrs.contains(&echo.destination) {
592 local_replies.push(echo);
593 } else if icmp_relay::should_relay_icmp(echo.destination, egress) {
594 match to_relay.try_send(echo) {
595 Ok(()) => woke = true,
596 Err(TrySendError::Full(_)) => {
597 virtio_net_log!("virtio-net: dropping guest ICMP echo (relay queue full)");
598 }
599 Err(TrySendError::Disconnected(_)) => return woke,
600 }
601 }
602 }
604
605 if !local_replies.is_empty() {
607 let socket = sockets.get_mut::<RawSocket>(handle);
608 for reply in local_replies {
609 let frame = if is_ipv6 {
610 icmp_relay::build_echo_reply_v6(&reply)
611 } else {
612 icmp_relay::build_echo_reply_v4(&reply)
613 };
614 if let Some(frame) = frame {
615 let _ = socket.send_slice(&frame);
616 }
617 }
618 }
619 woke
620}
621
622fn deliver_icmp_replies(
626 sockets: &mut SocketSet<'_>,
627 icmp4_handle: SocketHandle,
628 icmp6_handle: SocketHandle,
629 from_relay: &Receiver<icmp_relay::IcmpEcho>,
630) {
631 while let Ok(reply) = from_relay.try_recv() {
632 let (handle, frame) = match reply.guest {
633 IpAddr::V4(_) => (icmp4_handle, icmp_relay::build_echo_reply_v4(&reply)),
634 IpAddr::V6(_) => (icmp6_handle, icmp_relay::build_echo_reply_v6(&reply)),
635 };
636 let Some(frame) = frame else {
637 continue;
638 };
639 let socket = sockets.get_mut::<RawSocket>(handle);
640 if socket.send_slice(&frame).is_err() {
641 virtio_net_log!(
642 "virtio-net: dropping ICMP reply to {} (raw socket buffer full)",
643 reply.guest
644 );
645 }
646 }
647}
648
649fn relay_accepted_tcp_connection(
652 tcp_receiver: &mut Option<Receiver<AcceptedTcpConnection>>,
653 relays: &mut TcpRelayTable,
654 interface: &mut Interface,
655 sockets: &mut SocketSet<'_>,
656 gateway_ipv4: Ipv4Addr,
657 guest_ipv4: Ipv4Addr,
658) {
659 let mut disconnected = false;
669
670 if let Some(receiver) = tcp_receiver.as_mut() {
671 loop {
672 match receiver.try_recv() {
673 Ok(connection) => {
674 let guest_destination =
675 SocketAddr::new(std::net::IpAddr::V4(guest_ipv4), connection.guest_port);
676 virtio_net_log!(
677 "virtio-net: accepted published TCP connection peer={} host_port={} guest_destination={}",
678 connection.peer_addr,
679 connection.host_port,
680 guest_destination
681 );
682 if !relays.create_published_socket(
683 interface,
684 gateway_ipv4,
685 guest_destination,
686 connection.stream,
687 sockets,
688 ) {
689 tracing::warn!(
690 host_port = connection.host_port,
691 guest_port = connection.guest_port,
692 peer_addr = %connection.peer_addr,
693 "dropping published TCP connection because the guest relay path could not be created"
694 );
695 }
696 }
697 Err(TryRecvError::Empty) => break,
698 Err(TryRecvError::Disconnected) => {
699 disconnected = true;
700 break;
701 }
702 }
703 }
704 }
705
706 if disconnected {
707 *tcp_receiver = None;
708 }
709}
710
711const MAX_PENDING_DNS: usize = 512;
716
717struct DnsGateway {
721 next_id: u64,
722 pending_udp: HashMap<u64, PendingDnsUdp>,
723}
724
725struct PendingDnsUdp {
727 endpoint: smoltcp::wire::IpEndpoint,
728 local_address: Option<IpAddress>,
729 learn: bool,
731}
732
733impl DnsGateway {
734 fn new() -> Self {
735 Self {
736 next_id: 0,
737 pending_udp: HashMap::new(),
738 }
739 }
740
741 fn next_id(&mut self) -> u64 {
742 let id = self.next_id;
743 self.next_id = self.next_id.wrapping_add(1);
744 id
745 }
746}
747
748enum DnsDecision {
751 Immediate(Vec<u8>),
754 Forward { learn: bool },
757}
758
759fn classify_dns_query(query: &[u8], egress: &EgressPolicy) -> DnsDecision {
764 if !egress.dns_filter_active() {
765 return DnsDecision::Forward { learn: false };
766 }
767 match dns::question_name(query) {
768 Some(name) if egress.hostname_allowed(&name) => DnsDecision::Forward { learn: true },
769 Some(name) => {
770 virtio_net_log!(
771 "virtio-net: blocking DNS query by allow-host policy name={}",
772 name
773 );
774 DnsDecision::Immediate(dns::error_response(query, dns::DNS_RCODE_NXDOMAIN))
775 }
776 None => DnsDecision::Immediate(dns::error_response(query, dns::DNS_RCODE_SERVFAIL)),
777 }
778}
779
780fn dispatch_dns_udp(
785 dns_socket_handle: SocketHandle,
786 sockets: &mut SocketSet<'_>,
787 egress: &EgressPolicy,
788 upstream_dns: Ipv4Addr,
789 gateway: &mut DnsGateway,
790 to_relay: &SyncSender<DnsQuery>,
791) -> bool {
792 let mut queued = false;
793 let socket = sockets.get_mut::<UdpSocket>(dns_socket_handle);
794 while socket.can_recv() {
795 let (query, endpoint, local_address) = match socket.recv() {
798 Ok((q, m)) => (q.to_vec(), m.endpoint, m.local_address),
799 Err(_) => break,
800 };
801 match classify_dns_query(&query, egress) {
802 DnsDecision::Immediate(response) => {
803 let response_meta = UdpMetadata {
804 endpoint,
805 local_address,
806 meta: Default::default(),
807 };
808 let _ = socket.send_slice(&response, response_meta);
809 }
810 DnsDecision::Forward { learn } => {
811 if gateway.pending_udp.len() >= MAX_PENDING_DNS {
812 virtio_net_log!("virtio-net: dropping guest DNS query (pending table full)");
813 continue;
814 }
815 let id = gateway.next_id();
816 gateway.pending_udp.insert(
817 id,
818 PendingDnsUdp {
819 endpoint,
820 local_address,
821 learn,
822 },
823 );
824 match to_relay.try_send(DnsQuery {
825 id,
826 transport: DnsTransport::Udp,
827 upstream: upstream_dns,
828 query,
829 }) {
830 Ok(()) => queued = true,
831 Err(TrySendError::Full(_)) => {
832 gateway.pending_udp.remove(&id);
833 virtio_net_log!("virtio-net: dropping guest DNS query (relay queue full)");
834 }
835 Err(TrySendError::Disconnected(_)) => {
836 gateway.pending_udp.remove(&id);
837 return queued;
838 }
839 }
840 }
841 }
842 }
843 queued
844}
845
846fn deliver_dns_responses(
852 dns_socket_handle: SocketHandle,
853 dns_tcp_handles: &[SocketHandle],
854 dns_tcp_conns: &mut [DnsTcpConn],
855 sockets: &mut SocketSet<'_>,
856 egress: &EgressPolicy,
857 gateway: &mut DnsGateway,
858 from_relay: &Receiver<DnsResponse>,
859) {
860 while let Ok(response) = from_relay.try_recv() {
861 if let Some(pending) = gateway.pending_udp.remove(&response.id) {
862 if let Some(answer) = response.answer {
863 if pending.learn {
864 egress.learn_ip_records(&dns::answer_ip_records(&answer));
865 }
866 let socket = sockets.get_mut::<UdpSocket>(dns_socket_handle);
867 let response_meta = UdpMetadata {
868 endpoint: pending.endpoint,
869 local_address: pending.local_address,
870 meta: Default::default(),
871 };
872 let _ = socket.send_slice(&answer, response_meta);
873 }
874 continue;
875 }
876
877 for (handle, conn) in dns_tcp_handles.iter().zip(dns_tcp_conns.iter_mut()) {
879 match conn.awaiting {
880 Some(pending) if pending.id == response.id => {
881 conn.awaiting = None;
882 if let Some(answer) = response.answer {
883 if pending.learn {
884 egress.learn_ip_records(&dns::answer_ip_records(&answer));
885 }
886 frame_dns_tcp_response(conn, &answer);
887 }
888 conn.done = true;
889 let socket = sockets.get_mut::<tcp::Socket>(*handle);
890 drain_dns_tcp_tx(socket, conn);
891 break;
892 }
893 _ => {}
894 }
895 }
896 }
897}
898
899fn frame_dns_tcp_response(conn: &mut DnsTcpConn, response: &[u8]) {
902 if let Ok(resp_len) = u16::try_from(response.len()) {
903 conn.tx.extend_from_slice(&resp_len.to_be_bytes());
904 conn.tx.extend_from_slice(response);
905 }
906}
907
908fn add_dns_tcp_sockets(sockets: &mut SocketSet<'_>) -> Vec<SocketHandle> {
912 (0..DNS_TCP_LISTENERS)
913 .map(|_| {
914 let rx_buffer = tcp::SocketBuffer::new(vec![0u8; DNS_TCP_RX_BYTES]);
915 let tx_buffer = tcp::SocketBuffer::new(vec![0u8; DNS_TCP_TX_BYTES]);
916 let mut socket = tcp::Socket::new(rx_buffer, tx_buffer);
917 socket
918 .listen(IpListenEndpoint {
919 addr: None,
920 port: DNS_SOCKET_PORT,
921 })
922 .expect("failed to listen on gateway DNS TCP socket");
923 sockets.add(socket)
924 })
925 .collect()
926}
927
928#[derive(Default)]
932struct DnsTcpConn {
933 rx: Vec<u8>,
935 tx: Vec<u8>,
937 tx_sent: usize,
939 done: bool,
941 awaiting: Option<DnsTcpPending>,
945}
946
947#[derive(Clone, Copy)]
949struct DnsTcpPending {
950 id: u64,
951 learn: bool,
952}
953
954fn process_dns_tcp(
959 handles: &[SocketHandle],
960 conns: &mut [DnsTcpConn],
961 sockets: &mut SocketSet<'_>,
962 egress: &EgressPolicy,
963 upstream_dns: Ipv4Addr,
964 gateway: &mut DnsGateway,
965 to_relay: &SyncSender<DnsQuery>,
966) -> bool {
967 let mut queued = false;
968 for (handle, conn) in handles.iter().zip(conns.iter_mut()) {
969 let socket = sockets.get_mut::<tcp::Socket>(*handle);
970
971 if !socket.is_open() {
976 if !conn.rx.is_empty() || !conn.tx.is_empty() || conn.done || conn.awaiting.is_some() {
977 *conn = DnsTcpConn::default();
978 }
979 let _ = socket.listen(IpListenEndpoint {
980 addr: None,
981 port: DNS_SOCKET_PORT,
982 });
983 continue;
984 }
985
986 if conn.awaiting.is_some() {
989 continue;
990 }
991
992 if conn.done {
994 drain_dns_tcp_tx(socket, conn);
995 continue;
996 }
997
998 while socket.can_recv() {
1000 let appended = socket.recv(|data| (data.len(), data.to_vec()));
1001 match appended {
1002 Ok(bytes) if !bytes.is_empty() => conn.rx.extend_from_slice(&bytes),
1003 _ => break,
1004 }
1005 }
1006
1007 if conn.rx.len() > DNS_TCP_MAX_MSG + 2 {
1009 conn.done = true;
1010 socket.close();
1011 continue;
1012 }
1013
1014 if conn.rx.len() >= 2 {
1015 let msg_len = u16::from_be_bytes([conn.rx[0], conn.rx[1]]) as usize;
1016 if msg_len == 0 || msg_len > DNS_TCP_MAX_MSG {
1017 conn.done = true;
1018 socket.close();
1019 continue;
1020 }
1021 if conn.rx.len() >= 2 + msg_len {
1022 let query = conn.rx[2..2 + msg_len].to_vec();
1023 virtio_net_log!(
1024 "virtio-net: DNS/TCP query query_len={} upstream_dns={}",
1025 query.len(),
1026 upstream_dns
1027 );
1028 match classify_dns_query(&query, egress) {
1029 DnsDecision::Immediate(response) => {
1030 frame_dns_tcp_response(conn, &response);
1031 conn.done = true;
1032 drain_dns_tcp_tx(socket, conn);
1033 }
1034 DnsDecision::Forward { learn } => {
1035 let id = gateway.next_id();
1038 match to_relay.try_send(DnsQuery {
1039 id,
1040 transport: DnsTransport::Tcp,
1041 upstream: upstream_dns,
1042 query,
1043 }) {
1044 Ok(()) => {
1045 conn.awaiting = Some(DnsTcpPending { id, learn });
1046 queued = true;
1047 }
1048 Err(_) => {
1049 conn.done = true;
1052 socket.close();
1053 }
1054 }
1055 }
1056 }
1057 }
1058 }
1059 }
1060 queued
1061}
1062
1063fn drain_dns_tcp_tx(socket: &mut tcp::Socket<'_>, conn: &mut DnsTcpConn) {
1066 while conn.tx_sent < conn.tx.len() && socket.can_send() {
1067 match socket.send_slice(&conn.tx[conn.tx_sent..]) {
1068 Ok(n) if n > 0 => conn.tx_sent += n,
1069 _ => break,
1070 }
1071 }
1072 if conn.tx_sent >= conn.tx.len() {
1073 socket.close();
1074 }
1075}
1076
1077fn flush_interface_egress(
1078 interface: &mut Interface,
1079 device: &mut VirtioNetworkDevice,
1080 sockets: &mut SocketSet<'_>,
1081 now: Instant,
1082) {
1083 loop {
1087 let result = interface.poll_egress(now, device, sockets);
1088 if matches!(result, PollResult::None) {
1089 break;
1090 }
1091 }
1092}
1093
1094fn wake_guest_if_needed(queues: &NetworkFrameQueues, device: &VirtioNetworkDevice) {
1095 if device.frames_emitted.swap(false, Ordering::Relaxed) {
1099 queues.host_wake.wake();
1100 }
1101}
1102
1103fn smoltcp_now(clock: StdInstant) -> Instant {
1104 let elapsed = clock.elapsed();
1105 Instant::from_millis(elapsed.as_millis() as i64)
1106}
1107
1108fn classify_guest_frame(frame: &[u8], gateway_addrs: &[IpAddr]) -> FrameAction {
1109 let ethernet = match EthernetFrame::new_checked(frame) {
1110 Ok(frame) => frame,
1111 Err(_) => return FrameAction::Passthrough,
1112 };
1113
1114 let (src_ip, dst_ip, protocol, transport): (IpAddr, IpAddr, _, _) = match ethernet.ethertype() {
1119 EthernetProtocol::Ipv4 => {
1120 let ipv4 = match Ipv4Packet::new_checked(ethernet.payload()) {
1121 Ok(packet) => packet,
1122 Err(_) => return FrameAction::Passthrough,
1123 };
1124 (
1125 IpAddr::V4(ipv4.src_addr()),
1126 IpAddr::V4(ipv4.dst_addr()),
1127 ipv4.next_header(),
1128 ipv4.payload(),
1129 )
1130 }
1131 EthernetProtocol::Ipv6 => {
1132 let ipv6 = match Ipv6Packet::new_checked(ethernet.payload()) {
1133 Ok(packet) => packet,
1134 Err(_) => return FrameAction::Passthrough,
1135 };
1136 (
1137 IpAddr::V6(ipv6.src_addr()),
1138 IpAddr::V6(ipv6.dst_addr()),
1139 ipv6.next_header(),
1140 ipv6.payload(),
1141 )
1142 }
1143 _ => return FrameAction::Passthrough,
1144 };
1145
1146 match protocol {
1147 smoltcp::wire::IpProtocol::Tcp => {
1148 let tcp = match TcpPacket::new_checked(transport) {
1149 Ok(packet) => packet,
1150 Err(_) => return FrameAction::Passthrough,
1151 };
1152
1153 if tcp.syn() && !tcp.ack() {
1154 if tcp.dst_port() == DNS_SOCKET_PORT && gateway_addrs.contains(&dst_ip) {
1159 FrameAction::Passthrough
1160 } else {
1161 FrameAction::TcpSyn {
1162 source: SocketAddr::new(src_ip, tcp.src_port()),
1163 destination: SocketAddr::new(dst_ip, tcp.dst_port()),
1164 }
1165 }
1166 } else {
1167 FrameAction::Passthrough
1168 }
1169 }
1170 smoltcp::wire::IpProtocol::Udp => {
1171 let udp = match UdpPacket::new_checked(transport) {
1172 Ok(packet) => packet,
1173 Err(_) => return FrameAction::Passthrough,
1174 };
1175
1176 if udp.dst_port() == DNS_SOCKET_PORT {
1177 FrameAction::DnsQuery
1178 } else {
1179 FrameAction::UdpFlow {
1180 destination: SocketAddr::new(dst_ip, udp.dst_port()),
1181 }
1182 }
1183 }
1184 _ => FrameAction::Passthrough,
1185 }
1186}
1187
1188#[cfg(feature = "fuzzing")]
1194pub fn fuzz_classify_guest_frame(frame: &[u8]) {
1195 let _ = classify_guest_frame(frame, &[]);
1196}
1197
1198#[cfg(test)]
1199mod tests {
1200 use super::*;
1201
1202 fn tcp_syn_frame(dst_ip: [u8; 4], dst_port: u16) -> Vec<u8> {
1206 let mut f = Vec::new();
1207 f.extend_from_slice(&[0xff; 6]);
1209 f.extend_from_slice(&[0x02, 0, 0, 0, 0, 1]);
1210 f.extend_from_slice(&[0x08, 0x00]);
1211 f.extend_from_slice(&[0x45, 0x00, 0x00, 0x28, 0, 0, 0, 0, 0x40, 0x06, 0, 0]);
1213 f.extend_from_slice(&[10, 0, 0, 2]); f.extend_from_slice(&dst_ip);
1215 f.extend_from_slice(&54321u16.to_be_bytes());
1217 f.extend_from_slice(&dst_port.to_be_bytes());
1218 f.extend_from_slice(&[0, 0, 0, 0, 0, 0, 0, 0]); f.extend_from_slice(&[0x50, 0x02, 0xff, 0xff, 0, 0, 0, 0]); f
1221 }
1222
1223 #[test]
1224 fn dns_tcp_to_gateway_is_intercepted_not_relayed() {
1225 let gw = IpAddr::V4(Ipv4Addr::new(100, 96, 0, 1));
1226 assert_eq!(
1228 classify_guest_frame(&tcp_syn_frame([100, 96, 0, 1], 53), &[gw]),
1229 FrameAction::Passthrough
1230 );
1231 }
1232
1233 #[test]
1234 fn dns_tcp_to_external_resolver_still_relayed() {
1235 let gw = IpAddr::V4(Ipv4Addr::new(100, 96, 0, 1));
1236 assert!(matches!(
1239 classify_guest_frame(&tcp_syn_frame([1, 1, 1, 1], 53), &[gw]),
1240 FrameAction::TcpSyn { .. }
1241 ));
1242 }
1243
1244 #[test]
1245 fn non_dns_tcp_to_gateway_still_relayed() {
1246 let gw = IpAddr::V4(Ipv4Addr::new(100, 96, 0, 1));
1247 assert!(matches!(
1249 classify_guest_frame(&tcp_syn_frame([100, 96, 0, 1], 443), &[gw]),
1250 FrameAction::TcpSyn { .. }
1251 ));
1252 }
1253}