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 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 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#[derive(Clone, Debug, Eq, PartialEq)]
54#[non_exhaustive]
55pub struct EchoReply {
56 pub ttl: Option<u8>,
59 pub source: IpAddr,
61 pub sequence: u16,
63 pub identifier: u16,
65 pub payload_len: usize,
67 pub payload: Vec<u8>,
69 #[deprecated(since = "0.6.0", note = "use payload_len instead")]
71 pub size: usize,
72}
73
74impl EchoReply {
75 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
95fn 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
104fn 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
110fn 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}