icmp_packet/
echo_request.rs

1use crate::{
2    types::{Identifier, LenWithPayloadLengthDelimited, Payload, SequenceNumber},
3    ICMP_HEADER_SIZE,
4};
5
6//
7#[derive(Debug, Clone)]
8#[non_exhaustive]
9pub struct PayloadLengthDelimitedEchoRequest {
10    pub identifier: Identifier,
11    pub sequence_number: SequenceNumber,
12    payload_with_len: Payload,
13}
14
15impl PayloadLengthDelimitedEchoRequest {
16    pub fn new(
17        identifier: Option<Identifier>,
18        sequence_number: Option<SequenceNumber>,
19        payload: impl AsRef<[u8]>,
20    ) -> Self {
21        let payload = payload.as_ref();
22        let len = LenWithPayloadLengthDelimited::new(payload.len());
23
24        let mut payload_with_len = len.to_bytes().to_vec();
25        payload_with_len.extend_from_slice(payload);
26
27        Self {
28            identifier: identifier.unwrap_or_else(Identifier::gen),
29            sequence_number: sequence_number.unwrap_or_default(),
30            payload_with_len: payload_with_len.into(),
31        }
32    }
33
34    pub fn payload(&self) -> &[u8] {
35        &self.payload_with_len[2..]
36    }
37
38    pub fn len(&self) -> LenWithPayloadLengthDelimited {
39        LenWithPayloadLengthDelimited::from_bytes(
40            self.payload_with_len[..2].try_into().expect("Never"),
41        )
42    }
43
44    pub fn render_v4_packet_bytes(&self) -> Vec<u8> {
45        use pnet_packet::{
46            icmp::{checksum, echo_request::MutableEchoRequestPacket, IcmpPacket, IcmpTypes},
47            Packet as _,
48        };
49
50        //
51        let mut buf = vec![0; ICMP_HEADER_SIZE + self.payload_with_len.len()];
52        let mut echo_request_packet = MutableEchoRequestPacket::new(&mut buf[..])
53            .expect("Never when MutableEchoRequestPacket::new");
54        echo_request_packet.set_icmp_type(IcmpTypes::EchoRequest);
55        echo_request_packet.set_identifier(self.identifier.into_inner());
56        echo_request_packet.set_sequence_number(self.sequence_number.into_inner());
57        echo_request_packet.set_payload(&self.payload_with_len);
58
59        let icmp_packet =
60            IcmpPacket::new(echo_request_packet.packet()).expect("Never when IcmpPacket::new");
61        let checksum = checksum(&icmp_packet);
62        echo_request_packet.set_checksum(checksum);
63
64        echo_request_packet.packet().to_vec()
65    }
66
67    pub fn render_v6_packet_bytes(&self) -> Vec<u8> {
68        use pnet_packet::{
69            icmpv6::{echo_request::MutableEchoRequestPacket, Icmpv6Types},
70            Packet as _,
71        };
72
73        let mut buf = vec![0; ICMP_HEADER_SIZE + self.payload_with_len.len()];
74        let mut echo_request_packet = MutableEchoRequestPacket::new(&mut buf[..])
75            .expect("Never when MutableEchoRequestPacket::new");
76        echo_request_packet.set_icmpv6_type(Icmpv6Types::EchoRequest);
77        echo_request_packet.set_identifier(self.identifier.into_inner());
78        echo_request_packet.set_sequence_number(self.sequence_number.into_inner());
79        echo_request_packet.set_payload(&self.payload_with_len);
80
81        // https://github.com/kolapapa/surge-ping/blob/0.7.3/src/icmp/icmpv6.rs#L26
82        // https://tools.ietf.org/html/rfc3542#section-3.1
83        // the checksum is omitted, the kernel will insert it.
84
85        echo_request_packet.packet().to_vec()
86    }
87}
88
89#[cfg(test)]
90mod tests {
91    use super::*;
92
93    #[test]
94    fn test_new() {
95        let echo_request =
96            PayloadLengthDelimitedEchoRequest::new(Some(1.into()), Some(2.into()), b"1234");
97        assert_eq!(echo_request.payload(), b"1234");
98        assert_eq!(echo_request.len(), LenWithPayloadLengthDelimited::new(4));
99    }
100
101    #[test]
102    fn test_render() {
103        let echo_request =
104            PayloadLengthDelimitedEchoRequest::new(Some(1.into()), Some(2.into()), b"1234");
105        assert_eq!(
106            echo_request.render_v4_packet_bytes(),
107            vec![8, 0, 147, 146, 0, 1, 0, 2, 0, 4, 49, 50, 51, 52]
108        );
109        assert_eq!(
110            echo_request.render_v6_packet_bytes(),
111            vec![128, 0, 0, 0, 0, 1, 0, 2, 0, 4, 49, 50, 51, 52]
112        );
113    }
114}