diff --git a/src/icmpd/error.rs b/src/icmpd/error.rs index cc939b6072..ae8007e961 100644 --- a/src/icmpd/error.rs +++ b/src/icmpd/error.rs @@ -4,15 +4,17 @@ use syscall::error::Error as SyscallError; use std::io::Error as IOError; use std::convert; -pub enum ParsingError { +pub enum PacketError { NotEnoughData, IncorrectChecksum, + NoEchoHeader, + SubheaderAlreadPresent, } enum ErrorType { Syscall(SyscallError), IOError(IOError), - ParsingError(ParsingError), + PacketError(PacketError), } pub struct Error { @@ -21,9 +23,9 @@ pub struct Error { } impl Error { - pub fn from_parsing_error>(parsing_error: ParsingError, descr: S) -> Error { + pub fn from_parsing_error>(parsing_error: PacketError, descr: S) -> Error { Error { - error_type: ErrorType::ParsingError(parsing_error), + error_type: ErrorType::PacketError(parsing_error), descr: descr.into(), } } @@ -40,13 +42,23 @@ impl Error { descr: descr.into(), } } + + pub fn is_unrecoverable(&self) -> bool { + match self.error_type { + ErrorType::PacketError(_) => false, + ErrorType::IOError(_) | + ErrorType::Syscall(_) => true, + } + } } -impl fmt::Display for ParsingError { +impl fmt::Display for PacketError { fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> { write!(f, "{}", match *self { - ParsingError::NotEnoughData => "not enough data", - ParsingError::IncorrectChecksum => "checksum error", + PacketError::NotEnoughData => "not enough data", + PacketError::IncorrectChecksum => "checksum error", + PacketError::NoEchoHeader => "echo header is missing", + PacketError::SubheaderAlreadPresent => "subheader is already present", }) } } @@ -60,7 +72,7 @@ impl fmt::Display for Error { ErrorType::IOError(ref io_error) => { write!(f, "{} : io error : {}", self.descr, io_error) } - ErrorType::ParsingError(ref parsign_error) => { + ErrorType::PacketError(ref parsign_error) => { write!(f, "{} : packet parsing error : {}", self.descr, @@ -77,4 +89,4 @@ impl convert::From for Error { } pub type Result = result::Result; -pub type ParsingResult = result::Result; +pub type PacketResult = result::Result; diff --git a/src/icmpd/main.rs b/src/icmpd/main.rs index 2623547228..f44089e1f5 100644 --- a/src/icmpd/main.rs +++ b/src/icmpd/main.rs @@ -40,11 +40,29 @@ fn run() -> Result<()> { let icmpd_ = icmpd.clone(); event_queue - .add(icmp_fd, move |_fd| icmpd_.borrow_mut().on_icmp_packet()) + .add(icmp_fd, move |_fd| { + if let Err(err) = icmpd_.borrow_mut().on_icmp_packet() { + if err.is_unrecoverable() { + return Err(err); + } else { + println!("icmpd: network error: {}", err); + } + } + Ok(None) + }) .map_err(|e| Error::from_io_error(e, "failed to listen to events on ip:1"))?; event_queue - .add(scheme_fd, move |_fd| icmpd.borrow_mut().on_scheme_event()) + .add(scheme_fd, move |_fd| { + if let Err(err) = icmpd.borrow_mut().on_scheme_event() { + if err.is_unrecoverable() { + return Err(err); + } else { + println!("icmpd: scheme error: {}", err); + } + } + Ok(None) + }) .map_err(|e| Error::from_io_error(e, "failed to listen to events on icmp"))?; event_queue.run() diff --git a/src/icmpd/packet.rs b/src/icmpd/packet.rs index fb35ce745a..fea7e9cfca 100644 --- a/src/icmpd/packet.rs +++ b/src/icmpd/packet.rs @@ -1,4 +1,4 @@ -use error::{ParsingResult, ParsingError}; +use error::{PacketResult, PacketError}; use netutils::Checksum; use std::mem; @@ -9,14 +9,28 @@ pub struct Header { crc: u16, } +#[derive(Copy, Clone)] +#[repr(packed)] +pub struct EchoHeader { + id: u16, + seq: u16, +} + +pub enum SubHeader<'a> { + Echo(&'a EchoHeader), + None, +} + pub struct Packet<'a> { header: &'a Header, payload: &'a [u8], + subheader: SubHeader<'a>, } pub struct MutPacket<'a> { header: &'a mut Header, payload: &'a mut [u8], + subheader: SubHeader<'a>, } pub enum PacketKind { @@ -28,22 +42,61 @@ pub enum PacketKind { Unknown, } +impl EchoHeader { + pub fn new(id: u16, seq: u16) -> EchoHeader { + EchoHeader { + id: id.to_be(), + seq: seq.to_be(), + } + } + + pub fn get_id(&self) -> u16 { + u16::from_be(self.id) + } + + pub fn get_seq(&self) -> u16 { + u16::from_be(self.seq) + } +} + +impl<'a> SubHeader<'a> { + pub fn get_size(&self) -> usize { + match *self { + SubHeader::None => 0, + SubHeader::Echo(_) => mem::size_of::(), + } + } +} + impl<'a> Packet<'a> { - pub fn from_bytes<'b>(bytes: &'b [u8]) -> ParsingResult> + pub fn from_bytes<'b>(bytes: &'b [u8]) -> PacketResult> where 'b: 'a { if bytes.len() < mem::size_of::
() { - Err(ParsingError::NotEnoughData) + Err(PacketError::NotEnoughData) } else { let (header_bytes, payload_bytes) = bytes.split_at(mem::size_of::
()); - let packet = Packet { + let mut packet = Packet { header: unsafe { mem::transmute(header_bytes.as_ptr()) }, payload: payload_bytes, + subheader: SubHeader::None, }; - if packet.is_checksum_ok() { - Ok(packet) - } else { - Err(ParsingError::IncorrectChecksum) + if !packet.is_checksum_ok() { + return Err(PacketError::IncorrectChecksum); + } + match packet.get_kind() { + PacketKind::EchoResponse => { + if packet.payload.len() < mem::size_of::() { + return Err(PacketError::NoEchoHeader); + } + let (echo_header_payload, payload) = + packet.payload.split_at(mem::size_of::()); + packet.subheader = + SubHeader::Echo(unsafe { mem::transmute(echo_header_payload.as_ptr()) }); + packet.payload = payload; + Ok(packet) + } + _ => Ok(packet), } } } @@ -73,33 +126,65 @@ impl<'a> Packet<'a> { } pub fn get_total_data_size(&self) -> usize { - mem::size_of::
() + self.payload.len() + mem::size_of::
() + self.subheader.get_size() + self.payload.len() + } + + pub fn get_subheader(&self) -> &SubHeader<'a> { + &self.subheader } } impl<'a> MutPacket<'a> { - pub fn from_bytes<'b>(bytes: &'b mut [u8]) -> ParsingResult> + pub fn from_bytes<'b>(bytes: &'b mut [u8]) -> PacketResult> where 'b: 'a { if bytes.len() < mem::size_of::
() { - Err(ParsingError::NotEnoughData) + Err(PacketError::NotEnoughData) } else { let (header_bytes, payload_bytes) = bytes.split_at_mut(mem::size_of::
()); Ok(MutPacket { header: unsafe { mem::transmute(header_bytes.as_ptr()) }, payload: payload_bytes, + subheader: SubHeader::None, }) } } + pub fn set_subheader(self, subheader: &SubHeader) -> PacketResult> { + match self.subheader { + SubHeader::None => {} + _ => return Err(PacketError::SubheaderAlreadPresent), + }; + + if self.payload.len() < subheader.get_size() { + return Err(PacketError::NotEnoughData); + } + + let (subheader_bytes, new_payload) = self.payload.split_at_mut(subheader.get_size()); + let new_subheader = match *subheader { + SubHeader::Echo(echo_sub_header) => { + let echo_sub_header_mut: &mut EchoHeader = + unsafe { mem::transmute(subheader_bytes.as_ptr()) }; + *echo_sub_header_mut = echo_sub_header.clone(); + SubHeader::Echo(echo_sub_header_mut) + } + SubHeader::None => SubHeader::None, + }; + Ok(MutPacket { + header: self.header, + payload: new_payload, + subheader: new_subheader, + }) + } + pub fn set_kind(&mut self, packet_type: PacketKind) { let (new_type, new_code) = match packet_type { - EchoRequest => (ECHO_REQUEST_TYPE, ECHO_REQUEST_CODE), - EchoResponse => (ECHO_RESPONSE_TYPE, ECHO_RESPONSE_CODE), - HostUnreachable => (UNREACHABLE_TYPE, UNREACHABLE_HOST_CODE), - PortUnreachable => (UNREACHABLE_TYPE, UNREACHABLE_PORT_CODE), - ProtoUnreachable => (UNREACHABLE_TYPE, UNREACHABLE_PROTO_CODE), - Unknown => (self.header.icmp_type, self.header.icmp_code), + PacketKind::EchoRequest => (ECHO_REQUEST_TYPE, ECHO_REQUEST_CODE), + PacketKind::EchoResponse => (ECHO_RESPONSE_TYPE, ECHO_RESPONSE_CODE), + PacketKind::HostUnreachable => (UNREACHABLE_TYPE, UNREACHABLE_HOST_CODE), + PacketKind::PortUnreachable => (UNREACHABLE_TYPE, UNREACHABLE_PORT_CODE), + PacketKind::ProtoUnreachable => (UNREACHABLE_TYPE, UNREACHABLE_PROTO_CODE), + PacketKind::Unknown => (self.header.icmp_type, self.header.icmp_code), }; self.header.icmp_type = new_type; self.header.icmp_code = new_code; @@ -116,6 +201,10 @@ impl<'a> MutPacket<'a> { pub fn get_payload(&mut self) -> &mut [u8] { self.payload } + + pub fn get_total_header_size(subheader: &SubHeader) -> usize { + mem::size_of::
() + subheader.get_size() + } } const ECHO_REQUEST_TYPE: u8 = 8; diff --git a/src/icmpd/scheme.rs b/src/icmpd/scheme.rs index 22616f6f67..e943f31a7d 100644 --- a/src/icmpd/scheme.rs +++ b/src/icmpd/scheme.rs @@ -1,7 +1,7 @@ -use error::{Result, Error, ParsingError}; +use error::{Result, Error, PacketError}; use netutils::{Ipv4, Ipv4Header, Checksum, n16}; use netutils; -use packet::{Header, Packet, MutPacket, PacketKind}; +use packet::{Header, Packet, MutPacket, PacketKind, SubHeader, EchoHeader}; use std::collections::{BTreeMap, HashSet, VecDeque}; use std::fs::File; use std::io::{Read, Write}; @@ -24,6 +24,7 @@ struct Handle { flags: usize, ip_addr: Ipv4Addr, payload_queue: VecDeque>, + seq: u16, } impl Handle { @@ -34,6 +35,7 @@ impl Handle { ip_addr, payload_queue: VecDeque::new(), flags, + seq: 0, } } } @@ -79,7 +81,7 @@ impl Icmpd { break; } let ip_packet = Ipv4::from_bytes(&packet_buffer[..bytes_readed]) - .ok_or(Error::from_parsing_error(ParsingError::NotEnoughData, + .ok_or(Error::from_parsing_error(PacketError::NotEnoughData, "failed to parse ip header"))?; let icmp_packet = Packet::from_bytes(&ip_packet.data) @@ -97,6 +99,7 @@ impl Icmpd { fn on_echo_request(&mut self, ip_packet: &Ipv4, icmp_packet: &Packet) -> Result<()> { let echo_response = produce_icmp_packet(Ipv4Addr::from(ip_packet.header.src.bytes), PacketKind::EchoResponse, + &SubHeader::None, icmp_packet.get_payload())?; self.icmp_file .write(&echo_response) @@ -109,13 +112,17 @@ impl Icmpd { .get_mut(&Ipv4Addr::from(ip_packet.header.src.bytes)) { for fd in fd_set.iter() { if let Some(handle) = self.handles.get_mut(fd) { - handle - .payload_queue - .push_back(Vec::from(icmp_packet.get_payload())); - post_fevent(&mut self.scheme_file, - *fd, - syscall::EVENT_READ, - icmp_packet.get_payload().len())?; + if let &SubHeader::Echo(echo_subheader) = icmp_packet.get_subheader() { + if echo_subheader.get_id() == *fd as u16 { + handle + .payload_queue + .push_back(Vec::from(icmp_packet.get_payload())); + post_fevent(&mut self.scheme_file, + *fd, + syscall::EVENT_READ, + icmp_packet.get_payload().len())?; + } + } } } } @@ -207,8 +214,12 @@ impl SchemeMut for Icmpd { match handle.handle_type { HandleType::Echo => { let echo_request = - produce_icmp_packet(handle.ip_addr, PacketKind::EchoRequest, buf) - .map_err(|_| syscall::Error::new(syscall::EPROTO))?; + produce_icmp_packet(handle.ip_addr, + PacketKind::EchoRequest, + &SubHeader::Echo(&EchoHeader::new(fd as u16, handle.seq)), + buf) + .map_err(|_| syscall::Error::new(syscall::EPROTO))?; + handle.seq += 1; self.icmp_file .write(&echo_request) .map_err(|_| syscall::Error::new(syscall::EPROTO)) @@ -234,17 +245,24 @@ impl SchemeMut for Icmpd { } } -fn produce_icmp_packet(to_ip: Ipv4Addr, kind: PacketKind, payload: &[u8]) -> Result> { - let mut ip_data = vec![0; mem::size_of::
() + payload.len()]; +fn produce_icmp_packet(to_ip: Ipv4Addr, + kind: PacketKind, + subheader: &SubHeader, + payload: &[u8]) + -> Result> { + let mut ip_data = vec![0; MutPacket::get_total_header_size(subheader) + payload.len()]; { 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 = out_icmp_packet + .set_subheader(subheader) + .map_err(|e| Error::from_parsing_error(e, "can't set subheader"))?; out_icmp_packet.set_kind(kind); { let out_payload = out_icmp_packet.get_payload(); if out_payload.len() != payload.len() { - return Err(Error::from_parsing_error(ParsingError::NotEnoughData, + return Err(Error::from_parsing_error(PacketError::NotEnoughData, " can't copy icmp payload to echo response")); } //WARNING: copy_from_slice can panic if the slices' lengths are different