use serde::Serialize; use strum::FromRepr; use thiserror::Error; /// A field holds a number that is not one of the values defined for it. #[derive(Debug, Clone, Copy, PartialEq, Eq, Error)] #[error("invalid {what} value {value}")] pub struct InvalidValue { pub what: &'static str, pub value: u32, } /// A message that could not be decoded, together with the NOTIFICATION that /// reports the problem to the peer (RFC 4271 §6). #[derive(Debug, Clone, PartialEq, Eq, Error)] #[error("{detail}")] pub struct DecodeError { /// The error code and subcode to send. pub notification: BgpError, /// The Data field of that NOTIFICATION, where the RFC defines one. pub data: Vec, /// What was wrong, for the log. pub detail: String, } /// The error a NOTIFICATION reports: its code and subcode (RFC 4271 §4.5). #[derive(Debug, Clone, PartialEq, Eq, Error, Serialize)] pub enum BgpError { #[error("Message header error: {0}")] MsgHeader(MsgHeaderSubcode), #[error("Open message error: {0}")] OpenMsg(OpenMsgSubcode), #[error("Update message error: {0}")] UpdateMsg(UpdateMsgSubcode), #[error("Hold timer expired")] HoldTimer, #[error("Finite state machine error: {0}")] FsmError(FsmSubcode), #[error("Cease: {0}")] Cease(CeaseSubcode), #[error("Route refresh message error: {0}")] RouteRefresh(RouteRefreshSubcode), #[error("Send hold timer expired")] SendHoldTimer, /// A code or subcode this implementation has no name for. A peer may /// send any: new ones are assigned, and zero ("unspecific") is always /// allowed. The session ends the same way, and the numbers are kept /// for the log. #[error("error code {code} subcode {subcode}")] Other { code: u8, subcode: u8 }, } impl BgpError { /// The error for a code and subcode off the wire. Every pair has one, /// so that a peer's NOTIFICATION can always be read. pub fn from_code_subcode(code: u8, subcode: u8) -> Self { let known = match code { 1 => MsgHeaderSubcode::from_repr(subcode).map(Self::MsgHeader), 2 => OpenMsgSubcode::from_repr(subcode).map(Self::OpenMsg), 3 => UpdateMsgSubcode::from_repr(subcode).map(Self::UpdateMsg), 4 => (subcode == 0).then_some(Self::HoldTimer), 5 => FsmSubcode::from_repr(subcode).map(Self::FsmError), 6 => CeaseSubcode::from_repr(subcode).map(Self::Cease), 7 => RouteRefreshSubcode::from_repr(subcode).map(Self::RouteRefresh), 8 => (subcode == 0).then_some(Self::SendHoldTimer), _ => None, }; known.unwrap_or(Self::Other { code, subcode }) } pub fn code_subcode(&self) -> (u8, u8) { match self { Self::MsgHeader(s) => (1, *s as u8), Self::OpenMsg(s) => (2, *s as u8), Self::UpdateMsg(s) => (3, *s as u8), Self::HoldTimer => (4, 0), Self::FsmError(s) => (5, *s as u8), Self::Cease(s) => (6, *s as u8), Self::RouteRefresh(s) => (7, *s as u8), Self::SendHoldTimer => (8, 0), Self::Other { code, subcode } => (*code, *subcode), } } } #[derive(Debug, Clone, Copy, PartialEq, Eq, Error, Serialize, FromRepr)] #[repr(u8)] pub enum MsgHeaderSubcode { #[error("Connection not synchronized")] ConnNotSynchronized = 1, #[error("Bad message length")] BadMessageLength = 2, #[error("Bad message type")] BadMessageType = 3, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Error, Serialize, FromRepr)] #[repr(u8)] pub enum OpenMsgSubcode { #[error("Unspecific")] Unspecific = 0, #[error("Unsupported BGP version number")] UnsupportedVersion = 1, #[error("Bad peer AS number")] BadPeerAs = 2, #[error("Bad BGP identifier")] BadBgpId = 3, #[error("Unsupported optional parameter")] UnsupportedOptionalParam = 4, #[error("Unacceptable hold time")] UnacceptableHoldTime = 6, #[error("Unsupported capability")] UnsupportedCapability = 7, #[error("Role mismatch")] RoleMismatch = 8, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Error, Serialize, FromRepr)] #[repr(u8)] pub enum UpdateMsgSubcode { #[error("Malformed attribute list")] MalformedAttrs = 1, #[error("Unrecognized well known attribute")] UnrecognizedWellKnownAttr = 2, #[error("Missing well known attribute")] MissingWellKnown = 3, #[error("Attribute flags error")] AttributeFlags = 4, #[error("Attribute length error")] AttributeLength = 5, #[error("Invalid origin")] InvalidOrigin = 6, #[error("Invalid next hop")] InvalidNextHop = 8, #[error("Optional attribute error")] OptionalAttribute = 9, #[error("Invalid network field")] InvalidNetworkField = 10, #[error("Malformed AS path")] MalformedAsPath = 11, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Error, Serialize, FromRepr)] #[repr(u8)] pub enum FsmSubcode { #[error("Received an unexpected message in OpenSent state")] UnexpectedOpenSent = 1, #[error("Received an unexpected message in OpenConfirm state")] UnexpectedOpenConfirm = 2, #[error("Received an unexpected message in Established state")] UnexpectedEstablished = 3, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Error, Serialize, FromRepr)] #[repr(u8)] pub enum CeaseSubcode { #[error("Maximum number of prefixes reached")] MaxPrefixes = 1, #[error("Administrative shutdown")] AdminShutdown = 2, #[error("Peer deconfigured")] PeerDeconf = 3, #[error("Administrative reset")] AdminReset = 4, #[error("Connection rejected")] ConnRejected = 5, #[error("Configuration change")] ConfChange = 6, #[error("Connection collision resolution")] ConnCollisionResolution = 7, #[error("Out of resources")] OutOfResources = 8, #[error("Hard reset")] HardReset = 9, #[error("BFD down")] BfdDown = 10, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Error, Serialize, FromRepr)] #[repr(u8)] pub enum RouteRefreshSubcode { #[error("Invalid message length")] InvalidLength = 1, }