1*cf78ab8cSAndroid Build Coastguard Worker // Copyright 2024 Google LLC
2*cf78ab8cSAndroid Build Coastguard Worker //
3*cf78ab8cSAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License");
4*cf78ab8cSAndroid Build Coastguard Worker // you may not use this file except in compliance with the License.
5*cf78ab8cSAndroid Build Coastguard Worker // You may obtain a copy of the License at
6*cf78ab8cSAndroid Build Coastguard Worker //
7*cf78ab8cSAndroid Build Coastguard Worker // https://www.apache.org/licenses/LICENSE-2.0
8*cf78ab8cSAndroid Build Coastguard Worker //
9*cf78ab8cSAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*cf78ab8cSAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS,
11*cf78ab8cSAndroid Build Coastguard Worker // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*cf78ab8cSAndroid Build Coastguard Worker // See the License for the specific language governing permissions and
13*cf78ab8cSAndroid Build Coastguard Worker // limitations under the License.
14*cf78ab8cSAndroid Build Coastguard Worker
15*cf78ab8cSAndroid Build Coastguard Worker use bytes::Bytes;
16*cf78ab8cSAndroid Build Coastguard Worker use etherparse::EtherType;
17*cf78ab8cSAndroid Build Coastguard Worker use etherparse::LinkHeader::Ethernet2;
18*cf78ab8cSAndroid Build Coastguard Worker use etherparse::{NetHeaders, PacketBuilder, PacketHeaders, PayloadSlice, TransportHeader};
19*cf78ab8cSAndroid Build Coastguard Worker use libslirp_rs::libslirp::LibSlirp;
20*cf78ab8cSAndroid Build Coastguard Worker use libslirp_rs::libslirp_config::SlirpConfig;
21*cf78ab8cSAndroid Build Coastguard Worker use std::fs;
22*cf78ab8cSAndroid Build Coastguard Worker use std::io;
23*cf78ab8cSAndroid Build Coastguard Worker use std::net::{SocketAddr, UdpSocket};
24*cf78ab8cSAndroid Build Coastguard Worker use std::sync::mpsc;
25*cf78ab8cSAndroid Build Coastguard Worker use std::thread;
26*cf78ab8cSAndroid Build Coastguard Worker use std::time::Duration;
27*cf78ab8cSAndroid Build Coastguard Worker
28*cf78ab8cSAndroid Build Coastguard Worker const PAYLOAD: &[u8; 23] = b"Hello, UDP echo server!";
29*cf78ab8cSAndroid Build Coastguard Worker const PAYLOAD_PONG: &[u8; 23] = b"Hello, UDP echo client!";
30*cf78ab8cSAndroid Build Coastguard Worker
31*cf78ab8cSAndroid Build Coastguard Worker /// Test UDP packets sent through libslirp
32*cf78ab8cSAndroid Build Coastguard Worker #[cfg(not(windows))] // TOOD: remove once test is working on windows.
33*cf78ab8cSAndroid Build Coastguard Worker #[test]
udp_echo()34*cf78ab8cSAndroid Build Coastguard Worker fn udp_echo() {
35*cf78ab8cSAndroid Build Coastguard Worker let config = SlirpConfig { ..Default::default() };
36*cf78ab8cSAndroid Build Coastguard Worker
37*cf78ab8cSAndroid Build Coastguard Worker let before_fd_count = count_open_fds().unwrap();
38*cf78ab8cSAndroid Build Coastguard Worker
39*cf78ab8cSAndroid Build Coastguard Worker let (tx, rx) = mpsc::channel::<Bytes>();
40*cf78ab8cSAndroid Build Coastguard Worker let slirp = LibSlirp::new(config, tx, None);
41*cf78ab8cSAndroid Build Coastguard Worker
42*cf78ab8cSAndroid Build Coastguard Worker // Start up an IPV4 UDP echo server
43*cf78ab8cSAndroid Build Coastguard Worker let server_addr = one_shot_udp_echo_server().unwrap();
44*cf78ab8cSAndroid Build Coastguard Worker
45*cf78ab8cSAndroid Build Coastguard Worker println!("server addr {:?}", server_addr);
46*cf78ab8cSAndroid Build Coastguard Worker let server_ip = match server_addr {
47*cf78ab8cSAndroid Build Coastguard Worker SocketAddr::V4(addr) => addr.ip().to_owned(),
48*cf78ab8cSAndroid Build Coastguard Worker _ => panic!("Unsupported address type"),
49*cf78ab8cSAndroid Build Coastguard Worker };
50*cf78ab8cSAndroid Build Coastguard Worker // Source address
51*cf78ab8cSAndroid Build Coastguard Worker let source_ip = server_ip.clone();
52*cf78ab8cSAndroid Build Coastguard Worker
53*cf78ab8cSAndroid Build Coastguard Worker // Source and destination ports
54*cf78ab8cSAndroid Build Coastguard Worker let source_port: u16 = 20000;
55*cf78ab8cSAndroid Build Coastguard Worker let destination_port = server_addr.port();
56*cf78ab8cSAndroid Build Coastguard Worker
57*cf78ab8cSAndroid Build Coastguard Worker // Build the UDP packet
58*cf78ab8cSAndroid Build Coastguard Worker // with abitrary source and destination mac addrs
59*cf78ab8cSAndroid Build Coastguard Worker // We use server address 0.0.0.0 to avoid ARP packets
60*cf78ab8cSAndroid Build Coastguard Worker let builder = PacketBuilder::ethernet2([1, 2, 3, 4, 5, 6], [7, 8, 9, 10, 11, 12])
61*cf78ab8cSAndroid Build Coastguard Worker .ipv4(source_ip.octets(), server_ip.octets(), 20)
62*cf78ab8cSAndroid Build Coastguard Worker .udp(source_port, destination_port);
63*cf78ab8cSAndroid Build Coastguard Worker
64*cf78ab8cSAndroid Build Coastguard Worker // Get some memory to store the result
65*cf78ab8cSAndroid Build Coastguard Worker let mut result = Vec::<u8>::with_capacity(builder.size(PAYLOAD.len()));
66*cf78ab8cSAndroid Build Coastguard Worker
67*cf78ab8cSAndroid Build Coastguard Worker // Serialize header and payload
68*cf78ab8cSAndroid Build Coastguard Worker builder.write(&mut result, PAYLOAD).unwrap();
69*cf78ab8cSAndroid Build Coastguard Worker
70*cf78ab8cSAndroid Build Coastguard Worker let headers = PacketHeaders::from_ethernet_slice(&result).unwrap();
71*cf78ab8cSAndroid Build Coastguard Worker if let Some(Ethernet2(ether_header)) = headers.link {
72*cf78ab8cSAndroid Build Coastguard Worker assert_eq!(ether_header.ether_type, EtherType::IPV4);
73*cf78ab8cSAndroid Build Coastguard Worker } else {
74*cf78ab8cSAndroid Build Coastguard Worker panic!("expected ethernet2 header");
75*cf78ab8cSAndroid Build Coastguard Worker }
76*cf78ab8cSAndroid Build Coastguard Worker
77*cf78ab8cSAndroid Build Coastguard Worker assert!(headers.net.is_some());
78*cf78ab8cSAndroid Build Coastguard Worker assert!(headers.transport.is_some());
79*cf78ab8cSAndroid Build Coastguard Worker
80*cf78ab8cSAndroid Build Coastguard Worker // Send to oneshot_udp_echo_server (via libslirp)
81*cf78ab8cSAndroid Build Coastguard Worker slirp.input(Bytes::from(result));
82*cf78ab8cSAndroid Build Coastguard Worker
83*cf78ab8cSAndroid Build Coastguard Worker // Read from oneshot_udp_echo server (via libslirp)
84*cf78ab8cSAndroid Build Coastguard Worker // No ARP packets will be seen
85*cf78ab8cSAndroid Build Coastguard Worker
86*cf78ab8cSAndroid Build Coastguard Worker // Try to receive a packet before end_time
87*cf78ab8cSAndroid Build Coastguard Worker match rx.recv_timeout(Duration::from_secs(2)) {
88*cf78ab8cSAndroid Build Coastguard Worker Ok(packet) => {
89*cf78ab8cSAndroid Build Coastguard Worker let headers = PacketHeaders::from_ethernet_slice(&packet).unwrap();
90*cf78ab8cSAndroid Build Coastguard Worker
91*cf78ab8cSAndroid Build Coastguard Worker if let Some(Ethernet2(ref ether_header)) = headers.link {
92*cf78ab8cSAndroid Build Coastguard Worker assert_eq!(ether_header.ether_type, EtherType::IPV4);
93*cf78ab8cSAndroid Build Coastguard Worker } else {
94*cf78ab8cSAndroid Build Coastguard Worker panic!("expected ethernet2 header");
95*cf78ab8cSAndroid Build Coastguard Worker }
96*cf78ab8cSAndroid Build Coastguard Worker
97*cf78ab8cSAndroid Build Coastguard Worker if let Some(NetHeaders::Ipv4(ipv4_header, _)) = headers.net {
98*cf78ab8cSAndroid Build Coastguard Worker assert_eq!(ipv4_header.source, [127, 0, 0, 1]);
99*cf78ab8cSAndroid Build Coastguard Worker assert_eq!(ipv4_header.destination, [0, 0, 0, 0]);
100*cf78ab8cSAndroid Build Coastguard Worker } else {
101*cf78ab8cSAndroid Build Coastguard Worker panic!("expected IpV4 header, got {:?}", headers.net);
102*cf78ab8cSAndroid Build Coastguard Worker }
103*cf78ab8cSAndroid Build Coastguard Worker
104*cf78ab8cSAndroid Build Coastguard Worker if let Some(TransportHeader::Udp(udp_header)) = headers.transport {
105*cf78ab8cSAndroid Build Coastguard Worker assert_eq!(udp_header.source_port, destination_port);
106*cf78ab8cSAndroid Build Coastguard Worker assert_eq!(udp_header.destination_port, source_port);
107*cf78ab8cSAndroid Build Coastguard Worker } else {
108*cf78ab8cSAndroid Build Coastguard Worker panic!("expected Udp header");
109*cf78ab8cSAndroid Build Coastguard Worker }
110*cf78ab8cSAndroid Build Coastguard Worker
111*cf78ab8cSAndroid Build Coastguard Worker if let PayloadSlice::Udp(payload) = headers.payload {
112*cf78ab8cSAndroid Build Coastguard Worker assert_eq!(payload, PAYLOAD_PONG);
113*cf78ab8cSAndroid Build Coastguard Worker } else {
114*cf78ab8cSAndroid Build Coastguard Worker panic!("expected Udp payload");
115*cf78ab8cSAndroid Build Coastguard Worker }
116*cf78ab8cSAndroid Build Coastguard Worker }
117*cf78ab8cSAndroid Build Coastguard Worker Err(mpsc::RecvTimeoutError::Timeout) => {
118*cf78ab8cSAndroid Build Coastguard Worker assert!(false, "Timeout waiting for udp packet");
119*cf78ab8cSAndroid Build Coastguard Worker }
120*cf78ab8cSAndroid Build Coastguard Worker Err(e) => {
121*cf78ab8cSAndroid Build Coastguard Worker panic!("Failed to receive data in main thread: {}", e);
122*cf78ab8cSAndroid Build Coastguard Worker }
123*cf78ab8cSAndroid Build Coastguard Worker }
124*cf78ab8cSAndroid Build Coastguard Worker
125*cf78ab8cSAndroid Build Coastguard Worker // validate data packet
126*cf78ab8cSAndroid Build Coastguard Worker
127*cf78ab8cSAndroid Build Coastguard Worker slirp.shutdown();
128*cf78ab8cSAndroid Build Coastguard Worker assert_eq!(
129*cf78ab8cSAndroid Build Coastguard Worker rx.recv_timeout(Duration::from_millis(5)),
130*cf78ab8cSAndroid Build Coastguard Worker Err(mpsc::RecvTimeoutError::Disconnected)
131*cf78ab8cSAndroid Build Coastguard Worker );
132*cf78ab8cSAndroid Build Coastguard Worker
133*cf78ab8cSAndroid Build Coastguard Worker let after_fd_count = count_open_fds().unwrap();
134*cf78ab8cSAndroid Build Coastguard Worker assert_eq!(before_fd_count, after_fd_count);
135*cf78ab8cSAndroid Build Coastguard Worker }
136*cf78ab8cSAndroid Build Coastguard Worker
one_shot_udp_echo_server() -> std::io::Result<SocketAddr>137*cf78ab8cSAndroid Build Coastguard Worker fn one_shot_udp_echo_server() -> std::io::Result<SocketAddr> {
138*cf78ab8cSAndroid Build Coastguard Worker let socket = UdpSocket::bind("0.0.0.0:0")?;
139*cf78ab8cSAndroid Build Coastguard Worker let addr = socket.local_addr()?;
140*cf78ab8cSAndroid Build Coastguard Worker thread::spawn(move || {
141*cf78ab8cSAndroid Build Coastguard Worker let mut buf = [0u8; 1024];
142*cf78ab8cSAndroid Build Coastguard Worker let (len, addr) = socket.recv_from(&mut buf).unwrap();
143*cf78ab8cSAndroid Build Coastguard Worker let data = &buf[..len];
144*cf78ab8cSAndroid Build Coastguard Worker if data != PAYLOAD {
145*cf78ab8cSAndroid Build Coastguard Worker panic!("mistmatch payload");
146*cf78ab8cSAndroid Build Coastguard Worker }
147*cf78ab8cSAndroid Build Coastguard Worker println!("sending to addr {addr:?}");
148*cf78ab8cSAndroid Build Coastguard Worker let _ = socket.send_to(PAYLOAD_PONG, addr);
149*cf78ab8cSAndroid Build Coastguard Worker });
150*cf78ab8cSAndroid Build Coastguard Worker Ok(addr)
151*cf78ab8cSAndroid Build Coastguard Worker }
152*cf78ab8cSAndroid Build Coastguard Worker
153*cf78ab8cSAndroid Build Coastguard Worker #[cfg(target_os = "linux")]
count_open_fds() -> io::Result<usize>154*cf78ab8cSAndroid Build Coastguard Worker fn count_open_fds() -> io::Result<usize> {
155*cf78ab8cSAndroid Build Coastguard Worker let entries = fs::read_dir("/proc/self/fd")?;
156*cf78ab8cSAndroid Build Coastguard Worker Ok(entries.count())
157*cf78ab8cSAndroid Build Coastguard Worker }
158*cf78ab8cSAndroid Build Coastguard Worker
159*cf78ab8cSAndroid Build Coastguard Worker #[cfg(not(target_os = "linux"))]
count_open_fds() -> io::Result<usize>160*cf78ab8cSAndroid Build Coastguard Worker fn count_open_fds() -> io::Result<usize> {
161*cf78ab8cSAndroid Build Coastguard Worker Ok(0)
162*cf78ab8cSAndroid Build Coastguard Worker }
163