Add support for Echo sub-header.
This commit is contained in:
+21
-9
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user