Skip to content

Commit 4d3dd89

Browse files
committed
feat(net): Parse payload for ICMP
ICMP Error messages (such as "Destination Unreachable") may contain a payload, made of a full or truncated version of the original IP packet that triggered the error. Parse this packet using the existing EmbeddedHeaders structure added in previous commits. Signed-off-by: Quentin Monnet <[email protected]>
1 parent d69fa96 commit 4d3dd89

File tree

4 files changed

+53
-6
lines changed

4 files changed

+53
-6
lines changed

net/src/headers/mod.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -300,8 +300,10 @@ impl ParsePayload for Header {
300300
None
301301
}
302302
}
303+
Icmp4(icmp4) => icmp4.parse_payload(cursor).map(Header::from),
304+
Icmp6(icmp6) => icmp6.parse_payload(cursor).map(Header::from),
303305
Udp(udp) => udp.parse_payload(cursor).map(Header::from),
304-
Encap(_) | Tcp(_) | Icmp4(_) | Icmp6(_) | EmbeddedIp(_) => None,
306+
Encap(_) | Tcp(_) | EmbeddedIp(_) => None,
305307
}
306308
}
307309
}

net/src/icmp4/mod.rs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@
33

44
//! `ICMPv4` header type and logic.
55
6-
use crate::parse::{DeParse, DeParseError, IntoNonZeroUSize, LengthError, Parse, ParseError};
7-
use etherparse::{Icmpv4Header, Icmpv4Type};
8-
96
mod checksum;
107

118
pub use checksum::*;
129

10+
use crate::headers::{EmbeddedHeaders, EmbeddedIpVersion};
11+
use crate::parse::{
12+
DeParse, DeParseError, IntoNonZeroUSize, LengthError, Parse, ParseError, ParseWith, Reader,
13+
};
14+
use etherparse::{Icmpv4Header, Icmpv4Type};
1315
use std::{net::IpAddr, num::NonZero};
1416

1517
#[allow(unused_imports)] // re-export
@@ -127,6 +129,16 @@ impl Icmp4 {
127129
checksum: 0,
128130
})
129131
}
132+
133+
pub(crate) fn parse_payload(&self, cursor: &mut Reader) -> Option<EmbeddedHeaders> {
134+
if !self.is_error_message() {
135+
return None;
136+
}
137+
let (headers, consumed) =
138+
EmbeddedHeaders::parse_with(EmbeddedIpVersion::Ipv4, cursor.inner).ok()?;
139+
cursor.consume(consumed).ok()?;
140+
Some(headers)
141+
}
130142
}
131143

132144
impl Parse for Icmp4 {

net/src/icmp6/mod.rs

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@ mod checksum;
77

88
pub use checksum::*;
99

10-
use crate::parse::{DeParse, DeParseError, IntoNonZeroUSize, LengthError, Parse, ParseError};
10+
use crate::headers::{EmbeddedHeaders, EmbeddedIpVersion};
11+
use crate::parse::{
12+
DeParse, DeParseError, IntoNonZeroUSize, LengthError, Parse, ParseError, ParseWith, Reader,
13+
};
1114
use etherparse::{Icmpv6Header, Icmpv6Type};
1215
use std::num::NonZero;
1316

@@ -31,6 +34,26 @@ impl Icmp6 {
3134
&mut self.0.icmp_type
3235
}
3336

37+
/// Returns true if the ICMP type is an error message
38+
#[must_use]
39+
pub fn is_error_message(&self) -> bool {
40+
// List all types to make it sure we catch any new addition to the enum
41+
match self.icmp_type() {
42+
Icmpv6Type::DestinationUnreachable(_)
43+
| Icmpv6Type::PacketTooBig { .. }
44+
| Icmpv6Type::TimeExceeded(_)
45+
| Icmpv6Type::ParameterProblem(_) => true,
46+
Icmpv6Type::Unknown { .. }
47+
| Icmpv6Type::EchoRequest(_)
48+
| Icmpv6Type::EchoReply(_)
49+
| Icmpv6Type::RouterSolicitation
50+
| Icmpv6Type::RouterAdvertisement(_)
51+
| Icmpv6Type::NeighborSolicitation
52+
| Icmpv6Type::NeighborAdvertisement(_)
53+
| Icmpv6Type::Redirect => false,
54+
}
55+
}
56+
3457
/// Creates a new `Icmp6` with the given type.
3558
///
3659
/// The checksum will be set to zero.
@@ -41,6 +64,16 @@ impl Icmp6 {
4164
checksum: 0,
4265
})
4366
}
67+
68+
pub(crate) fn parse_payload(&self, cursor: &mut Reader) -> Option<EmbeddedHeaders> {
69+
if !self.is_error_message() {
70+
return None;
71+
}
72+
let (headers, consumed) =
73+
EmbeddedHeaders::parse_with(EmbeddedIpVersion::Ipv6, cursor.inner).ok()?;
74+
cursor.consume(consumed).ok()?;
75+
Some(headers)
76+
}
4477
}
4578

4679
impl Parse for Icmp6 {

net/src/parse.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ impl Reader<'_> {
151151
})
152152
}
153153

154-
fn consume(&mut self, n: NonZero<u16>) -> Result<(), LengthError> {
154+
pub(crate) fn consume(&mut self, n: NonZero<u16>) -> Result<(), LengthError> {
155155
if n.get() > self.remaining {
156156
return Err(LengthError {
157157
expected: n.into_non_zero_usize(),

0 commit comments

Comments
 (0)