Add icmpd daemon, only Echo reply for now.
This commit is contained in:
+5
-1
@@ -18,8 +18,12 @@ path = "src/tcpd/main.rs"
|
||||
name = "udpd"
|
||||
path = "src/udpd/main.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "icmpd"
|
||||
path = "src/icmpd/main.rs"
|
||||
|
||||
[dependencies]
|
||||
netutils = { git = "https://github.com/redox-os/netutils.git" }
|
||||
rand = "0.3"
|
||||
redox_event = "0.1"
|
||||
redox_event = { path = "../event" }
|
||||
redox_syscall = "0.1"
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
use std::result;
|
||||
use std::fmt;
|
||||
use syscall::error::Error as SyscallError;
|
||||
use std::io::Error as IOError;
|
||||
use std::convert;
|
||||
|
||||
pub enum ParsingError {
|
||||
NotEnoughData,
|
||||
IncorrectChecksum,
|
||||
}
|
||||
|
||||
enum ErrorType {
|
||||
Syscall(SyscallError),
|
||||
IOError(IOError),
|
||||
ParsingError(ParsingError),
|
||||
}
|
||||
|
||||
pub struct Error {
|
||||
error_type: ErrorType,
|
||||
descr: String,
|
||||
}
|
||||
|
||||
impl Error {
|
||||
pub fn from_parsing_error<S: Into<String>>(parsing_error: ParsingError, descr: S) -> Error {
|
||||
Error {
|
||||
error_type: ErrorType::ParsingError(parsing_error),
|
||||
descr: descr.into(),
|
||||
}
|
||||
}
|
||||
pub fn from_syscall_error<S: Into<String>>(syscall_error: SyscallError, descr: S) -> Error {
|
||||
Error {
|
||||
error_type: ErrorType::Syscall(syscall_error),
|
||||
descr: descr.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_io_error<S: Into<String>>(io_error: IOError, descr: S) -> Error {
|
||||
Error {
|
||||
error_type: ErrorType::IOError(io_error),
|
||||
descr: descr.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ParsingError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
|
||||
write!(f, "{}", match *self {
|
||||
ParsingError::NotEnoughData => "not enough data",
|
||||
ParsingError::IncorrectChecksum => "checksum error",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
|
||||
match self.error_type {
|
||||
ErrorType::Syscall(ref syscall_error) => {
|
||||
write!(f, "{} : syscall error: {}", self.descr, syscall_error)
|
||||
}
|
||||
ErrorType::IOError(ref io_error) => {
|
||||
write!(f, "{} : io error : {}", self.descr, io_error)
|
||||
}
|
||||
ErrorType::ParsingError(ref parsign_error) => {
|
||||
write!(f,
|
||||
"{} : packet parsing error : {}",
|
||||
self.descr,
|
||||
parsign_error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl convert::From<IOError> for Error {
|
||||
fn from(e: IOError) -> Self {
|
||||
Error::from_io_error(e, "")
|
||||
}
|
||||
}
|
||||
|
||||
pub type Result<T> = result::Result<T, Error>;
|
||||
pub type ParsingResult<T> = result::Result<T, ParsingError>;
|
||||
@@ -0,0 +1,115 @@
|
||||
extern crate event;
|
||||
extern crate syscall;
|
||||
extern crate netutils;
|
||||
|
||||
use error::{Result, Error, ParsingError};
|
||||
use event::EventQueue;
|
||||
use netutils::{Ipv4, Ipv4Header, Checksum, n16};
|
||||
use packet::{Packet, MutPacket};
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Write};
|
||||
use std::os::unix::io::{RawFd, FromRawFd};
|
||||
use std::process;
|
||||
use std::mem;
|
||||
|
||||
mod error;
|
||||
mod packet;
|
||||
|
||||
const MAX_PACKET_SIZE: usize = 2048;
|
||||
|
||||
fn do_echo_response(in_ip_packet: &Ipv4,
|
||||
in_icmp_packet: &Packet,
|
||||
icmp_file: &mut File)
|
||||
-> Result<()> {
|
||||
let mut ip_data = vec![0; in_icmp_packet.get_total_data_size()];
|
||||
{
|
||||
let mut out_icmp_packet =
|
||||
MutPacket::from_bytes(&mut ip_data)
|
||||
.map_err(|e| Error::from_parsing_error(e, "can't parse empty icmp header"))?;
|
||||
out_icmp_packet.set_echo_response();
|
||||
{
|
||||
let payload = out_icmp_packet.get_payload();
|
||||
let in_payload = in_icmp_packet.get_payload();
|
||||
if payload.len() != in_payload.len() {
|
||||
return Err(Error::from_parsing_error(ParsingError::NotEnoughData,
|
||||
" can't copy icmp payload to echo response"));
|
||||
}
|
||||
//WARNING: copy_from_slice can panic if the slices' lengths are different
|
||||
payload.copy_from_slice(in_icmp_packet.get_payload());
|
||||
}
|
||||
out_icmp_packet.compute_checksum();
|
||||
}
|
||||
let out_ip_packet = Ipv4 {
|
||||
header: Ipv4Header {
|
||||
ver_hlen: 0x45,
|
||||
services: 0,
|
||||
len: n16::new((ip_data.len() + mem::size_of::<Ipv4Header>()) as u16),
|
||||
id: n16::new(0),
|
||||
flags_fragment: n16::new(0),
|
||||
ttl: in_ip_packet.header.ttl,
|
||||
proto: 1,
|
||||
checksum: Checksum { data: 0 },
|
||||
src: in_ip_packet.header.dst,
|
||||
dst: in_ip_packet.header.src,
|
||||
},
|
||||
options: Vec::new(),
|
||||
data: ip_data,
|
||||
};
|
||||
icmp_file
|
||||
.write(&out_ip_packet.to_bytes())
|
||||
.map_err(|e| Error::from_io_error(e, " can't send an echo response packet"))
|
||||
.map(|_| ())
|
||||
}
|
||||
|
||||
fn on_icmp_packet(icmp_file: &mut File) -> Result<Option<()>> {
|
||||
let mut packet_buffer = [0; MAX_PACKET_SIZE];
|
||||
loop {
|
||||
let bytes_readed =
|
||||
icmp_file
|
||||
.read(&mut packet_buffer)
|
||||
.map_err(|e| Error::from_io_error(e, "failed to read a packet from ip:1"))?;
|
||||
if bytes_readed == 0 {
|
||||
break;
|
||||
}
|
||||
let ip_packet = Ipv4::from_bytes(&packet_buffer[..bytes_readed])
|
||||
.ok_or(Error::from_parsing_error(ParsingError::NotEnoughData,
|
||||
"failed to parse ip header"))?;
|
||||
let icmp_packet =
|
||||
Packet::from_bytes(&ip_packet.data)
|
||||
.map_err(|e| Error::from_parsing_error(e, "failed to parse ICMP packet"))?;
|
||||
|
||||
if icmp_packet.is_echo_request() {
|
||||
do_echo_response(&ip_packet, &icmp_packet, icmp_file)?;
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn run() -> Result<()> {
|
||||
use syscall::flag::*;
|
||||
|
||||
let icmp_fd = syscall::open("ip:1", O_RDWR | O_NONBLOCK)
|
||||
.map_err(|e| Error::from_syscall_error(e, "failed to open ip:1"))?;
|
||||
|
||||
if unsafe { syscall::clone(0).unwrap() } != 0 {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut event_queue =
|
||||
EventQueue::<(), Error>::new()
|
||||
.map_err(|e| Error::from_io_error(e, "failed to create event queue"))?;
|
||||
let mut icmp_file = unsafe { File::from_raw_fd(icmp_fd as RawFd) };
|
||||
event_queue
|
||||
.add(icmp_fd as RawFd,
|
||||
move |_fd| -> Result<Option<()>> { on_icmp_packet(&mut icmp_file) })
|
||||
.map_err(|e| Error::from_io_error(e, "failed to listen to events on ip:1"))?;
|
||||
event_queue.run()
|
||||
}
|
||||
|
||||
fn main() {
|
||||
match run() {
|
||||
Err(err) => println!("icmpd: {}", err),
|
||||
_ => {}
|
||||
}
|
||||
process::exit(0);
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
use error::{ParsingResult, ParsingError};
|
||||
use netutils::Checksum;
|
||||
use std::mem;
|
||||
|
||||
#[repr(packed)]
|
||||
pub struct Header {
|
||||
icmp_type: u8,
|
||||
icmp_code: u8,
|
||||
crc: u16,
|
||||
}
|
||||
|
||||
pub struct Packet<'a> {
|
||||
header: &'a Header,
|
||||
payload: &'a [u8],
|
||||
}
|
||||
|
||||
pub struct MutPacket<'a> {
|
||||
header: &'a mut Header,
|
||||
payload: &'a mut [u8],
|
||||
}
|
||||
|
||||
impl<'a> Packet<'a> {
|
||||
pub fn from_bytes<'b>(bytes: &'b [u8]) -> ParsingResult<Packet<'a>>
|
||||
where 'b: 'a
|
||||
{
|
||||
if bytes.len() < mem::size_of::<Header>() {
|
||||
Err(ParsingError::NotEnoughData)
|
||||
} else {
|
||||
let (header_bytes, payload_bytes) = bytes.split_at(mem::size_of::<Header>());
|
||||
let packet = Packet {
|
||||
header: unsafe { mem::transmute(header_bytes.as_ptr()) },
|
||||
payload: payload_bytes,
|
||||
};
|
||||
if packet.is_checksum_ok() {
|
||||
Ok(packet)
|
||||
} else {
|
||||
Err(ParsingError::IncorrectChecksum)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_checksum_ok(&self) -> bool {
|
||||
let header_ptr = self.header as *const Header as usize;
|
||||
let total_size = self.get_total_data_size();
|
||||
let mut crc = unsafe { Checksum::sum(header_ptr, total_size) };
|
||||
crc -= u16::from_be(self.header.crc) as usize;
|
||||
let crc = Checksum::compile(crc);
|
||||
crc == u16::from_be(self.header.crc)
|
||||
}
|
||||
|
||||
pub fn is_echo_request(&self) -> bool {
|
||||
self.header.icmp_type == ECHO_REQUEST_TYPE && self.header.icmp_code == ECHO_REQUEST_CODE
|
||||
}
|
||||
|
||||
pub fn get_payload(&self) -> &[u8] {
|
||||
self.payload
|
||||
}
|
||||
|
||||
pub fn get_total_data_size(&self) -> usize {
|
||||
mem::size_of::<Header>() + self.payload.len()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> MutPacket<'a> {
|
||||
pub fn from_bytes<'b>(bytes: &'b mut [u8]) -> ParsingResult<MutPacket<'a>>
|
||||
where 'b: 'a
|
||||
{
|
||||
if bytes.len() < mem::size_of::<Header>() {
|
||||
Err(ParsingError::NotEnoughData)
|
||||
} else {
|
||||
let (header_bytes, payload_bytes) = bytes.split_at_mut(mem::size_of::<Header>());
|
||||
Ok(MutPacket {
|
||||
header: unsafe { mem::transmute(header_bytes.as_ptr()) },
|
||||
payload: payload_bytes,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_echo_response(&mut self) {
|
||||
self.header.icmp_type = ECHO_RESPONSE_TYPE;
|
||||
self.header.icmp_code = ECHO_RESPONSE_CODE;
|
||||
}
|
||||
|
||||
pub fn compute_checksum(&mut self) {
|
||||
self.header.crc = 0;
|
||||
let header_ptr = self.header as *mut Header as usize;
|
||||
let total_size = mem::size_of::<Header>() + self.payload.len();
|
||||
let crc = Checksum::compile(unsafe { Checksum::sum(header_ptr, total_size) });
|
||||
self.header.crc = crc
|
||||
}
|
||||
|
||||
pub fn get_payload(&mut self) -> &mut [u8] {
|
||||
self.payload
|
||||
}
|
||||
}
|
||||
|
||||
const ECHO_REQUEST_TYPE: u8 = 8;
|
||||
const ECHO_REQUEST_CODE: u8 = 0;
|
||||
const ECHO_RESPONSE_TYPE: u8 = 0;
|
||||
const ECHO_RESPONSE_CODE: u8 = 0;
|
||||
Reference in New Issue
Block a user