1 mod utils;
2 
3 use byteorder::{ByteOrder, NetworkEndian};
4 use smoltcp::iface::{Interface, SocketSet};
5 use std::cmp;
6 use std::collections::HashMap;
7 use std::os::unix::io::AsRawFd;
8 use std::str::FromStr;
9 
10 use smoltcp::iface::Config;
11 use smoltcp::phy::wait as phy_wait;
12 use smoltcp::phy::Device;
13 use smoltcp::socket::icmp;
14 use smoltcp::wire::{
15     EthernetAddress, Icmpv4Packet, Icmpv4Repr, Icmpv6Packet, Icmpv6Repr, IpAddress, IpCidr,
16     Ipv4Address, Ipv6Address,
17 };
18 use smoltcp::{
19     phy::Medium,
20     time::{Duration, Instant},
21 };
22 
23 macro_rules! send_icmp_ping {
24     ( $repr_type:ident, $packet_type:ident, $ident:expr, $seq_no:expr,
25       $echo_payload:expr, $socket:expr, $remote_addr:expr ) => {{
26         let icmp_repr = $repr_type::EchoRequest {
27             ident: $ident,
28             seq_no: $seq_no,
29             data: &$echo_payload,
30         };
31 
32         let icmp_payload = $socket.send(icmp_repr.buffer_len(), $remote_addr).unwrap();
33 
34         let icmp_packet = $packet_type::new_unchecked(icmp_payload);
35         (icmp_repr, icmp_packet)
36     }};
37 }
38 
39 macro_rules! get_icmp_pong {
40     ( $repr_type:ident, $repr:expr, $payload:expr, $waiting_queue:expr, $remote_addr:expr,
41       $timestamp:expr, $received:expr ) => {{
42         if let $repr_type::EchoReply { seq_no, data, .. } = $repr {
43             if let Some(_) = $waiting_queue.get(&seq_no) {
44                 let packet_timestamp_ms = NetworkEndian::read_i64(data);
45                 println!(
46                     "{} bytes from {}: icmp_seq={}, time={}ms",
47                     data.len(),
48                     $remote_addr,
49                     seq_no,
50                     $timestamp.total_millis() - packet_timestamp_ms
51                 );
52                 $waiting_queue.remove(&seq_no);
53                 $received += 1;
54             }
55         }
56     }};
57 }
58 
main()59 fn main() {
60     utils::setup_logging("warn");
61 
62     let (mut opts, mut free) = utils::create_options();
63     utils::add_tuntap_options(&mut opts, &mut free);
64     utils::add_middleware_options(&mut opts, &mut free);
65     opts.optopt(
66         "c",
67         "count",
68         "Amount of echo request packets to send (default: 4)",
69         "COUNT",
70     );
71     opts.optopt(
72         "i",
73         "interval",
74         "Interval between successive packets sent (seconds) (default: 1)",
75         "INTERVAL",
76     );
77     opts.optopt(
78         "",
79         "timeout",
80         "Maximum wait duration for an echo response packet (seconds) (default: 5)",
81         "TIMEOUT",
82     );
83     free.push("ADDRESS");
84 
85     let mut matches = utils::parse_options(&opts, free);
86     let device = utils::parse_tuntap_options(&mut matches);
87     let fd = device.as_raw_fd();
88     let mut device =
89         utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false);
90     let device_caps = device.capabilities();
91     let remote_addr = IpAddress::from_str(&matches.free[0]).expect("invalid address format");
92     let count = matches
93         .opt_str("count")
94         .map(|s| usize::from_str(&s).unwrap())
95         .unwrap_or(4);
96     let interval = matches
97         .opt_str("interval")
98         .map(|s| Duration::from_secs(u64::from_str(&s).unwrap()))
99         .unwrap_or_else(|| Duration::from_secs(1));
100     let timeout = Duration::from_secs(
101         matches
102             .opt_str("timeout")
103             .map(|s| u64::from_str(&s).unwrap())
104             .unwrap_or(5),
105     );
106 
107     // Create interface
108     let mut config = Config::new();
109     config.random_seed = rand::random();
110     if device.capabilities().medium == Medium::Ethernet {
111         config.hardware_addr = Some(EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]).into());
112     }
113 
114     let mut iface = Interface::new(config, &mut device);
115     iface.update_ip_addrs(|ip_addrs| {
116         ip_addrs
117             .push(IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24))
118             .unwrap();
119         ip_addrs
120             .push(IpCidr::new(IpAddress::v6(0xfdaa, 0, 0, 0, 0, 0, 0, 1), 64))
121             .unwrap();
122         ip_addrs
123             .push(IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1), 64))
124             .unwrap();
125     });
126     iface
127         .routes_mut()
128         .add_default_ipv4_route(Ipv4Address::new(192, 168, 69, 100))
129         .unwrap();
130     iface
131         .routes_mut()
132         .add_default_ipv6_route(Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 0x100))
133         .unwrap();
134 
135     // Create sockets
136     let icmp_rx_buffer = icmp::PacketBuffer::new(vec![icmp::PacketMetadata::EMPTY], vec![0; 256]);
137     let icmp_tx_buffer = icmp::PacketBuffer::new(vec![icmp::PacketMetadata::EMPTY], vec![0; 256]);
138     let icmp_socket = icmp::Socket::new(icmp_rx_buffer, icmp_tx_buffer);
139     let mut sockets = SocketSet::new(vec![]);
140     let icmp_handle = sockets.add(icmp_socket);
141 
142     let mut send_at = Instant::from_millis(0);
143     let mut seq_no = 0;
144     let mut received = 0;
145     let mut echo_payload = [0xffu8; 40];
146     let mut waiting_queue = HashMap::new();
147     let ident = 0x22b;
148 
149     loop {
150         let timestamp = Instant::now();
151         iface.poll(timestamp, &mut device, &mut sockets);
152 
153         let timestamp = Instant::now();
154         let socket = sockets.get_mut::<icmp::Socket>(icmp_handle);
155         if !socket.is_open() {
156             socket.bind(icmp::Endpoint::Ident(ident)).unwrap();
157             send_at = timestamp;
158         }
159 
160         if socket.can_send() && seq_no < count as u16 && send_at <= timestamp {
161             NetworkEndian::write_i64(&mut echo_payload, timestamp.total_millis());
162 
163             match remote_addr {
164                 IpAddress::Ipv4(_) => {
165                     let (icmp_repr, mut icmp_packet) = send_icmp_ping!(
166                         Icmpv4Repr,
167                         Icmpv4Packet,
168                         ident,
169                         seq_no,
170                         echo_payload,
171                         socket,
172                         remote_addr
173                     );
174                     icmp_repr.emit(&mut icmp_packet, &device_caps.checksum);
175                 }
176                 IpAddress::Ipv6(_) => {
177                     let (icmp_repr, mut icmp_packet) = send_icmp_ping!(
178                         Icmpv6Repr,
179                         Icmpv6Packet,
180                         ident,
181                         seq_no,
182                         echo_payload,
183                         socket,
184                         remote_addr
185                     );
186                     icmp_repr.emit(
187                         &iface.ipv6_addr().unwrap().into_address(),
188                         &remote_addr,
189                         &mut icmp_packet,
190                         &device_caps.checksum,
191                     );
192                 }
193             }
194 
195             waiting_queue.insert(seq_no, timestamp);
196             seq_no += 1;
197             send_at += interval;
198         }
199 
200         if socket.can_recv() {
201             let (payload, _) = socket.recv().unwrap();
202 
203             match remote_addr {
204                 IpAddress::Ipv4(_) => {
205                     let icmp_packet = Icmpv4Packet::new_checked(&payload).unwrap();
206                     let icmp_repr = Icmpv4Repr::parse(&icmp_packet, &device_caps.checksum).unwrap();
207                     get_icmp_pong!(
208                         Icmpv4Repr,
209                         icmp_repr,
210                         payload,
211                         waiting_queue,
212                         remote_addr,
213                         timestamp,
214                         received
215                     );
216                 }
217                 IpAddress::Ipv6(_) => {
218                     let icmp_packet = Icmpv6Packet::new_checked(&payload).unwrap();
219                     let icmp_repr = Icmpv6Repr::parse(
220                         &remote_addr,
221                         &iface.ipv6_addr().unwrap().into_address(),
222                         &icmp_packet,
223                         &device_caps.checksum,
224                     )
225                     .unwrap();
226                     get_icmp_pong!(
227                         Icmpv6Repr,
228                         icmp_repr,
229                         payload,
230                         waiting_queue,
231                         remote_addr,
232                         timestamp,
233                         received
234                     );
235                 }
236             }
237         }
238 
239         waiting_queue.retain(|seq, from| {
240             if timestamp - *from < timeout {
241                 true
242             } else {
243                 println!("From {remote_addr} icmp_seq={seq} timeout");
244                 false
245             }
246         });
247 
248         if seq_no == count as u16 && waiting_queue.is_empty() {
249             break;
250         }
251 
252         let timestamp = Instant::now();
253         match iface.poll_at(timestamp, &sockets) {
254             Some(poll_at) if timestamp < poll_at => {
255                 let resume_at = cmp::min(poll_at, send_at);
256                 phy_wait(fd, Some(resume_at - timestamp)).expect("wait error");
257             }
258             Some(_) => (),
259             None => {
260                 phy_wait(fd, Some(send_at - timestamp)).expect("wait error");
261             }
262         }
263     }
264 
265     println!("--- {remote_addr} ping statistics ---");
266     println!(
267         "{} packets transmitted, {} received, {:.0}% packet loss",
268         seq_no,
269         received,
270         100.0 * (seq_no - received) as f64 / seq_no as f64
271     );
272 }
273