Revision control
Copy as Markdown
Other Tools
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::fmt::Debug;
use enum_map::Enum;
/// ECN (Explicit Congestion Notification) codepoints mapped to the
/// lower 2 bits of the TOS field.
#[derive(Copy, Clone, PartialEq, Eq, Enum, Default, Debug)]
#[repr(u8)]
pub enum IpTosEcn {
#[default]
/// Not-ECT, Not ECN-Capable Transport, RFC3168
NotEct = 0b00,
/// ECT(1), ECN-Capable Transport(1), RFC8311 and RFC9331
Ect1 = 0b01,
/// ECT(0), ECN-Capable Transport(0), RFC3168
Ect0 = 0b10,
/// CE, Congestion Experienced, RFC3168
Ce = 0b11,
}
impl From<IpTosEcn> for u8 {
fn from(v: IpTosEcn) -> Self {
v as Self
}
}
impl From<u8> for IpTosEcn {
fn from(v: u8) -> Self {
match v & 0b0000_0011 {
0b00 => Self::NotEct,
0b01 => Self::Ect1,
0b10 => Self::Ect0,
0b11 => Self::Ce,
_ => unreachable!(),
}
}
}
impl From<IpTos> for IpTosEcn {
fn from(v: IpTos) -> Self {
Self::from(u8::from(v))
}
}
impl IpTosEcn {
#[must_use]
pub const fn is_ecn_marked(&self) -> bool {
match self {
Self::Ect0 | Self::Ect1 | Self::Ce => true,
Self::NotEct => false,
}
}
}
/// Diffserv Codepoints, mapped to the upper six bits of the TOS field.
#[derive(Copy, Clone, PartialEq, Eq, Enum, Default, Debug)]
#[repr(u8)]
pub enum IpTosDscp {
#[default]
/// Class Selector 0, RFC2474
Cs0 = 0b0000_0000,
/// Class Selector 1, RFC2474
Cs1 = 0b0010_0000,
/// Class Selector 2, RFC2474
Cs2 = 0b0100_0000,
/// Class Selector 3, RFC2474
Cs3 = 0b0110_0000,
/// Class Selector 4, RFC2474
Cs4 = 0b1000_0000,
/// Class Selector 5, RFC2474
Cs5 = 0b1010_0000,
/// Class Selector 6, RFC2474
Cs6 = 0b1100_0000,
/// Class Selector 7, RFC2474
Cs7 = 0b1110_0000,
/// Assured Forwarding 11, RFC2597
Af11 = 0b0010_1000,
/// Assured Forwarding 12, RFC2597
Af12 = 0b0011_0000,
/// Assured Forwarding 13, RFC2597
Af13 = 0b0011_1000,
/// Assured Forwarding 21, RFC2597
Af21 = 0b0100_1000,
/// Assured Forwarding 22, RFC2597
Af22 = 0b0101_0000,
/// Assured Forwarding 23, RFC2597
Af23 = 0b0101_1000,
/// Assured Forwarding 31, RFC2597
Af31 = 0b0110_1000,
/// Assured Forwarding 32, RFC2597
Af32 = 0b0111_0000,
/// Assured Forwarding 33, RFC2597
Af33 = 0b0111_1000,
/// Assured Forwarding 41, RFC2597
Af41 = 0b1000_1000,
/// Assured Forwarding 42, RFC2597
Af42 = 0b1001_0000,
/// Assured Forwarding 43, RFC2597
Af43 = 0b1001_1000,
/// Expedited Forwarding, RFC3246
Ef = 0b1011_1000,
/// Capacity-Admitted Traffic, RFC5865
VoiceAdmit = 0b1011_0000,
/// Lower-Effort, RFC8622
Le = 0b0000_0100,
}
impl From<IpTosDscp> for u8 {
fn from(v: IpTosDscp) -> Self {
v as Self
}
}
impl From<u8> for IpTosDscp {
fn from(v: u8) -> Self {
match v & 0b1111_1100 {
0b0000_0000 => Self::Cs0,
0b0010_0000 => Self::Cs1,
0b0100_0000 => Self::Cs2,
0b0110_0000 => Self::Cs3,
0b1000_0000 => Self::Cs4,
0b1010_0000 => Self::Cs5,
0b1100_0000 => Self::Cs6,
0b1110_0000 => Self::Cs7,
0b0010_1000 => Self::Af11,
0b0011_0000 => Self::Af12,
0b0011_1000 => Self::Af13,
0b0100_1000 => Self::Af21,
0b0101_0000 => Self::Af22,
0b0101_1000 => Self::Af23,
0b0110_1000 => Self::Af31,
0b0111_0000 => Self::Af32,
0b0111_1000 => Self::Af33,
0b1000_1000 => Self::Af41,
0b1001_0000 => Self::Af42,
0b1001_1000 => Self::Af43,
0b1011_1000 => Self::Ef,
0b1011_0000 => Self::VoiceAdmit,
0b0000_0100 => Self::Le,
_ => unreachable!(),
}
}
}
impl From<IpTos> for IpTosDscp {
fn from(v: IpTos) -> Self {
Self::from(u8::from(v))
}
}
/// The type-of-service field in an IP packet.
#[derive(Copy, Clone, PartialEq, Eq, Default)]
pub struct IpTos(u8);
impl From<IpTosEcn> for IpTos {
fn from(v: IpTosEcn) -> Self {
Self(u8::from(v))
}
}
impl From<IpTosDscp> for IpTos {
fn from(v: IpTosDscp) -> Self {
Self(u8::from(v))
}
}
impl From<(IpTosDscp, IpTosEcn)> for IpTos {
fn from(v: (IpTosDscp, IpTosEcn)) -> Self {
Self(u8::from(v.0) | u8::from(v.1))
}
}
impl From<(IpTosEcn, IpTosDscp)> for IpTos {
fn from(v: (IpTosEcn, IpTosDscp)) -> Self {
Self(u8::from(v.0) | u8::from(v.1))
}
}
impl From<IpTos> for u8 {
fn from(v: IpTos) -> Self {
v.0
}
}
impl From<u8> for IpTos {
fn from(v: u8) -> Self {
Self(v)
}
}
impl Debug for IpTos {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("IpTos")
.field(&IpTosDscp::from(*self))
.field(&IpTosEcn::from(*self))
.finish()
}
}
impl IpTos {
pub fn set_ecn(&mut self, ecn: IpTosEcn) {
self.0 = u8::from(IpTosDscp::from(*self)) | u8::from(ecn);
}
pub fn set_dscp(&mut self, dscp: IpTosDscp) {
self.0 = u8::from(IpTosEcn::from(*self)) | u8::from(dscp);
}
#[must_use]
pub fn is_ecn_marked(&self) -> bool {
IpTosEcn::from(*self).is_ecn_marked()
}
}
#[cfg(test)]
mod tests {
use crate::{IpTos, IpTosDscp, IpTosEcn};
#[test]
fn iptosecn_into_u8() {
assert_eq!(u8::from(IpTosEcn::NotEct), 0b00);
assert_eq!(u8::from(IpTosEcn::Ect1), 0b01);
assert_eq!(u8::from(IpTosEcn::Ect0), 0b10);
assert_eq!(u8::from(IpTosEcn::Ce), 0b11);
}
#[test]
fn u8_into_iptosecn() {
assert_eq!(IpTosEcn::from(0b00), IpTosEcn::NotEct);
assert_eq!(IpTosEcn::from(0b01), IpTosEcn::Ect1);
assert_eq!(IpTosEcn::from(0b10), IpTosEcn::Ect0);
assert_eq!(IpTosEcn::from(0b11), IpTosEcn::Ce);
}
#[test]
fn iptosdscp_into_u8() {
assert_eq!(u8::from(IpTosDscp::Cs0), 0b0000_0000);
assert_eq!(u8::from(IpTosDscp::Cs1), 0b0010_0000);
assert_eq!(u8::from(IpTosDscp::Cs2), 0b0100_0000);
assert_eq!(u8::from(IpTosDscp::Cs3), 0b0110_0000);
assert_eq!(u8::from(IpTosDscp::Cs4), 0b1000_0000);
assert_eq!(u8::from(IpTosDscp::Cs5), 0b1010_0000);
assert_eq!(u8::from(IpTosDscp::Cs6), 0b1100_0000);
assert_eq!(u8::from(IpTosDscp::Cs7), 0b1110_0000);
assert_eq!(u8::from(IpTosDscp::Af11), 0b0010_1000);
assert_eq!(u8::from(IpTosDscp::Af12), 0b0011_0000);
assert_eq!(u8::from(IpTosDscp::Af13), 0b0011_1000);
assert_eq!(u8::from(IpTosDscp::Af21), 0b0100_1000);
assert_eq!(u8::from(IpTosDscp::Af22), 0b0101_0000);
assert_eq!(u8::from(IpTosDscp::Af23), 0b0101_1000);
assert_eq!(u8::from(IpTosDscp::Af31), 0b0110_1000);
assert_eq!(u8::from(IpTosDscp::Af32), 0b0111_0000);
assert_eq!(u8::from(IpTosDscp::Af33), 0b0111_1000);
assert_eq!(u8::from(IpTosDscp::Af41), 0b1000_1000);
assert_eq!(u8::from(IpTosDscp::Af42), 0b1001_0000);
assert_eq!(u8::from(IpTosDscp::Af43), 0b1001_1000);
assert_eq!(u8::from(IpTosDscp::Ef), 0b1011_1000);
assert_eq!(u8::from(IpTosDscp::VoiceAdmit), 0b1011_0000);
assert_eq!(u8::from(IpTosDscp::Le), 0b0000_0100);
}
#[test]
fn u8_into_iptosdscp() {
assert_eq!(IpTosDscp::from(0b0000_0000), IpTosDscp::Cs0);
assert_eq!(IpTosDscp::from(0b0010_0000), IpTosDscp::Cs1);
assert_eq!(IpTosDscp::from(0b0100_0000), IpTosDscp::Cs2);
assert_eq!(IpTosDscp::from(0b0110_0000), IpTosDscp::Cs3);
assert_eq!(IpTosDscp::from(0b1000_0000), IpTosDscp::Cs4);
assert_eq!(IpTosDscp::from(0b1010_0000), IpTosDscp::Cs5);
assert_eq!(IpTosDscp::from(0b1100_0000), IpTosDscp::Cs6);
assert_eq!(IpTosDscp::from(0b1110_0000), IpTosDscp::Cs7);
assert_eq!(IpTosDscp::from(0b0010_1000), IpTosDscp::Af11);
assert_eq!(IpTosDscp::from(0b0011_0000), IpTosDscp::Af12);
assert_eq!(IpTosDscp::from(0b0011_1000), IpTosDscp::Af13);
assert_eq!(IpTosDscp::from(0b0100_1000), IpTosDscp::Af21);
assert_eq!(IpTosDscp::from(0b0101_0000), IpTosDscp::Af22);
assert_eq!(IpTosDscp::from(0b0101_1000), IpTosDscp::Af23);
assert_eq!(IpTosDscp::from(0b0110_1000), IpTosDscp::Af31);
assert_eq!(IpTosDscp::from(0b0111_0000), IpTosDscp::Af32);
assert_eq!(IpTosDscp::from(0b0111_1000), IpTosDscp::Af33);
assert_eq!(IpTosDscp::from(0b1000_1000), IpTosDscp::Af41);
assert_eq!(IpTosDscp::from(0b1001_0000), IpTosDscp::Af42);
assert_eq!(IpTosDscp::from(0b1001_1000), IpTosDscp::Af43);
assert_eq!(IpTosDscp::from(0b1011_1000), IpTosDscp::Ef);
assert_eq!(IpTosDscp::from(0b1011_0000), IpTosDscp::VoiceAdmit);
assert_eq!(IpTosDscp::from(0b0000_0100), IpTosDscp::Le);
}
#[test]
fn iptosecn_into_iptos() {
let ecn = IpTosEcn::default();
let iptos_ecn: IpTos = ecn.into();
assert_eq!(u8::from(iptos_ecn), ecn as u8);
}
#[test]
fn iptosdscp_into_iptos() {
let dscp = IpTosDscp::default();
let iptos_dscp: IpTos = dscp.into();
assert_eq!(u8::from(iptos_dscp), dscp as u8);
}
#[test]
fn u8_to_iptos() {
let tos = 0x8b;
let iptos: IpTos = (IpTosEcn::Ce, IpTosDscp::Af41).into();
assert_eq!(tos, u8::from(iptos));
assert_eq!(IpTos::from(tos), iptos);
}
#[test]
fn iptos_to_iptosdscp() {
let tos = IpTos::from((IpTosDscp::Af41, IpTosEcn::NotEct));
let dscp = IpTosDscp::from(tos);
assert_eq!(dscp, IpTosDscp::Af41);
}
#[test]
fn tos_modify_ecn() {
let mut iptos: IpTos = (IpTosDscp::Af41, IpTosEcn::NotEct).into();
iptos.set_ecn(IpTosEcn::Ce);
assert_eq!(u8::from(iptos), 0b1000_1011);
}
#[test]
fn tos_modify_dscp() {
let mut iptos: IpTos = (IpTosDscp::Af41, IpTosEcn::Ect1).into();
iptos.set_dscp(IpTosDscp::Le);
assert_eq!(u8::from(iptos), 0b0000_0101);
}
#[test]
fn iptos_is_ecn_marked() {
let iptos: IpTos = (IpTosDscp::Af41, IpTosEcn::Ce).into();
assert!(iptos.is_ecn_marked());
}
#[test]
fn iptosecn_is_ecn_marked() {
assert!(IpTosEcn::Ce.is_ecn_marked());
assert!(!IpTosEcn::NotEct.is_ecn_marked());
}
}