Add MLDv2 Repr support

Closes: #252
Approved by: whitequark
v0.7.x
Dan Robertson 2018-06-26 12:24:24 +00:00 committed by Homu
parent 125a102b71
commit 8a214f3850
4 changed files with 196 additions and 5 deletions

View File

@ -5,7 +5,7 @@ use {Error, Result};
use phy::ChecksumCapabilities;
use super::ip::checksum;
use super::{IpAddress, IpProtocol, Ipv6Packet, Ipv6Repr};
use super::NdiscRepr;
use super::{MldRepr, NdiscRepr};
enum_with_unknown! {
/// Internet protocol control message type.
@ -538,6 +538,7 @@ pub enum Repr<'a> {
data: &'a [u8]
},
Ndisc(NdiscRepr<'a>),
Mld(MldRepr<'a>),
#[doc(hidden)]
__Nonexhaustive
}
@ -621,6 +622,9 @@ impl<'a> Repr<'a> {
(msg_type, 0) if msg_type.is_ndisc() => {
NdiscRepr::parse(packet).map(|repr| Repr::Ndisc(repr))
},
(msg_type, 0) if msg_type.is_mld() => {
MldRepr::parse(packet).map(|repr| Repr::Mld(repr))
},
_ => Err(Error::Unrecognized)
}
}
@ -639,6 +643,9 @@ impl<'a> Repr<'a> {
&Repr::Ndisc(ndisc) => {
ndisc.buffer_len()
},
&Repr::Mld(mld) => {
mld.buffer_len()
},
&Repr::__Nonexhaustive => unreachable!()
}
}
@ -708,6 +715,10 @@ impl<'a> Repr<'a> {
ndisc.emit(packet)
},
&Repr::Mld(mld) => {
mld.emit(packet)
},
&Repr::__Nonexhaustive => unreachable!(),
}

View File

@ -7,7 +7,7 @@
use byteorder::{ByteOrder, NetworkEndian};
use {Error, Result};
use super::icmpv6::{field, Packet};
use super::icmpv6::{field, Message, Packet};
use super::Ipv6Address;
enum_with_unknown! {
@ -131,6 +131,7 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
/// Set the Querier's Robustness Variable.
#[inline]
pub fn set_qrv(&mut self, value: u8) {
assert!(value < 8);
let data = self.buffer.as_mut();
data[field::SQRV] = (data[field::SQRV] & 0x8) | value & 0x7;
}
@ -292,8 +293,98 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> AddressRecord<T> {
}
}
/// A high-level representation of an MLDv2 packet header.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum Repr<'a> {
Query {
max_resp_code: u16,
mcast_addr: Ipv6Address,
s_flag: bool,
qrv: u8,
qqic: u8,
num_srcs: u16,
data: &'a [u8]
},
Report {
nr_mcast_addr_rcrds: u16,
data: &'a [u8]
}
}
impl<'a> Repr<'a> {
/// Parse an MLDv2 packet and return a high-level representation.
pub fn parse<T>(packet: &Packet<&'a T>) -> Result<Repr<'a>>
where T: AsRef<[u8]> + ?Sized {
match packet.msg_type() {
Message::MldQuery => {
Ok(Repr::Query {
max_resp_code: packet.max_resp_code(),
mcast_addr: packet.mcast_addr(),
s_flag: packet.s_flag(),
qrv: packet.qrv(),
qqic: packet.qqic(),
num_srcs: packet.num_srcs(),
data: packet.payload()
})
},
Message::MldReport => {
Ok(Repr::Report {
nr_mcast_addr_rcrds: packet.nr_mcast_addr_rcrds(),
data: packet.payload()
})
},
_ => Err(Error::Unrecognized)
}
}
/// Return the length of a packet that will be emitted from this high-level representation.
pub fn buffer_len(&self) -> usize {
match self {
Repr::Query { .. } => {
field::QUERY_NUM_SRCS.end
}
Repr::Report { .. } => {
field::NR_MCAST_RCRDS.end
}
}
}
/// Emit a high-level representation into an MLDv2 packet.
pub fn emit<T>(&self, packet: &mut Packet<&mut T>)
where T: AsRef<[u8]> + AsMut<[u8]> + ?Sized {
match self {
Repr::Query { max_resp_code, mcast_addr, s_flag,
qrv, qqic, num_srcs, data } => {
packet.set_msg_type(Message::MldQuery);
packet.set_msg_code(0);
packet.clear_reserved();
packet.set_max_resp_code(*max_resp_code);
packet.set_mcast_addr(*mcast_addr);
if *s_flag {
packet.set_s_flag();
} else {
packet.clear_s_flag();
}
packet.set_qrv(*qrv);
packet.set_qqic(*qqic);
packet.set_num_srcs(*num_srcs);
packet.payload_mut().copy_from_slice(&data[..]);
},
Repr::Report { nr_mcast_addr_rcrds, data } => {
packet.set_msg_type(Message::MldReport);
packet.set_msg_code(0);
packet.clear_reserved();
packet.set_nr_mcast_addr_rcrds(*nr_mcast_addr_rcrds);
packet.payload_mut().copy_from_slice(&data[..]);
}
}
}
}
#[cfg(test)]
mod test {
use phy::ChecksumCapabilities;
use wire::Icmpv6Repr;
use wire::icmpv6::Message;
use super::*;
@ -310,6 +401,12 @@ mod test {
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02];
static QUERY_PACKET_PAYLOAD: [u8; 16] =
[0xff, 0x02, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02];
static REPORT_PACKET_BYTES: [u8; 44] =
[0x8f, 0x00, 0x73, 0x85,
0x00, 0x00, 0x00, 0x01,
@ -323,6 +420,43 @@ mod test {
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02];
static REPORT_PACKET_PAYLOAD: [u8; 36] =
[0x01, 0x00, 0x00, 0x01,
0xff, 0x02, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01,
0xff, 0x02, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02];
fn create_repr<'a>(ty: Message) -> Icmpv6Repr<'a> {
match ty {
Message::MldQuery => {
Icmpv6Repr::Mld(Repr::Query {
max_resp_code: 0x400,
mcast_addr: Ipv6Address::LINK_LOCAL_ALL_NODES,
s_flag: true,
qrv: 0x02,
qqic: 0x12,
num_srcs: 0x01,
data: &QUERY_PACKET_PAYLOAD
})
},
Message::MldReport => {
Icmpv6Repr::Mld(Repr::Report {
nr_mcast_addr_rcrds: 1,
data: &REPORT_PACKET_PAYLOAD
})
},
_ => {
panic!("Message type must be a MLDv2 message type");
}
}
}
#[test]
fn test_query_deconstruct() {
let packet = Packet::new(&QUERY_PACKET_BYTES[..]);
@ -363,7 +497,7 @@ mod test {
let packet = Packet::new(&REPORT_PACKET_BYTES[..]);
assert_eq!(packet.msg_type(), Message::MldReport);
assert_eq!(packet.msg_code(), 0);
//assert_eq!(packet.checksum(), 0x7374);
assert_eq!(packet.checksum(), 0x7385);
assert_eq!(packet.nr_mcast_addr_rcrds(), 0x01);
let addr_rcrd = AddressRecord::new(packet.payload());
assert_eq!(addr_rcrd.record_type(), RecordType::ModeIsInclude);
@ -395,4 +529,48 @@ mod test {
&Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into());
assert_eq!(&packet.into_inner()[..], &REPORT_PACKET_BYTES[..]);
}
#[test]
fn test_query_repr_parse() {
let packet = Packet::new(&QUERY_PACKET_BYTES[..]);
let repr = Icmpv6Repr::parse(&Ipv6Address::LINK_LOCAL_ALL_NODES.into(),
&Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(),
&packet,
&ChecksumCapabilities::default());
assert_eq!(repr, Ok(create_repr(Message::MldQuery)));
}
#[test]
fn test_report_repr_parse() {
let packet = Packet::new(&REPORT_PACKET_BYTES[..]);
let repr = Icmpv6Repr::parse(&Ipv6Address::LINK_LOCAL_ALL_NODES.into(),
&Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(),
&packet,
&ChecksumCapabilities::default());
assert_eq!(repr, Ok(create_repr(Message::MldReport)));
}
#[test]
fn test_query_repr_emit() {
let mut bytes = [0x2a; 44];
let mut packet = Packet::new(&mut bytes[..]);
let repr = create_repr(Message::MldQuery);
repr.emit(&Ipv6Address::LINK_LOCAL_ALL_NODES.into(),
&Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(),
&mut packet,
&ChecksumCapabilities::default());
assert_eq!(&packet.into_inner()[..], &QUERY_PACKET_BYTES[..]);
}
#[test]
fn test_report_repr_emit() {
let mut bytes = [0x2a; 44];
let mut packet = Packet::new(&mut bytes[..]);
let repr = create_repr(Message::MldReport);
repr.emit(&Ipv6Address::LINK_LOCAL_ALL_NODES.into(),
&Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(),
&mut packet,
&ChecksumCapabilities::default());
assert_eq!(&packet.into_inner()[..], &REPORT_PACKET_BYTES[..]);
}
}

View File

@ -204,7 +204,8 @@ pub use self::ndiscoption::{NdiscOption,
PrefixInfoFlags as NdiscPrefixInfoFlags};
#[cfg(feature = "proto-ipv6")]
pub use self::mld::AddressRecord as MldAddressRecord;
pub use self::mld::{AddressRecord as MldAddressRecord,
Repr as MldRepr};
pub use self::udp::{Packet as UdpPacket,
Repr as UdpRepr};

View File

@ -3,7 +3,8 @@ use byteorder::{ByteOrder, NetworkEndian};
use {Error, Result};
use super::icmpv6::{field, Message, Packet};
use wire::{EthernetAddress, Ipv6Repr, Ipv6Packet};
use wire::{NdiscOption, NdiscOptionRepr, NdiscOptionType, NdiscPrefixInformation, NdiscRedirectedHeader};
use wire::{NdiscOption, NdiscOptionRepr, NdiscOptionType};
use wire::{NdiscPrefixInformation, NdiscRedirectedHeader};
use time::Duration;
use super::Ipv6Address;