use std::convert::TryInto;
use std::net::SocketAddr;
use anyhow::Result;
use tokio_raw::{RawSocket, Domain, Type, Protocol};
#[tokio::main]
async fn main() -> Result<()> {
let ip4 = Domain::ipv4();
let dgram = Type::dgram();
let icmp4 = Protocol::icmpv4();
let mut sock = RawSocket::new(ip4, dgram, Some(icmp4))?;
let ping = IcmpPacket::echo_request(1, 2, b"asdf");
let dst = SocketAddr::new("1.1.1.1".parse()?, 0);
let mut buf = [0u8; 64];
let pkt = ping.encode(&mut buf);
sock.send_to(pkt, dst).await?;
let mut buf = [0u8; 64];
let (n, from) = sock.recv_from(&mut buf).await?;
let pong = IcmpPacket::decode(&pkt[..n]);
println!("{:?}: {:?}", from, pong);
Ok(())
}
#[derive(Debug)]
enum IcmpPacket<'a> {
EchoRequest(Echo<'a>),
EchoReply(Echo<'a>),
}
#[derive(Debug)]
struct Echo<'a> {
ident: u16,
seq: u16,
body: &'a [u8],
}
impl<'a> IcmpPacket<'a> {
const HEADER_SIZE: usize = 8;
const ECHO_REPLY: u8 = 0;
const ECHO_REQUEST: u8 = 8;
fn echo_request(ident: u16, seq: u16, body: &'a [u8]) -> Self {
Self::EchoRequest(Echo::new(ident, seq, body))
}
fn encode<'b>(&self, pkt: &'b mut [u8]) -> &'b [u8] {
match self {
Self::EchoRequest(e) => e.encode(Self::ECHO_REQUEST, pkt),
Self::EchoReply(e) => e.encode(Self::ECHO_REPLY, pkt),
}
}
fn decode(pkt: &'a [u8]) -> Self {
let kind = pkt[0];
let code = pkt[1];
match (kind, code) {
(Self::ECHO_REPLY, 0) => Self::EchoReply(Echo::decode(pkt)),
(Self::ECHO_REQUEST, 0) => Self::EchoRequest(Echo::decode(pkt)),
other => panic!("unexpected ICMP msg {:?}", other),
}
}
}
impl<'a> Echo<'a> {
fn new(ident: u16, seq: u16, body: &'a [u8]) -> Self {
Self { ident, seq, body }
}
fn encode<'b>(&self, kind: u8, pkt: &'b mut [u8]) -> &'b [u8] {
let code: u8 = 0;
let cksum: u16 = 0;
let n = IcmpPacket::HEADER_SIZE + self.body.len();
pkt[0..2].copy_from_slice(&[kind, code]);
pkt[2..4].copy_from_slice(&cksum.to_be_bytes());
pkt[4..6].copy_from_slice(&self.ident.to_be_bytes());
pkt[6..8].copy_from_slice(&self.seq.to_be_bytes());
pkt[8..n].copy_from_slice(&self.body);
&pkt[..n]
}
fn decode(pkt: &'a [u8]) -> Self {
let ident = pkt[4..6].try_into().unwrap();
let seq = pkt[6..8].try_into().unwrap();
Self {
ident: u16::from_be_bytes(ident),
seq: u16::from_be_bytes(seq),
body: &pkt[8..],
}
}
}