Skip to main content

tiny_ping/
icmp.rs

1use std::net::IpAddr;
2
3use crate::error::Result;
4use crate::packet::{
5    EchoReply as PacketEchoReply, EchoRequest as PacketEchoRequest, IcmpV4, IcmpV6, IpV4Packet,
6};
7
8#[derive(Debug)]
9pub struct EchoRequest {
10    pub destination: IpAddr,
11    pub ident: u16,
12    pub seq_cnt: u16,
13}
14
15impl EchoRequest {
16    pub fn new(destination: IpAddr, ident: u16, seq_cnt: u16) -> Self {
17        EchoRequest {
18            destination,
19            ident,
20            seq_cnt,
21        }
22    }
23
24    pub(crate) fn encode_with_payload(&self, payload: &[u8]) -> Result<Vec<u8>> {
25        match self.destination {
26            IpAddr::V4(_) => self.encode_icmp_v4(payload),
27            IpAddr::V6(_) => self.encode_icmp_v6(payload),
28        }
29    }
30
31    /// Encodes as an ICMPv4 EchoRequest.
32    fn encode_icmp_v4(&self, payload: &[u8]) -> Result<Vec<u8>> {
33        let req = PacketEchoRequest {
34            ident: self.ident,
35            seq_cnt: self.seq_cnt,
36        };
37        let mut buffer = vec![0; 8 + payload.len()];
38        req.encode::<IcmpV4>(&mut buffer, payload)
39    }
40
41    /// Encodes as an ICMPv6 EchoRequest.
42    fn encode_icmp_v6(&self, payload: &[u8]) -> Result<Vec<u8>> {
43        let req = PacketEchoRequest {
44            ident: self.ident,
45            seq_cnt: self.seq_cnt,
46        };
47        let mut buffer = vec![0; 8 + payload.len()];
48        req.encode::<IcmpV6>(&mut buffer, payload)
49    }
50}
51
52/// `EchoReply` struct, which contains some packet information.
53#[derive(Clone, Debug, Eq, PartialEq)]
54#[non_exhaustive]
55pub struct EchoReply {
56    /// IP Time To Live for outgoing packets. Present for ICMPv4 replies,
57    /// absent for ICMPv6 replies.
58    pub ttl: Option<u8>,
59    /// Source address of ICMP packet.
60    pub source: IpAddr,
61    /// Sequence of ICMP packet.
62    pub sequence: u16,
63    /// Identifier of ICMP packet.
64    pub identifier: u16,
65    /// Size of ICMP echo payload.
66    pub payload_len: usize,
67    /// ICMP echo payload.
68    pub payload: Vec<u8>,
69    /// Deprecated alias for `payload_len`.
70    #[deprecated(since = "0.6.0", note = "use payload_len instead")]
71    pub size: usize,
72}
73
74impl EchoReply {
75    /// Unpack IP packets received from socket as `EchoReply` struct.
76    pub fn decode(addr: IpAddr, buf: &[u8]) -> Result<EchoReply> {
77        Self::decode_raw(addr, buf)
78    }
79
80    pub(crate) fn decode_raw(addr: IpAddr, buf: &[u8]) -> Result<EchoReply> {
81        match addr {
82            IpAddr::V4(_) => decode_icmpv4(addr, buf),
83            IpAddr::V6(_) => decode_icmpv6(addr, buf),
84        }
85    }
86
87    pub(crate) fn decode_dgram(source: IpAddr, buf: &[u8]) -> Result<EchoReply> {
88        match source {
89            IpAddr::V4(_) => decode_icmpv4_dgram(source, buf),
90            IpAddr::V6(_) => decode_icmpv6(source, buf),
91        }
92    }
93}
94
95/// Decodes an ICMPv4 packet received from an IPv4 raw socket
96fn decode_icmpv4(_addr: IpAddr, buf: &[u8]) -> Result<EchoReply> {
97    let ipv4_decoded = IpV4Packet::decode(buf)?;
98    let source = ipv4_decoded.source;
99    let ttl = Some(ipv4_decoded.ttl);
100    let icmp_decoded = PacketEchoReply::decode::<IcmpV4>(ipv4_decoded.data)?;
101    Ok(reply_from_packet(source, ttl, icmp_decoded))
102}
103
104/// Decodes an ICMPv4 packet received from an IPv4 datagram socket.
105fn decode_icmpv4_dgram(source: IpAddr, buf: &[u8]) -> Result<EchoReply> {
106    let icmp_decoded = PacketEchoReply::decode::<IcmpV4>(buf)?;
107    Ok(reply_from_packet(source, None, icmp_decoded))
108}
109
110/// Decodes an ICMPv6 packet received from an IPv6 raw socket
111fn decode_icmpv6(source: IpAddr, buf: &[u8]) -> Result<EchoReply> {
112    let icmp_decoded = PacketEchoReply::decode::<IcmpV6>(buf)?;
113    Ok(reply_from_packet(source, None, icmp_decoded))
114}
115
116#[allow(deprecated)]
117fn reply_from_packet(source: IpAddr, ttl: Option<u8>, packet: PacketEchoReply<'_>) -> EchoReply {
118    EchoReply {
119        ttl,
120        source,
121        sequence: packet.seq_cnt,
122        identifier: packet.ident,
123        payload_len: packet.payload.len(),
124        payload: packet.payload.to_vec(),
125        size: packet.payload.len(),
126    }
127}
128
129#[cfg(test)]
130mod tests {
131    use super::*;
132    use std::net::Ipv4Addr;
133
134    #[test]
135    fn decodes_raw_ipv4_reply_with_header_metadata() {
136        let packet = [
137            0x45, 0, 0, 30, 0, 0, 0, 0, 42, 1, 0, 0, 203, 0, 113, 9, 8, 8, 8, 8, 0, 0, 0, 0, 0x12,
138            0x34, 0, 7, b'o', b'k',
139        ];
140
141        let reply = EchoReply::decode_raw(IpAddr::V4(Ipv4Addr::new(8, 8, 8, 8)), &packet).unwrap();
142
143        assert_eq!(reply.source, IpAddr::V4(Ipv4Addr::new(203, 0, 113, 9)));
144        assert_eq!(reply.ttl, Some(42));
145        assert_eq!(reply.identifier, 0x1234);
146        assert_eq!(reply.sequence, 7);
147        assert_eq!(reply.payload, b"ok");
148        assert_eq!(reply.payload_len, 2);
149    }
150
151    #[test]
152    fn decodes_dgram_ipv4_reply_without_ip_header() {
153        let packet = [0, 0, 0, 0, 0x12, 0x34, 0, 7, b'o', b'k'];
154        let source = IpAddr::V4(Ipv4Addr::new(203, 0, 113, 9));
155
156        let reply = EchoReply::decode_dgram(source, &packet).unwrap();
157
158        assert_eq!(reply.source, source);
159        assert_eq!(reply.ttl, None);
160        assert_eq!(reply.identifier, 0x1234);
161        assert_eq!(reply.sequence, 7);
162        assert_eq!(reply.payload, b"ok");
163    }
164
165    #[test]
166    fn decodes_ipv6_reply_without_ip_header() {
167        let source = "2001:db8::1".parse().unwrap();
168        let packet = [129, 0, 0, 0, 0x12, 0x34, 0, 7, b'o', b'k'];
169
170        let reply = EchoReply::decode_raw(source, &packet).unwrap();
171
172        assert_eq!(reply.source, source);
173        assert_eq!(reply.ttl, None);
174        assert_eq!(reply.identifier, 0x1234);
175        assert_eq!(reply.sequence, 7);
176        assert_eq!(reply.payload, b"ok");
177    }
178}