tiny_ping/packet/
icmp.rs

1use crate::error::Error;
2use std::io::Write;
3
4pub const HEADER_SIZE: usize = 8;
5
6pub struct IcmpV4;
7pub struct IcmpV6;
8
9pub trait Proto {
10    const ECHO_REQUEST_TYPE: u8;
11    const ECHO_REQUEST_CODE: u8;
12    const ECHO_REPLY_TYPE: u8;
13    const ECHO_REPLY_CODE: u8;
14}
15
16impl Proto for IcmpV4 {
17    const ECHO_REQUEST_TYPE: u8 = 8;
18    const ECHO_REQUEST_CODE: u8 = 0;
19    const ECHO_REPLY_TYPE: u8 = 0;
20    const ECHO_REPLY_CODE: u8 = 0;
21}
22
23impl Proto for IcmpV6 {
24    const ECHO_REQUEST_TYPE: u8 = 128;
25    const ECHO_REQUEST_CODE: u8 = 0;
26    const ECHO_REPLY_TYPE: u8 = 129;
27    const ECHO_REPLY_CODE: u8 = 0;
28}
29
30pub struct EchoRequest {
31    pub ident: u16,
32    pub seq_cnt: u16,
33}
34
35impl EchoRequest {
36    pub fn encode<P: Proto>(&self, buffer: &mut [u8], payload: &[u8]) -> Result<Vec<u8>, Error> {
37        buffer[0] = P::ECHO_REQUEST_TYPE;
38        buffer[1] = P::ECHO_REQUEST_CODE;
39
40        buffer[4] = (self.ident >> 8) as u8;
41        buffer[5] = self.ident as u8;
42        buffer[6] = (self.seq_cnt >> 8) as u8;
43        buffer[7] = self.seq_cnt as u8;
44
45        if (&mut buffer[8..]).write(payload).is_err() {
46            return Err(Error::InvalidSize);
47        }
48
49        write_checksum(buffer);
50        Ok(buffer.to_vec())
51    }
52}
53
54pub struct EchoReply<'a> {
55    pub ident: u16,
56    pub seq_cnt: u16,
57    pub payload: &'a [u8],
58}
59
60impl<'a> EchoReply<'a> {
61    pub fn decode<P: Proto>(buffer: &'a [u8]) -> Result<Self, Error> {
62        if buffer.as_ref().len() < HEADER_SIZE {
63            return Err(Error::InvalidSize);
64        }
65
66        let type_ = buffer[0];
67        let code = buffer[1];
68        if type_ != P::ECHO_REPLY_TYPE && code != P::ECHO_REPLY_CODE {
69            return Err(Error::InvalidPacket);
70        }
71
72        let ident = (u16::from(buffer[4]) << 8) + u16::from(buffer[5]);
73        let seq_cnt = (u16::from(buffer[6]) << 8) + u16::from(buffer[7]);
74
75        let payload = &buffer[HEADER_SIZE..];
76
77        Ok(EchoReply {
78            ident,
79            seq_cnt,
80            payload,
81        })
82    }
83}
84
85fn write_checksum(buffer: &mut [u8]) {
86    let mut sum = 0u32;
87    for word in buffer.chunks(2) {
88        let mut part = u16::from(word[0]) << 8;
89        if word.len() > 1 {
90            part += u16::from(word[1]);
91        }
92        sum = sum.wrapping_add(u32::from(part));
93    }
94
95    while (sum >> 16) > 0 {
96        sum = (sum & 0xffff) + (sum >> 16);
97    }
98
99    let sum = !sum as u16;
100
101    buffer[2] = (sum >> 8) as u8;
102    buffer[3] = (sum & 0xff) as u8;
103}