Add Packet support for NDISC packet types
Add packet layer support for NDISC packet types. Closes: #180 Approved by: whitequark
This commit is contained in:
parent
96118d370d
commit
e97b50419f
|
@ -10,17 +10,27 @@ enum_with_unknown! {
|
|||
/// Internet protocol control message type.
|
||||
pub doc enum Message(u8) {
|
||||
/// Destination Unreachable.
|
||||
DstUnreachable = 0x01,
|
||||
DstUnreachable = 0x01,
|
||||
/// Packet Too Big.
|
||||
PktTooBig = 0x02,
|
||||
PktTooBig = 0x02,
|
||||
/// Time Exceeded.
|
||||
TimeExceeded = 0x03,
|
||||
TimeExceeded = 0x03,
|
||||
/// Parameter Problem.
|
||||
ParamProblem = 0x04,
|
||||
ParamProblem = 0x04,
|
||||
/// Echo Request
|
||||
EchoRequest = 0x80,
|
||||
EchoRequest = 0x80,
|
||||
/// Echo Reply
|
||||
EchoReply = 0x81
|
||||
EchoReply = 0x81,
|
||||
/// Router Solicitation
|
||||
RouterSolicit = 0x85,
|
||||
/// Router Advertisement
|
||||
RouterAdvert = 0x86,
|
||||
/// Neighbor Solicitation
|
||||
NeighborSolicit = 0x87,
|
||||
/// Neighbor Advertisement
|
||||
NeighborAdvert = 0x88,
|
||||
/// Redirect
|
||||
Redirect = 0x89
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -38,13 +48,18 @@ impl Message {
|
|||
impl fmt::Display for Message {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
&Message::DstUnreachable => write!(f, "destination unreachable"),
|
||||
&Message::PktTooBig => write!(f, "packet too big"),
|
||||
&Message::TimeExceeded => write!(f, "time exceeded"),
|
||||
&Message::ParamProblem => write!(f, "parameter problem"),
|
||||
&Message::EchoReply => write!(f, "echo reply"),
|
||||
&Message::EchoRequest => write!(f, "echo request"),
|
||||
&Message::Unknown(id) => write!(f, "{}", id)
|
||||
&Message::DstUnreachable => write!(f, "destination unreachable"),
|
||||
&Message::PktTooBig => write!(f, "packet too big"),
|
||||
&Message::TimeExceeded => write!(f, "time exceeded"),
|
||||
&Message::ParamProblem => write!(f, "parameter problem"),
|
||||
&Message::EchoReply => write!(f, "echo reply"),
|
||||
&Message::EchoRequest => write!(f, "echo request"),
|
||||
&Message::RouterSolicit => write!(f, "router solicitation"),
|
||||
&Message::RouterAdvert => write!(f, "router advertisement"),
|
||||
&Message::NeighborSolicit => write!(f, "neighbor solicitation"),
|
||||
&Message::NeighborAdvert => write!(f, "neighbor advert"),
|
||||
&Message::Redirect => write!(f, "redirect"),
|
||||
&Message::Unknown(id) => write!(f, "{}", id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -145,25 +160,56 @@ impl fmt::Display for TimeExceeded {
|
|||
/// A read/write wrapper around an Internet Control Message Protocol version 6 packet buffer.
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct Packet<T: AsRef<[u8]>> {
|
||||
buffer: T
|
||||
pub(super) buffer: T
|
||||
}
|
||||
|
||||
// Ranges and constants describing key boundaries in the ICMPv6 header.
|
||||
// See https://tools.ietf.org/html/rfc4443 for details.
|
||||
mod field {
|
||||
pub(super) mod field {
|
||||
use wire::field::*;
|
||||
|
||||
pub const TYPE: usize = 0;
|
||||
pub const CODE: usize = 1;
|
||||
pub const CHECKSUM: Field = 2..4;
|
||||
pub const TYPE: usize = 0;
|
||||
pub const CODE: usize = 1;
|
||||
pub const CHECKSUM: Field = 2..4;
|
||||
|
||||
pub const UNUSED: Field = 4..8;
|
||||
pub const MTU: Field = 4..8;
|
||||
pub const POINTER: Field = 4..8;
|
||||
pub const ECHO_IDENT: Field = 4..6;
|
||||
pub const ECHO_SEQNO: Field = 6..8;
|
||||
pub const UNUSED: Field = 4..8;
|
||||
pub const MTU: Field = 4..8;
|
||||
pub const POINTER: Field = 4..8;
|
||||
pub const ECHO_IDENT: Field = 4..6;
|
||||
pub const ECHO_SEQNO: Field = 6..8;
|
||||
|
||||
pub const HEADER_END: usize = 8;
|
||||
pub const HEADER_END: usize = 8;
|
||||
|
||||
// Router Advertisement message offsets
|
||||
pub const CUR_HOP_LIMIT: usize = 4;
|
||||
pub const ROUTER_FLAGS: usize = 5;
|
||||
pub const ROUTER_LT: Field = 6..8;
|
||||
pub const REACHABLE_TM: Field = 8..12;
|
||||
pub const RETRANS_TM: Field = 12..16;
|
||||
|
||||
// Neighbor Solicitation message offsets
|
||||
pub const TARGET_ADDR: Field = 8..24;
|
||||
|
||||
// Neighbor Advertisement message offsets
|
||||
pub const NEIGH_FLAGS: usize = 4;
|
||||
|
||||
// Redirected Header message offsets
|
||||
pub const DEST_ADDR: Field = 24..40;
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
pub struct RouterFlags: u8 {
|
||||
const MANAGED = 0b10000000;
|
||||
const OTHER = 0b01000000;
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
pub struct NeighborFlags: u8 {
|
||||
const ROUTER = 0b10000000;
|
||||
const SOLICITED = 0b01000000;
|
||||
const OVERRIDE = 0b00100000;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsRef<[u8]>> Packet<T> {
|
||||
|
@ -189,7 +235,11 @@ impl<T: AsRef<[u8]>> Packet<T> {
|
|||
if len < field::HEADER_END {
|
||||
Err(Error::Truncated)
|
||||
} else {
|
||||
Ok(())
|
||||
if len < self.header_len() {
|
||||
Err(Error::Truncated)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -251,12 +301,17 @@ impl<T: AsRef<[u8]>> Packet<T> {
|
|||
/// the message type field.
|
||||
pub fn header_len(&self) -> usize {
|
||||
match self.msg_type() {
|
||||
Message::DstUnreachable => field::UNUSED.end,
|
||||
Message::PktTooBig => field::MTU.end,
|
||||
Message::TimeExceeded => field::UNUSED.end,
|
||||
Message::ParamProblem => field::POINTER.end,
|
||||
Message::EchoRequest => field::ECHO_SEQNO.end,
|
||||
Message::EchoReply => field::ECHO_SEQNO.end,
|
||||
Message::DstUnreachable => field::UNUSED.end,
|
||||
Message::PktTooBig => field::MTU.end,
|
||||
Message::TimeExceeded => field::UNUSED.end,
|
||||
Message::ParamProblem => field::POINTER.end,
|
||||
Message::EchoRequest => field::ECHO_SEQNO.end,
|
||||
Message::EchoReply => field::ECHO_SEQNO.end,
|
||||
Message::RouterSolicit => field::UNUSED.end,
|
||||
Message::RouterAdvert => field::RETRANS_TM.end,
|
||||
Message::NeighborSolicit => field::TARGET_ADDR.end,
|
||||
Message::NeighborAdvert => field::TARGET_ADDR.end,
|
||||
Message::Redirect => field::DEST_ADDR.end,
|
||||
// For packets that are not included in RFC 4443, do not
|
||||
// include the last 32 bits of the ICMPv6 header in
|
||||
// `header_bytes`. This must be done so that these bytes
|
||||
|
|
|
@ -98,6 +98,8 @@ mod icmpv6;
|
|||
#[cfg(feature = "proto-ipv4")]
|
||||
mod igmp;
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
mod ndisc;
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
mod ndiscoption;
|
||||
mod udp;
|
||||
mod tcp;
|
||||
|
@ -173,6 +175,10 @@ pub use self::icmpv6::{Message as Icmpv6Message,
|
|||
Packet as Icmpv6Packet,
|
||||
Repr as Icmpv6Repr};
|
||||
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
pub use self::icmpv6::{RouterFlags as NdiscRouterFlags,
|
||||
NeighborFlags as NdiscNeighborFlags};
|
||||
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
pub use self::ndiscoption::{NdiscOption,
|
||||
Repr as NdiscOptionRepr,
|
||||
|
|
|
@ -0,0 +1,220 @@
|
|||
use byteorder::{ByteOrder, NetworkEndian};
|
||||
|
||||
use super::icmpv6::*;
|
||||
use time::Duration;
|
||||
use super::Ipv6Address;
|
||||
|
||||
/// Getters for the Router Advertisement message header.
|
||||
/// See [RFC 4861 § 4.2].
|
||||
///
|
||||
/// [RFC 4861 § 4.2]: https://tools.ietf.org/html/rfc4861#section-4.2
|
||||
impl<T: AsRef<[u8]>> Packet<T> {
|
||||
/// Return the current hop limit field.
|
||||
#[inline]
|
||||
pub fn current_hop_limit(&self) -> u8 {
|
||||
let data = self.buffer.as_ref();
|
||||
data[field::CUR_HOP_LIMIT]
|
||||
}
|
||||
|
||||
/// Return the Router Advertisement flags.
|
||||
#[inline]
|
||||
pub fn router_flags(&self) -> RouterFlags {
|
||||
let data = self.buffer.as_ref();
|
||||
RouterFlags::from_bits_truncate(data[field::ROUTER_FLAGS])
|
||||
}
|
||||
|
||||
/// Return the router lifetime field.
|
||||
#[inline]
|
||||
pub fn router_lifetime(&self) -> Duration {
|
||||
let data = self.buffer.as_ref();
|
||||
Duration::from_secs(NetworkEndian::read_u16(&data[field::ROUTER_LT]) as u64)
|
||||
}
|
||||
|
||||
/// Return the reachable time field.
|
||||
#[inline]
|
||||
pub fn reachable_time(&self) -> Duration {
|
||||
let data = self.buffer.as_ref();
|
||||
Duration::from_millis(NetworkEndian::read_u32(&data[field::REACHABLE_TM]) as u64)
|
||||
}
|
||||
|
||||
/// Return the retransmit time field.
|
||||
#[inline]
|
||||
pub fn retrans_time(&self) -> Duration {
|
||||
let data = self.buffer.as_ref();
|
||||
Duration::from_millis(NetworkEndian::read_u32(&data[field::RETRANS_TM]) as u64)
|
||||
}
|
||||
}
|
||||
|
||||
/// Getters for the [Neighbor Solicitation], [Neighbor Advertisement], and
|
||||
/// [Redirect] message types.
|
||||
///
|
||||
/// [Neighbor Solicitation]: https://tools.ietf.org/html/rfc4861#section-4.3
|
||||
/// [Neighbor Advertisement]: https://tools.ietf.org/html/rfc4861#section-4.4
|
||||
/// [Redirect]: https://tools.ietf.org/html/rfc4861#section-4.5
|
||||
impl<T: AsRef<[u8]>> Packet<T> {
|
||||
/// Return the target address field.
|
||||
#[inline]
|
||||
pub fn target_addr(&self) -> Ipv6Address {
|
||||
let data = self.buffer.as_ref();
|
||||
Ipv6Address::from_bytes(&data[field::TARGET_ADDR])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Getters for the Neighbor Solicitation message header.
|
||||
/// See [RFC 4861 § 4.3].
|
||||
///
|
||||
/// [RFC 4861 § 4.3]: https://tools.ietf.org/html/rfc4861#section-4.3
|
||||
impl<T: AsRef<[u8]>> Packet<T> {
|
||||
/// Return the Neighbor Solicitation flags.
|
||||
#[inline]
|
||||
pub fn neighbor_flags(&self) -> NeighborFlags {
|
||||
let data = self.buffer.as_ref();
|
||||
NeighborFlags::from_bits_truncate(data[field::NEIGH_FLAGS])
|
||||
}
|
||||
}
|
||||
|
||||
/// Getters for the Redirect message header.
|
||||
/// See [RFC 4861 § 4.5].
|
||||
///
|
||||
/// [RFC 4861 § 4.5]: https://tools.ietf.org/html/rfc4861#section-4.5
|
||||
impl<T: AsRef<[u8]>> Packet<T> {
|
||||
/// Return the destination address field.
|
||||
#[inline]
|
||||
pub fn dest_addr(&self) -> Ipv6Address {
|
||||
let data = self.buffer.as_ref();
|
||||
Ipv6Address::from_bytes(&data[field::DEST_ADDR])
|
||||
}
|
||||
}
|
||||
|
||||
/// Setters for the Router Solicitation message header.
|
||||
/// See [RFC 4861 § 4.1].
|
||||
///
|
||||
/// [RFC 4861 § 4.1]: https://tools.ietf.org/html/rfc4861#section-4.1
|
||||
impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
|
||||
/// Clear the reserved field.
|
||||
#[inline]
|
||||
pub fn clear_reserved(&mut self) {
|
||||
let data = self.buffer.as_mut();
|
||||
NetworkEndian::write_u32(&mut data[field::UNUSED], 0);
|
||||
}
|
||||
}
|
||||
|
||||
/// Setters for the Router Advertisement message header.
|
||||
/// See [RFC 4861 § 4.2].
|
||||
///
|
||||
/// [RFC 4861 § 4.2]: https://tools.ietf.org/html/rfc4861#section-4.2
|
||||
impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
|
||||
/// Set the current hop limit field.
|
||||
#[inline]
|
||||
pub fn set_current_hop_limit(&mut self, value: u8) {
|
||||
let data = self.buffer.as_mut();
|
||||
data[field::CUR_HOP_LIMIT] = value;
|
||||
}
|
||||
|
||||
/// Set the Router Advertisement flags.
|
||||
#[inline]
|
||||
pub fn set_router_flags(&mut self, flags: RouterFlags) {
|
||||
self.buffer.as_mut()[field::ROUTER_FLAGS] = flags.bits();
|
||||
}
|
||||
|
||||
/// Set the router lifetime field.
|
||||
#[inline]
|
||||
pub fn set_router_lifetime(&mut self, value: Duration) {
|
||||
let data = self.buffer.as_mut();
|
||||
NetworkEndian::write_u16(&mut data[field::ROUTER_LT], value.secs() as u16);
|
||||
}
|
||||
|
||||
/// Set the reachable time field.
|
||||
#[inline]
|
||||
pub fn set_reachable_time(&mut self, value: Duration) {
|
||||
let data = self.buffer.as_mut();
|
||||
NetworkEndian::write_u32(&mut data[field::REACHABLE_TM], value.total_millis() as u32);
|
||||
}
|
||||
|
||||
/// Set the retransmit time field.
|
||||
#[inline]
|
||||
pub fn set_retrans_time(&mut self, value: Duration) {
|
||||
let data = self.buffer.as_mut();
|
||||
NetworkEndian::write_u32(&mut data[field::RETRANS_TM], value.total_millis() as u32);
|
||||
}
|
||||
}
|
||||
|
||||
/// Setters for the [Neighbor Solicitation], [Neighbor Advertisement], and
|
||||
/// [Redirect] message types.
|
||||
///
|
||||
/// [Neighbor Solicitation]: https://tools.ietf.org/html/rfc4861#section-4.3
|
||||
/// [Neighbor Advertisement]: https://tools.ietf.org/html/rfc4861#section-4.4
|
||||
/// [Redirect]: https://tools.ietf.org/html/rfc4861#section-4.5
|
||||
impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
|
||||
/// Set the target address field.
|
||||
#[inline]
|
||||
pub fn set_target_addr(&mut self, value: Ipv6Address) {
|
||||
let data = self.buffer.as_mut();
|
||||
data[field::TARGET_ADDR].copy_from_slice(value.as_bytes());
|
||||
}
|
||||
}
|
||||
|
||||
/// Setters for the Neighbor Solicitation message header.
|
||||
/// See [RFC 4861 § 4.3].
|
||||
///
|
||||
/// [RFC 4861 § 4.3]: https://tools.ietf.org/html/rfc4861#section-4.3
|
||||
impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
|
||||
/// Set the Neighbor Solicitation flags.
|
||||
#[inline]
|
||||
pub fn set_neighbor_flags(&mut self, flags: NeighborFlags) {
|
||||
self.buffer.as_mut()[field::NEIGH_FLAGS] = flags.bits();
|
||||
}
|
||||
}
|
||||
|
||||
/// Setters for the Redirect message header.
|
||||
/// See [RFC 4861 § 4.5].
|
||||
///
|
||||
/// [RFC 4861 § 4.5]: https://tools.ietf.org/html/rfc4861#section-4.5
|
||||
impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
|
||||
/// Set the destination address field.
|
||||
#[inline]
|
||||
pub fn set_dest_addr(&mut self, value: Ipv6Address) {
|
||||
let data = self.buffer.as_mut();
|
||||
data[field::DEST_ADDR].copy_from_slice(value.as_bytes());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
static ROUTER_ADVERT_BYTES: [u8; 16] =
|
||||
[0x86, 0x00, 0x2e, 0xf3,
|
||||
0x40, 0x80, 0x03, 0x84,
|
||||
0x00, 0x00, 0x03, 0x84,
|
||||
0x00, 0x00, 0x03, 0x84];
|
||||
|
||||
#[test]
|
||||
fn test_router_advert_deconstruct() {
|
||||
let packet = Packet::new(&ROUTER_ADVERT_BYTES[..]);
|
||||
assert_eq!(packet.msg_type(), Message::RouterAdvert);
|
||||
assert_eq!(packet.msg_code(), 0);
|
||||
assert_eq!(packet.current_hop_limit(), 64);
|
||||
assert_eq!(packet.router_flags(), RouterFlags::MANAGED);
|
||||
assert_eq!(packet.router_lifetime(), Duration::from_secs(900));
|
||||
assert_eq!(packet.reachable_time(), Duration::from_millis(900));
|
||||
assert_eq!(packet.retrans_time(), Duration::from_millis(900));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_router_advert_construct() {
|
||||
let mut bytes = vec![0x0; 16];
|
||||
let mut packet = Packet::new(&mut bytes);
|
||||
packet.set_msg_type(Message::RouterAdvert);
|
||||
packet.set_msg_code(0);
|
||||
packet.set_current_hop_limit(64);
|
||||
packet.set_router_flags(RouterFlags::MANAGED);
|
||||
packet.set_router_lifetime(Duration::from_secs(900));
|
||||
packet.set_reachable_time(Duration::from_millis(900));
|
||||
packet.set_retrans_time(Duration::from_millis(900));
|
||||
packet.fill_checksum();
|
||||
assert_eq!(&packet.into_inner()[..], &ROUTER_ADVERT_BYTES[..]);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue