use std::convert::TryFrom;
use std::net::Ipv6Addr;
fn ipv6_sum_words(ip: &Ipv6Addr) -> u32 {
ip.segments().iter().map(|x| *x as u32).sum()
}
fn sum_big_endian_words(bs: &[u8]) -> u32 {
if bs.len() == 0 {
return 0;
}
let len = bs.len();
let mut data = &bs[..];
let mut sum = 0u32;
while data.len() >= 2 {
sum += u16_from_be(&data[0..2]) as u32;
data = &data[2..];
}
if (len % 2) != 0 {
sum += (data[0] as u32) << 8;
}
return sum;
}
#[inline]
fn u16_from_be(data: &[u8]) -> u16 {
u16::from_be_bytes(data.try_into().expect("Invalid slice size for u16"))
}
#[inline]
fn u32_from_be(data: &[u8]) -> u32 {
u32::from_be_bytes(data.try_into().expect("Invalid slice size for u32"))
}
pub trait WithEchoRequest {
type Packet;
fn with_echo_request(
identifier: u16,
sequence: u16,
payload: Vec<u8>,
) -> Result<Self::Packet, IcmpPacketBuildError>;
}
pub trait WithEchoReply {
type Packet;
fn with_echo_reply(
identifier: u16,
sequence: u16,
payload: Vec<u8>,
) -> Result<Self::Packet, IcmpPacketBuildError>;
}
pub trait WithTimestampRequest {
type Packet;
fn with_timestamp_request(
identifier: u16,
sequence: u16,
originate: u32,
receive: u32,
transmit: u32,
) -> Result<Self::Packet, IcmpPacketBuildError>;
}
pub trait WithTimestampReply {
type Packet;
fn with_timestamp_reply(
identifier: u16,
sequence: u16,
originate: u32,
receive: u32,
transmit: u32,
) -> Result<Self::Packet, IcmpPacketBuildError>;
}
pub trait WithUnreachable {
type Packet;
fn with_unreachable(code: u8, packet: Vec<u8>) -> Result<Self::Packet, IcmpPacketBuildError>;
}
pub trait WithParameterProblem {
type Packet;
type Pointer;
fn with_parameter_problem(
code: u8,
pointer: Self::Pointer,
packet: Vec<u8>,
) -> Result<Self::Packet, IcmpPacketBuildError>;
}
pub trait WithTimeExceeded {
type Packet;
fn with_time_exceeded(code: u8, packet: Vec<u8>) -> Result<Self::Packet, IcmpPacketBuildError>;
}
#[derive(Debug, PartialEq)]
pub enum Icmpv6Message {
Unreachable {
_unused: u32,
invoking_packet: Vec<u8>,
},
PacketTooBig {
mtu: u32,
invoking_packet: Vec<u8>,
},
TimeExceeded {
_unused: u32,
invoking_packet: Vec<u8>,
},
ParameterProblem {
pointer: u32,
invoking_packet: Vec<u8>,
},
PrivateExperimental {
padding: u32,
payload: Vec<u8>,
},
EchoRequest {
identifier: u16,
sequence: u16,
payload: Vec<u8>,
},
EchoReply {
identifier: u16,
sequence: u16,
payload: Vec<u8>,
},
}
use Icmpv6Message::{
EchoReply, EchoRequest, PacketTooBig, ParameterProblem, PrivateExperimental, TimeExceeded,
Unreachable,
};
impl Icmpv6Message {
pub fn get_bytes(&self) -> Vec<u8> {
let mut bytes = Vec::new();
match self {
Unreachable {
_unused: field1,
invoking_packet: field2,
}
| PacketTooBig {
mtu: field1,
invoking_packet: field2,
}
| TimeExceeded {
_unused: field1,
invoking_packet: field2,
}
| ParameterProblem {
pointer: field1,
invoking_packet: field2,
}
| PrivateExperimental {
padding: field1,
payload: field2,
} => {
bytes.extend_from_slice(&field1.to_be_bytes());
bytes.extend_from_slice(field2);
}
EchoRequest {
identifier,
sequence,
payload,
}
| EchoReply {
identifier,
sequence,
payload,
} => {
bytes.extend_from_slice(&identifier.to_be_bytes());
bytes.extend_from_slice(&sequence.to_be_bytes());
bytes.extend_from_slice(payload);
}
}
bytes
}
}
#[derive(Debug)]
pub struct Icmpv6Packet {
pub typ: u8,
pub code: u8,
pub checksum: u16,
pub message: Icmpv6Message,
}
#[derive(Debug)]
pub enum PacketParseError {
PacketTooSmall(usize),
UnrecognizedICMPType(u8),
}
impl Icmpv6Packet {
pub fn parse<B: AsRef<[u8]>>(bytes: B) -> Result<Self, PacketParseError> {
let bytes = bytes.as_ref();
if bytes.len() < 8 {
return Err(PacketParseError::PacketTooSmall(bytes.len()));
}
let (typ, code, checksum) = (bytes[0], bytes[1], u16_from_be(&bytes[2..4]));
let next_field = u32_from_be(&bytes[4..8]);
let payload = bytes[8..].to_owned();
let message = match typ {
1 => Unreachable {
_unused: next_field,
invoking_packet: payload,
},
2 => PacketTooBig {
mtu: next_field,
invoking_packet: payload,
},
3 => TimeExceeded {
_unused: next_field,
invoking_packet: payload,
},
4 => ParameterProblem {
pointer: next_field,
invoking_packet: payload,
},
100 | 101 | 200 | 201 => PrivateExperimental {
padding: next_field,
payload: payload,
},
128 => EchoRequest {
identifier: u16_from_be(&bytes[4..6]),
sequence: u16_from_be(&bytes[6..8]),
payload: payload,
},
129 => EchoReply {
identifier: u16_from_be(&bytes[4..6]),
sequence: u16_from_be(&bytes[6..8]),
payload: payload,
},
t => return Err(PacketParseError::UnrecognizedICMPType(t)),
};
return Ok(Icmpv6Packet {
typ: typ,
code: code,
checksum: checksum,
message: message,
});
}
pub fn get_bytes(&self, with_checksum: bool) -> Vec<u8> {
let mut bytes = Vec::new();
bytes.push(self.typ);
bytes.push(self.code);
bytes.extend_from_slice(&(if with_checksum { self.checksum } else { 0 }).to_be_bytes());
bytes.append(&mut self.message.get_bytes());
return bytes;
}
pub fn calculate_checksum(&self, source: &Ipv6Addr, dest: &Ipv6Addr) -> u16 {
let mut sum = 0u32;
sum += ipv6_sum_words(source);
sum += ipv6_sum_words(dest);
sum += 58;
let bytes = self.get_bytes(false);
let len = bytes.len();
sum += len as u32;
sum += sum_big_endian_words(&bytes);
while sum >> 16 != 0 {
sum = (sum >> 16) + (sum & 0xFFFF);
}
!sum as u16
}
pub fn with_checksum(mut self, source: &Ipv6Addr, dest: &Ipv6Addr) -> Self {
self.checksum = self.calculate_checksum(source, dest);
self
}
pub fn with_packet_too_big(mtu: u32, packet: Vec<u8>) -> Result<Self, IcmpPacketBuildError> {
Ok(Self {
typ: 2,
code: 0,
checksum: 0,
message: PacketTooBig {
mtu: mtu,
invoking_packet: packet,
},
})
}
}
impl WithEchoRequest for Icmpv6Packet {
type Packet = Icmpv6Packet;
fn with_echo_request(
identifier: u16,
sequence: u16,
payload: Vec<u8>,
) -> Result<Self::Packet, IcmpPacketBuildError> {
Ok(Self {
typ: 128,
code: 0,
checksum: 0,
message: EchoRequest {
identifier: identifier,
sequence: sequence,
payload: payload,
},
})
}
}
impl WithEchoReply for Icmpv6Packet {
type Packet = Icmpv6Packet;
fn with_echo_reply(
identifier: u16,
sequence: u16,
payload: Vec<u8>,
) -> Result<Self, IcmpPacketBuildError> {
Ok(Self {
typ: 129,
code: 0,
checksum: 0,
message: EchoReply {
identifier: identifier,
sequence: sequence,
payload: payload,
},
})
}
}
impl WithUnreachable for Icmpv6Packet {
type Packet = Icmpv6Packet;
fn with_unreachable(code: u8, packet: Vec<u8>) -> Result<Self, IcmpPacketBuildError> {
if code > 6 {
return Err(IcmpPacketBuildError::InvalidCode(code));
}
Ok(Self {
typ: 1,
code: code,
checksum: 0,
message: Unreachable {
_unused: 0,
invoking_packet: packet,
},
})
}
}
impl WithParameterProblem for Icmpv6Packet {
type Packet = Icmpv6Packet;
type Pointer = u32;
fn with_parameter_problem(
code: u8,
pointer: Self::Pointer,
packet: Vec<u8>,
) -> Result<Self, IcmpPacketBuildError> {
if code > 1 {
return Err(IcmpPacketBuildError::InvalidCode(code));
}
Ok(Self {
typ: 4,
code: code,
checksum: 0,
message: ParameterProblem {
pointer: pointer,
invoking_packet: packet,
},
})
}
}
impl WithTimeExceeded for Icmpv6Packet {
type Packet = Icmpv6Packet;
fn with_time_exceeded(code: u8, packet: Vec<u8>) -> Result<Self, IcmpPacketBuildError> {
if code > 1 {
return Err(IcmpPacketBuildError::InvalidCode(code));
}
Ok(Self {
typ: 3,
code: code,
checksum: 0,
message: TimeExceeded {
_unused: 0,
invoking_packet: packet,
},
})
}
}
impl TryFrom<&[u8]> for Icmpv6Packet {
type Error = PacketParseError;
fn try_from(b: &[u8]) -> Result<Self, Self::Error> {
Icmpv6Packet::parse(b)
}
}
#[derive(Debug, PartialEq)]
pub enum IcmpPacketBuildError {
InvalidCode(u8),
}
use IcmpPacketBuildError::InvalidCode;
impl std::fmt::Display for IcmpPacketBuildError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self {
InvalidCode(c) => format!("Invalid Code: {}", c),
}
)
}
}
impl std::fmt::Display for PacketParseError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self {
PacketParseError::PacketTooSmall(c) => format!("Packet Too Small size: {}", c),
PacketParseError::UnrecognizedICMPType(t) => format!("UnrecognizedIcmpType({})", t),
}
)
}
}
impl From<IcmpPacketBuildError> for std::io::Error {
fn from(err: IcmpPacketBuildError) -> Self {
std::io::Error::new(std::io::ErrorKind::Other, format!("{}", err))
}
}
impl From<PacketParseError> for std::io::Error {
fn from(err: PacketParseError) -> Self {
std::io::Error::new(std::io::ErrorKind::Other, format!("{}", err))
}
}
#[derive(Debug)]
pub enum Icmpv4Message {
Unreachable {
padding: u32,
header: Vec<u8>,
},
TimeExceeded {
padding: u32,
header: Vec<u8>,
},
ParameterProblem {
pointer: u8,
padding: (u8, u16),
header: Vec<u8>,
},
Quench {
padding: u32,
header: Vec<u8>,
},
Redirect {
gateway: u32,
header: Vec<u8>,
},
Echo {
identifier: u16,
sequence: u16,
payload: Vec<u8>,
},
EchoReply {
identifier: u16,
sequence: u16,
payload: Vec<u8>,
},
Timestamp {
identifier: u16,
sequence: u16,
originate: u32,
receive: u32,
transmit: u32,
},
TimestampReply {
identifier: u16,
sequence: u16,
originate: u32,
receive: u32,
transmit: u32,
},
Information {
identifier: u16,
sequence: u16,
},
InformationReply {
identifier: u16,
sequence: u16,
},
}
impl Icmpv4Message {
pub fn get_bytes(&self) -> Vec<u8> {
let mut bytes = Vec::with_capacity(20);
match self {
Self::Unreachable {
padding,
header,
}
| Self::TimeExceeded {
padding,
header,
}
| Self::Quench {
padding,
header,
}
| Self::Redirect {
gateway: padding,
header,
} => {
bytes.extend_from_slice(&padding.to_be_bytes());
bytes.extend_from_slice(header);
}
Self::Echo {
identifier,
sequence,
payload,
}
| Self::EchoReply {
identifier,
sequence,
payload,
} => {
bytes.extend_from_slice(&identifier.to_be_bytes());
bytes.extend_from_slice(&sequence.to_be_bytes());
bytes.extend_from_slice(payload);
}
Self::ParameterProblem {
pointer,
padding,
header,
} => {
bytes.push(*pointer);
bytes.push(padding.0);
bytes.extend_from_slice(&padding.1.to_be_bytes());
bytes.extend_from_slice(header);
}
Self::Timestamp {
identifier,
sequence,
originate,
receive,
transmit,
}
| Self::TimestampReply {
identifier,
sequence,
originate,
receive,
transmit,
} => {
bytes.extend_from_slice(&identifier.to_be_bytes());
bytes.extend_from_slice(&sequence.to_be_bytes());
bytes.extend_from_slice(&originate.to_be_bytes());
bytes.extend_from_slice(&receive.to_be_bytes());
bytes.extend_from_slice(&transmit.to_be_bytes());
}
Self::Information {
identifier,
sequence,
}
| Self::InformationReply {
identifier,
sequence,
} => {
bytes.extend_from_slice(&identifier.to_be_bytes());
bytes.extend_from_slice(&sequence.to_be_bytes());
}
}
bytes
}
}
#[derive(Debug)]
pub struct Icmpv4Packet {
pub typ: u8,
pub code: u8,
pub checksum: u16,
pub message: Icmpv4Message,
}
impl Icmpv4Packet {
pub fn parse<B: AsRef<[u8]>>(bytes: B) -> Result<Self, PacketParseError> {
let mut bytes = bytes.as_ref();
let mut packet_len = bytes.len();
if bytes.len() < 28 {
return Err(PacketParseError::PacketTooSmall(packet_len));
}
bytes = &bytes[20..];
packet_len = bytes.len();
let (typ, code, checksum) = (bytes[0], bytes[1], u16_from_be(&bytes[2..4]));
let message = match typ {
3 => Icmpv4Message::Unreachable {
padding: u32_from_be(&bytes[4..8]),
header: bytes[8..].to_owned(),
},
11 => Icmpv4Message::TimeExceeded {
padding: u32_from_be(&bytes[4..8]),
header: bytes[8..].to_owned(),
},
4 => Icmpv4Message::Quench {
padding: u32_from_be(&bytes[4..8]),
header: bytes[8..].to_owned(),
},
5 => Icmpv4Message::Redirect {
gateway: u32_from_be(&bytes[4..8]),
header: bytes[8..].to_owned(),
},
8 => Icmpv4Message::Echo {
identifier: u16_from_be(&bytes[4..6]),
sequence: u16_from_be(&bytes[6..8]),
payload: bytes[8..].to_owned(),
},
0 => Icmpv4Message::EchoReply {
identifier: u16_from_be(&bytes[4..6]),
sequence: u16_from_be(&bytes[6..8]),
payload: bytes[8..].to_owned(),
},
15 => Icmpv4Message::Information {
identifier: u16_from_be(&bytes[4..6]),
sequence: u16_from_be(&bytes[6..8]),
},
16 => Icmpv4Message::InformationReply {
identifier: u16_from_be(&bytes[4..6]),
sequence: u16_from_be(&bytes[6..8]),
},
13 => {
if packet_len < 20 {
return Err(PacketParseError::PacketTooSmall(bytes.len()));
}
Icmpv4Message::Timestamp {
identifier: u16_from_be(&bytes[4..6]),
sequence: u16_from_be(&bytes[6..8]),
originate: u32_from_be(&bytes[8..12]),
receive: u32_from_be(&bytes[12..16]),
transmit: u32_from_be(&bytes[16..20]),
}
}
14 => {
if packet_len < 20 {
return Err(PacketParseError::PacketTooSmall(bytes.len()));
}
Icmpv4Message::TimestampReply {
identifier: u16_from_be(&bytes[4..6]),
sequence: u16_from_be(&bytes[6..8]),
originate: u32_from_be(&bytes[8..12]),
receive: u32_from_be(&bytes[12..16]),
transmit: u32_from_be(&bytes[16..20]),
}
}
t => {
dbg!(bytes);
return Err(PacketParseError::UnrecognizedICMPType(t));
}
};
return Ok(Icmpv4Packet {
typ: typ,
code: code,
checksum: checksum,
message: message,
});
}
pub fn get_bytes(&self, with_checksum: bool) -> Vec<u8> {
let mut bytes = Vec::new();
bytes.push(self.typ);
bytes.push(self.code);
bytes.extend_from_slice(&(if with_checksum { self.checksum } else { 0 }).to_be_bytes());
bytes.append(&mut self.message.get_bytes());
return bytes;
}
pub fn calculate_checksum(&self) -> u16 {
let mut sum = 0u32;
let bytes = self.get_bytes(false);
sum += sum_big_endian_words(&bytes);
while sum >> 16 != 0 {
sum = (sum >> 16) + (sum & 0xFFFF);
}
!sum as u16
}
pub fn with_checksum(mut self) -> Self {
self.checksum = self.calculate_checksum();
self
}
}
impl WithEchoReply for Icmpv4Packet {
type Packet = Icmpv4Packet;
fn with_echo_reply(
identifier: u16,
sequence: u16,
payload: Vec<u8>,
) -> Result<Self, IcmpPacketBuildError> {
Ok(Self {
typ: 0,
code: 0,
checksum: 0,
message: Icmpv4Message::EchoReply {
identifier: identifier,
sequence: sequence,
payload: payload,
},
})
}
}
impl TryFrom<&[u8]> for Icmpv4Packet {
type Error = PacketParseError;
fn try_from(b: &[u8]) -> Result<Self, Self::Error> {
Icmpv4Packet::parse(b)
}
}
impl WithEchoRequest for Icmpv4Packet {
type Packet = Icmpv4Packet;
fn with_echo_request(
identifier: u16,
sequence: u16,
payload: Vec<u8>,
) -> Result<Self::Packet, IcmpPacketBuildError> {
Ok(Self {
typ: 8,
code: 0,
checksum: 0,
message: Icmpv4Message::Echo {
identifier,
sequence,
payload,
},
})
}
}
impl WithTimestampRequest for Icmpv4Packet {
type Packet = Icmpv4Packet;
fn with_timestamp_request(
identifier: u16,
sequence: u16,
originate: u32,
receive: u32,
transmit: u32,
) -> Result<Self::Packet, IcmpPacketBuildError> {
Ok(Self {
typ: 13,
code: 0,
checksum: 0,
message: Icmpv4Message::Timestamp {
identifier,
sequence,
originate,
receive,
transmit,
},
})
}
}
impl WithTimestampReply for Icmpv4Packet {
type Packet = Icmpv4Packet;
fn with_timestamp_reply(
identifier: u16,
sequence: u16,
originate: u32,
receive: u32,
transmit: u32,
) -> Result<Self::Packet, IcmpPacketBuildError> {
Ok(Self {
typ: 14,
code: 0,
checksum: 0,
message: Icmpv4Message::TimestampReply {
identifier,
sequence,
originate,
receive,
transmit,
},
})
}
}
impl WithUnreachable for Icmpv4Packet {
type Packet = Icmpv4Packet;
fn with_unreachable(code: u8, packet: Vec<u8>) -> Result<Self::Packet, IcmpPacketBuildError> {
if code > 5 {
return Err(IcmpPacketBuildError::InvalidCode(code));
}
Ok(Self {
typ: 3,
code: code,
checksum: 0,
message: Icmpv4Message::Unreachable {
padding: 0,
header: packet,
},
})
}
}
impl WithParameterProblem for Icmpv4Packet {
type Packet = Icmpv4Packet;
type Pointer = u8;
fn with_parameter_problem(
code: u8,
pointer: Self::Pointer,
packet: Vec<u8>,
) -> Result<Self::Packet, IcmpPacketBuildError> {
if code != 0 {
return Err(IcmpPacketBuildError::InvalidCode(code));
}
Ok(Self {
typ: 12,
code: code,
checksum: 0,
message: Icmpv4Message::ParameterProblem {
pointer: pointer,
padding: (0, 0),
header: packet,
},
})
}
}
impl WithTimeExceeded for Icmpv4Packet {
type Packet = Icmpv4Packet;
fn with_time_exceeded(code: u8, packet: Vec<u8>) -> Result<Self::Packet, IcmpPacketBuildError> {
if code > 1 {
return Err(IcmpPacketBuildError::InvalidCode(code));
}
Ok(Self {
typ: 11,
code: code,
checksum: 0,
message: Icmpv4Message::TimeExceeded {
padding: 0,
header: packet,
},
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn packet_construction_echo_request_test() {
let pkt = Icmpv6Packet::with_echo_request(42, 1, vec![1, 2, 3, 4]).unwrap();
assert_eq!(pkt.typ, 128);
assert_eq!(pkt.code, 0);
assert_eq!(
pkt.message,
EchoRequest {
identifier: 42,
sequence: 1,
payload: vec![1, 2, 3, 4],
}
);
}
#[test]
fn packet_construction_echo_reply_test() {
let pkt = Icmpv6Packet::with_echo_reply(42, 1, vec![1, 2, 3, 4]).unwrap();
assert_eq!(pkt.typ, 129);
assert_eq!(pkt.code, 0);
assert_eq!(
pkt.message,
EchoReply {
identifier: 42,
sequence: 1,
payload: vec![1, 2, 3, 4],
}
);
}
#[test]
fn packet_construction_too_big_test() {
let pkt = Icmpv6Packet::with_packet_too_big(3, vec![1, 2, 3, 4]).unwrap();
assert_eq!(pkt.typ, 2);
assert_eq!(pkt.code, 0);
assert_eq!(
pkt.message,
PacketTooBig {
mtu: 3,
invoking_packet: vec![1, 2, 3, 4],
}
);
}
#[test]
fn packet_construction_time_exceeded() {
let pkt = Icmpv6Packet::with_time_exceeded(0, vec![1, 2, 3, 4]).unwrap();
assert_eq!(pkt.typ, 3);
assert_eq!(pkt.code, 0);
assert_eq!(
pkt.message,
TimeExceeded {
_unused: 0,
invoking_packet: vec![1, 2, 3, 4],
}
);
}
#[test]
fn packet_construction_time_exceeded_invalid_code() {
let pkt = Icmpv6Packet::with_time_exceeded(2, vec![1, 2, 3, 4]);
assert!(pkt.is_err());
let e = pkt.unwrap_err();
assert_eq!(e, IcmpPacketBuildError::InvalidCode(2));
}
#[test]
fn packet_construction_parameter_problem() {
let pkt = Icmpv6Packet::with_parameter_problem(0, 30, vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
.unwrap();
assert_eq!(pkt.typ, 4);
assert_eq!(pkt.code, 0);
assert_eq!(
pkt.message,
ParameterProblem {
pointer: 30,
invoking_packet: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
}
);
}
#[test]
fn packet_construction_parameter_problem_invalid_code() {
let pkt = Icmpv6Packet::with_parameter_problem(3, 30, vec![1, 2, 3, 4]);
assert!(pkt.is_err());
let e = pkt.unwrap_err();
assert_eq!(e, IcmpPacketBuildError::InvalidCode(3));
}
#[test]
fn echo_packet_parse_test() {
let lo = &Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1);
let mut data = vec![
0x80, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x20, 0x20, 0x75, 0x73, 0x74, 0x20, 0x61, 0x20, 0x66, 0x6c, 0x65, 0x73, 0x68, 0x20,
0x77, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x20, 0x74, 0x69, 0x73, 0x20, 0x62, 0x75, 0x74,
0x20, 0x61, 0x20, 0x73, 0x63, 0x72, 0x61, 0x74, 0x63, 0x68, 0x20, 0x20, 0x6b, 0x6e,
0x69, 0x67, 0x68, 0x74, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x6e, 0x69, 0x20, 0x20, 0x20,
];
let mut pkt = Icmpv6Packet::parse(&data).unwrap();
assert_eq!(pkt.typ, 128);
assert_eq!(pkt.code, 0x00);
if let EchoRequest {
identifier,
sequence,
payload,
} = &pkt.message
{
assert_eq!(*identifier, 0);
assert_eq!(*sequence, 1);
assert_eq!(
payload,
&[
0x20, 0x20, 0x75, 0x73, 0x74, 0x20, 0x61, 0x20, 0x66, 0x6c, 0x65, 0x73, 0x68,
0x20, 0x77, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x20, 0x74, 0x69, 0x73, 0x20, 0x62,
0x75, 0x74, 0x20, 0x61, 0x20, 0x73, 0x63, 0x72, 0x61, 0x74, 0x63, 0x68, 0x20,
0x20, 0x6b, 0x6e, 0x69, 0x67, 0x68, 0x74, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x6e,
0x69, 0x20, 0x20, 0x20
]
);
} else {
assert!(
false,
"Packet did not parse as an EchoRequest {:?}",
pkt.message
);
}
assert_eq!(pkt.get_bytes(true), data);
assert_eq!(pkt.calculate_checksum(lo, lo), 0x1d2e);
pkt = pkt.with_checksum(lo, lo);
assert_eq!(pkt.checksum, 0x1d2e);
data[0] = 0x81;
let pkt = Icmpv6Packet::parse(&data).unwrap();
assert_eq!(pkt.typ, 129);
assert_eq!(pkt.code, 0);
if let EchoReply {
identifier,
sequence,
payload,
} = &pkt.message
{
assert_eq!(*identifier, 0);
assert_eq!(*sequence, 1);
assert_eq!(
payload,
&[
0x20, 0x20, 0x75, 0x73, 0x74, 0x20, 0x61, 0x20, 0x66, 0x6c, 0x65, 0x73, 0x68,
0x20, 0x77, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x20, 0x74, 0x69, 0x73, 0x20, 0x62,
0x75, 0x74, 0x20, 0x61, 0x20, 0x73, 0x63, 0x72, 0x61, 0x74, 0x63, 0x68, 0x20,
0x20, 0x6b, 0x6e, 0x69, 0x67, 0x68, 0x74, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x6e,
0x69, 0x20, 0x20, 0x20
]
);
} else {
assert!(
false,
"Packet did not parse as an EchoReply {:?}",
pkt.message
);
}
assert_eq!(pkt.get_bytes(true), data);
assert_eq!(pkt.calculate_checksum(lo, lo), 0x1c2e);
}
}