123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278 |
- mod utils;
- use byteorder::{ByteOrder, NetworkEndian};
- use log::debug;
- use smoltcp::iface::SocketSet;
- use std::cmp;
- use std::collections::BTreeMap;
- use std::collections::HashMap;
- use std::os::unix::io::AsRawFd;
- use std::str::FromStr;
- use smoltcp::iface::{InterfaceBuilder, NeighborCache, Routes};
- use smoltcp::phy::wait as phy_wait;
- use smoltcp::phy::Device;
- use smoltcp::socket::icmp;
- use smoltcp::wire::{
- EthernetAddress, Icmpv4Packet, Icmpv4Repr, Icmpv6Packet, Icmpv6Repr, IpAddress, IpCidr,
- Ipv4Address, Ipv6Address,
- };
- use smoltcp::{
- phy::Medium,
- time::{Duration, Instant},
- };
- macro_rules! send_icmp_ping {
- ( $repr_type:ident, $packet_type:ident, $ident:expr, $seq_no:expr,
- $echo_payload:expr, $socket:expr, $remote_addr:expr ) => {{
- let icmp_repr = $repr_type::EchoRequest {
- ident: $ident,
- seq_no: $seq_no,
- data: &$echo_payload,
- };
- let icmp_payload = $socket.send(icmp_repr.buffer_len(), $remote_addr).unwrap();
- let icmp_packet = $packet_type::new_unchecked(icmp_payload);
- (icmp_repr, icmp_packet)
- }};
- }
- macro_rules! get_icmp_pong {
- ( $repr_type:ident, $repr:expr, $payload:expr, $waiting_queue:expr, $remote_addr:expr,
- $timestamp:expr, $received:expr ) => {{
- if let $repr_type::EchoReply { seq_no, data, .. } = $repr {
- if let Some(_) = $waiting_queue.get(&seq_no) {
- let packet_timestamp_ms = NetworkEndian::read_i64(data);
- println!(
- "{} bytes from {}: icmp_seq={}, time={}ms",
- data.len(),
- $remote_addr,
- seq_no,
- $timestamp.total_millis() - packet_timestamp_ms
- );
- $waiting_queue.remove(&seq_no);
- $received += 1;
- }
- }
- }};
- }
- fn main() {
- utils::setup_logging("warn");
- let (mut opts, mut free) = utils::create_options();
- utils::add_tuntap_options(&mut opts, &mut free);
- utils::add_middleware_options(&mut opts, &mut free);
- opts.optopt(
- "c",
- "count",
- "Amount of echo request packets to send (default: 4)",
- "COUNT",
- );
- opts.optopt(
- "i",
- "interval",
- "Interval between successive packets sent (seconds) (default: 1)",
- "INTERVAL",
- );
- opts.optopt(
- "",
- "timeout",
- "Maximum wait duration for an echo response packet (seconds) (default: 5)",
- "TIMEOUT",
- );
- free.push("ADDRESS");
- let mut matches = utils::parse_options(&opts, free);
- let device = utils::parse_tuntap_options(&mut matches);
- let fd = device.as_raw_fd();
- let mut device =
- utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false);
- let device_caps = device.capabilities();
- let address = IpAddress::from_str(&matches.free[0]).expect("invalid address format");
- let count = matches
- .opt_str("count")
- .map(|s| usize::from_str(&s).unwrap())
- .unwrap_or(4);
- let interval = matches
- .opt_str("interval")
- .map(|s| Duration::from_secs(u64::from_str(&s).unwrap()))
- .unwrap_or_else(|| Duration::from_secs(1));
- let timeout = Duration::from_secs(
- matches
- .opt_str("timeout")
- .map(|s| u64::from_str(&s).unwrap())
- .unwrap_or(5),
- );
- let neighbor_cache = NeighborCache::new(BTreeMap::new());
- let remote_addr = address;
- let icmp_rx_buffer = icmp::PacketBuffer::new(vec![icmp::PacketMetadata::EMPTY], vec![0; 256]);
- let icmp_tx_buffer = icmp::PacketBuffer::new(vec![icmp::PacketMetadata::EMPTY], vec![0; 256]);
- let icmp_socket = icmp::Socket::new(icmp_rx_buffer, icmp_tx_buffer);
- let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x02]);
- let src_ipv6 = IpAddress::v6(0xfdaa, 0, 0, 0, 0, 0, 0, 1);
- let ip_addrs = [
- IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24),
- IpCidr::new(src_ipv6, 64),
- IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1), 64),
- ];
- let default_v4_gw = Ipv4Address::new(192, 168, 69, 100);
- let default_v6_gw = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 0x100);
- let mut routes_storage = [None; 2];
- let mut routes = Routes::new(&mut routes_storage[..]);
- routes.add_default_ipv4_route(default_v4_gw).unwrap();
- routes.add_default_ipv6_route(default_v6_gw).unwrap();
- let medium = device.capabilities().medium;
- let mut builder = InterfaceBuilder::new().ip_addrs(ip_addrs).routes(routes);
- if medium == Medium::Ethernet {
- builder = builder
- .hardware_addr(ethernet_addr.into())
- .neighbor_cache(neighbor_cache);
- }
- let mut iface = builder.finalize(&mut device);
- let mut sockets = SocketSet::new(vec![]);
- let icmp_handle = sockets.add(icmp_socket);
- let mut send_at = Instant::from_millis(0);
- let mut seq_no = 0;
- let mut received = 0;
- let mut echo_payload = [0xffu8; 40];
- let mut waiting_queue = HashMap::new();
- let ident = 0x22b;
- loop {
- let timestamp = Instant::now();
- match iface.poll(timestamp, &mut device, &mut sockets) {
- Ok(_) => {}
- Err(e) => {
- debug!("poll error: {}", e);
- }
- }
- let timestamp = Instant::now();
- let socket = sockets.get_mut::<icmp::Socket>(icmp_handle);
- if !socket.is_open() {
- socket.bind(icmp::Endpoint::Ident(ident)).unwrap();
- send_at = timestamp;
- }
- if socket.can_send() && seq_no < count as u16 && send_at <= timestamp {
- NetworkEndian::write_i64(&mut echo_payload, timestamp.total_millis());
- match remote_addr {
- IpAddress::Ipv4(_) => {
- let (icmp_repr, mut icmp_packet) = send_icmp_ping!(
- Icmpv4Repr,
- Icmpv4Packet,
- ident,
- seq_no,
- echo_payload,
- socket,
- remote_addr
- );
- icmp_repr.emit(&mut icmp_packet, &device_caps.checksum);
- }
- IpAddress::Ipv6(_) => {
- let (icmp_repr, mut icmp_packet) = send_icmp_ping!(
- Icmpv6Repr,
- Icmpv6Packet,
- ident,
- seq_no,
- echo_payload,
- socket,
- remote_addr
- );
- icmp_repr.emit(
- &src_ipv6,
- &remote_addr,
- &mut icmp_packet,
- &device_caps.checksum,
- );
- }
- }
- waiting_queue.insert(seq_no, timestamp);
- seq_no += 1;
- send_at += interval;
- }
- if socket.can_recv() {
- let (payload, _) = socket.recv().unwrap();
- match remote_addr {
- IpAddress::Ipv4(_) => {
- let icmp_packet = Icmpv4Packet::new_checked(&payload).unwrap();
- let icmp_repr = Icmpv4Repr::parse(&icmp_packet, &device_caps.checksum).unwrap();
- get_icmp_pong!(
- Icmpv4Repr,
- icmp_repr,
- payload,
- waiting_queue,
- remote_addr,
- timestamp,
- received
- );
- }
- IpAddress::Ipv6(_) => {
- let icmp_packet = Icmpv6Packet::new_checked(&payload).unwrap();
- let icmp_repr = Icmpv6Repr::parse(
- &remote_addr,
- &src_ipv6,
- &icmp_packet,
- &device_caps.checksum,
- )
- .unwrap();
- get_icmp_pong!(
- Icmpv6Repr,
- icmp_repr,
- payload,
- waiting_queue,
- remote_addr,
- timestamp,
- received
- );
- }
- }
- }
- waiting_queue.retain(|seq, from| {
- if timestamp - *from < timeout {
- true
- } else {
- println!("From {} icmp_seq={} timeout", remote_addr, seq);
- false
- }
- });
- if seq_no == count as u16 && waiting_queue.is_empty() {
- break;
- }
- let timestamp = Instant::now();
- match iface.poll_at(timestamp, &sockets) {
- Some(poll_at) if timestamp < poll_at => {
- let resume_at = cmp::min(poll_at, send_at);
- phy_wait(fd, Some(resume_at - timestamp)).expect("wait error");
- }
- Some(_) => (),
- None => {
- phy_wait(fd, Some(send_at - timestamp)).expect("wait error");
- }
- }
- }
- println!("--- {} ping statistics ---", remote_addr);
- println!(
- "{} packets transmitted, {} received, {:.0}% packet loss",
- seq_no,
- received,
- 100.0 * (seq_no - received) as f64 / seq_no as f64
- );
- }
|