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