cargo fmt

This commit is contained in:
Jeremy Wall 2021-01-25 19:50:40 -05:00
parent e850bf0236
commit 6de7e27242
5 changed files with 294 additions and 173 deletions

View File

@ -1,11 +1,11 @@
// Copyright 2021 Jeremy Wall // Copyright 2021 Jeremy Wall
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
// You may obtain a copy of the License at // You may obtain a copy of the License at
// //
// http://www.apache.org/licenses/LICENSE-2.0 // http://www.apache.org/licenses/LICENSE-2.0
// //
// Unless required by applicable law or agreed to in writing, software // Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, // distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -16,18 +16,27 @@ use std::net::Ipv6Addr;
use icmp_socket::*; use icmp_socket::*;
pub fn main() { pub fn main() {
let mut socket6 = IcmpSocket6::new().unwrap(); let mut socket6 = IcmpSocket6::new().unwrap();
socket6.bind("::1".parse::<Ipv6Addr>().unwrap()).unwrap(); socket6.bind("::1".parse::<Ipv6Addr>().unwrap()).unwrap();
let mut echo_socket = echo::EchoSocket6::new(socket6); let mut echo_socket = echo::EchoSocket6::new(socket6);
echo_socket.send_ping("::1".parse::<Ipv6Addr>().unwrap(), 42, &[ echo_socket
0x20, 0x20, 0x75, 0x73, 0x74, 0x20, 0x61, 0x20, .send_ping(
0x66, 0x6c, 0x65, 0x73, 0x68, 0x20, 0x77, 0x6f, "::1".parse::<Ipv6Addr>().unwrap(),
0x75, 0x6e, 0x64, 0x20, 0x20, 0x74, 0x69, 0x73, 42,
0x20, 0x62, 0x75, 0x74, 0x20, 0x61, 0x20, 0x73, &[
0x63, 0x72, 0x61, 0x74, 0x63, 0x68, 0x20, 0x20, 0x20, 0x20, 0x75, 0x73, 0x74, 0x20, 0x61, 0x20, 0x66, 0x6c, 0x65, 0x73, 0x68, 0x20,
0x6b, 0x6e, 0x69, 0x67, 0x68, 0x74, 0x73, 0x20, 0x77, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x20, 0x74, 0x69, 0x73, 0x20, 0x62, 0x75, 0x74,
0x6f, 0x66, 0x20, 0x6e, 0x69, 0x20, 0x20, 0x20]).unwrap(); 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,
],
)
.unwrap();
let _ = echo_socket.recv_ping(); let _ = echo_socket.recv_ping();
let resp = echo_socket.recv_ping().unwrap(); let resp = echo_socket.recv_ping().unwrap();
println!("seq: {}, identifier: {} payload: {}", resp.sequence, resp.identifier, resp.payload.len()); println!(
} "seq: {}, identifier: {} payload: {}",
resp.sequence,
resp.identifier,
resp.payload.len()
);
}

View File

@ -1,23 +1,23 @@
// Copyright 2021 Jeremy Wall // Copyright 2021 Jeremy Wall
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
// You may obtain a copy of the License at // You may obtain a copy of the License at
// //
// http://www.apache.org/licenses/LICENSE-2.0 // http://www.apache.org/licenses/LICENSE-2.0
// //
// Unless required by applicable law or agreed to in writing, software // Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, // distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
use std::convert::{TryFrom, From, TryInto}; use std::convert::{From, TryFrom, TryInto};
use std::net::{Ipv4Addr, Ipv6Addr}; use std::net::{Ipv4Addr, Ipv6Addr};
use packet::{Builder, Packet as P};
use packet::icmp::echo::Packet; use packet::icmp::echo::Packet;
use packet::{Builder, Packet as P};
use crate::packet::{Icmpv6Packet, Icmpv6Message::EchoReply}; use crate::packet::{Icmpv6Message::EchoReply, Icmpv6Packet};
// TODO(jwall): It turns out that the ICMPv6 packets are sufficiently // TODO(jwall): It turns out that the ICMPv6 packets are sufficiently
// different from the ICMPv4 packets. In order to handle them appropriately // different from the ICMPv4 packets. In order to handle them appropriately
@ -35,18 +35,25 @@ impl TryFrom<Icmpv6Packet> for EchoResponse {
type Error = std::io::Error; type Error = std::io::Error;
fn try_from(pkt: Icmpv6Packet) -> Result<Self, Self::Error> { fn try_from(pkt: Icmpv6Packet) -> Result<Self, Self::Error> {
if let EchoReply{ if let EchoReply {
identifier, identifier,
sequence, sequence,
payload, payload,
} = pkt.message { } = pkt.message
Ok(EchoResponse{ {
Ok(EchoResponse {
identifier, identifier,
sequence, sequence,
payload, payload,
}) })
} else { } else {
Err(std::io::Error::new(std::io::ErrorKind::Other, format!("Incorrect icmpv6 message: {:?}, code: {}", pkt.message, pkt.code))) Err(std::io::Error::new(
std::io::ErrorKind::Other,
format!(
"Incorrect icmpv6 message: {:?}, code: {}",
pkt.message, pkt.code
),
))
} }
} }
} }
@ -59,19 +66,36 @@ pub struct EchoSocket4 {
impl EchoSocket4 { impl EchoSocket4 {
pub fn new(sock: IcmpSocket4) -> Self { pub fn new(sock: IcmpSocket4) -> Self {
EchoSocket4{inner:sock, sequence: 0, buf: Vec::with_capacity(512)} EchoSocket4 {
inner: sock,
sequence: 0,
buf: Vec::with_capacity(512),
}
} }
pub fn set_max_hops(&mut self, hops: u32) { pub fn set_max_hops(&mut self, hops: u32) {
self.inner.set_max_hops(hops); self.inner.set_max_hops(hops);
} }
pub fn send_ping(&mut self, dest: Ipv4Addr, identifier: u16, payload: &[u8]) -> std::io::Result<()> { pub fn send_ping(
&mut self,
dest: Ipv4Addr,
identifier: u16,
payload: &[u8],
) -> std::io::Result<()> {
let packet = packet::icmp::Builder::default() let packet = packet::icmp::Builder::default()
.echo().unwrap().request().unwrap() .echo()
.identifier(identifier).unwrap() .unwrap()
.sequence(self.sequence).unwrap() .request()
.payload(payload).unwrap().build().unwrap(); .unwrap()
.identifier(identifier)
.unwrap()
.sequence(self.sequence)
.unwrap()
.payload(payload)
.unwrap()
.build()
.unwrap();
self.sequence += 1; self.sequence += 1;
self.inner.send_to(dest, &packet)?; self.inner.send_to(dest, &packet)?;
Ok(()) Ok(())
@ -80,12 +104,19 @@ impl EchoSocket4 {
pub fn recv_ping(&mut self) -> std::io::Result<EchoResponse> { pub fn recv_ping(&mut self) -> std::io::Result<EchoResponse> {
let bytes_read = self.inner.rcv_from(&mut self.buf)?; let bytes_read = self.inner.rcv_from(&mut self.buf)?;
match Packet::new(&self.buf[0..bytes_read]) { match Packet::new(&self.buf[0..bytes_read]) {
Ok(p) => return Ok(EchoResponse{ Ok(p) => {
sequence: p.sequence(), return Ok(EchoResponse {
identifier: p.identifier(), sequence: p.sequence(),
payload: p.payload().to_owned(), identifier: p.identifier(),
}), payload: p.payload().to_owned(),
Err(e) => return Err(std::io::Error::new(std::io::ErrorKind::Other, format!("Malformed ICMP Response: {:?}", e))), })
}
Err(e) => {
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
format!("Malformed ICMP Response: {:?}", e),
))
}
}; };
} }
} }
@ -102,18 +133,26 @@ pub struct EchoSocket6 {
} }
impl EchoSocket6 { impl EchoSocket6 {
pub fn new(sock: IcmpSocket6) -> Self { pub fn new(sock: IcmpSocket6) -> Self {
// TODO(jwall): How to set ICMPv6 filters. // TODO(jwall): How to set ICMPv6 filters.
EchoSocket6{inner:sock, sequence: 0} EchoSocket6 {
inner: sock,
sequence: 0,
}
} }
pub fn set_max_hops(&mut self, hops: u32) { pub fn set_max_hops(&mut self, hops: u32) {
self.inner.set_max_hops(hops); self.inner.set_max_hops(hops);
} }
pub fn send_ping(&mut self, dest: Ipv6Addr, identifier: u16, payload: &[u8]) -> std::io::Result<()> { pub fn send_ping(
let packet = Icmpv6Packet::with_echo_request(identifier, self.sequence, payload.to_owned())?; &mut self,
dest: Ipv6Addr,
identifier: u16,
payload: &[u8],
) -> std::io::Result<()> {
let packet =
Icmpv6Packet::with_echo_request(identifier, self.sequence, payload.to_owned())?;
self.sequence += 1; self.sequence += 1;
self.inner.send_to(dest, packet)?; self.inner.send_to(dest, packet)?;
Ok(()) Ok(())

View File

@ -1,20 +1,20 @@
// Copyright 2021 Jeremy Wall // Copyright 2021 Jeremy Wall
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
// You may obtain a copy of the License at // You may obtain a copy of the License at
// //
// http://www.apache.org/licenses/LICENSE-2.0 // http://www.apache.org/licenses/LICENSE-2.0
// //
// Unless required by applicable law or agreed to in writing, software // Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, // distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
pub mod socket;
pub mod echo; pub mod echo;
pub mod packet; pub mod packet;
pub mod socket;
pub use crate::packet::{Icmpv6Packet, Icmpv6Message}; pub use crate::packet::{Icmpv6Message, Icmpv6Packet};
pub use socket::{IcmpSocket4, IcmpSocket6}; pub use socket::{IcmpSocket4, IcmpSocket6};

View File

@ -1,18 +1,18 @@
// Copyright 2021 Jeremy Wall // Copyright 2021 Jeremy Wall
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
// You may obtain a copy of the License at // You may obtain a copy of the License at
// //
// http://www.apache.org/licenses/LICENSE-2.0 // http://www.apache.org/licenses/LICENSE-2.0
// //
// Unless required by applicable law or agreed to in writing, software // Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, // distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
use byteorder::{BigEndian, ByteOrder};
use std::net::Ipv6Addr; use std::net::Ipv6Addr;
use byteorder::{ByteOrder, BigEndian};
fn ipv6_sum_words(ip: &Ipv6Addr) -> u32 { fn ipv6_sum_words(ip: &Ipv6Addr) -> u32 {
ip.segments().iter().map(|x| *x as u32).sum() ip.segments().iter().map(|x| *x as u32).sum()
@ -33,7 +33,8 @@ fn sum_big_endian_words(bs: &[u8]) -> u32 {
data = &data[2..]; data = &data[2..];
} }
if (len % 2) != 0 { // If odd then checksum the last byte if (len % 2) != 0 {
// If odd then checksum the last byte
sum += (data[0] as u32) << 8; sum += (data[0] as u32) << 8;
} }
return sum; return sum;
@ -72,30 +73,49 @@ pub enum Icmpv6Message {
identifier: u16, identifier: u16,
sequence: u16, sequence: u16,
payload: Vec<u8>, payload: Vec<u8>,
} },
} }
use Icmpv6Message::{Unreachable, PacketTooBig, TimeExceeded, ParameterProblem, PrivateExperimental, EchoRequest, EchoReply}; use Icmpv6Message::{
EchoReply, EchoRequest, PacketTooBig, ParameterProblem, PrivateExperimental, TimeExceeded,
Unreachable,
};
impl Icmpv6Message { impl Icmpv6Message {
pub fn get_bytes(&self) -> Vec<u8> { pub fn get_bytes(&self) -> Vec<u8> {
let mut bytes = Vec::new(); let mut bytes = Vec::new();
match self { match self {
Unreachable {_unused: field1, invoking_packet: field2 } | Unreachable {
PacketTooBig {mtu: field1, invoking_packet: field2 } | _unused: field1,
TimeExceeded {_unused: field1, invoking_packet: field2 } | invoking_packet: field2,
ParameterProblem {pointer: field1, invoking_packet: field2 } | }
PrivateExperimental {padding: field1, payload: 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]; let mut buf = vec![0; 4];
BigEndian::write_u32(&mut buf, *field1); BigEndian::write_u32(&mut buf, *field1);
bytes.append(&mut buf); bytes.append(&mut buf);
bytes.extend_from_slice(field2); bytes.extend_from_slice(field2);
}, }
EchoRequest{ EchoRequest {
identifier, identifier,
sequence, sequence,
payload, payload,
} | EchoReply{ }
| EchoReply {
identifier, identifier,
sequence, sequence,
payload, payload,
@ -141,15 +161,15 @@ impl Icmpv6Packet {
let next_field = BigEndian::read_u32(&bytes[4..8]); let next_field = BigEndian::read_u32(&bytes[4..8]);
let payload = bytes[8..].to_owned(); let payload = bytes[8..].to_owned();
let message = match typ { let message = match typ {
1 => Unreachable{ 1 => Unreachable {
_unused: next_field, _unused: next_field,
invoking_packet: payload, invoking_packet: payload,
}, },
2 => PacketTooBig{ 2 => PacketTooBig {
mtu: next_field, mtu: next_field,
invoking_packet: payload, invoking_packet: payload,
}, },
3 => TimeExceeded{ 3 => TimeExceeded {
_unused: next_field, _unused: next_field,
invoking_packet: payload, invoking_packet: payload,
}, },
@ -157,28 +177,28 @@ impl Icmpv6Packet {
pointer: next_field, pointer: next_field,
invoking_packet: payload, invoking_packet: payload,
}, },
100 | 101 | 200 | 201 => PrivateExperimental{ 100 | 101 | 200 | 201 => PrivateExperimental {
padding: next_field, padding: next_field,
payload: payload, payload: payload,
}, },
128 => EchoRequest{ 128 => EchoRequest {
identifier: BigEndian::read_u16(&bytes[4..6]), identifier: BigEndian::read_u16(&bytes[4..6]),
sequence: BigEndian::read_u16(&bytes[6..8]), sequence: BigEndian::read_u16(&bytes[6..8]),
payload: payload, payload: payload,
}, },
129 => EchoReply{ 129 => EchoReply {
identifier: BigEndian::read_u16(&bytes[4..6]), identifier: BigEndian::read_u16(&bytes[4..6]),
sequence: BigEndian::read_u16(&bytes[6..8]), sequence: BigEndian::read_u16(&bytes[6..8]),
payload: payload, payload: payload,
}, },
_ => return Err(PacketParseError::UnrecognizedICMPType), _ => return Err(PacketParseError::UnrecognizedICMPType),
}; };
return Ok(Icmpv6Packet{ return Ok(Icmpv6Packet {
typ: typ, typ: typ,
code: code, code: code,
checksum: checksum, checksum: checksum,
message: message, message: message,
}) });
} }
/// Get this packet serialized to bytes suitable for sending on the wire. /// Get this packet serialized to bytes suitable for sending on the wire.
@ -188,11 +208,7 @@ impl Icmpv6Packet {
bytes.push(self.code); bytes.push(self.code);
let mut buf = Vec::with_capacity(2); let mut buf = Vec::with_capacity(2);
buf.resize(2, 0); buf.resize(2, 0);
BigEndian::write_u16(&mut buf, if with_checksum { BigEndian::write_u16(&mut buf, if with_checksum { self.checksum } else { 0 });
self.checksum
} else {
0
});
bytes.append(&mut buf); bytes.append(&mut buf);
bytes.append(&mut self.message.get_bytes()); bytes.append(&mut self.message.get_bytes());
return bytes; return bytes;
@ -215,7 +231,7 @@ impl Icmpv6Packet {
let len = bytes.len(); let len = bytes.len();
sum += len as u32; sum += len as u32;
sum += sum_big_endian_words(&bytes); sum += sum_big_endian_words(&bytes);
// handle the carry // handle the carry
while sum >> 16 != 0 { while sum >> 16 != 0 {
sum = (sum >> 16) + (sum & 0xFFFF); sum = (sum >> 16) + (sum & 0xFFFF);
@ -241,40 +257,40 @@ impl Icmpv6Packet {
checksum: 0, checksum: 0,
// TODO(jwall): Should we enforce that the packet isn't too big? // TODO(jwall): Should we enforce that the packet isn't too big?
// It is not supposed to be larger than the minimum IPv6 MTU // It is not supposed to be larger than the minimum IPv6 MTU
message: Unreachable{ message: Unreachable {
_unused: 0, _unused: 0,
invoking_packet: packet, invoking_packet: packet,
}, },
}) })
} }
/// Construct a packet for Packet Too Big messages. /// Construct a packet for Packet Too Big messages.
pub fn with_packet_too_big(mtu: u32, packet: Vec<u8>) -> Result<Self, Icmpv6PacketBuildError> { pub fn with_packet_too_big(mtu: u32, packet: Vec<u8>) -> Result<Self, Icmpv6PacketBuildError> {
Ok(Self{ Ok(Self {
typ: 2, typ: 2,
code: 0, code: 0,
checksum: 0, checksum: 0,
// TODO(jwall): Should we enforce that the packet isn't too big? // TODO(jwall): Should we enforce that the packet isn't too big?
// It is not supposed to be larger than the minimum IPv6 MTU // It is not supposed to be larger than the minimum IPv6 MTU
message: PacketTooBig{ message: PacketTooBig {
mtu: mtu, mtu: mtu,
invoking_packet: packet, invoking_packet: packet,
}, },
}) })
} }
/// Construct a packet for Time Exceeded messages. /// Construct a packet for Time Exceeded messages.
pub fn with_time_exceeded(code: u8, packet: Vec<u8>) -> Result<Self, Icmpv6PacketBuildError> { pub fn with_time_exceeded(code: u8, packet: Vec<u8>) -> Result<Self, Icmpv6PacketBuildError> {
if code > 1 { if code > 1 {
return Err(Icmpv6PacketBuildError::InvalidCode(code)); return Err(Icmpv6PacketBuildError::InvalidCode(code));
} }
Ok(Self{ Ok(Self {
typ: 3, typ: 3,
code: code, code: code,
checksum: 0, checksum: 0,
// TODO(jwall): Should we enforce that the packet isn't too big? // TODO(jwall): Should we enforce that the packet isn't too big?
// It is not supposed to be larger than the minimum IPv6 MTU // It is not supposed to be larger than the minimum IPv6 MTU
message: TimeExceeded{ message: TimeExceeded {
_unused: 0, _unused: 0,
invoking_packet: packet, invoking_packet: packet,
}, },
@ -282,7 +298,11 @@ impl Icmpv6Packet {
} }
/// Construct a packet for Parameter Problem messages. /// Construct a packet for Parameter Problem messages.
pub fn with_parameter_problem(code: u8, pointer: u32, packet: Vec<u8>) -> Result<Self, Icmpv6PacketBuildError> { pub fn with_parameter_problem(
code: u8,
pointer: u32,
packet: Vec<u8>,
) -> Result<Self, Icmpv6PacketBuildError> {
if code > 1 { if code > 1 {
return Err(Icmpv6PacketBuildError::InvalidCode(code)); return Err(Icmpv6PacketBuildError::InvalidCode(code));
} }
@ -290,38 +310,46 @@ impl Icmpv6Packet {
typ: 4, typ: 4,
code: code, code: code,
checksum: 0, checksum: 0,
message: ParameterProblem{ message: ParameterProblem {
pointer: pointer, pointer: pointer,
invoking_packet: packet, invoking_packet: packet,
} },
}) })
} }
/// Construct a packet for Echo Request messages. /// Construct a packet for Echo Request messages.
pub fn with_echo_request(identifier: u16, sequence: u16, payload: Vec<u8>) -> Result<Self, Icmpv6PacketBuildError> { pub fn with_echo_request(
identifier: u16,
sequence: u16,
payload: Vec<u8>,
) -> Result<Self, Icmpv6PacketBuildError> {
Ok(Self { Ok(Self {
typ: 128, typ: 128,
code: 0, code: 0,
checksum: 0, checksum: 0,
message: EchoRequest{ message: EchoRequest {
identifier: identifier, identifier: identifier,
sequence: sequence, sequence: sequence,
payload: payload, payload: payload,
} },
}) })
} }
/// Construct a packet for Echo Reply messages. /// Construct a packet for Echo Reply messages.
pub fn with_echo_reply(identifier: u16, sequence: u16, payload: Vec<u8>) -> Result<Self, Icmpv6PacketBuildError> { pub fn with_echo_reply(
identifier: u16,
sequence: u16,
payload: Vec<u8>,
) -> Result<Self, Icmpv6PacketBuildError> {
Ok(Self { Ok(Self {
typ: 129, typ: 129,
code: 0, code: 0,
checksum: 0, checksum: 0,
message: EchoReply{ message: EchoReply {
identifier: identifier, identifier: identifier,
sequence: sequence, sequence: sequence,
payload: payload, payload: payload,
} },
}) })
} }
} }
@ -334,18 +362,26 @@ use Icmpv6PacketBuildError::InvalidCode;
impl std::fmt::Display for Icmpv6PacketBuildError { impl std::fmt::Display for Icmpv6PacketBuildError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", match self { write!(
InvalidCode(c) => format!("Invalid Code: {}", c), f,
}) "{}",
match self {
InvalidCode(c) => format!("Invalid Code: {}", c),
}
)
} }
} }
impl std::fmt::Display for PacketParseError { impl std::fmt::Display for PacketParseError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", match self { write!(
PacketParseError::PacketTooSmall(c) => format!("Packet Too Small size: {}", c), f,
PacketParseError::UnrecognizedICMPType => "UnrecognizedIcmpType".to_owned(), "{}",
}) match self {
PacketParseError::PacketTooSmall(c) => format!("Packet Too Small size: {}", c),
PacketParseError::UnrecognizedICMPType => "UnrecognizedIcmpType".to_owned(),
}
)
} }
} }
@ -367,47 +403,65 @@ mod checksum_tests {
#[test] #[test]
fn packet_construction_echo_request_test() { fn packet_construction_echo_request_test() {
let pkt = Icmpv6Packet::with_echo_request(42, 1, vec![1,2,3,4]).unwrap(); let pkt = Icmpv6Packet::with_echo_request(42, 1, vec![1, 2, 3, 4]).unwrap();
assert_eq!(pkt.typ, 128); assert_eq!(pkt.typ, 128);
assert_eq!(pkt.code, 0); assert_eq!(pkt.code, 0);
assert_eq!(pkt.message, EchoRequest{ assert_eq!(
identifier: 42, sequence: 1, payload: vec![1,2,3,4], pkt.message,
}); EchoRequest {
identifier: 42,
sequence: 1,
payload: vec![1, 2, 3, 4],
}
);
} }
#[test] #[test]
fn packet_construction_echo_reply_test() { fn packet_construction_echo_reply_test() {
let pkt = Icmpv6Packet::with_echo_reply(42, 1, vec![1,2,3,4]).unwrap(); let pkt = Icmpv6Packet::with_echo_reply(42, 1, vec![1, 2, 3, 4]).unwrap();
assert_eq!(pkt.typ, 129); assert_eq!(pkt.typ, 129);
assert_eq!(pkt.code, 0); assert_eq!(pkt.code, 0);
assert_eq!(pkt.message, EchoReply{ assert_eq!(
identifier: 42, sequence: 1, payload: vec![1,2,3,4], pkt.message,
}); EchoReply {
identifier: 42,
sequence: 1,
payload: vec![1, 2, 3, 4],
}
);
} }
#[test] #[test]
fn packet_construction_too_big_test() { fn packet_construction_too_big_test() {
let pkt = Icmpv6Packet::with_packet_too_big(3, vec![1,2,3,4]).unwrap(); let pkt = Icmpv6Packet::with_packet_too_big(3, vec![1, 2, 3, 4]).unwrap();
assert_eq!(pkt.typ, 2); assert_eq!(pkt.typ, 2);
assert_eq!(pkt.code, 0); assert_eq!(pkt.code, 0);
assert_eq!(pkt.message, PacketTooBig{ assert_eq!(
mtu: 3, invoking_packet: vec![1,2,3,4], pkt.message,
}); PacketTooBig {
mtu: 3,
invoking_packet: vec![1, 2, 3, 4],
}
);
} }
#[test] #[test]
fn packet_construction_time_exceeded() { fn packet_construction_time_exceeded() {
let pkt = Icmpv6Packet::with_time_exceeded(0, vec![1,2,3,4]).unwrap(); let pkt = Icmpv6Packet::with_time_exceeded(0, vec![1, 2, 3, 4]).unwrap();
assert_eq!(pkt.typ, 3); assert_eq!(pkt.typ, 3);
assert_eq!(pkt.code, 0); assert_eq!(pkt.code, 0);
assert_eq!(pkt.message, TimeExceeded{ assert_eq!(
_unused: 0, invoking_packet: vec![1,2,3,4], pkt.message,
}); TimeExceeded {
_unused: 0,
invoking_packet: vec![1, 2, 3, 4],
}
);
} }
#[test] #[test]
fn packet_construction_time_exceeded_invalid_code() { fn packet_construction_time_exceeded_invalid_code() {
let pkt = Icmpv6Packet::with_time_exceeded(2, vec![1,2,3,4]); let pkt = Icmpv6Packet::with_time_exceeded(2, vec![1, 2, 3, 4]);
assert!(pkt.is_err()); assert!(pkt.is_err());
let e = pkt.unwrap_err(); let e = pkt.unwrap_err();
assert_eq!(e, Icmpv6PacketBuildError::InvalidCode(2)); assert_eq!(e, Icmpv6PacketBuildError::InvalidCode(2));
@ -415,17 +469,22 @@ mod checksum_tests {
#[test] #[test]
fn packet_construction_parameter_problem() { 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(); 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.typ, 4);
assert_eq!(pkt.code, 0); assert_eq!(pkt.code, 0);
assert_eq!(pkt.message, ParameterProblem{ assert_eq!(
pointer: 30, invoking_packet: vec![1,2,3,4,5,6,7,8,9,10], pkt.message,
}); ParameterProblem {
pointer: 30,
invoking_packet: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
}
);
} }
#[test] #[test]
fn packet_construction_parameter_problem_invalid_code() { fn packet_construction_parameter_problem_invalid_code() {
let pkt = Icmpv6Packet::with_parameter_problem(3, 30, vec![1,2,3,4]); let pkt = Icmpv6Packet::with_parameter_problem(3, 30, vec![1, 2, 3, 4]);
assert!(pkt.is_err()); assert!(pkt.is_err());
let e = pkt.unwrap_err(); let e = pkt.unwrap_err();
assert_eq!(e, Icmpv6PacketBuildError::InvalidCode(3)); assert_eq!(e, Icmpv6PacketBuildError::InvalidCode(3));
@ -443,35 +502,38 @@ mod checksum_tests {
0x00, 0x00, // Id 0x00, 0x00, // Id
0x00, 0x01, // Sequence 0x00, 0x01, // Sequence
// 56 bytes of "random" data // 56 bytes of "random" data
0x20, 0x20, 0x75, 0x73, 0x74, 0x20, 0x61, 0x20, 0x20, 0x20, 0x75, 0x73, 0x74, 0x20, 0x61, 0x20, 0x66, 0x6c, 0x65, 0x73, 0x68, 0x20,
0x66, 0x6c, 0x65, 0x73, 0x68, 0x20, 0x77, 0x6f, 0x77, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x20, 0x74, 0x69, 0x73, 0x20, 0x62, 0x75, 0x74,
0x75, 0x6e, 0x64, 0x20, 0x20, 0x74, 0x69, 0x73, 0x20, 0x61, 0x20, 0x73, 0x63, 0x72, 0x61, 0x74, 0x63, 0x68, 0x20, 0x20, 0x6b, 0x6e,
0x20, 0x62, 0x75, 0x74, 0x20, 0x61, 0x20, 0x73, 0x69, 0x67, 0x68, 0x74, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x6e, 0x69, 0x20, 0x20, 0x20,
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(); let mut pkt = Icmpv6Packet::parse(&data).unwrap();
assert_eq!(pkt.typ, 128); assert_eq!(pkt.typ, 128);
assert_eq!(pkt.code, 0x00); assert_eq!(pkt.code, 0x00);
if let EchoRequest{ if let EchoRequest {
identifier, identifier,
sequence, sequence,
payload, payload,
}= &pkt.message { } = &pkt.message
{
assert_eq!(*identifier, 0); assert_eq!(*identifier, 0);
assert_eq!(*sequence, 1); assert_eq!(*sequence, 1);
assert_eq!(payload, &[ assert_eq!(
0x20, 0x20, 0x75, 0x73, 0x74, 0x20, 0x61, 0x20, payload,
0x66, 0x6c, 0x65, 0x73, 0x68, 0x20, 0x77, 0x6f, &[
0x75, 0x6e, 0x64, 0x20, 0x20, 0x74, 0x69, 0x73, 0x20, 0x20, 0x75, 0x73, 0x74, 0x20, 0x61, 0x20, 0x66, 0x6c, 0x65, 0x73, 0x68,
0x20, 0x62, 0x75, 0x74, 0x20, 0x61, 0x20, 0x73, 0x20, 0x77, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x20, 0x74, 0x69, 0x73, 0x20, 0x62,
0x63, 0x72, 0x61, 0x74, 0x63, 0x68, 0x20, 0x20, 0x75, 0x74, 0x20, 0x61, 0x20, 0x73, 0x63, 0x72, 0x61, 0x74, 0x63, 0x68, 0x20,
0x6b, 0x6e, 0x69, 0x67, 0x68, 0x74, 0x73, 0x20, 0x20, 0x6b, 0x6e, 0x69, 0x67, 0x68, 0x74, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x6e,
0x6f, 0x66, 0x20, 0x6e, 0x69, 0x20, 0x20, 0x20 0x69, 0x20, 0x20, 0x20
]); ]
} else { );
assert!(false, "Packet did not parse as an EchoRequest {:?}", pkt.message); } else {
assert!(
false,
"Packet did not parse as an EchoRequest {:?}",
pkt.message
);
} }
assert_eq!(pkt.get_bytes(true), data); assert_eq!(pkt.get_bytes(true), data);
assert_eq!(pkt.calculate_checksum(lo, lo), 0x1d2e); assert_eq!(pkt.calculate_checksum(lo, lo), 0x1d2e);
@ -483,26 +545,32 @@ mod checksum_tests {
let pkt = Icmpv6Packet::parse(&data).unwrap(); let pkt = Icmpv6Packet::parse(&data).unwrap();
assert_eq!(pkt.typ, 129); assert_eq!(pkt.typ, 129);
assert_eq!(pkt.code, 0); assert_eq!(pkt.code, 0);
if let EchoReply{ if let EchoReply {
identifier, identifier,
sequence, sequence,
payload, payload,
}= &pkt.message { } = &pkt.message
{
assert_eq!(*identifier, 0); assert_eq!(*identifier, 0);
assert_eq!(*sequence, 1); assert_eq!(*sequence, 1);
assert_eq!(payload, &[ assert_eq!(
0x20, 0x20, 0x75, 0x73, 0x74, 0x20, 0x61, 0x20, payload,
0x66, 0x6c, 0x65, 0x73, 0x68, 0x20, 0x77, 0x6f, &[
0x75, 0x6e, 0x64, 0x20, 0x20, 0x74, 0x69, 0x73, 0x20, 0x20, 0x75, 0x73, 0x74, 0x20, 0x61, 0x20, 0x66, 0x6c, 0x65, 0x73, 0x68,
0x20, 0x62, 0x75, 0x74, 0x20, 0x61, 0x20, 0x73, 0x20, 0x77, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x20, 0x74, 0x69, 0x73, 0x20, 0x62,
0x63, 0x72, 0x61, 0x74, 0x63, 0x68, 0x20, 0x20, 0x75, 0x74, 0x20, 0x61, 0x20, 0x73, 0x63, 0x72, 0x61, 0x74, 0x63, 0x68, 0x20,
0x6b, 0x6e, 0x69, 0x67, 0x68, 0x74, 0x73, 0x20, 0x20, 0x6b, 0x6e, 0x69, 0x67, 0x68, 0x74, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x6e,
0x6f, 0x66, 0x20, 0x6e, 0x69, 0x20, 0x20, 0x20 0x69, 0x20, 0x20, 0x20
]); ]
} else { );
assert!(false, "Packet did not parse as an EchoReply {:?}", pkt.message); } else {
assert!(
false,
"Packet did not parse as an EchoReply {:?}",
pkt.message
);
} }
assert_eq!(pkt.get_bytes(true), data); assert_eq!(pkt.get_bytes(true), data);
assert_eq!(pkt.calculate_checksum(lo, lo), 0x1c2e); assert_eq!(pkt.calculate_checksum(lo, lo), 0x1c2e);
} }
} }

View File

@ -1,21 +1,21 @@
// Copyright 2021 Jeremy Wall // Copyright 2021 Jeremy Wall
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
// You may obtain a copy of the License at // You may obtain a copy of the License at
// //
// http://www.apache.org/licenses/LICENSE-2.0 // http://www.apache.org/licenses/LICENSE-2.0
// //
// Unless required by applicable law or agreed to in writing, software // Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, // distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
use std::convert::{TryFrom, Into}; use std::convert::{Into, TryFrom};
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
use socket2::{Socket, Domain, Type, Protocol}; use socket2::{Domain, Protocol, Socket, Type};
use crate::packet::Icmpv6Packet; use crate::packet::Icmpv6Packet;
@ -35,17 +35,17 @@ pub struct IcmpSocket4 {
impl IcmpSocket4 { impl IcmpSocket4 {
pub fn new() -> std::io::Result<Self> { pub fn new() -> std::io::Result<Self> {
Ok(Self{ Ok(Self {
bound_to: None, bound_to: None,
inner: Socket::new(Domain::ipv4(), Type::raw(), Some(Protocol::icmpv4()))?, inner: Socket::new(Domain::ipv4(), Type::raw(), Some(Protocol::icmpv4()))?,
opts: Opts{ hops: 50 }, opts: Opts { hops: 50 },
}) })
} }
pub fn set_max_hops(&mut self, hops: u32) { pub fn set_max_hops(&mut self, hops: u32) {
self.opts.hops = hops; self.opts.hops = hops;
} }
pub fn bind<A: Into<Ipv4Addr>>(&mut self, addr: A) -> std::io::Result<()> { pub fn bind<A: Into<Ipv4Addr>>(&mut self, addr: A) -> std::io::Result<()> {
let addr = addr.into(); let addr = addr.into();
self.bound_to = Some(addr.clone()); self.bound_to = Some(addr.clone());
@ -61,7 +61,7 @@ impl IcmpSocket4 {
self.inner.send_to(payload, &(dest.into()))?; self.inner.send_to(payload, &(dest.into()))?;
Ok(()) Ok(())
} }
pub fn rcv_from(&self, buf: &mut [u8]) -> std::io::Result<usize> { pub fn rcv_from(&self, buf: &mut [u8]) -> std::io::Result<usize> {
let (read_count, _addr) = self.inner.recv_from(buf)?; let (read_count, _addr) = self.inner.recv_from(buf)?;
Ok(read_count) Ok(read_count)
@ -75,18 +75,18 @@ pub struct IcmpSocket6 {
} }
impl IcmpSocket6 { impl IcmpSocket6 {
pub fn new()-> std::io::Result<Self> { pub fn new() -> std::io::Result<Self> {
Ok(Self{ Ok(Self {
bound_to: None, bound_to: None,
inner: Socket::new(Domain::ipv6(), Type::raw(), Some(Protocol::icmpv6()))?, inner: Socket::new(Domain::ipv6(), Type::raw(), Some(Protocol::icmpv6()))?,
opts: Opts{ hops: 50 }, opts: Opts { hops: 50 },
}) })
} }
pub fn set_max_hops(&mut self, hops: u32) { pub fn set_max_hops(&mut self, hops: u32) {
self.opts.hops = hops; self.opts.hops = hops;
} }
pub fn bind<A: Into<Ipv6Addr>>(&mut self, addr: A) -> std::io::Result<()> { pub fn bind<A: Into<Ipv6Addr>>(&mut self, addr: A) -> std::io::Result<()> {
let addr = addr.into(); let addr = addr.into();
self.bound_to = Some(addr.clone()); self.bound_to = Some(addr.clone());
@ -98,7 +98,12 @@ impl IcmpSocket6 {
pub fn send_to(&mut self, dest: Ipv6Addr, mut packet: Icmpv6Packet) -> std::io::Result<()> { pub fn send_to(&mut self, dest: Ipv6Addr, mut packet: Icmpv6Packet) -> std::io::Result<()> {
let source = match self.bound_to { let source = match self.bound_to {
Some(ref addr) => addr, Some(ref addr) => addr,
None => return Err(std::io::Error::new(std::io::ErrorKind::Other, "Socket not bound to an address")), None => {
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
"Socket not bound to an address",
))
}
}; };
packet = packet.with_checksum(source, &dest); packet = packet.with_checksum(source, &dest);
let dest = ip_to_socket(&IpAddr::V6(dest)); let dest = ip_to_socket(&IpAddr::V6(dest));
@ -107,7 +112,7 @@ impl IcmpSocket6 {
self.inner.send_to(&pkt, &(dest.into()))?; self.inner.send_to(&pkt, &(dest.into()))?;
Ok(()) Ok(())
} }
pub fn rcv_from(&self) -> std::io::Result<Icmpv6Packet> { pub fn rcv_from(&self) -> std::io::Result<Icmpv6Packet> {
let mut buf = vec![0; 512]; let mut buf = vec![0; 512];
let (read_count, _addr) = self.inner.recv_from(&mut buf)?; let (read_count, _addr) = self.inner.recv_from(&mut buf)?;
@ -132,6 +137,6 @@ impl TryFrom<Ipv6Addr> for IcmpSocket6 {
fn try_from(addr: Ipv6Addr) -> Result<Self, Self::Error> { fn try_from(addr: Ipv6Addr) -> Result<Self, Self::Error> {
let mut sock = IcmpSocket6::new()?; let mut sock = IcmpSocket6::new()?;
sock.bind(addr)?; sock.bind(addr)?;
Ok(sock) Ok(sock)
} }
} }