mirror of
https://github.com/zaphar/icmp-socket.git
synced 2025-07-26 19:59:51 -04:00
Compare commits
16 Commits
Author | SHA1 | Date | |
---|---|---|---|
02b0714302 | |||
b01f831a61 | |||
6d12901286 | |||
4c666baf78 | |||
dad85434a4 | |||
![]() |
65ef9acf45 | ||
![]() |
b30dfb48bd | ||
a798645326 | |||
07c88084d8 | |||
d5672b00c6 | |||
3b5f3f9a25 | |||
5a00edf5e8 | |||
2dda318253 | |||
6eb42ddf36 | |||
3a73734015 | |||
e331d94a26 |
@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "icmp-socket"
|
name = "icmp-socket"
|
||||||
description = "ICMP sockets for both IPv4 and IPv6"
|
description = "ICMP sockets for both IPv4 and IPv6"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
repository = "https://github.com/zaphar/icmp-socket"
|
repository = "https://github.com/zaphar/icmp-socket"
|
||||||
authors = ["Jeremy Wall <jeremy@marzhillstudios.com>"]
|
authors = ["Jeremy Wall <jeremy@marzhillstudios.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
@ -10,5 +10,8 @@ license = "Apache-2.0"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
socket2 = "0.3.19"
|
byteorder = "1.3.4"
|
||||||
byteorder = "1.3.4"
|
|
||||||
|
[dependencies.socket2]
|
||||||
|
version = "0.4.4"
|
||||||
|
features=[ "all" ]
|
||||||
|
22
README.md
22
README.md
@ -1 +1,21 @@
|
|||||||
# ICMP Sockets for both IPv4 and IPv6
|
# ICMP Sockets for both IPv4 and IPv6
|
||||||
|
|
||||||
|
An implementation of ICMP Sockets for both IPv4 and IPv6.
|
||||||
|
|
||||||
|
Sockets can be created from IP addresses. IPv4 addresses will construct ICMP4 sockets. IPv6 will construct ICMP6 sockets.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let parsed_addr = "127.0.0.1".parse::<Ipv4Addr>().unwrap();
|
||||||
|
let socket = IcmpSocket4::try_from(parsed_addr).unwrap();
|
||||||
|
```
|
||||||
|
|
||||||
|
It can construct and parse the common ICMP packets for both ICMP4 and ICMP6.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let packet4 = Icmpv4Packet::with_echo_request(42, 1, "payload".to_bytes());
|
||||||
|
let packet6 = Icmpv6Packet::with_echo_request(42, 1, "payload".to_bytes());
|
||||||
|
```
|
||||||
|
|
||||||
|
# API Documentation
|
||||||
|
|
||||||
|
https://docs.rs/icmp-socket/0.2.0
|
@ -74,15 +74,17 @@ pub fn main() {
|
|||||||
socket4
|
socket4
|
||||||
.send_to(address.parse::<Ipv4Addr>().unwrap(), packet)
|
.send_to(address.parse::<Ipv4Addr>().unwrap(), packet)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
socket4.set_timeout(Some(Duration::from_secs(1)));
|
||||||
loop {
|
loop {
|
||||||
let (resp, sock_addr) = match socket4.rcv_with_timeout(Duration::from_secs(1)) {
|
let (resp, sock_addr) = match socket4.rcv_from() {
|
||||||
Ok(tpl) => tpl,
|
Ok(tpl) => tpl,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
//eprintln!("{:?}", e);
|
eprintln!("{:?}", e);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if packet_handler(resp, send_time, *sock_addr.as_inet().unwrap().ip()).is_some() {
|
if packet_handler(resp, send_time, *sock_addr.as_socket_ipv4().unwrap().ip()).is_some()
|
||||||
|
{
|
||||||
std::thread::sleep(Duration::from_secs(1));
|
std::thread::sleep(Duration::from_secs(1));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -72,15 +72,17 @@ pub fn main() {
|
|||||||
socket6
|
socket6
|
||||||
.send_to(address.parse::<Ipv6Addr>().unwrap(), packet)
|
.send_to(address.parse::<Ipv6Addr>().unwrap(), packet)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
socket6.set_timeout(Some(Duration::from_secs(1)));
|
||||||
loop {
|
loop {
|
||||||
let (resp, sock_addr) = match socket6.rcv_with_timeout(Duration::from_secs(1)) {
|
let (resp, sock_addr) = match socket6.rcv_from() {
|
||||||
Ok(tpl) => tpl,
|
Ok(tpl) => tpl,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
//eprintln!("{:?}", e);
|
eprintln!("{:?}", e);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if packet_handler(resp, send_time, *sock_addr.as_inet6().unwrap().ip()).is_some() {
|
if packet_handler(resp, send_time, *sock_addr.as_socket_ipv6().unwrap().ip()).is_some()
|
||||||
|
{
|
||||||
std::thread::sleep(Duration::from_millis(1000));
|
std::thread::sleep(Duration::from_millis(1000));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,10 @@
|
|||||||
// 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.
|
||||||
|
//! An ICMP socket library that tries to be ergonomic to use.
|
||||||
|
//!
|
||||||
|
//! The standard ping examples for both Ipv6 and IPv4 are in the examples
|
||||||
|
//! directory.
|
||||||
pub mod packet;
|
pub mod packet;
|
||||||
pub mod socket;
|
pub mod socket;
|
||||||
|
|
||||||
|
307
src/packet.rs
307
src/packet.rs
@ -11,6 +11,38 @@
|
|||||||
// 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.
|
||||||
|
//! Packet parsing and construction.
|
||||||
|
//!
|
||||||
|
//! Where possible we use traits to support a common API for constructing the
|
||||||
|
//! ICMPv4 and ICMPv6 versions of the packets.
|
||||||
|
//!
|
||||||
|
//! Both packet types can be constructed from a slice: `&[u8]` via the [`TryFrom`] trait.
|
||||||
|
//!
|
||||||
|
//! # Examples
|
||||||
|
//!
|
||||||
|
//! Constructing an ICMPv4 echo request.
|
||||||
|
//! ```
|
||||||
|
//! # use icmp_socket::packet::*;
|
||||||
|
//! let packet = Icmpv4Packet::with_echo_request(
|
||||||
|
//! 42, // An identifier so you can recognize responses to your own packets.
|
||||||
|
//! 0, // the first echo request packet in our sequence.
|
||||||
|
//! "a payload big enough to matter".as_bytes().to_vec()
|
||||||
|
//! ).unwrap();
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! Parsing an ICMPv4 packet from a byte buffer.
|
||||||
|
//! ```
|
||||||
|
//! # use icmp_socket::packet::*;
|
||||||
|
//! use std::convert::TryFrom;
|
||||||
|
//! # let packet = Icmpv4Packet::with_echo_request(
|
||||||
|
//! # 42, // An identifier so you can recognize responses to your own packets.
|
||||||
|
//! # 0, // the first echo request packet in our sequence.
|
||||||
|
//! # "a payload big enough to matter".as_bytes().to_vec()
|
||||||
|
//! # ).unwrap();
|
||||||
|
//! # let mut byte_buffer = vec![0; 20];
|
||||||
|
//! # byte_buffer.extend(packet.get_bytes(true)); // convert a packet to bytes with a checksum.
|
||||||
|
//! let parsed_packet = Icmpv4Packet::try_from(byte_buffer.as_slice()).unwrap();
|
||||||
|
//! ```
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
use byteorder::{BigEndian, ByteOrder};
|
use byteorder::{BigEndian, ByteOrder};
|
||||||
@ -42,6 +74,7 @@ fn sum_big_endian_words(bs: &[u8]) -> u32 {
|
|||||||
return sum;
|
return sum;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Construct a packet for the EchoRequest messages.
|
||||||
pub trait WithEchoRequest {
|
pub trait WithEchoRequest {
|
||||||
type Packet;
|
type Packet;
|
||||||
|
|
||||||
@ -52,6 +85,45 @@ pub trait WithEchoRequest {
|
|||||||
) -> Result<Self::Packet, IcmpPacketBuildError>;
|
) -> Result<Self::Packet, IcmpPacketBuildError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Construct a packet for Echo Reply messages.
|
||||||
|
/// This packet type is really only used for the ICMPv6 protocol.
|
||||||
|
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)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum Icmpv6Message {
|
pub enum Icmpv6Message {
|
||||||
// NOTE(JWALL): All of the below integers should be parsed as big endian on the
|
// NOTE(JWALL): All of the below integers should be parsed as big endian on the
|
||||||
@ -94,6 +166,7 @@ use Icmpv6Message::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
impl Icmpv6Message {
|
impl Icmpv6Message {
|
||||||
|
/// Get this Icmpv6Message serialized to bytes.
|
||||||
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 {
|
||||||
@ -155,9 +228,12 @@ pub struct Icmpv6Packet {
|
|||||||
pub message: Icmpv6Message,
|
pub message: Icmpv6Message,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Error type returned by parsing the ICMP packets.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum PacketParseError {
|
pub enum PacketParseError {
|
||||||
|
/// Not enough bytes to properly parse the packet from.
|
||||||
PacketTooSmall(usize),
|
PacketTooSmall(usize),
|
||||||
|
/// An unrecognized ICMP type.
|
||||||
UnrecognizedICMPType(u8),
|
UnrecognizedICMPType(u8),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -258,24 +334,6 @@ impl Icmpv6Packet {
|
|||||||
self
|
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.
|
/// Construct a packet for Packet Too Big messages.
|
||||||
pub fn with_packet_too_big(mtu: u32, packet: Vec<u8>) -> Result<Self, IcmpPacketBuildError> {
|
pub fn with_packet_too_big(mtu: u32, packet: Vec<u8>) -> Result<Self, IcmpPacketBuildError> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
@ -290,62 +348,6 @@ impl Icmpv6Packet {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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 {
|
impl WithEchoRequest for Icmpv6Packet {
|
||||||
@ -369,6 +371,93 @@ impl WithEchoRequest for Icmpv6Packet {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
// 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,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
// 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,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl TryFrom<&[u8]> for Icmpv6Packet {
|
impl TryFrom<&[u8]> for Icmpv6Packet {
|
||||||
type Error = PacketParseError;
|
type Error = PacketParseError;
|
||||||
fn try_from(b: &[u8]) -> Result<Self, Self::Error> {
|
fn try_from(b: &[u8]) -> Result<Self, Self::Error> {
|
||||||
@ -376,8 +465,10 @@ impl TryFrom<&[u8]> for Icmpv6Packet {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Errors returned by constructors for a given packet.
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum IcmpPacketBuildError {
|
pub enum IcmpPacketBuildError {
|
||||||
|
/// The code passed in for the payload was invalid for the message type.
|
||||||
InvalidCode(u8),
|
InvalidCode(u8),
|
||||||
}
|
}
|
||||||
use IcmpPacketBuildError::InvalidCode;
|
use IcmpPacketBuildError::InvalidCode;
|
||||||
@ -419,6 +510,7 @@ impl From<PacketParseError> for std::io::Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The various messages for an Icmpv4 packet.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Icmpv4Message {
|
pub enum Icmpv4Message {
|
||||||
Unreachable {
|
Unreachable {
|
||||||
@ -488,6 +580,7 @@ pub enum Icmpv4Message {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Icmpv4Message {
|
impl Icmpv4Message {
|
||||||
|
/// Get this Icmpv4Message serialized as bytes.
|
||||||
pub fn get_bytes(&self) -> Vec<u8> {
|
pub fn get_bytes(&self) -> Vec<u8> {
|
||||||
let mut bytes = Vec::with_capacity(20);
|
let mut bytes = Vec::with_capacity(20);
|
||||||
match self {
|
match self {
|
||||||
@ -599,6 +692,7 @@ impl Icmpv4Message {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An Icmpv4 Packet.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Icmpv4Packet {
|
pub struct Icmpv4Packet {
|
||||||
pub typ: u8,
|
pub typ: u8,
|
||||||
@ -608,6 +702,7 @@ pub struct Icmpv4Packet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Icmpv4Packet {
|
impl Icmpv4Packet {
|
||||||
|
/// Parse an Icmpv4Packet from bytes including the IPv4 header.
|
||||||
pub fn parse<B: AsRef<[u8]>>(bytes: B) -> Result<Self, PacketParseError> {
|
pub fn parse<B: AsRef<[u8]>>(bytes: B) -> Result<Self, PacketParseError> {
|
||||||
let mut bytes = bytes.as_ref();
|
let mut bytes = bytes.as_ref();
|
||||||
let mut packet_len = bytes.len();
|
let mut packet_len = bytes.len();
|
||||||
@ -721,6 +816,7 @@ impl Icmpv4Packet {
|
|||||||
!sum as u16
|
!sum as u16
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Populate the checksum field of this Packet.
|
||||||
pub fn with_checksum(mut self) -> Self {
|
pub fn with_checksum(mut self) -> Self {
|
||||||
self.checksum = self.calculate_checksum();
|
self.checksum = self.calculate_checksum();
|
||||||
self
|
self
|
||||||
@ -755,6 +851,69 @@ impl WithEchoRequest for Icmpv4Packet {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
105
src/socket.rs
105
src/socket.rs
@ -11,10 +11,14 @@
|
|||||||
// 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.
|
||||||
|
//! ICMP Socket implementations for both ICMP4 and ICMP6 protocols.
|
||||||
|
//!
|
||||||
|
//! There is a common IcmpSocket trait implemented for both the v4 and v6 protocols.
|
||||||
|
//! The socket is associated to both an address type and packet type.
|
||||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
|
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
|
||||||
use std::{
|
use std::{
|
||||||
convert::{Into, TryFrom, TryInto},
|
convert::{Into, TryFrom, TryInto},
|
||||||
|
mem::MaybeUninit,
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -26,28 +30,38 @@ fn ip_to_socket(ip: &IpAddr) -> SocketAddr {
|
|||||||
SocketAddr::new(*ip, 0)
|
SocketAddr::new(*ip, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Trait for an IcmpSocket implemented by Icmpv4Socket and Icmpv6Socket.
|
||||||
pub trait IcmpSocket {
|
pub trait IcmpSocket {
|
||||||
|
/// The type of address this socket operates on.
|
||||||
type AddrType;
|
type AddrType;
|
||||||
|
/// The type of packet this socket handles.
|
||||||
type PacketType;
|
type PacketType;
|
||||||
|
|
||||||
|
/// Sets the timeout on the socket for rcv_from. A value of None
|
||||||
|
/// will cause rcv_from to block.
|
||||||
|
fn set_timeout(&mut self, timeout: Option<Duration>);
|
||||||
|
|
||||||
|
/// Sets the ttl for packets sent on this socket. Controls the number of
|
||||||
|
/// hops the packet will be allowed to traverse.
|
||||||
fn set_max_hops(&mut self, hops: u32);
|
fn set_max_hops(&mut self, hops: u32);
|
||||||
|
|
||||||
|
/// Binds this socket to an address.
|
||||||
fn bind<A: Into<Self::AddrType>>(&mut self, addr: A) -> std::io::Result<()>;
|
fn bind<A: Into<Self::AddrType>>(&mut self, addr: A) -> std::io::Result<()>;
|
||||||
|
|
||||||
|
/// Sends the packet to the given destination.
|
||||||
fn send_to(&mut self, dest: Self::AddrType, packet: Self::PacketType) -> std::io::Result<()>;
|
fn send_to(&mut self, dest: Self::AddrType, packet: Self::PacketType) -> std::io::Result<()>;
|
||||||
|
|
||||||
|
/// Receive a packet on this socket.
|
||||||
fn rcv_from(&mut self) -> std::io::Result<(Self::PacketType, SockAddr)>;
|
fn rcv_from(&mut self) -> std::io::Result<(Self::PacketType, SockAddr)>;
|
||||||
|
|
||||||
fn rcv_with_timeout(
|
|
||||||
&mut self,
|
|
||||||
timeout: Duration,
|
|
||||||
) -> std::io::Result<(Self::PacketType, SockAddr)>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Opts {
|
/// Options for this socket.
|
||||||
|
struct Opts {
|
||||||
hops: u32,
|
hops: u32,
|
||||||
|
timeout: Option<Duration>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An ICMPv4 socket.
|
||||||
pub struct IcmpSocket4 {
|
pub struct IcmpSocket4 {
|
||||||
bound_to: Option<Ipv4Addr>,
|
bound_to: Option<Ipv4Addr>,
|
||||||
buf: Vec<u8>,
|
buf: Vec<u8>,
|
||||||
@ -56,16 +70,32 @@ pub struct IcmpSocket4 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl IcmpSocket4 {
|
impl IcmpSocket4 {
|
||||||
|
/// Construct a new raw socket. The socket must be bound to an address using `bind_to`
|
||||||
|
/// before it can be used to send and receive packets.
|
||||||
pub fn new() -> std::io::Result<Self> {
|
pub fn new() -> std::io::Result<Self> {
|
||||||
let socket = Socket::new(Domain::ipv4(), Type::raw(), Some(Protocol::icmpv4()))?;
|
let socket = Socket::new(Domain::IPV4, Type::RAW, Some(Protocol::ICMPV4))?;
|
||||||
|
Self::new_from_socket(socket)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_from_socket(socket: Socket) -> std::io::Result<Self> {
|
||||||
socket.set_recv_buffer_size(512)?;
|
socket.set_recv_buffer_size(512)?;
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
bound_to: None,
|
bound_to: None,
|
||||||
inner: socket,
|
inner: socket,
|
||||||
buf: vec![0; 512],
|
buf: vec![0; 512],
|
||||||
opts: Opts { hops: 50 },
|
opts: Opts {
|
||||||
|
hops: 50,
|
||||||
|
timeout: None,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Construct a new dgram socket. The socket must be bound to an address using `bind_to`
|
||||||
|
/// before it can be used to send and receive packets.
|
||||||
|
pub fn new_dgram_socket() -> std::io::Result<Self> {
|
||||||
|
let socket = Socket::new(Domain::IPV4, Type::DGRAM, Some(Protocol::ICMPV4))?;
|
||||||
|
Self::new_from_socket(socket)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IcmpSocket for IcmpSocket4 {
|
impl IcmpSocket for IcmpSocket4 {
|
||||||
@ -93,21 +123,22 @@ impl IcmpSocket for IcmpSocket4 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn rcv_from(&mut self) -> std::io::Result<(Self::PacketType, SockAddr)> {
|
fn rcv_from(&mut self) -> std::io::Result<(Self::PacketType, SockAddr)> {
|
||||||
self.inner.set_read_timeout(None);
|
self.inner.set_read_timeout(self.opts.timeout)?;
|
||||||
let (read_count, addr) = self.inner.recv_from(&mut self.buf)?;
|
// NOTE(jwall): the `recv_from` implementation promises not to write uninitialised
|
||||||
|
// bytes to the `buf`fer, so this casting is safe.
|
||||||
|
// TODO(jwall): change to `Vec::spare_capacity_mut` when it stabilizes.
|
||||||
|
let mut buf =
|
||||||
|
unsafe { &mut *(self.buf.as_mut_slice() as *mut [u8] as *mut [MaybeUninit<u8>]) };
|
||||||
|
let (read_count, addr) = self.inner.recv_from(&mut buf)?;
|
||||||
Ok((self.buf[0..read_count].try_into()?, addr))
|
Ok((self.buf[0..read_count].try_into()?, addr))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rcv_with_timeout(
|
fn set_timeout(&mut self, timeout: Option<Duration>) {
|
||||||
&mut self,
|
self.opts.timeout = timeout;
|
||||||
timeout: Duration,
|
|
||||||
) -> std::io::Result<(Self::PacketType, SockAddr)> {
|
|
||||||
self.inner.set_read_timeout(Some(timeout));
|
|
||||||
let (read_count, addr) = self.inner.recv_from(&mut self.buf)?;
|
|
||||||
Ok((self.buf[0..read_count].try_into()?, addr))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An Icmpv6 socket.
|
||||||
pub struct IcmpSocket6 {
|
pub struct IcmpSocket6 {
|
||||||
bound_to: Option<Ipv6Addr>,
|
bound_to: Option<Ipv6Addr>,
|
||||||
inner: Socket,
|
inner: Socket,
|
||||||
@ -116,16 +147,32 @@ pub struct IcmpSocket6 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl IcmpSocket6 {
|
impl IcmpSocket6 {
|
||||||
|
/// Construct a new raw socket. The socket must be bound to an address using `bind_to`
|
||||||
|
/// before it can be used to send and receive packets.
|
||||||
pub fn new() -> std::io::Result<Self> {
|
pub fn new() -> std::io::Result<Self> {
|
||||||
let socket = Socket::new(Domain::ipv6(), Type::raw(), Some(Protocol::icmpv6()))?;
|
let socket = Socket::new(Domain::IPV6, Type::RAW, Some(Protocol::ICMPV6))?;
|
||||||
|
Self::new_from_socket(socket)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_from_socket(socket: Socket) -> std::io::Result<Self> {
|
||||||
socket.set_recv_buffer_size(512)?;
|
socket.set_recv_buffer_size(512)?;
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
bound_to: None,
|
bound_to: None,
|
||||||
inner: socket,
|
inner: socket,
|
||||||
buf: vec![0; 512],
|
buf: vec![0; 512],
|
||||||
opts: Opts { hops: 50 },
|
opts: Opts {
|
||||||
|
hops: 50,
|
||||||
|
timeout: None,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Construct a new dgram socket. The socket must be bound to an address using `bind_to`
|
||||||
|
/// before it can be used to send and receive packets.
|
||||||
|
pub fn new_dgram_socket() -> std::io::Result<Self> {
|
||||||
|
let socket = Socket::new(Domain::IPV6, Type::DGRAM, Some(Protocol::ICMPV6))?;
|
||||||
|
Self::new_from_socket(socket)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IcmpSocket for IcmpSocket6 {
|
impl IcmpSocket for IcmpSocket6 {
|
||||||
@ -167,18 +214,18 @@ impl IcmpSocket for IcmpSocket6 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn rcv_from(&mut self) -> std::io::Result<(Self::PacketType, SockAddr)> {
|
fn rcv_from(&mut self) -> std::io::Result<(Self::PacketType, SockAddr)> {
|
||||||
self.inner.set_read_timeout(None);
|
self.inner.set_read_timeout(self.opts.timeout)?;
|
||||||
let (read_count, addr) = self.inner.recv_from(&mut self.buf)?;
|
// NOTE(jwall): the `recv_from` implementation promises not to write uninitialised
|
||||||
|
// bytes to the `buf`fer, so this casting is safe.
|
||||||
|
// TODO(jwall): change to `Vec::spare_capacity_mut` when it stabilizes.
|
||||||
|
let mut buf =
|
||||||
|
unsafe { &mut *(self.buf.as_mut_slice() as *mut [u8] as *mut [MaybeUninit<u8>]) };
|
||||||
|
let (read_count, addr) = self.inner.recv_from(&mut buf)?;
|
||||||
Ok((self.buf[0..read_count].try_into()?, addr))
|
Ok((self.buf[0..read_count].try_into()?, addr))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rcv_with_timeout(
|
fn set_timeout(&mut self, timeout: Option<Duration>) {
|
||||||
&mut self,
|
self.opts.timeout = timeout;
|
||||||
timeout: Duration,
|
|
||||||
) -> std::io::Result<(Self::PacketType, SockAddr)> {
|
|
||||||
self.inner.set_read_timeout(Some(timeout));
|
|
||||||
let (read_count, addr) = self.inner.recv_from(&mut self.buf)?;
|
|
||||||
Ok((self.buf[0..read_count].try_into()?, addr))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user