icmp-socket/src/packet.rs

935 lines
28 KiB
Rust

// Copyright 2021 Jeremy Wall
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use std::convert::TryFrom;
use byteorder::{BigEndian, ByteOrder};
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;
// Iterate by word which is two bytes.
while data.len() >= 2 {
sum += BigEndian::read_u16(&data[0..2]) as u32;
// remove the first two bytes now that we've already summed them
data = &data[2..];
}
if (len % 2) != 0 {
// If odd then checksum the last byte
sum += (data[0] as u32) << 8;
}
return sum;
}
pub trait WithEchoRequest {
type Packet;
fn with_echo_request(
identifier: u16,
sequence: u16,
payload: Vec<u8>,
) -> Result<Self::Packet, IcmpPacketBuildError>;
}
#[derive(Debug, PartialEq)]
pub enum Icmpv6Message {
// NOTE(JWALL): All of the below integers should be parsed as big endian on the
// wire.
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,
} => {
let mut buf = vec![0; 4];
BigEndian::write_u32(&mut buf, *field1);
bytes.append(&mut buf);
bytes.extend_from_slice(field2);
}
EchoRequest {
identifier,
sequence,
payload,
}
| EchoReply {
identifier,
sequence,
payload,
} => {
let mut buf = vec![0; 2];
BigEndian::write_u16(&mut buf, *identifier);
bytes.append(&mut buf);
buf.resize(2, 0);
BigEndian::write_u16(&mut buf, *sequence);
bytes.append(&mut buf);
bytes.extend_from_slice(payload);
}
}
bytes
}
}
#[derive(Debug)]
pub struct Icmpv6Packet {
// NOTE(JWALL): All of the below integers should be parsed as big endian on the
// wire.
pub typ: u8,
pub code: u8,
pub checksum: u16,
pub message: Icmpv6Message,
}
#[derive(Debug)]
pub enum PacketParseError {
PacketTooSmall(usize),
UnrecognizedICMPType(u8),
}
impl Icmpv6Packet {
/// Construct a packet by parsing the provided bytes.
pub fn parse<B: AsRef<[u8]>>(bytes: B) -> Result<Self, PacketParseError> {
let bytes = bytes.as_ref();
// NOTE(jwall): All ICMP packets are at least 8 bytes long.
if bytes.len() < 8 {
return Err(PacketParseError::PacketTooSmall(bytes.len()));
}
let (typ, code, checksum) = (bytes[0], bytes[1], BigEndian::read_u16(&bytes[2..4]));
let next_field = BigEndian::read_u32(&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: BigEndian::read_u16(&bytes[4..6]),
sequence: BigEndian::read_u16(&bytes[6..8]),
payload: payload,
},
129 => EchoReply {
identifier: BigEndian::read_u16(&bytes[4..6]),
sequence: BigEndian::read_u16(&bytes[6..8]),
payload: payload,
},
t => return Err(PacketParseError::UnrecognizedICMPType(t)),
};
return Ok(Icmpv6Packet {
typ: typ,
code: code,
checksum: checksum,
message: message,
});
}
/// Get this packet serialized to bytes suitable for sending on the wire.
pub fn get_bytes(&self, with_checksum: bool) -> Vec<u8> {
let mut bytes = Vec::new();
bytes.push(self.typ);
bytes.push(self.code);
let mut buf = Vec::with_capacity(2);
buf.resize(2, 0);
BigEndian::write_u16(&mut buf, if with_checksum { self.checksum } else { 0 });
bytes.append(&mut buf);
bytes.append(&mut self.message.get_bytes());
return bytes;
}
/// Calculate the checksum for the packet given the provided source and destination
/// addresses.
pub fn calculate_checksum(&self, source: &Ipv6Addr, dest: &Ipv6Addr) -> u16 {
// First sum the pseudo header
let mut sum = 0u32;
sum += ipv6_sum_words(source);
sum += ipv6_sum_words(dest);
// according to rfc4443: https://tools.ietf.org/html/rfc4443#section-2.3
// the ip next header value is 58
sum += 58;
// Then sum the len of the message bytes and then the message bytes starting
// with the message type field and with the checksum field set to 0.
let bytes = self.get_bytes(false);
let len = bytes.len();
sum += len as u32;
sum += sum_big_endian_words(&bytes);
// handle the carry
while sum >> 16 != 0 {
sum = (sum >> 16) + (sum & 0xFFFF);
}
!sum as u16
}
/// Fill the checksum for the packet using the given source and destination
/// addresses.
pub fn with_checksum(mut self, source: &Ipv6Addr, dest: &Ipv6Addr) -> Self {
self.checksum = self.calculate_checksum(source, dest);
self
}
/// Construct a packet for Destination Unreachable messages.
pub 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,
// TODO(jwall): Should we enforce that the packet isn't too big?
// It is not supposed to be larger than the minimum IPv6 MTU
message: Unreachable {
_unused: 0,
invoking_packet: packet,
},
})
}
/// Construct a packet for Packet Too Big messages.
pub fn with_packet_too_big(mtu: u32, packet: Vec<u8>) -> Result<Self, IcmpPacketBuildError> {
Ok(Self {
typ: 2,
code: 0,
checksum: 0,
// TODO(jwall): Should we enforce that the packet isn't too big?
// It is not supposed to be larger than the minimum IPv6 MTU
message: PacketTooBig {
mtu: mtu,
invoking_packet: packet,
},
})
}
/// Construct a packet for Time Exceeded messages.
pub 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,
// TODO(jwall): Should we enforce that the packet isn't too big?
// It is not supposed to be larger than the minimum IPv6 MTU
message: TimeExceeded {
_unused: 0,
invoking_packet: packet,
},
})
}
/// Construct a packet for Parameter Problem messages.
pub fn with_parameter_problem(
code: u8,
pointer: u32,
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,
},
})
}
/// Construct a packet for Echo Reply messages.
pub 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 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 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 {
// type 3
padding: u32,
header: Vec<u8>,
},
TimeExceeded {
// type 11
padding: u32,
header: Vec<u8>,
},
ParameterProblem {
// type 12
pointer: u8,
padding: (u8, u16),
header: Vec<u8>,
},
Quench {
// type 4
padding: u32,
header: Vec<u8>,
},
Redirect {
// type 5
gateway: u32,
header: Vec<u8>,
},
Echo {
// type 8
identifier: u16,
sequence: u16,
payload: Vec<u8>,
},
EchoReply {
// type 0
identifier: u16,
sequence: u16,
payload: Vec<u8>,
},
Timestamp {
// type 13
identifier: u16,
sequence: u16,
originate: u32,
receive: u32,
transmit: u32,
},
TimestampReply {
// type 14
identifier: u16,
sequence: u16,
originate: u32,
receive: u32,
transmit: u32,
},
Information {
// type 15
identifier: u16,
sequence: u16,
},
InformationReply {
// type 16
identifier: u16,
sequence: u16,
},
}
impl Icmpv4Message {
pub fn get_bytes(&self) -> Vec<u8> {
let mut bytes = Vec::with_capacity(20);
match self {
Self::Unreachable {
// type 3
padding,
header,
}
| Self::TimeExceeded {
// type 11
padding,
header,
}
| Self::Quench {
// type 4
padding,
header,
}
| Self::Redirect {
// type 5
gateway: padding,
header,
} => {
let mut buf = vec![0; 4];
BigEndian::write_u32(&mut buf, *padding);
bytes.append(&mut buf);
bytes.extend_from_slice(header);
}
Self::Echo {
// type 8
identifier,
sequence,
payload,
}
| Self::EchoReply {
// type 0
identifier,
sequence,
payload,
} => {
let mut buf = vec![0; 2];
BigEndian::write_u16(&mut buf, *identifier);
bytes.append(&mut buf);
buf.resize(2, 0);
BigEndian::write_u16(&mut buf, *sequence);
bytes.append(&mut buf);
bytes.extend_from_slice(payload);
}
Self::ParameterProblem {
// type 12
pointer,
padding,
header,
} => {
bytes.push(*pointer);
bytes.push(padding.0);
let mut buf = vec![0, 2];
BigEndian::write_u16(&mut buf, padding.1);
bytes.append(&mut buf);
bytes.extend_from_slice(header);
}
Self::Timestamp {
// type 13
identifier,
sequence,
originate,
receive,
transmit,
}
| Self::TimestampReply {
// type 14
identifier,
sequence,
originate,
receive,
transmit,
} => {
let mut buf = vec![0, 2];
BigEndian::write_u16(&mut buf, *identifier);
bytes.append(&mut buf);
BigEndian::write_u16(&mut buf, *sequence);
bytes.append(&mut buf);
buf = vec![0, 4];
BigEndian::write_u32(&mut buf, *originate);
bytes.append(&mut buf);
BigEndian::write_u32(&mut buf, *receive);
bytes.append(&mut buf);
BigEndian::write_u32(&mut buf, *transmit);
bytes.append(&mut buf);
}
Self::Information {
// type 15
identifier,
sequence,
}
| Self::InformationReply {
// type 16
identifier,
sequence,
} => {
let mut buf = vec![0, 2];
BigEndian::write_u16(&mut buf, *identifier);
bytes.append(&mut buf);
BigEndian::write_u16(&mut buf, *sequence);
bytes.append(&mut buf);
}
}
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));
}
// NOTE(jwall) Because we use raw sockets the first 20 bytes are the IPv4 header.
bytes = &bytes[20..];
// NOTE(jwall): All ICMP packets are at least 8 bytes long.
packet_len = bytes.len();
let (typ, code, checksum) = (bytes[0], bytes[1], BigEndian::read_u16(&bytes[2..4]));
let message = match typ {
3 => Icmpv4Message::Unreachable {
padding: BigEndian::read_u32(&bytes[4..8]),
header: bytes[8..].to_owned(),
},
11 => Icmpv4Message::TimeExceeded {
padding: BigEndian::read_u32(&bytes[4..8]),
header: bytes[8..].to_owned(),
},
4 => Icmpv4Message::Quench {
padding: BigEndian::read_u32(&bytes[4..8]),
header: bytes[8..].to_owned(),
},
5 => Icmpv4Message::Redirect {
gateway: BigEndian::read_u32(&bytes[4..8]),
header: bytes[8..].to_owned(),
},
8 => Icmpv4Message::Echo {
identifier: BigEndian::read_u16(&bytes[4..6]),
sequence: BigEndian::read_u16(&bytes[6..8]),
payload: bytes[8..].to_owned(),
},
0 => Icmpv4Message::EchoReply {
identifier: BigEndian::read_u16(&bytes[4..6]),
sequence: BigEndian::read_u16(&bytes[6..8]),
payload: bytes[8..].to_owned(),
},
15 => Icmpv4Message::Information {
identifier: BigEndian::read_u16(&bytes[4..6]),
sequence: BigEndian::read_u16(&bytes[6..8]),
},
16 => Icmpv4Message::InformationReply {
identifier: BigEndian::read_u16(&bytes[4..6]),
sequence: BigEndian::read_u16(&bytes[6..8]),
},
13 => {
if packet_len < 20 {
return Err(PacketParseError::PacketTooSmall(bytes.len()));
}
Icmpv4Message::Timestamp {
identifier: BigEndian::read_u16(&bytes[4..6]),
sequence: BigEndian::read_u16(&bytes[6..8]),
originate: BigEndian::read_u32(&bytes[8..12]),
receive: BigEndian::read_u32(&bytes[12..16]),
transmit: BigEndian::read_u32(&bytes[16..20]),
}
}
14 => {
if packet_len < 20 {
return Err(PacketParseError::PacketTooSmall(bytes.len()));
}
Icmpv4Message::TimestampReply {
identifier: BigEndian::read_u16(&bytes[4..6]),
sequence: BigEndian::read_u16(&bytes[6..8]),
originate: BigEndian::read_u32(&bytes[8..12]),
receive: BigEndian::read_u32(&bytes[12..16]),
transmit: BigEndian::read_u32(&bytes[16..20]),
}
}
t => {
dbg!(bytes);
return Err(PacketParseError::UnrecognizedICMPType(t));
}
};
return Ok(Icmpv4Packet {
typ: typ,
code: code,
checksum: checksum,
message: message,
});
}
/// Get this packet serialized to bytes suitable for sending on the wire.
pub fn get_bytes(&self, with_checksum: bool) -> Vec<u8> {
let mut bytes = Vec::new();
bytes.push(self.typ);
bytes.push(self.code);
let mut buf = vec![0; 2];
BigEndian::write_u16(&mut buf, if with_checksum { self.checksum } else { 0 });
bytes.append(&mut buf);
bytes.append(&mut self.message.get_bytes());
return bytes;
}
/// Calculate the checksum for the packet given the provided source and destination
/// addresses.
pub fn calculate_checksum(&self) -> u16 {
// First sum the pseudo header
let mut sum = 0u32;
// Then sum the len of the message bytes and then the message bytes starting
// with the message type field and with the checksum field set to 0.
let bytes = self.get_bytes(false);
sum += sum_big_endian_words(&bytes);
// handle the carry
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 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,
},
})
}
}
#[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() {
// NOTE(jwall): I am shamelessly ripping ff the cases for this from libpnet
// The equivalent of your typical ping -6 ::1%lo
let lo = &Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1);
let mut data = vec![
0x80, // Icmpv6 Type
0x00, // Code
0xff, 0xff, // Checksum
0x00, 0x00, // Id
0x00, 0x01, // Sequence
// 56 bytes of "random" data
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);
// Check echo response as well
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);
}
}