Add icmpd daemon, only Echo reply for now.

This commit is contained in:
Egor Karavaev
2017-06-11 00:53:30 +03:00
parent 6bfd4be40e
commit 69bb8fb80b
4 changed files with 300 additions and 1 deletions
+5 -1
View File
@@ -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"
+80
View File
@@ -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>;
+115
View File
@@ -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);
}
+100
View File
@@ -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;