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:
Dan Robertson 2018-02-09 17:43:42 +00:00 committed by Homu
parent 96118d370d
commit e97b50419f
3 changed files with 312 additions and 31 deletions

View File

@ -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

View File

@ -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,

220
src/wire/ndisc.rs Normal file
View File

@ -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[..]);
}
}