ping6/
ping6.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::Ipv6Addr,
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("::1".to_owned());
25    let parsed_addr = address.parse::<Ipv6Addr>().unwrap();
26    let packet_handler = |pkt: Icmpv6Packet, send_time: Instant, addr: Ipv6Addr| -> Option<()> {
27        let now = Instant::now();
28        let elapsed = now - send_time;
29        if addr == parsed_addr {
30            // TODO
31            if let Icmpv6Message::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 socket6 = IcmpSocket6::new().unwrap();
55    socket6.bind("::0".parse::<Ipv6Addr>().unwrap()).unwrap();
56    // TODO(jwall): The first packet we recieve will be the one we sent.
57    // We need to implement packet filtering for the socket.
58    let mut sequence = 0 as u16;
59    loop {
60        let packet = Icmpv6Packet::with_echo_request(
61            42,
62            sequence,
63            vec![
64                0x20, 0x20, 0x75, 0x73, 0x74, 0x20, 0x61, 0x20, 0x66, 0x6c, 0x65, 0x73, 0x68, 0x20,
65                0x77, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x20, 0x74, 0x69, 0x73, 0x20, 0x62, 0x75, 0x74,
66                0x20, 0x61, 0x20, 0x73, 0x63, 0x72, 0x61, 0x74, 0x63, 0x68, 0x20, 0x20, 0x6b, 0x6e,
67                0x69, 0x67, 0x68, 0x74, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x6e, 0x69, 0x20, 0x20, 0x20,
68            ],
69        )
70        .unwrap();
71        let send_time = Instant::now();
72        socket6
73            .send_to(address.parse::<Ipv6Addr>().unwrap(), packet)
74            .unwrap();
75        socket6.set_timeout(Some(Duration::from_secs(1)));
76        loop {
77            let (resp, sock_addr) = match socket6.rcv_from() {
78                Ok(tpl) => tpl,
79                Err(e) => {
80                    eprintln!("{:?}", e);
81                    break;
82                }
83            };
84            if packet_handler(resp, send_time, *sock_addr.as_socket_ipv6().unwrap().ip()).is_some()
85            {
86                std::thread::sleep(Duration::from_millis(1000));
87                break;
88            }
89        }
90        sequence = sequence.wrapping_add(1);
91    }
92}