Add support for Echo sub-header.

This commit is contained in:
Egor Karavaev
2017-06-12 15:25:15 +03:00
parent 8301ee85a1
commit 47563fa29e
4 changed files with 180 additions and 43 deletions
+21 -9
View File
@@ -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<S: Into<String>>(parsing_error: ParsingError, descr: S) -> Error {
pub fn from_parsing_error<S: Into<String>>(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<IOError> for Error {
}
pub type Result<T> = result::Result<T, Error>;
pub type ParsingResult<T> = result::Result<T, ParsingError>;
pub type PacketResult<T> = result::Result<T, PacketError>;
+20 -2
View File
@@ -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()
+106 -17
View File
@@ -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::<EchoHeader>(),
}
}
}
impl<'a> Packet<'a> {
pub fn from_bytes<'b>(bytes: &'b [u8]) -> ParsingResult<Packet<'a>>
pub fn from_bytes<'b>(bytes: &'b [u8]) -> PacketResult<Packet<'a>>
where 'b: 'a
{
if bytes.len() < mem::size_of::<Header>() {
Err(ParsingError::NotEnoughData)
Err(PacketError::NotEnoughData)
} else {
let (header_bytes, payload_bytes) = bytes.split_at(mem::size_of::<Header>());
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::<EchoHeader>() {
return Err(PacketError::NoEchoHeader);
}
let (echo_header_payload, payload) =
packet.payload.split_at(mem::size_of::<EchoHeader>());
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::<Header>() + self.payload.len()
mem::size_of::<Header>() + 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<MutPacket<'a>>
pub fn from_bytes<'b>(bytes: &'b mut [u8]) -> PacketResult<MutPacket<'a>>
where 'b: 'a
{
if bytes.len() < mem::size_of::<Header>() {
Err(ParsingError::NotEnoughData)
Err(PacketError::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,
subheader: SubHeader::None,
})
}
}
pub fn set_subheader(self, subheader: &SubHeader) -> PacketResult<MutPacket<'a>> {
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::<Header>() + subheader.get_size()
}
}
const ECHO_REQUEST_TYPE: u8 = 8;
+33 -15
View File
@@ -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<Vec<u8>>,
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<Vec<u8>> {
let mut ip_data = vec![0; mem::size_of::<Header>() + payload.len()];
fn produce_icmp_packet(to_ip: Ipv4Addr,
kind: PacketKind,
subheader: &SubHeader,
payload: &[u8])
-> Result<Vec<u8>> {
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