icmp-socket/src/packet.rs

1061 lines
31 KiB
Rust
Raw Normal View History

2021-01-12 21:10:11 -05:00
// Copyright 2021 Jeremy Wall
2021-01-25 19:50:40 -05:00
//
2021-01-12 21:10:11 -05:00
// 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
2021-01-25 19:50:40 -05:00
//
2021-01-12 21:10:11 -05:00
// http://www.apache.org/licenses/LICENSE-2.0
2021-01-25 19:50:40 -05:00
//
2021-01-12 21:10:11 -05:00
// 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.
2021-01-25 20:35:34 -05:00
use std::convert::TryFrom;
2021-01-25 19:50:40 -05:00
use byteorder::{BigEndian, ByteOrder};
2021-01-12 21:10:11 -05:00
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.
2021-01-12 21:10:11 -05:00
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
2021-01-12 21:10:11 -05:00
data = &data[2..];
}
2021-01-25 19:50:40 -05:00
if (len % 2) != 0 {
// If odd then checksum the last byte
sum += (data[0] as u32) << 8;
2021-01-12 21:10:11 -05:00
}
return sum;
}
/// Construct a packet for the EchoRequest messages.
pub trait WithEchoRequest {
type Packet;
fn with_echo_request(
identifier: u16,
sequence: u16,
payload: Vec<u8>,
) -> Result<Self::Packet, IcmpPacketBuildError>;
}
/// Construct a packet for Echo Reply messages.
pub trait WithEchoReply {
type Packet;
fn with_echo_reply(
identifier: u16,
sequence: u16,
payload: Vec<u8>,
) -> Result<Self::Packet, IcmpPacketBuildError>;
}
/// Construct a packet for Destination Unreachable messages.
pub trait WithUnreachable {
type Packet;
fn with_unreachable(code: u8, packet: Vec<u8>) -> Result<Self::Packet, IcmpPacketBuildError>;
}
/// Construct a packet for Parameter Problem messages.
pub trait WithParameterProblem {
type Packet;
type Pointer;
fn with_parameter_problem(
code: u8,
pointer: Self::Pointer,
packet: Vec<u8>,
) -> Result<Self::Packet, IcmpPacketBuildError>;
}
/// Construct a packet for Time Exceeded messages.
pub trait WithTimeExceeded {
type Packet;
fn with_time_exceeded(code: u8, packet: Vec<u8>) -> Result<Self::Packet, IcmpPacketBuildError>;
}
/// The possible Icmpv6 Message types.
#[derive(Debug, PartialEq)]
2021-01-12 21:10:11 -05:00
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>,
2021-01-25 19:50:40 -05:00
},
2021-01-12 21:10:11 -05:00
}
2021-01-25 19:50:40 -05:00
use Icmpv6Message::{
EchoReply, EchoRequest, PacketTooBig, ParameterProblem, PrivateExperimental, TimeExceeded,
Unreachable,
};
2021-01-12 21:10:11 -05:00
impl Icmpv6Message {
/// Get this Icmpv6Message serialized to bytes.
2021-01-12 21:10:11 -05:00
pub fn get_bytes(&self) -> Vec<u8> {
let mut bytes = Vec::new();
match self {
2021-01-25 19:50:40 -05:00
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,
} => {
2021-01-12 21:10:11 -05:00
let mut buf = vec![0; 4];
BigEndian::write_u32(&mut buf, *field1);
bytes.append(&mut buf);
bytes.extend_from_slice(field2);
2021-01-25 19:50:40 -05:00
}
EchoRequest {
2021-01-12 21:10:11 -05:00
identifier,
sequence,
payload,
2021-01-25 19:50:40 -05:00
}
| EchoReply {
2021-01-12 21:10:11 -05:00
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)]
2021-01-12 21:10:11 -05:00
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,
}
/// Error type returned by parsing the ICMP packets.
2021-01-12 21:10:11 -05:00
#[derive(Debug)]
pub enum PacketParseError {
/// Not enough bytes to properly parse the packet from.
2021-01-12 21:10:11 -05:00
PacketTooSmall(usize),
/// An unrecognized ICMP type.
UnrecognizedICMPType(u8),
2021-01-12 21:10:11 -05:00
}
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]);
2021-01-12 21:10:11 -05:00
let payload = bytes[8..].to_owned();
let message = match typ {
2021-01-25 19:50:40 -05:00
1 => Unreachable {
2021-01-12 21:10:11 -05:00
_unused: next_field,
invoking_packet: payload,
},
2021-01-25 19:50:40 -05:00
2 => PacketTooBig {
2021-01-12 21:10:11 -05:00
mtu: next_field,
invoking_packet: payload,
},
2021-01-25 19:50:40 -05:00
3 => TimeExceeded {
2021-01-12 21:10:11 -05:00
_unused: next_field,
invoking_packet: payload,
},
4 => ParameterProblem {
pointer: next_field,
invoking_packet: payload,
},
2021-01-25 19:50:40 -05:00
100 | 101 | 200 | 201 => PrivateExperimental {
2021-01-12 21:10:11 -05:00
padding: next_field,
2021-01-25 19:50:40 -05:00
payload: payload,
2021-01-12 21:10:11 -05:00
},
2021-01-25 19:50:40 -05:00
128 => EchoRequest {
identifier: BigEndian::read_u16(&bytes[4..6]),
sequence: BigEndian::read_u16(&bytes[6..8]),
2021-01-12 21:10:11 -05:00
payload: payload,
},
2021-01-25 19:50:40 -05:00
129 => EchoReply {
identifier: BigEndian::read_u16(&bytes[4..6]),
sequence: BigEndian::read_u16(&bytes[6..8]),
2021-01-12 21:10:11 -05:00
payload: payload,
},
t => return Err(PacketParseError::UnrecognizedICMPType(t)),
2021-01-12 21:10:11 -05:00
};
2021-01-25 19:50:40 -05:00
return Ok(Icmpv6Packet {
2021-01-12 21:10:11 -05:00
typ: typ,
code: code,
checksum: checksum,
message: message,
2021-01-25 19:50:40 -05:00
});
2021-01-12 21:10:11 -05:00
}
/// 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);
2021-01-25 19:50:40 -05:00
BigEndian::write_u16(&mut buf, if with_checksum { self.checksum } else { 0 });
2021-01-12 21:10:11 -05:00
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;
2021-01-12 21:10:11 -05:00
// 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.
2021-01-12 21:10:11 -05:00
let bytes = self.get_bytes(false);
let len = bytes.len();
sum += len as u32;
sum += sum_big_endian_words(&bytes);
2021-01-25 19:50:40 -05:00
2021-01-12 21:10:11 -05:00
// 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 Packet Too Big messages.
pub fn with_packet_too_big(mtu: u32, packet: Vec<u8>) -> Result<Self, IcmpPacketBuildError> {
2021-01-12 21:10:11 -05:00
Ok(Self {
typ: 2,
code: 0,
2021-01-12 21:10:11 -05:00
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,
2021-01-12 21:10:11 -05:00
invoking_packet: packet,
},
})
}
}
2021-01-25 19:50:40 -05:00
impl WithEchoRequest for Icmpv6Packet {
type Packet = Icmpv6Packet;
fn with_echo_request(
identifier: u16,
sequence: u16,
payload: Vec<u8>,
) -> Result<Self::Packet, IcmpPacketBuildError> {
2021-01-25 19:50:40 -05:00
Ok(Self {
typ: 128,
2021-01-12 21:10:11 -05:00
code: 0,
checksum: 0,
message: EchoRequest {
identifier: identifier,
sequence: sequence,
payload: payload,
2021-01-12 21:10:11 -05:00
},
})
}
}
2021-01-25 19:50:40 -05:00
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));
2021-01-12 21:10:11 -05:00
}
2021-01-25 19:50:40 -05:00
Ok(Self {
typ: 1,
2021-01-12 21:10:11 -05:00
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 {
2021-01-12 21:10:11 -05:00
_unused: 0,
invoking_packet: packet,
},
})
}
}
impl WithParameterProblem for Icmpv6Packet {
type Packet = Icmpv6Packet;
type Pointer = u32;
2021-01-12 21:10:11 -05:00
fn with_parameter_problem(
2021-01-25 19:50:40 -05:00
code: u8,
pointer: Self::Pointer,
2021-01-25 19:50:40 -05:00
packet: Vec<u8>,
) -> Result<Self, IcmpPacketBuildError> {
2021-01-12 21:10:11 -05:00
if code > 1 {
return Err(IcmpPacketBuildError::InvalidCode(code));
2021-01-12 21:10:11 -05:00
}
Ok(Self {
typ: 4,
code: code,
checksum: 0,
2021-01-25 19:50:40 -05:00
message: ParameterProblem {
2021-01-12 21:10:11 -05:00
pointer: pointer,
invoking_packet: packet,
2021-01-25 19:50:40 -05:00
},
2021-01-12 21:10:11 -05:00
})
}
}
2021-01-12 21:10:11 -05:00
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));
}
2021-01-12 21:10:11 -05:00
Ok(Self {
typ: 3,
code: code,
2021-01-12 21:10:11 -05:00
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,
2021-01-25 19:50:40 -05:00
},
2021-01-12 21:10:11 -05:00
})
}
}
2021-01-25 20:35:34 -05:00
impl TryFrom<&[u8]> for Icmpv6Packet {
type Error = PacketParseError;
fn try_from(b: &[u8]) -> Result<Self, Self::Error> {
Icmpv6Packet::parse(b)
}
}
/// Errors returned by constructors for a given packet.
#[derive(Debug, PartialEq)]
pub enum IcmpPacketBuildError {
/// The code passed in for the payload was invalid for the message type.
2021-01-12 21:10:11 -05:00
InvalidCode(u8),
}
use IcmpPacketBuildError::InvalidCode;
impl std::fmt::Display for IcmpPacketBuildError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2021-01-25 19:50:40 -05:00
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 {
2021-01-25 19:50:40 -05:00
write!(
f,
"{}",
match self {
PacketParseError::PacketTooSmall(c) => format!("Packet Too Small size: {}", c),
PacketParseError::UnrecognizedICMPType(t) => format!("UnrecognizedIcmpType({})", t),
2021-01-25 19:50:40 -05:00
}
)
}
}
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))
}
}
/// The various messages for an Icmpv4 packet.
2021-01-25 20:55:25 -05:00
#[derive(Debug)]
2021-01-25 20:35:34 -05:00
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 {
/// Get this Icmpv4Message serialized as bytes.
2021-01-25 20:35:34 -05:00
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
}
}
/// An Icmpv4 Packet.
2021-01-25 20:55:25 -05:00
#[derive(Debug)]
2021-01-25 20:35:34 -05:00
pub struct Icmpv4Packet {
pub typ: u8,
pub code: u8,
pub checksum: u16,
pub message: Icmpv4Message,
}
impl Icmpv4Packet {
/// Parse an Icmpv4Packet from bytes.
2021-01-25 20:35:34 -05:00
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));
2021-01-25 20:35:34 -05:00
}
// 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();
2021-01-25 20:35:34 -05:00
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));
}
2021-01-25 20:35:34 -05:00
};
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];
2021-01-25 20:35:34 -05:00
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
}
/// Populate the checksum field of this Packet.
2021-01-25 20:35:34 -05:00
pub fn with_checksum(mut self) -> Self {
self.checksum = self.calculate_checksum();
self
}
}
2021-01-25 20:35:34 -05:00
impl TryFrom<&[u8]> for Icmpv4Packet {
type Error = PacketParseError;
fn try_from(b: &[u8]) -> Result<Self, Self::Error> {
Icmpv4Packet::parse(b)
2021-01-25 20:35:34 -05:00
}
}
2021-01-25 20:35:34 -05:00
impl WithEchoRequest for Icmpv4Packet {
type Packet = Icmpv4Packet;
fn with_echo_request(
2021-01-25 20:35:34 -05:00
identifier: u16,
sequence: u16,
payload: Vec<u8>,
) -> Result<Self::Packet, IcmpPacketBuildError> {
2021-01-25 20:35:34 -05:00
Ok(Self {
typ: 8,
2021-01-25 20:35:34 -05:00
code: 0,
checksum: 0,
message: Icmpv4Message::Echo {
2021-01-25 20:35:34 -05:00
identifier,
sequence,
payload,
},
})
}
}
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() {
2021-01-25 19:50:40 -05:00
let pkt = Icmpv6Packet::with_echo_request(42, 1, vec![1, 2, 3, 4]).unwrap();
assert_eq!(pkt.typ, 128);
assert_eq!(pkt.code, 0);
2021-01-25 19:50:40 -05:00
assert_eq!(
pkt.message,
EchoRequest {
identifier: 42,
sequence: 1,
payload: vec![1, 2, 3, 4],
}
);
}
#[test]
fn packet_construction_echo_reply_test() {
2021-01-25 19:50:40 -05:00
let pkt = Icmpv6Packet::with_echo_reply(42, 1, vec![1, 2, 3, 4]).unwrap();
assert_eq!(pkt.typ, 129);
assert_eq!(pkt.code, 0);
2021-01-25 19:50:40 -05:00
assert_eq!(
pkt.message,
EchoReply {
identifier: 42,
sequence: 1,
payload: vec![1, 2, 3, 4],
}
);
}
#[test]
fn packet_construction_too_big_test() {
2021-01-25 19:50:40 -05:00
let pkt = Icmpv6Packet::with_packet_too_big(3, vec![1, 2, 3, 4]).unwrap();
assert_eq!(pkt.typ, 2);
assert_eq!(pkt.code, 0);
2021-01-25 19:50:40 -05:00
assert_eq!(
pkt.message,
PacketTooBig {
mtu: 3,
invoking_packet: vec![1, 2, 3, 4],
}
);
}
#[test]
fn packet_construction_time_exceeded() {
2021-01-25 19:50:40 -05:00
let pkt = Icmpv6Packet::with_time_exceeded(0, vec![1, 2, 3, 4]).unwrap();
assert_eq!(pkt.typ, 3);
assert_eq!(pkt.code, 0);
2021-01-25 19:50:40 -05:00
assert_eq!(
pkt.message,
TimeExceeded {
_unused: 0,
invoking_packet: vec![1, 2, 3, 4],
}
);
}
2021-01-25 19:50:40 -05:00
#[test]
fn packet_construction_time_exceeded_invalid_code() {
2021-01-25 19:50:40 -05:00
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() {
2021-01-25 19:50:40 -05:00
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);
2021-01-25 19:50:40 -05:00
assert_eq!(
pkt.message,
ParameterProblem {
pointer: 30,
invoking_packet: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
}
);
}
2021-01-25 19:50:40 -05:00
#[test]
fn packet_construction_parameter_problem_invalid_code() {
2021-01-25 19:50:40 -05:00
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
2021-01-25 19:50:40 -05:00
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,
];
2021-01-16 13:45:12 -05:00
let mut pkt = Icmpv6Packet::parse(&data).unwrap();
assert_eq!(pkt.typ, 128);
assert_eq!(pkt.code, 0x00);
2021-01-25 19:50:40 -05:00
if let EchoRequest {
identifier,
sequence,
payload,
2021-01-25 19:50:40 -05:00
} = &pkt.message
{
assert_eq!(*identifier, 0);
assert_eq!(*sequence, 1);
2021-01-25 19:50:40 -05:00
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);
2021-01-16 13:45:12 -05:00
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);
2021-01-25 19:50:40 -05:00
if let EchoReply {
identifier,
sequence,
payload,
2021-01-25 19:50:40 -05:00
} = &pkt.message
{
assert_eq!(*identifier, 0);
assert_eq!(*sequence, 1);
2021-01-25 19:50:40 -05:00
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
);
}
2021-01-16 13:45:12 -05:00
assert_eq!(pkt.get_bytes(true), data);
assert_eq!(pkt.calculate_checksum(lo, lo), 0x1c2e);
}
2021-01-25 19:50:40 -05:00
}