blob: cb7f04e8bef26e50c4fc9f70da702d33090ebdeb [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//! Custom error types for the netstack.
use net_types::ip::{Ip, IpAddress};
use net_types::MulticastAddress;
use thiserror::Error;
use crate::device::AddressError;
use crate::wire::icmp::IcmpIpTypes;
/// Results returned from many functions in the netstack.
pub type Result<T> = core::result::Result<T, NetstackError>;
/// Results returned from parsing functions in the netstack.
pub(crate) type ParseResult<T> = core::result::Result<T, ParseError>;
/// Results returned from IP packet parsing functions in the netstack.
pub(crate) type IpParseResult<I, T> = core::result::Result<T, IpParseError<I>>;
/// Top-level error type the netstack.
#[derive(Error, Debug, PartialEq)]
pub enum NetstackError {
#[error("{}", _0)]
/// Errors related to packet parsing.
Parse(ParseError),
/// Error when item already exists.
#[error("Item already exists")]
Exists,
/// Error when item is not found.
#[error("Item not found")]
NotFound,
/// Errors related to sending UDP frames/packets.
#[error("{}", _0)]
SendUdp(crate::transport::udp::SendError),
/// Errors related to connections.
#[error("{}", _0)]
Connect(SocketError),
/// Error when there is no route to an address.
#[error("No route to address")]
NoRoute,
/// Error when a maximum transmission unit (MTU) is exceeded.
#[error("MTU exceeded")]
Mtu,
// Add error types here as we add more to the stack
}
impl From<AddressError> for NetstackError {
fn from(error: AddressError) -> Self {
match error {
AddressError::AlreadyExists => Self::Exists,
AddressError::NotFound => Self::NotFound,
}
}
}
/// Error type for packet parsing.
#[derive(Error, Debug, PartialEq)]
pub enum ParseError {
/// Operation is not supported.
#[error("Operation is not supported")]
NotSupported,
/// Operation is not expected in this context.
#[error("Operation is not expected in this context")]
NotExpected,
/// Checksum is invalid.
#[error("Invalid checksum")]
Checksum,
/// Packet is not formatted properly.
#[error("Packet is not formatted properly")]
Format,
}
/// Action to take when an error is encountered while parsing an IP packet.
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum IpParseErrorAction {
/// Discard the packet and do nothing further.
DiscardPacket,
/// Discard the packet and send an ICMP response.
DiscardPacketSendICMP,
/// Discard the packet and send an ICMP response if the packet's
/// destination address was not a multicast address.
DiscardPacketSendICMPNoMulticast,
}
impl IpParseErrorAction {
/// Determines whether or not an ICMP message should be sent.
///
/// Returns `true` if we should send an ICMP response. We should send
/// an ICMP response if an action is set to `DiscardPacketSendICMP`, or
/// if an action is set to `DiscardPacketSendICMPNoMulticast` and `dst_addr`
/// (the destination address of the original packet that lead to a parsing
/// error) is not a multicast address.
pub(crate) fn should_send_icmp<A: IpAddress>(&self, dst_addr: &A) -> bool {
match *self {
IpParseErrorAction::DiscardPacket => false,
IpParseErrorAction::DiscardPacketSendICMP => true,
IpParseErrorAction::DiscardPacketSendICMPNoMulticast => !dst_addr.is_multicast(),
}
}
/// Determines whether or not an ICMP message should be sent even if
/// the original packet's destination address is a multicast.
pub(crate) fn should_send_icmp_to_multicast(&self) -> bool {
match *self {
IpParseErrorAction::DiscardPacketSendICMP => true,
IpParseErrorAction::DiscardPacket
| IpParseErrorAction::DiscardPacketSendICMPNoMulticast => false,
}
}
}
/// Error type for IP packet parsing.
#[derive(Error, Debug, PartialEq)]
pub(crate) enum IpParseError<I: IcmpIpTypes> {
#[error("Parsing Error")]
Parse { error: ParseError },
/// For errors where an ICMP Parameter Problem error needs to be
/// sent to the source of a packet.
///
/// `src_ip` and `dst_ip` are the source and destination IP addresses of the
/// original packet. `header_len` is the length of the header that we know
/// about up to the point of the parameter problem error. `action` is the
/// action we should take after encountering the error. If `must_send_icmp`
/// is `true` we MUST send an ICMP response if `action` specifies it;
/// otherwise, we MAY choose to just discard the packet and do nothing
/// further. `code` is the ICMP (ICMPv4 or ICMPv6) specific parameter
/// problem code that provides more granular information about the parameter
/// problem encountered. `pointer` is the offset of the erroneous value within
/// the IP packet, calculated from the beginning of the IP packet.
#[error("Parameter Problem")]
ParameterProblem {
src_ip: I::Addr,
dst_ip: I::Addr,
code: I::ParameterProblemCode,
pointer: I::ParameterProblemPointer,
must_send_icmp: bool,
header_len: I::HeaderLen,
action: IpParseErrorAction,
},
}
impl<I: Ip> From<ParseError> for IpParseError<I> {
fn from(error: ParseError) -> Self {
IpParseError::Parse { error }
}
}
/// Error when something exists unexpectedly, such as trying to add an
/// element when the element is already present.
#[derive(Debug, PartialEq, Eq)]
pub(crate) struct ExistsError;
impl From<ExistsError> for NetstackError {
fn from(_: ExistsError) -> NetstackError {
NetstackError::Exists
}
}
impl From<ExistsError> for SocketError {
fn from(_: ExistsError) -> SocketError {
SocketError::Local(LocalAddressError::AddressInUse)
}
}
/// Error when something unexpectedly doesn't exist, such as trying to
/// remove an element when the element is not present.
#[derive(Debug, PartialEq, Eq)]
pub(crate) struct NotFoundError;
impl From<NotFoundError> for NetstackError {
fn from(_: NotFoundError) -> NetstackError {
NetstackError::NotFound
}
}
/// Error type for errors common to local addresses.
#[derive(Error, Debug, PartialEq)]
pub enum LocalAddressError {
/// Cannot bind to address.
#[error("can't bind to address")]
CannotBindToAddress,
/// Failed to allocate local port.
#[error("failed to allocate local port")]
FailedToAllocateLocalPort,
/// Specified local address does not match any expected address.
#[error("specified local address does not match any expected address")]
AddressMismatch,
/// The requested address/socket pair is in use.
#[error("Address in use")]
AddressInUse,
}
// TODO(joshlf): Once we support a more general model of sockets in which UDP and ICMP
// connections are special cases of UDP and ICMP sockets, we can introduce a more
// specialized ListenerError which does not contain the NoRoute variant.
/// An error encountered when attempting to create a UDP, TCP, or ICMP connection.
#[derive(Error, Debug, PartialEq)]
pub enum RemoteAddressError {
/// No route to host.
#[error("no route to host")]
NoRoute,
}
/// Error type for connection errors.
#[derive(Error, Debug, PartialEq)]
pub enum SocketError {
#[error("{}", _0)]
/// Errors related to the local address.
Local(LocalAddressError),
#[error("{}", _0)]
/// Errors related to the remote address.
Remote(RemoteAddressError),
}
/// Error when no route exists to a remote address.
#[derive(Debug, PartialEq, Eq)]
pub struct NoRouteError;
impl From<NoRouteError> for NetstackError {
fn from(_: NoRouteError) -> NetstackError {
NetstackError::NoRoute
}
}
impl From<NoRouteError> for SocketError {
fn from(_: NoRouteError) -> SocketError {
SocketError::Remote(RemoteAddressError::NoRoute)
}
}