use thiserror::Error;
#[derive(Debug, Error)]
pub enum Error {
#[error("transport error: {0}")]
Transport(#[from] rovs_transport::Error),
#[error("I/O error: {0}")]
Io(#[from] std::io::Error),
#[error("unsupported OpenFlow version: {0}")]
UnsupportedVersion(u8),
#[error("parse error: {0}")]
Parse(String),
#[error("invalid message: {0}")]
InvalidMessage(String),
#[error("connection closed")]
ConnectionClosed,
#[error("timeout")]
Timeout,
#[error("{0}")]
OfError(OfError),
}
#[derive(Debug, Clone)]
pub struct OfError {
pub error_type: OfErrorType,
pub code: u16,
pub data: Vec<u8>,
}
impl std::fmt::Display for OfError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}: ", self.error_type)?;
match self.error_type {
OfErrorType::HelloFailed => write!(f, "{}", HelloFailedCode::from(self.code)),
OfErrorType::BadRequest => write!(f, "{}", BadRequestCode::from(self.code)),
OfErrorType::BadAction => write!(f, "{}", BadActionCode::from(self.code)),
OfErrorType::BadInstruction => write!(f, "{}", BadInstructionCode::from(self.code)),
OfErrorType::BadMatch => write!(f, "{}", BadMatchCode::from(self.code)),
OfErrorType::FlowModFailed => write!(f, "{}", FlowModFailedCode::from(self.code)),
OfErrorType::GroupModFailed => write!(f, "{}", GroupModFailedCode::from(self.code)),
OfErrorType::PortModFailed => write!(f, "{}", PortModFailedCode::from(self.code)),
OfErrorType::TableModFailed => write!(f, "{}", TableModFailedCode::from(self.code)),
OfErrorType::MeterModFailed => write!(f, "{}", MeterModFailedCode::from(self.code)),
OfErrorType::TableFeaturesFailed => {
write!(f, "{}", TableFeaturesFailedCode::from(self.code))
}
OfErrorType::Unknown(_) => write!(f, "code {}", self.code),
}
}
}
impl OfError {
pub fn parse(body: &[u8]) -> Result<Self, Error> {
if body.len() < 4 {
return Err(Error::Parse("error message too short".into()));
}
let error_type = u16::from_be_bytes([body[0], body[1]]);
let code = u16::from_be_bytes([body[2], body[3]]);
let data = body.get(4..).unwrap_or(&[]).to_vec();
Ok(Self {
error_type: OfErrorType::from(error_type),
code,
data,
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OfErrorType {
HelloFailed,
BadRequest,
BadAction,
BadInstruction,
BadMatch,
FlowModFailed,
GroupModFailed,
PortModFailed,
TableModFailed,
MeterModFailed,
TableFeaturesFailed,
Unknown(u16),
}
impl From<u16> for OfErrorType {
fn from(v: u16) -> Self {
match v {
0 => Self::HelloFailed,
1 => Self::BadRequest,
2 => Self::BadAction,
3 => Self::BadInstruction,
4 => Self::BadMatch,
5 => Self::FlowModFailed,
6 => Self::GroupModFailed,
7 => Self::PortModFailed,
8 => Self::TableModFailed,
12 => Self::MeterModFailed,
13 => Self::TableFeaturesFailed,
_ => Self::Unknown(v),
}
}
}
impl std::fmt::Display for OfErrorType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::HelloFailed => write!(f, "HELLO_FAILED"),
Self::BadRequest => write!(f, "BAD_REQUEST"),
Self::BadAction => write!(f, "BAD_ACTION"),
Self::BadInstruction => write!(f, "BAD_INSTRUCTION"),
Self::BadMatch => write!(f, "BAD_MATCH"),
Self::FlowModFailed => write!(f, "FLOW_MOD_FAILED"),
Self::GroupModFailed => write!(f, "GROUP_MOD_FAILED"),
Self::PortModFailed => write!(f, "PORT_MOD_FAILED"),
Self::TableModFailed => write!(f, "TABLE_MOD_FAILED"),
Self::MeterModFailed => write!(f, "METER_MOD_FAILED"),
Self::TableFeaturesFailed => write!(f, "TABLE_FEATURES_FAILED"),
Self::Unknown(v) => write!(f, "UNKNOWN({v})"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum HelloFailedCode {
Incompatible,
EpermError,
Unknown(u16),
}
impl From<u16> for HelloFailedCode {
fn from(v: u16) -> Self {
match v {
0 => Self::Incompatible,
1 => Self::EpermError,
_ => Self::Unknown(v),
}
}
}
impl std::fmt::Display for HelloFailedCode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Incompatible => write!(f, "incompatible version"),
Self::EpermError => write!(f, "permissions error"),
Self::Unknown(v) => write!(f, "unknown code {v}"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BadRequestCode {
BadVersion,
BadType,
BadMultipart,
BadExperimenter,
BadExpType,
Eperm,
BadLen,
BufferEmpty,
BufferUnknown,
BadTableId,
IsSlave,
BadPort,
BadPacket,
MultipartBufferOverflow,
Unknown(u16),
}
impl From<u16> for BadRequestCode {
fn from(v: u16) -> Self {
match v {
0 => Self::BadVersion,
1 => Self::BadType,
2 => Self::BadMultipart,
3 => Self::BadExperimenter,
4 => Self::BadExpType,
5 => Self::Eperm,
6 => Self::BadLen,
7 => Self::BufferEmpty,
8 => Self::BufferUnknown,
9 => Self::BadTableId,
10 => Self::IsSlave,
11 => Self::BadPort,
12 => Self::BadPacket,
13 => Self::MultipartBufferOverflow,
_ => Self::Unknown(v),
}
}
}
impl std::fmt::Display for BadRequestCode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::BadVersion => write!(f, "unknown version"),
Self::BadType => write!(f, "unknown message type"),
Self::BadMultipart => write!(f, "unknown multipart type"),
Self::BadExperimenter => write!(f, "unknown experimenter ID"),
Self::BadExpType => write!(f, "unknown experimenter type"),
Self::Eperm => write!(f, "permissions error"),
Self::BadLen => write!(f, "wrong request length"),
Self::BufferEmpty => write!(f, "buffer does not exist"),
Self::BufferUnknown => write!(f, "buffer already used"),
Self::BadTableId => write!(f, "invalid table ID"),
Self::IsSlave => write!(f, "controller is slave"),
Self::BadPort => write!(f, "invalid port"),
Self::BadPacket => write!(f, "invalid packet in buffer-id"),
Self::MultipartBufferOverflow => write!(f, "multipart request overflowed"),
Self::Unknown(v) => write!(f, "unknown code {v}"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BadActionCode {
BadType,
BadLen,
BadExperimenter,
BadExpType,
BadOutPort,
BadArgument,
Eperm,
TooMany,
BadQueue,
BadOutGroup,
MatchInconsistent,
UnsupportedOrder,
BadTag,
BadSetType,
BadSetLen,
BadSetArgument,
Unknown(u16),
}
impl From<u16> for BadActionCode {
fn from(v: u16) -> Self {
match v {
0 => Self::BadType,
1 => Self::BadLen,
2 => Self::BadExperimenter,
3 => Self::BadExpType,
4 => Self::BadOutPort,
5 => Self::BadArgument,
6 => Self::Eperm,
7 => Self::TooMany,
8 => Self::BadQueue,
9 => Self::BadOutGroup,
10 => Self::MatchInconsistent,
11 => Self::UnsupportedOrder,
12 => Self::BadTag,
13 => Self::BadSetType,
14 => Self::BadSetLen,
15 => Self::BadSetArgument,
_ => Self::Unknown(v),
}
}
}
impl std::fmt::Display for BadActionCode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::BadType => write!(f, "unknown action type"),
Self::BadLen => write!(f, "length problem in actions"),
Self::BadExperimenter => write!(f, "unknown experimenter ID"),
Self::BadExpType => write!(f, "unknown action for experimenter"),
Self::BadOutPort => write!(f, "problem validating output port"),
Self::BadArgument => write!(f, "bad action argument"),
Self::Eperm => write!(f, "permissions error"),
Self::TooMany => write!(f, "too many actions"),
Self::BadQueue => write!(f, "problem validating output queue"),
Self::BadOutGroup => write!(f, "invalid group ID"),
Self::MatchInconsistent => write!(f, "action can't apply for this match"),
Self::UnsupportedOrder => write!(f, "action order unsupported"),
Self::BadTag => write!(f, "unsupported tag/encap"),
Self::BadSetType => write!(f, "unsupported SET_FIELD type"),
Self::BadSetLen => write!(f, "length problem in SET_FIELD"),
Self::BadSetArgument => write!(f, "bad argument in SET_FIELD"),
Self::Unknown(v) => write!(f, "unknown code {v}"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BadInstructionCode {
UnknownInst,
UnsupInst,
BadTableId,
UnsupMetadata,
UnsupMetadataMask,
BadExperimenter,
BadExpType,
BadLen,
Eperm,
Unknown(u16),
}
impl From<u16> for BadInstructionCode {
fn from(v: u16) -> Self {
match v {
0 => Self::UnknownInst,
1 => Self::UnsupInst,
2 => Self::BadTableId,
3 => Self::UnsupMetadata,
4 => Self::UnsupMetadataMask,
5 => Self::BadExperimenter,
6 => Self::BadExpType,
7 => Self::BadLen,
8 => Self::Eperm,
_ => Self::Unknown(v),
}
}
}
impl std::fmt::Display for BadInstructionCode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::UnknownInst => write!(f, "unknown instruction type"),
Self::UnsupInst => write!(f, "unsupported instruction"),
Self::BadTableId => write!(f, "invalid table ID"),
Self::UnsupMetadata => write!(f, "metadata mask unsupported"),
Self::UnsupMetadataMask => write!(f, "metadata mask unsupported in write"),
Self::BadExperimenter => write!(f, "unknown experimenter ID"),
Self::BadExpType => write!(f, "unknown instruction for experimenter"),
Self::BadLen => write!(f, "length problem in instructions"),
Self::Eperm => write!(f, "permissions error"),
Self::Unknown(v) => write!(f, "unknown code {v}"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BadMatchCode {
BadType,
BadLen,
BadTag,
BadDlAddrMask,
BadNwAddrMask,
BadWildcards,
BadField,
BadValue,
BadMask,
BadPrereq,
DupField,
Eperm,
Unknown(u16),
}
impl From<u16> for BadMatchCode {
fn from(v: u16) -> Self {
match v {
0 => Self::BadType,
1 => Self::BadLen,
2 => Self::BadTag,
3 => Self::BadDlAddrMask,
4 => Self::BadNwAddrMask,
5 => Self::BadWildcards,
6 => Self::BadField,
7 => Self::BadValue,
8 => Self::BadMask,
9 => Self::BadPrereq,
10 => Self::DupField,
11 => Self::Eperm,
_ => Self::Unknown(v),
}
}
}
impl std::fmt::Display for BadMatchCode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::BadType => write!(f, "unsupported match type"),
Self::BadLen => write!(f, "length problem in match"),
Self::BadTag => write!(f, "unsupported tag/encap"),
Self::BadDlAddrMask => write!(f, "unsupported datalink address mask"),
Self::BadNwAddrMask => write!(f, "unsupported network address mask"),
Self::BadWildcards => write!(f, "unsupported field combination"),
Self::BadField => write!(f, "unsupported field type"),
Self::BadValue => write!(f, "unsupported value in match field"),
Self::BadMask => write!(f, "unsupported mask specified"),
Self::BadPrereq => write!(f, "prerequisite not met"),
Self::DupField => write!(f, "field appeared more than once"),
Self::Eperm => write!(f, "permissions error"),
Self::Unknown(v) => write!(f, "unknown code {v}"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FlowModFailedCode {
Unknown,
TableFull,
BadTableId,
Overlap,
Eperm,
BadTimeout,
BadCommand,
BadFlags,
UnknownCode(u16),
}
impl From<u16> for FlowModFailedCode {
fn from(v: u16) -> Self {
match v {
0 => Self::Unknown,
1 => Self::TableFull,
2 => Self::BadTableId,
3 => Self::Overlap,
4 => Self::Eperm,
5 => Self::BadTimeout,
6 => Self::BadCommand,
7 => Self::BadFlags,
_ => Self::UnknownCode(v),
}
}
}
impl std::fmt::Display for FlowModFailedCode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Unknown => write!(f, "unspecified error"),
Self::TableFull => write!(f, "table full"),
Self::BadTableId => write!(f, "table does not exist"),
Self::Overlap => write!(f, "overlapping flow entry"),
Self::Eperm => write!(f, "permissions error"),
Self::BadTimeout => write!(f, "bad timeout"),
Self::BadCommand => write!(f, "unsupported command"),
Self::BadFlags => write!(f, "unsupported flags"),
Self::UnknownCode(v) => write!(f, "unknown code {v}"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GroupModFailedCode {
GroupExists,
InvalidGroup,
UnknownGroup,
WeightUnsupported,
OutOfBuckets,
OutOfGroups,
ChainingUnsupported,
WatchUnsupported,
Loop,
ChainsNotSupported,
Unknown(u16),
}
impl From<u16> for GroupModFailedCode {
fn from(v: u16) -> Self {
match v {
0 => Self::GroupExists,
1 => Self::InvalidGroup,
2 => Self::WeightUnsupported,
3 => Self::OutOfGroups,
4 => Self::OutOfBuckets,
5 => Self::ChainingUnsupported,
6 => Self::WatchUnsupported,
7 => Self::Loop,
8 => Self::UnknownGroup,
9 => Self::ChainsNotSupported,
_ => Self::Unknown(v),
}
}
}
impl std::fmt::Display for GroupModFailedCode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::GroupExists => write!(f, "group already exists"),
Self::InvalidGroup => write!(f, "invalid group"),
Self::UnknownGroup => write!(f, "group does not exist"),
Self::WeightUnsupported => write!(f, "bucket weight unsupported"),
Self::OutOfBuckets => write!(f, "bucket maximum exceeded"),
Self::OutOfGroups => write!(f, "group maximum exceeded"),
Self::ChainingUnsupported => write!(f, "chaining unsupported"),
Self::WatchUnsupported => write!(f, "watch bucket unsupported"),
Self::Loop => write!(f, "loop in group"),
Self::ChainsNotSupported => write!(f, "chains not supported"),
Self::Unknown(v) => write!(f, "unknown code {v}"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PortModFailedCode {
BadPort,
BadHwAddr,
BadConfig,
BadAdvertise,
Eperm,
Unknown(u16),
}
impl From<u16> for PortModFailedCode {
fn from(v: u16) -> Self {
match v {
0 => Self::BadPort,
1 => Self::BadHwAddr,
2 => Self::BadConfig,
3 => Self::BadAdvertise,
4 => Self::Eperm,
_ => Self::Unknown(v),
}
}
}
impl std::fmt::Display for PortModFailedCode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::BadPort => write!(f, "port does not exist"),
Self::BadHwAddr => write!(f, "hardware address mismatch"),
Self::BadConfig => write!(f, "invalid config"),
Self::BadAdvertise => write!(f, "invalid advertise"),
Self::Eperm => write!(f, "permissions error"),
Self::Unknown(v) => write!(f, "unknown code {v}"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TableModFailedCode {
BadTable,
BadConfig,
Eperm,
Unknown(u16),
}
impl From<u16> for TableModFailedCode {
fn from(v: u16) -> Self {
match v {
0 => Self::BadTable,
1 => Self::BadConfig,
2 => Self::Eperm,
_ => Self::Unknown(v),
}
}
}
impl std::fmt::Display for TableModFailedCode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::BadTable => write!(f, "table does not exist"),
Self::BadConfig => write!(f, "invalid config"),
Self::Eperm => write!(f, "permissions error"),
Self::Unknown(v) => write!(f, "unknown code {v}"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MeterModFailedCode {
Unknown,
MeterExists,
InvalidMeter,
UnknownMeter,
BadCommand,
BadFlags,
BadRate,
BadBurst,
BadBand,
BadBandValue,
OutOfMeters,
OutOfBands,
UnknownCode(u16),
}
impl From<u16> for MeterModFailedCode {
fn from(v: u16) -> Self {
match v {
0 => Self::Unknown,
1 => Self::MeterExists,
2 => Self::InvalidMeter,
3 => Self::UnknownMeter,
4 => Self::BadCommand,
5 => Self::BadFlags,
6 => Self::BadRate,
7 => Self::BadBurst,
8 => Self::BadBand,
9 => Self::BadBandValue,
10 => Self::OutOfMeters,
11 => Self::OutOfBands,
_ => Self::UnknownCode(v),
}
}
}
impl std::fmt::Display for MeterModFailedCode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Unknown => write!(f, "unspecified error"),
Self::MeterExists => write!(f, "meter already exists"),
Self::InvalidMeter => write!(f, "invalid meter"),
Self::UnknownMeter => write!(f, "meter does not exist"),
Self::BadCommand => write!(f, "unsupported command"),
Self::BadFlags => write!(f, "unsupported flags"),
Self::BadRate => write!(f, "rate unsupported"),
Self::BadBurst => write!(f, "burst size unsupported"),
Self::BadBand => write!(f, "band unsupported"),
Self::BadBandValue => write!(f, "band value unsupported"),
Self::OutOfMeters => write!(f, "meter maximum exceeded"),
Self::OutOfBands => write!(f, "maximum bands exceeded"),
Self::UnknownCode(v) => write!(f, "unknown code {v}"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TableFeaturesFailedCode {
BadTable,
BadMetadata,
BadType,
BadLen,
BadArgument,
Eperm,
Unknown(u16),
}
impl From<u16> for TableFeaturesFailedCode {
fn from(v: u16) -> Self {
match v {
0 => Self::BadTable,
1 => Self::BadMetadata,
2 => Self::BadType,
3 => Self::BadLen,
4 => Self::BadArgument,
5 => Self::Eperm,
_ => Self::Unknown(v),
}
}
}
impl std::fmt::Display for TableFeaturesFailedCode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::BadTable => write!(f, "table does not exist"),
Self::BadMetadata => write!(f, "invalid metadata mask"),
Self::BadType => write!(f, "unknown property type"),
Self::BadLen => write!(f, "length problem"),
Self::BadArgument => write!(f, "unsupported property value"),
Self::Eperm => write!(f, "permissions error"),
Self::Unknown(v) => write!(f, "unknown code {v}"),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_of_error_flow_mod_failed() {
let body = [0x00, 0x05, 0x00, 0x01, 0xde, 0xad, 0xbe, 0xef];
let err = OfError::parse(&body).unwrap();
assert_eq!(err.error_type, OfErrorType::FlowModFailed);
assert_eq!(err.code, 1);
assert_eq!(err.data, vec![0xde, 0xad, 0xbe, 0xef]);
let msg = err.to_string();
assert!(msg.contains("FLOW_MOD_FAILED"));
assert!(msg.contains("table full"));
}
#[test]
fn parse_of_error_bad_match() {
let body = [0x00, 0x04, 0x00, 0x09];
let err = OfError::parse(&body).unwrap();
assert_eq!(err.error_type, OfErrorType::BadMatch);
assert_eq!(err.code, 9);
assert!(err.data.is_empty());
let msg = err.to_string();
assert!(msg.contains("BAD_MATCH"));
assert!(msg.contains("prerequisite not met"));
}
#[test]
fn parse_of_error_too_short() {
let body = [0x00, 0x01];
assert!(OfError::parse(&body).is_err());
}
#[test]
fn of_error_type_from_u16() {
assert_eq!(OfErrorType::from(0), OfErrorType::HelloFailed);
assert_eq!(OfErrorType::from(1), OfErrorType::BadRequest);
assert_eq!(OfErrorType::from(5), OfErrorType::FlowModFailed);
assert_eq!(OfErrorType::from(99), OfErrorType::Unknown(99));
}
#[test]
fn flow_mod_failed_codes() {
assert_eq!(
FlowModFailedCode::from(1).to_string(),
"table full"
);
assert_eq!(
FlowModFailedCode::from(3).to_string(),
"overlapping flow entry"
);
assert_eq!(
FlowModFailedCode::from(4).to_string(),
"permissions error"
);
}
#[test]
fn bad_action_codes() {
assert_eq!(
BadActionCode::from(0).to_string(),
"unknown action type"
);
assert_eq!(
BadActionCode::from(4).to_string(),
"problem validating output port"
);
}
#[test]
fn bad_match_codes() {
assert_eq!(
BadMatchCode::from(9).to_string(),
"prerequisite not met"
);
assert_eq!(
BadMatchCode::from(10).to_string(),
"field appeared more than once"
);
}
}