ping4/
ping4.rs

1// Copyright 2021 Jeremy Wall
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14use std::{
15    net::Ipv4Addr,
16    time::{Duration, Instant},
17};
18
19use icmp_socket::packet::WithEchoRequest;
20use icmp_socket::socket::IcmpSocket;
21use icmp_socket::*;
22
23pub fn main() {
24    let address = std::env::args().nth(1).unwrap_or("127.0.0.1".to_owned());
25    let parsed_addr = address.parse::<Ipv4Addr>().unwrap();
26    let packet_handler = |pkt: Icmpv4Packet, send_time: Instant, addr: Ipv4Addr| -> Option<()> {
27        let now = Instant::now();
28        let elapsed = now - send_time;
29        if addr == parsed_addr {
30            // TODO
31            if let Icmpv4Message::EchoReply {
32                identifier: _,
33                sequence,
34                payload,
35            } = pkt.message
36            {
37                println!(
38                    "Ping {} seq={} time={}ms size={}",
39                    addr,
40                    sequence,
41                    (elapsed.as_micros() as f64) / 1000.0,
42                    payload.len()
43                );
44            } else {
45                //eprintln!("Discarding non-reply {:?}", pkt);
46                return None;
47            }
48            Some(())
49        } else {
50            eprintln!("Discarding packet from {}", addr);
51            None
52        }
53    };
54    let mut socket4 = IcmpSocket4::new().unwrap();
55    socket4
56        .bind("0.0.0.0".parse::<Ipv4Addr>().unwrap())
57        .unwrap();
58    // TODO(jwall): The first packet we recieve will be the one we sent.
59    // We need to implement packet filtering for the socket.
60    let mut sequence = 0 as u16;
61    loop {
62        let packet = Icmpv4Packet::with_echo_request(
63            42,
64            sequence,
65            vec![
66                0x20, 0x20, 0x75, 0x73, 0x74, 0x20, 0x61, 0x20, 0x66, 0x6c, 0x65, 0x73, 0x68, 0x20,
67                0x77, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x20, 0x74, 0x69, 0x73, 0x20, 0x62, 0x75, 0x74,
68                0x20, 0x61, 0x20, 0x73, 0x63, 0x72, 0x61, 0x74, 0x63, 0x68, 0x20, 0x20, 0x6b, 0x6e,
69                0x69, 0x67, 0x68, 0x74, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x6e, 0x69, 0x20, 0x20, 0x20,
70            ],
71        )
72        .unwrap();
73        let send_time = Instant::now();
74        socket4
75            .send_to(address.parse::<Ipv4Addr>().unwrap(), packet)
76            .unwrap();
77        socket4.set_timeout(Some(Duration::from_secs(1)));
78        loop {
79            let (resp, sock_addr) = match socket4.rcv_from() {
80                Ok(tpl) => tpl,
81                Err(e) => {
82                    eprintln!("{:?}", e);
83                    break;
84                }
85            };
86            if packet_handler(resp, send_time, *sock_addr.as_socket_ipv4().unwrap().ip()).is_some()
87            {
88                std::thread::sleep(Duration::from_secs(1));
89                break;
90            }
91        }
92        sequence = sequence.wrapping_add(1);
93    }
94}