Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions etherparse/src/transport/icmpv6/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ pub use parameter_problem_header::*;
mod time_exceeded_code;
pub use time_exceeded_code::*;

mod neighbour_discovery;
pub use neighbour_discovery::*;

/// The maximum number of bytes/octets the ICMPv6 part of a packet can contain.
///
/// The value is determined by the maximum value of the "Upper-Layer Packet Length"
Expand Down
70 changes: 70 additions & 0 deletions etherparse/src/transport/icmpv6/neighbour_discovery.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct NeighbourAdverisementHeader {
pub router: bool,
pub solicited: bool,
pub r#override: bool,
}

const ROUTER_MASK: u8 = 0b10000000;
const SOLICITED_MASK: u8 = 0b01000000;
const OVERRIDE_MASK: u8 = 0b00100000;

impl NeighbourAdverisementHeader {
pub fn from_bytes(bytes: [u8; 4]) -> Self {
let first_byte = bytes[0];

Self {
router: (first_byte & ROUTER_MASK) == ROUTER_MASK,
solicited: (first_byte & SOLICITED_MASK) == SOLICITED_MASK,
r#override: (first_byte & OVERRIDE_MASK) == OVERRIDE_MASK,
}
}

pub fn to_bytes(&self) -> [u8; 4] {
let mut first_byte = 0u8;

if self.router {
first_byte |= ROUTER_MASK;
}
if self.solicited {
first_byte |= SOLICITED_MASK;
}
if self.r#override {
first_byte |= OVERRIDE_MASK;
}

[first_byte, 0, 0, 0]
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn reads_router_bit_correctly() {
assert!(NeighbourAdverisementHeader::from_bytes([0b10000000, 0, 0, 0]).router);
assert!(!NeighbourAdverisementHeader::from_bytes([0, 0, 0, 0]).router);
}

#[test]
fn reads_solicited_bit_correctly() {
assert!(NeighbourAdverisementHeader::from_bytes([0b01000000, 0, 0, 0]).solicited);
assert!(!NeighbourAdverisementHeader::from_bytes([0, 0, 0, 0]).solicited);
}

#[test]
fn reads_override_bit_correctly() {
assert!(NeighbourAdverisementHeader::from_bytes([0b00100000, 0, 0, 0]).r#override);
assert!(!NeighbourAdverisementHeader::from_bytes([0, 0, 0, 0]).r#override);
}

#[test]
fn reads_combined_bit_correctly() {
let header = NeighbourAdverisementHeader::from_bytes([0b11100000, 0, 0, 0]);

assert!(header.router);
assert!(header.solicited);
assert!(header.r#override);
}
}
4 changes: 4 additions & 0 deletions etherparse/src/transport/icmpv6_header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,10 @@
),
EchoRequest(echo) => return_4u8(TYPE_ECHO_REQUEST, 0, echo.to_bytes()),
EchoReply(echo) => return_4u8(TYPE_ECHO_REPLY, 0, echo.to_bytes()),
NeighbourSoliciation => return_4u8(TYPE_NEIGHBOR_SOLICITATION, 0, [0; 4]),
NeighbourAdvertisement(header) => {
return_4u8(TYPE_NEIGHBOR_ADVERTISEMENT, 0, header.to_bytes())
}
}
}
}
Expand All @@ -191,7 +195,7 @@
#[cfg(test)]
mod test {
use crate::{
err::{ValueTooBigError, ValueType},

Check warning on line 198 in etherparse/src/transport/icmpv6_header.rs

View workflow job for this annotation

GitHub Actions / cargo build and test (linux-32bit-stable)

unused imports: `ValueTooBigError` and `ValueType`

Check warning on line 198 in etherparse/src/transport/icmpv6_header.rs

View workflow job for this annotation

GitHub Actions / cargo build and test (linux-32bit-stable)

unused imports: `ValueTooBigError` and `ValueType`
icmpv6::*,
test_gens::*,
*,
Expand Down
12 changes: 12 additions & 0 deletions etherparse/src/transport/icmpv6_slice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,18 @@ impl<'a> Icmpv6Slice<'a> {
return EchoReply(IcmpEchoHeader::from_bytes(self.bytes5to8()));
}
}
TYPE_NEIGHBOR_SOLICITATION => {
if 0 == self.code_u8() {
return NeighbourSoliciation;
}
}
TYPE_NEIGHBOR_ADVERTISEMENT => {
if 0 == self.code_u8() {
return NeighbourAdvertisement(NeighbourAdverisementHeader::from_bytes(
self.bytes5to8(),
));
}
}
_ => {}
}
Unknown {
Expand Down
115 changes: 113 additions & 2 deletions etherparse/src/transport/icmpv6_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@
/// ParameterProblem(header) => println!("{:?}", header),
/// EchoRequest(header) => println!("{:?}", header),
/// EchoReply(header) => println!("{:?}", header),
/// NeighbourSoliciation => println!("NeighbourSoliciation"),
/// NeighbourAdvertisement(header) => println!("{:?}", header),
/// }
/// },
/// _ => {},
Expand Down Expand Up @@ -329,6 +331,101 @@
/// The data received in the ICMPv6 Echo Request message MUST be returned
/// entirely and unmodified in the ICMPv6 Echo Reply message.
EchoReply(IcmpEchoHeader),

/// Requesting the link-layer address of a target node.
///
/// # What is part of the header for `Icmpv6Type::NeighbourSoliciation`?
///
/// For the [`Icmpv6Type::NeighbourSoliciation`] type the first 8 bytes/octets
/// of the ICMPv6 packet are part of the header.
///
/// The data part of the ICMP Neighbor Advertisement packet is part of the payload
/// ([`Icmpv6Slice::payload`] & [`PacketHeaders::payload`]) and not part of the
/// [`Icmpv6Header`].
/// ```text
/// 0 1 2 3 4
/// +---------------------------------------------------------------+ -
/// | 135 | 0 | checksum (in Icmpv6Header) | |
/// +---------------------------------------------------------------+ | part of header & type
/// | <unused> | ↓
/// +---------------------------------------------------------------+ -
/// | | |
/// | Target Address | part of payload
/// | | ↓
/// +---------------------------------------------------------------+ -
/// | Options ...
/// +-----------------
/// ```
///
/// # RFC 4861 Description
///
/// Nodes send Neighbor Solicitations to request the link-layer address
/// of a target node while also providing their own link-layer address to
/// the target. Neighbor Solicitations are multicast when the node needs
/// to resolve an address and unicast when the node seeks to verify the
/// reachability of a neighbor.
NeighbourSoliciation,

/// Response to a `NeighbourSoliciation` message (or sent proactively).
///
/// # What is part of the header for `Icmpv6Type::NeighbourAdvertisement`?
///
/// For the [`Icmpv6Type::NeighbourAdvertisement`] type the first 8 bytes/octets
/// of the ICMPv6 packet are part of the header. This includes 3 bits for
/// - router
/// - solicited
/// - override
///
/// The data part of the ICMP Neighbor Advertisement packet is part of the payload
/// ([`Icmpv6Slice::payload`] & [`PacketHeaders::payload`]) and not part of the
/// [`Icmpv6Header`].
///
/// ```text
/// 0 1 2 3 4
/// +---------------------------------------------------------------+ -
/// | 136 | 0 | checksum (in Icmpv6Header) | |
/// +---------------------------------------------------------------+ | part of header & type
/// |R|S|O| <unused> | ↓
/// +---------------------------------------------------------------+ -
/// | | |
/// | Target Address | part of payload
/// | | ↓
/// +---------------------------------------------------------------+ -
/// | Options ...
/// +-----------------
/// ```
///
/// # RFC 4861 Description
///
/// A node sends Neighbor Advertisements in response to Neighbor
/// Solicitations and sends unsolicited Neighbor Advertisements in order
/// to (unreliably) propagate new information quickly.
///
/// R Router flag. When set, the R-bit indicates that
/// the sender is a router. The R-bit is used by
/// Neighbor Unreachability Detection to detect a
/// router that changes to a host.
///
/// S Solicited flag. When set, the S-bit indicates that
/// the advertisement was sent in response to a
/// Neighbor Solicitation from the Destination address.
/// The S-bit is used as a reachability confirmation
/// for Neighbor Unreachability Detection. It MUST NOT
/// be set in multicast advertisements or in
/// unsolicited unicast advertisements.
///
/// O Override flag. When set, the O-bit indicates that
/// the advertisement should override an existing cache
/// entry and update the cached link-layer address.
/// When it is not set the advertisement will not
/// update a cached link-layer address though it will
/// update an existing Neighbor Cache entry for which
/// no link-layer address is known. It SHOULD NOT be
/// set in solicited advertisements for anycast
/// addresses and in solicited proxy advertisements.
/// It SHOULD be set in other solicited advertisements
/// and in unsolicited advertisements.
NeighbourAdvertisement(icmpv6::NeighbourAdverisementHeader),
}

impl Icmpv6Type {
Expand All @@ -348,6 +445,8 @@
ParameterProblem(_) => TYPE_PARAMETER_PROBLEM,
EchoRequest(_) => TYPE_ECHO_REQUEST,
EchoReply(_) => TYPE_ECHO_REPLY,
NeighbourSoliciation => TYPE_NEIGHBOR_SOLICITATION,
NeighbourAdvertisement { .. } => TYPE_NEIGHBOR_ADVERTISEMENT,
}
}

Expand All @@ -367,6 +466,8 @@
ParameterProblem(header) => header.code.code_u8(),
EchoRequest(_) => 0,
EchoReply(_) => 0,
NeighbourSoliciation => 0,
NeighbourAdvertisement { .. } => 0,
}
}

Expand Down Expand Up @@ -439,6 +540,12 @@
EchoReply(echo) => pseudo_sum
.add_2bytes([TYPE_ECHO_REPLY, 0])
.add_4bytes(echo.to_bytes()),
NeighbourSoliciation => pseudo_sum
.add_2bytes([TYPE_NEIGHBOR_SOLICITATION, 0])
.add_4bytes([0; 4]),
NeighbourAdvertisement(header) => pseudo_sum
.add_2bytes([TYPE_NEIGHBOR_ADVERTISEMENT, 0])
.add_4bytes(header.to_bytes()),
}
.add_slice(payload)
.ones_complement()
Expand Down Expand Up @@ -475,7 +582,9 @@
| TimeExceeded(_)
| ParameterProblem(_)
| EchoRequest(_)
| EchoReply(_) => 8,
| EchoReply(_)
| NeighbourSoliciation
| NeighbourAdvertisement { .. } => 8,
}
}

Expand All @@ -495,7 +604,9 @@
| TimeExceeded(_)
| ParameterProblem(_)
| EchoRequest(_)
| EchoReply(_) => None,
| EchoReply(_)
| NeighbourSoliciation
| NeighbourAdvertisement { .. } => None,
}
}
}
Expand All @@ -503,7 +614,7 @@
#[cfg(test)]
mod test {
use crate::{
err::{ValueTooBigError, ValueType},

Check warning on line 617 in etherparse/src/transport/icmpv6_type.rs

View workflow job for this annotation

GitHub Actions / cargo build and test (linux-32bit-stable)

unused imports: `ValueTooBigError` and `ValueType`

Check warning on line 617 in etherparse/src/transport/icmpv6_type.rs

View workflow job for this annotation

GitHub Actions / cargo build and test (linux-32bit-stable)

unused imports: `ValueTooBigError` and `ValueType`
icmpv6::*,
test_gens::*,
Icmpv6Type::*,
Expand Down
Loading