#[allow(dead_code)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u16)]
pub enum OxmClass {
OpenflowBasic = 0x8000,
Experimenter = 0xffff,
Nxm0 = 0x0000,
Nxm1 = 0x0001,
}
#[allow(dead_code)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum OxmField {
InPort = 0,
InPhyPort = 1,
Metadata = 2,
EthDst = 3,
EthSrc = 4,
EthType = 5,
VlanVid = 6,
VlanPcp = 7,
IpDscp = 8,
IpEcn = 9,
IpProto = 10,
Ipv4Src = 11,
Ipv4Dst = 12,
TcpSrc = 13,
TcpDst = 14,
UdpSrc = 15,
UdpDst = 16,
SctpSrc = 17,
SctpDst = 18,
Icmpv4Type = 19,
Icmpv4Code = 20,
ArpOp = 21,
ArpSpa = 22,
ArpTpa = 23,
ArpSha = 24,
ArpTha = 25,
Ipv6Src = 26,
Ipv6Dst = 27,
Ipv6Flabel = 28,
Icmpv6Type = 29,
Icmpv6Code = 30,
TunnelId = 38,
}
#[allow(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum NxmField {
Reg0 = 0,
Reg1 = 1,
Reg2 = 2,
Reg3 = 3,
Reg4 = 4,
Reg5 = 5,
Reg6 = 6,
Reg7 = 7,
Reg8 = 8,
Reg9 = 9,
Reg10 = 10,
Reg11 = 11,
Reg12 = 12,
Reg13 = 13,
Reg14 = 14,
Reg15 = 15,
TunId = 16,
TunIpv4Src = 31,
TunIpv4Dst = 32,
PktMark = 33,
CtState = 105,
CtZone = 106,
CtMark = 107,
CtLabel = 108,
XxReg0 = 111,
XxReg1 = 112,
XxReg2 = 113,
XxReg3 = 114,
}
#[allow(dead_code)]
pub mod ct_state {
pub const TRK: u32 = 0x20;
pub const NEW: u32 = 0x01;
pub const EST: u32 = 0x02;
pub const REL: u32 = 0x04;
pub const RPL: u32 = 0x08;
pub const INV: u32 = 0x10;
pub const SNAT: u32 = 0x40;
pub const DNAT: u32 = 0x80;
}
#[allow(dead_code)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct OxmHeader {
pub class: OxmClass,
pub field: u8,
pub has_mask: bool,
pub length: u8,
}
#[allow(dead_code)] impl OxmHeader {
#[must_use]
pub const fn new(class: OxmClass, field: u8, has_mask: bool, length: u8) -> Self {
Self {
class,
field,
has_mask,
length,
}
}
#[must_use]
pub const fn openflow_basic(field: OxmField, has_mask: bool, length: u8) -> Self {
Self {
class: OxmClass::OpenflowBasic,
field: field as u8,
has_mask,
length,
}
}
#[must_use]
pub const fn encode(self) -> [u8; 4] {
let class_val = self.class as u16;
let field_and_mask = (self.field << 1) | (self.has_mask as u8);
[
(class_val >> 8) as u8, class_val as u8, field_and_mask, self.length, ]
}
#[must_use]
pub const fn as_u32(self) -> u32 {
let bytes = self.encode();
u32::from_be_bytes(bytes)
}
}
#[allow(dead_code)]
#[must_use]
pub fn encode_u8(class: OxmClass, field: u8, value: u8) -> Vec<u8> {
let header = OxmHeader::new(class, field, false, 1);
let mut buf = Vec::with_capacity(5);
buf.extend_from_slice(&header.encode());
buf.push(value);
buf
}
#[allow(dead_code)]
#[must_use]
pub fn encode_u8_field(field: OxmField, value: u8) -> Vec<u8> {
encode_u8(OxmClass::OpenflowBasic, field as u8, value)
}
#[allow(dead_code)]
#[must_use]
pub fn encode_u16(class: OxmClass, field: u8, value: u16) -> Vec<u8> {
let header = OxmHeader::new(class, field, false, 2);
let mut buf = Vec::with_capacity(6);
buf.extend_from_slice(&header.encode());
buf.extend_from_slice(&value.to_be_bytes());
buf
}
#[allow(dead_code)]
#[must_use]
pub fn encode_u16_field(field: OxmField, value: u16) -> Vec<u8> {
encode_u16(OxmClass::OpenflowBasic, field as u8, value)
}
#[allow(dead_code)]
#[must_use]
pub fn encode_u16_masked(class: OxmClass, field: u8, value: u16, mask: u16) -> Vec<u8> {
let header = OxmHeader::new(class, field, true, 4);
let mut buf = Vec::with_capacity(8);
buf.extend_from_slice(&header.encode());
buf.extend_from_slice(&value.to_be_bytes());
buf.extend_from_slice(&mask.to_be_bytes());
buf
}
#[allow(dead_code)]
#[must_use]
pub fn encode_u32(class: OxmClass, field: u8, value: u32) -> Vec<u8> {
let header = OxmHeader::new(class, field, false, 4);
let mut buf = Vec::with_capacity(8);
buf.extend_from_slice(&header.encode());
buf.extend_from_slice(&value.to_be_bytes());
buf
}
#[allow(dead_code)]
#[must_use]
pub fn encode_u32_field(field: OxmField, value: u32) -> Vec<u8> {
encode_u32(OxmClass::OpenflowBasic, field as u8, value)
}
#[allow(dead_code)]
#[must_use]
pub fn encode_u64(class: OxmClass, field: u8, value: u64) -> Vec<u8> {
let header = OxmHeader::new(class, field, false, 8);
let mut buf = Vec::with_capacity(12);
buf.extend_from_slice(&header.encode());
buf.extend_from_slice(&value.to_be_bytes());
buf
}
#[allow(dead_code)]
#[must_use]
pub fn encode_u64_field(field: OxmField, value: u64) -> Vec<u8> {
encode_u64(OxmClass::OpenflowBasic, field as u8, value)
}
#[allow(dead_code)]
#[must_use]
pub fn encode_mac(class: OxmClass, field: u8, value: [u8; 6]) -> Vec<u8> {
let header = OxmHeader::new(class, field, false, 6);
let mut buf = Vec::with_capacity(10);
buf.extend_from_slice(&header.encode());
buf.extend_from_slice(&value);
buf
}
#[allow(dead_code)]
#[must_use]
pub fn encode_mac_field(field: OxmField, value: [u8; 6]) -> Vec<u8> {
encode_mac(OxmClass::OpenflowBasic, field as u8, value)
}
#[allow(dead_code)]
#[must_use]
pub fn encode_u32_masked(class: OxmClass, field: u8, value: u32, mask: u32) -> Vec<u8> {
let header = OxmHeader::new(class, field, true, 8);
let mut buf = Vec::with_capacity(12);
buf.extend_from_slice(&header.encode());
buf.extend_from_slice(&value.to_be_bytes());
buf.extend_from_slice(&mask.to_be_bytes());
buf
}
#[allow(dead_code)]
#[must_use]
pub fn encode_u32_masked_field(field: OxmField, value: u32, mask: u32) -> Vec<u8> {
encode_u32_masked(OxmClass::OpenflowBasic, field as u8, value, mask)
}
#[allow(dead_code)]
#[must_use]
pub fn encode_mac_masked(class: OxmClass, field: u8, value: [u8; 6], mask: [u8; 6]) -> Vec<u8> {
let header = OxmHeader::new(class, field, true, 12);
let mut buf = Vec::with_capacity(16);
buf.extend_from_slice(&header.encode());
buf.extend_from_slice(&value);
buf.extend_from_slice(&mask);
buf
}
#[allow(dead_code)]
#[must_use]
pub fn encode_mac_masked_field(field: OxmField, value: [u8; 6], mask: [u8; 6]) -> Vec<u8> {
encode_mac_masked(OxmClass::OpenflowBasic, field as u8, value, mask)
}
#[allow(dead_code)]
#[must_use]
pub fn encode_u64_masked(class: OxmClass, field: u8, value: u64, mask: u64) -> Vec<u8> {
let header = OxmHeader::new(class, field, true, 16);
let mut buf = Vec::with_capacity(20);
buf.extend_from_slice(&header.encode());
buf.extend_from_slice(&value.to_be_bytes());
buf.extend_from_slice(&mask.to_be_bytes());
buf
}
#[allow(dead_code)]
#[must_use]
pub fn encode_u64_masked_field(field: OxmField, value: u64, mask: u64) -> Vec<u8> {
encode_u64_masked(OxmClass::OpenflowBasic, field as u8, value, mask)
}
#[allow(dead_code, clippy::missing_panics_doc)]
#[must_use]
pub fn encode_reg(reg_num: u8, value: u32) -> Vec<u8> {
assert!(reg_num <= 15, "Register number must be 0-15");
encode_u32(OxmClass::Nxm1, reg_num, value)
}
#[allow(dead_code, clippy::missing_panics_doc)]
#[must_use]
pub fn encode_reg_masked(reg_num: u8, value: u32, mask: u32) -> Vec<u8> {
assert!(reg_num <= 15, "Register number must be 0-15");
encode_u32_masked(OxmClass::Nxm1, reg_num, value, mask)
}
#[allow(dead_code)]
#[must_use]
pub fn encode_nxm_u32(field: NxmField, value: u32) -> Vec<u8> {
encode_u32(OxmClass::Nxm1, field as u8, value)
}
#[allow(dead_code)]
#[must_use]
pub fn encode_nxm_u32_masked(field: NxmField, value: u32, mask: u32) -> Vec<u8> {
encode_u32_masked(OxmClass::Nxm1, field as u8, value, mask)
}
#[allow(dead_code)]
#[must_use]
pub fn encode_tun_id(value: u64) -> Vec<u8> {
encode_u64(OxmClass::Nxm1, NxmField::TunId as u8, value)
}
#[allow(dead_code)]
#[must_use]
pub fn encode_tun_id_masked(value: u64, mask: u64) -> Vec<u8> {
encode_u64_masked(OxmClass::Nxm1, NxmField::TunId as u8, value, mask)
}
#[allow(dead_code)]
#[must_use]
pub fn encode_tun_ipv4_src(addr: u32) -> Vec<u8> {
encode_u32(OxmClass::Nxm1, NxmField::TunIpv4Src as u8, addr)
}
#[allow(dead_code)]
#[must_use]
pub fn encode_tun_ipv4_dst(addr: u32) -> Vec<u8> {
encode_u32(OxmClass::Nxm1, NxmField::TunIpv4Dst as u8, addr)
}
#[allow(dead_code)]
#[must_use]
pub fn encode_pkt_mark(value: u32) -> Vec<u8> {
encode_u32(OxmClass::Nxm1, NxmField::PktMark as u8, value)
}
#[allow(dead_code)]
#[must_use]
pub fn encode_pkt_mark_masked(value: u32, mask: u32) -> Vec<u8> {
encode_u32_masked(OxmClass::Nxm1, NxmField::PktMark as u8, value, mask)
}
#[allow(dead_code)]
#[must_use]
pub fn encode_ct_state(state: u32) -> Vec<u8> {
encode_u32(OxmClass::Nxm1, NxmField::CtState as u8, state)
}
#[allow(dead_code)]
#[must_use]
pub fn encode_ct_state_masked(state: u32, mask: u32) -> Vec<u8> {
encode_u32_masked(OxmClass::Nxm1, NxmField::CtState as u8, state, mask)
}
#[allow(dead_code)]
#[must_use]
pub fn encode_ct_zone(zone: u16) -> Vec<u8> {
encode_u16(OxmClass::Nxm1, NxmField::CtZone as u8, zone)
}
#[allow(dead_code)]
#[must_use]
pub fn encode_ct_mark(mark: u32) -> Vec<u8> {
encode_u32(OxmClass::Nxm1, NxmField::CtMark as u8, mark)
}
#[allow(dead_code, clippy::similar_names)]
#[must_use]
pub fn encode_ct_mark_masked(mark: u32, mask: u32) -> Vec<u8> {
encode_u32_masked(OxmClass::Nxm1, NxmField::CtMark as u8, mark, mask)
}
#[allow(dead_code)]
#[must_use]
pub fn encode_ct_label(label: u128) -> Vec<u8> {
let header = OxmHeader::new(OxmClass::Nxm1, NxmField::CtLabel as u8, false, 16);
let mut buf = Vec::with_capacity(20);
buf.extend_from_slice(&header.encode());
buf.extend_from_slice(&label.to_be_bytes());
buf
}
#[allow(dead_code)]
#[must_use]
pub fn encode_ct_label_masked(label: u128, mask: u128) -> Vec<u8> {
let header = OxmHeader::new(OxmClass::Nxm1, NxmField::CtLabel as u8, true, 32);
let mut buf = Vec::with_capacity(36);
buf.extend_from_slice(&header.encode());
buf.extend_from_slice(&label.to_be_bytes());
buf.extend_from_slice(&mask.to_be_bytes());
buf
}
#[allow(dead_code)]
#[must_use]
pub fn encode_xxreg(reg_num: u8, value: u128) -> Vec<u8> {
assert!(reg_num <= 3, "Extended register number must be 0-3");
let field = NxmField::XxReg0 as u8 + reg_num;
let header = OxmHeader::new(OxmClass::Nxm1, field, false, 16);
let mut buf = Vec::with_capacity(20);
buf.extend_from_slice(&header.encode());
buf.extend_from_slice(&value.to_be_bytes());
buf
}
#[allow(dead_code)]
#[must_use]
pub fn encode_xxreg_masked(reg_num: u8, value: u128, mask: u128) -> Vec<u8> {
assert!(reg_num <= 3, "Extended register number must be 0-3");
let field = NxmField::XxReg0 as u8 + reg_num;
let header = OxmHeader::new(OxmClass::Nxm1, field, true, 32);
let mut buf = Vec::with_capacity(36);
buf.extend_from_slice(&header.encode());
buf.extend_from_slice(&value.to_be_bytes());
buf.extend_from_slice(&mask.to_be_bytes());
buf
}
#[allow(dead_code)]
#[must_use]
pub fn encode_xxreg_ipv6(reg_num: u8, addr: std::net::Ipv6Addr) -> Vec<u8> {
encode_xxreg(reg_num, u128::from(addr))
}
#[allow(dead_code)]
#[must_use]
pub fn encode_xxreg_ipv6_masked(reg_num: u8, addr: std::net::Ipv6Addr, mask: std::net::Ipv6Addr) -> Vec<u8> {
encode_xxreg_masked(reg_num, u128::from(addr), u128::from(mask))
}
#[allow(dead_code)]
#[must_use]
pub const fn prefix_to_mask_v6(prefix_len: u8) -> u128 {
if prefix_len == 0 {
0
} else if prefix_len >= 128 {
u128::MAX
} else {
u128::MAX << (128 - prefix_len)
}
}
#[allow(dead_code)]
#[must_use]
pub const fn prefix_to_mask(prefix_len: u8) -> u32 {
if prefix_len == 0 {
0
} else if prefix_len >= 32 {
0xffff_ffff
} else {
0xffff_ffff << (32 - prefix_len)
}
}
#[allow(dead_code)]
#[must_use]
pub fn encode_ipv4_prefix(field: OxmField, addr: u32, prefix_len: u8) -> Vec<u8> {
let mask = prefix_to_mask(prefix_len);
encode_u32_masked_field(field, addr & mask, mask)
}
pub trait OxmEncode {
fn encode(&self) -> Vec<u8>;
}
#[allow(dead_code)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum OxmMatchField {
InPort(u32),
InPhyPort(u32),
Metadata(u64),
MetadataMasked(u64, u64),
EthDst([u8; 6]),
EthDstMasked([u8; 6], [u8; 6]),
EthSrc([u8; 6]),
EthSrcMasked([u8; 6], [u8; 6]),
EthType(u16),
VlanVid(u16),
VlanVidMasked(u16, u16),
VlanPcp(u8),
IpDscp(u8),
IpEcn(u8),
IpProto(u8),
Ipv4Src(u32),
Ipv4SrcMasked(u32, u32),
Ipv4Dst(u32),
Ipv4DstMasked(u32, u32),
TcpSrc(u16),
TcpDst(u16),
UdpSrc(u16),
UdpDst(u16),
SctpSrc(u16),
SctpDst(u16),
Icmpv4Type(u8),
Icmpv4Code(u8),
ArpOp(u16),
ArpSpa(u32),
ArpSpaMasked(u32, u32),
ArpTpa(u32),
ArpTpaMasked(u32, u32),
ArpSha([u8; 6]),
ArpTha([u8; 6]),
TunnelId(u64),
TunnelIdMasked(u64, u64),
}
#[allow(dead_code)]
impl OxmEncode for OxmMatchField {
fn encode(&self) -> Vec<u8> {
match self {
Self::InPort(v) => encode_u32_field(OxmField::InPort, *v),
Self::InPhyPort(v) => encode_u32_field(OxmField::InPhyPort, *v),
Self::Metadata(v) => encode_u64_field(OxmField::Metadata, *v),
Self::MetadataMasked(v, m) => encode_u64_masked_field(OxmField::Metadata, *v, *m),
Self::EthDst(v) => encode_mac_field(OxmField::EthDst, *v),
Self::EthDstMasked(v, m) => encode_mac_masked_field(OxmField::EthDst, *v, *m),
Self::EthSrc(v) => encode_mac_field(OxmField::EthSrc, *v),
Self::EthSrcMasked(v, m) => encode_mac_masked_field(OxmField::EthSrc, *v, *m),
Self::EthType(v) => encode_u16_field(OxmField::EthType, *v),
Self::VlanVid(v) => encode_u16_field(OxmField::VlanVid, *v),
Self::VlanVidMasked(v, m) => {
encode_u16_masked(OxmClass::OpenflowBasic, OxmField::VlanVid as u8, *v, *m)
}
Self::VlanPcp(v) => encode_u8_field(OxmField::VlanPcp, *v),
Self::IpDscp(v) => encode_u8_field(OxmField::IpDscp, *v),
Self::IpEcn(v) => encode_u8_field(OxmField::IpEcn, *v),
Self::IpProto(v) => encode_u8_field(OxmField::IpProto, *v),
Self::Ipv4Src(v) => encode_u32_field(OxmField::Ipv4Src, *v),
Self::Ipv4SrcMasked(v, m) => encode_u32_masked_field(OxmField::Ipv4Src, *v, *m),
Self::Ipv4Dst(v) => encode_u32_field(OxmField::Ipv4Dst, *v),
Self::Ipv4DstMasked(v, m) => encode_u32_masked_field(OxmField::Ipv4Dst, *v, *m),
Self::TcpSrc(v) => encode_u16_field(OxmField::TcpSrc, *v),
Self::TcpDst(v) => encode_u16_field(OxmField::TcpDst, *v),
Self::UdpSrc(v) => encode_u16_field(OxmField::UdpSrc, *v),
Self::UdpDst(v) => encode_u16_field(OxmField::UdpDst, *v),
Self::SctpSrc(v) => encode_u16_field(OxmField::SctpSrc, *v),
Self::SctpDst(v) => encode_u16_field(OxmField::SctpDst, *v),
Self::Icmpv4Type(v) => encode_u8_field(OxmField::Icmpv4Type, *v),
Self::Icmpv4Code(v) => encode_u8_field(OxmField::Icmpv4Code, *v),
Self::ArpOp(v) => encode_u16_field(OxmField::ArpOp, *v),
Self::ArpSpa(v) => encode_u32_field(OxmField::ArpSpa, *v),
Self::ArpSpaMasked(v, m) => encode_u32_masked_field(OxmField::ArpSpa, *v, *m),
Self::ArpTpa(v) => encode_u32_field(OxmField::ArpTpa, *v),
Self::ArpTpaMasked(v, m) => encode_u32_masked_field(OxmField::ArpTpa, *v, *m),
Self::ArpSha(v) => encode_mac_field(OxmField::ArpSha, *v),
Self::ArpTha(v) => encode_mac_field(OxmField::ArpTha, *v),
Self::TunnelId(v) => encode_u64_field(OxmField::TunnelId, *v),
Self::TunnelIdMasked(v, m) => encode_u64_masked_field(OxmField::TunnelId, *v, *m),
}
}
}
#[allow(dead_code)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum NxmMatchField {
Reg(u8, u32),
RegMasked(u8, u32, u32),
XxReg(u8, u128),
XxRegMasked(u8, u128, u128),
TunId(u64),
TunIdMasked(u64, u64),
TunIpv4Src(u32),
TunIpv4Dst(u32),
PktMark(u32),
PktMarkMasked(u32, u32),
CtState(u32),
CtStateMasked(u32, u32),
CtZone(u16),
CtMark(u32),
CtMarkMasked(u32, u32),
CtLabel(u128),
CtLabelMasked(u128, u128),
}
#[allow(dead_code)]
impl OxmEncode for NxmMatchField {
fn encode(&self) -> Vec<u8> {
match self {
Self::Reg(n, v) => encode_reg(*n, *v),
Self::RegMasked(n, v, m) => encode_reg_masked(*n, *v, *m),
Self::XxReg(n, v) => encode_xxreg(*n, *v),
Self::XxRegMasked(n, v, m) => encode_xxreg_masked(*n, *v, *m),
Self::TunId(v) => encode_tun_id(*v),
Self::TunIdMasked(v, m) => encode_tun_id_masked(*v, *m),
Self::TunIpv4Src(v) => encode_tun_ipv4_src(*v),
Self::TunIpv4Dst(v) => encode_tun_ipv4_dst(*v),
Self::PktMark(v) => encode_pkt_mark(*v),
Self::PktMarkMasked(v, m) => encode_pkt_mark_masked(*v, *m),
Self::CtState(v) => encode_ct_state(*v),
Self::CtStateMasked(v, m) => encode_ct_state_masked(*v, *m),
Self::CtZone(v) => encode_ct_zone(*v),
Self::CtMark(v) => encode_ct_mark(*v),
Self::CtMarkMasked(v, m) => encode_ct_mark_masked(*v, *m),
Self::CtLabel(v) => encode_ct_label(*v),
Self::CtLabelMasked(v, m) => encode_ct_label_masked(*v, *m),
}
}
}
#[allow(dead_code)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum MatchField {
Oxm(OxmMatchField),
Nxm(NxmMatchField),
}
#[allow(dead_code)]
impl OxmEncode for MatchField {
fn encode(&self) -> Vec<u8> {
match self {
Self::Oxm(f) => f.encode(),
Self::Nxm(f) => f.encode(),
}
}
}
impl From<OxmMatchField> for MatchField {
fn from(f: OxmMatchField) -> Self {
Self::Oxm(f)
}
}
impl From<NxmMatchField> for MatchField {
fn from(f: NxmMatchField) -> Self {
Self::Nxm(f)
}
}
#[allow(dead_code)] pub fn oxm_header(class: OxmClass, field: OxmField, has_mask: bool, length: u8) -> u32 {
let class_val = class as u32;
let field_val = (field as u32) << 1;
let mask_val = if has_mask { 1u32 } else { 0u32 };
let len_val = length as u32;
(class_val << 16) | (field_val << 8) | (mask_val << 8) | len_val
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn oxm_header_in_port() {
let header = OxmHeader::openflow_basic(OxmField::InPort, false, 4);
let bytes = header.encode();
assert_eq!(bytes, [0x80, 0x00, 0x00, 0x04]);
assert_eq!(header.as_u32(), 0x8000_0004);
}
#[test]
fn oxm_header_eth_type() {
let header = OxmHeader::openflow_basic(OxmField::EthType, false, 2);
let bytes = header.encode();
assert_eq!(bytes, [0x80, 0x00, 0x0a, 0x02]);
assert_eq!(header.as_u32(), 0x8000_0a02);
}
#[test]
fn oxm_header_eth_dst_masked() {
let header = OxmHeader::openflow_basic(OxmField::EthDst, true, 12);
let bytes = header.encode();
assert_eq!(bytes, [0x80, 0x00, 0x07, 0x0c]);
assert_eq!(header.as_u32(), 0x8000_070c);
}
#[test]
fn oxm_header_ipv4_src_no_mask() {
let header = OxmHeader::openflow_basic(OxmField::Ipv4Src, false, 4);
let bytes = header.encode();
assert_eq!(bytes, [0x80, 0x00, 0x16, 0x04]);
assert_eq!(header.as_u32(), 0x8000_1604);
}
#[test]
fn oxm_header_ipv4_src_masked() {
let header = OxmHeader::openflow_basic(OxmField::Ipv4Src, true, 8);
let bytes = header.encode();
assert_eq!(bytes, [0x80, 0x00, 0x17, 0x08]);
assert_eq!(header.as_u32(), 0x8000_1708);
}
#[test]
fn oxm_header_tcp_dst() {
let header = OxmHeader::openflow_basic(OxmField::TcpDst, false, 2);
let bytes = header.encode();
assert_eq!(bytes, [0x80, 0x00, 0x1c, 0x02]);
}
#[test]
fn oxm_header_metadata_masked() {
let header = OxmHeader::openflow_basic(OxmField::Metadata, true, 16);
let bytes = header.encode();
assert_eq!(bytes, [0x80, 0x00, 0x05, 0x10]);
}
#[test]
fn oxm_header_nxm_register() {
let header = OxmHeader::new(OxmClass::Nxm1, 0, false, 4);
let bytes = header.encode();
assert_eq!(bytes, [0x00, 0x01, 0x00, 0x04]);
assert_eq!(header.as_u32(), 0x0001_0004);
}
#[test]
fn oxm_header_nxm_register_masked() {
let header = OxmHeader::new(OxmClass::Nxm1, 0, true, 8);
let bytes = header.encode();
assert_eq!(bytes, [0x00, 0x01, 0x01, 0x08]);
}
#[test]
fn oxm_header_tunnel_id() {
let header = OxmHeader::openflow_basic(OxmField::TunnelId, false, 8);
let bytes = header.encode();
assert_eq!(bytes, [0x80, 0x00, 0x4c, 0x08]);
}
#[test]
fn oxm_header_compatible_with_legacy_function() {
let header = OxmHeader::openflow_basic(OxmField::EthType, false, 2);
let legacy = oxm_header(OxmClass::OpenflowBasic, OxmField::EthType, false, 2);
assert_eq!(header.as_u32(), legacy);
}
#[test]
fn encode_u8_ip_proto_tcp() {
let bytes = encode_u8_field(OxmField::IpProto, 6);
assert_eq!(bytes.len(), 5);
assert_eq!(&bytes[0..4], &[0x80, 0x00, 0x14, 0x01]);
assert_eq!(bytes[4], 6);
}
#[test]
fn encode_u8_ip_dscp() {
let bytes = encode_u8_field(OxmField::IpDscp, 46);
assert_eq!(bytes.len(), 5);
assert_eq!(&bytes[0..4], &[0x80, 0x00, 0x10, 0x01]);
assert_eq!(bytes[4], 46);
}
#[test]
fn encode_u16_eth_type_ipv4() {
let bytes = encode_u16_field(OxmField::EthType, 0x0800);
assert_eq!(bytes.len(), 6);
assert_eq!(&bytes[0..4], &[0x80, 0x00, 0x0a, 0x02]);
assert_eq!(&bytes[4..6], &[0x08, 0x00]);
}
#[test]
fn encode_u16_tcp_dst_http() {
let bytes = encode_u16_field(OxmField::TcpDst, 80);
assert_eq!(bytes.len(), 6);
assert_eq!(&bytes[0..4], &[0x80, 0x00, 0x1c, 0x02]);
assert_eq!(&bytes[4..6], &[0x00, 0x50]);
}
#[test]
fn encode_u16_vlan_vid() {
let bytes = encode_u16_field(OxmField::VlanVid, 100);
assert_eq!(bytes.len(), 6);
assert_eq!(&bytes[0..4], &[0x80, 0x00, 0x0c, 0x02]);
assert_eq!(&bytes[4..6], &[0x00, 0x64]);
}
#[test]
fn encode_u32_in_port() {
let bytes = encode_u32_field(OxmField::InPort, 1);
assert_eq!(bytes.len(), 8);
assert_eq!(&bytes[0..4], &[0x80, 0x00, 0x00, 0x04]);
assert_eq!(&bytes[4..8], &[0x00, 0x00, 0x00, 0x01]);
}
#[test]
fn encode_u32_ipv4_src() {
let ip: u32 = (10 << 24) | (0 << 16) | (0 << 8) | 1;
let bytes = encode_u32_field(OxmField::Ipv4Src, ip);
assert_eq!(bytes.len(), 8);
assert_eq!(&bytes[0..4], &[0x80, 0x00, 0x16, 0x04]);
assert_eq!(&bytes[4..8], &[10, 0, 0, 1]);
}
#[test]
fn encode_u32_ipv4_dst() {
let ip: u32 = (192 << 24) | (168 << 16) | (1 << 8) | 1;
let bytes = encode_u32_field(OxmField::Ipv4Dst, ip);
assert_eq!(bytes.len(), 8);
assert_eq!(&bytes[0..4], &[0x80, 0x00, 0x18, 0x04]);
assert_eq!(&bytes[4..8], &[192, 168, 1, 1]);
}
#[test]
fn encode_u64_metadata() {
let bytes = encode_u64_field(OxmField::Metadata, 0x123456789ABCDEF0);
assert_eq!(bytes.len(), 12);
assert_eq!(&bytes[0..4], &[0x80, 0x00, 0x04, 0x08]);
assert_eq!(
&bytes[4..12],
&[0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0]
);
}
#[test]
fn encode_u64_tunnel_id() {
let bytes = encode_u64_field(OxmField::TunnelId, 42);
assert_eq!(bytes.len(), 12);
assert_eq!(&bytes[0..4], &[0x80, 0x00, 0x4c, 0x08]);
assert_eq!(&bytes[4..12], &[0, 0, 0, 0, 0, 0, 0, 42]);
}
#[test]
fn encode_mac_eth_src() {
let mac = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55];
let bytes = encode_mac_field(OxmField::EthSrc, mac);
assert_eq!(bytes.len(), 10);
assert_eq!(&bytes[0..4], &[0x80, 0x00, 0x08, 0x06]);
assert_eq!(&bytes[4..10], &mac);
}
#[test]
fn encode_mac_eth_dst() {
let mac = [0xff, 0xff, 0xff, 0xff, 0xff, 0xff];
let bytes = encode_mac_field(OxmField::EthDst, mac);
assert_eq!(bytes.len(), 10);
assert_eq!(&bytes[0..4], &[0x80, 0x00, 0x06, 0x06]);
assert_eq!(&bytes[4..10], &mac);
}
#[test]
fn encode_mac_arp_sha() {
let mac = [0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe];
let bytes = encode_mac_field(OxmField::ArpSha, mac);
assert_eq!(bytes.len(), 10);
assert_eq!(&bytes[0..4], &[0x80, 0x00, 0x30, 0x06]);
assert_eq!(&bytes[4..10], &mac);
}
#[test]
fn encode_nxm_register_raw() {
let bytes = encode_u32(OxmClass::Nxm1, 0, 0x12345678);
assert_eq!(bytes.len(), 8);
assert_eq!(&bytes[0..4], &[0x00, 0x01, 0x00, 0x04]);
assert_eq!(&bytes[4..8], &[0x12, 0x34, 0x56, 0x78]);
}
#[test]
fn prefix_to_mask_24() {
assert_eq!(prefix_to_mask(24), 0xffff_ff00);
}
#[test]
fn prefix_to_mask_16() {
assert_eq!(prefix_to_mask(16), 0xffff_0000);
}
#[test]
fn prefix_to_mask_8() {
assert_eq!(prefix_to_mask(8), 0xff00_0000);
}
#[test]
fn prefix_to_mask_32() {
assert_eq!(prefix_to_mask(32), 0xffff_ffff);
}
#[test]
fn prefix_to_mask_0() {
assert_eq!(prefix_to_mask(0), 0x0000_0000);
}
#[test]
fn prefix_to_mask_various() {
assert_eq!(prefix_to_mask(1), 0x8000_0000);
assert_eq!(prefix_to_mask(25), 0xffff_ff80);
assert_eq!(prefix_to_mask(30), 0xffff_fffc); assert_eq!(prefix_to_mask(31), 0xffff_fffe); }
#[test]
fn encode_u32_masked_ipv4_24() {
let addr: u32 = 10 << 24; let mask: u32 = 0xffff_ff00; let bytes = encode_u32_masked_field(OxmField::Ipv4Dst, addr, mask);
assert_eq!(bytes.len(), 12);
assert_eq!(&bytes[0..4], &[0x80, 0x00, 0x19, 0x08]);
assert_eq!(&bytes[4..8], &[10, 0, 0, 0]);
assert_eq!(&bytes[8..12], &[255, 255, 255, 0]);
}
#[test]
fn encode_u32_masked_ipv4_16() {
let addr: u32 = (192 << 24) | (168 << 16); let mask: u32 = 0xffff_0000; let bytes = encode_u32_masked_field(OxmField::Ipv4Src, addr, mask);
assert_eq!(bytes.len(), 12);
assert_eq!(&bytes[0..4], &[0x80, 0x00, 0x17, 0x08]);
assert_eq!(&bytes[4..8], &[192, 168, 0, 0]);
assert_eq!(&bytes[8..12], &[255, 255, 0, 0]);
}
#[test]
fn encode_ipv4_prefix_convenience() {
let addr: u32 = (10 << 24) | (0 << 16) | (0 << 8) | 1; let bytes = encode_ipv4_prefix(OxmField::Ipv4Dst, addr, 24);
assert_eq!(bytes.len(), 12);
assert_eq!(&bytes[4..8], &[10, 0, 0, 0]);
assert_eq!(&bytes[8..12], &[255, 255, 255, 0]);
}
#[test]
fn encode_mac_masked_oui() {
let mac = [0x00, 0x11, 0x22, 0x00, 0x00, 0x00];
let mask = [0xff, 0xff, 0xff, 0x00, 0x00, 0x00];
let bytes = encode_mac_masked_field(OxmField::EthSrc, mac, mask);
assert_eq!(bytes.len(), 16);
assert_eq!(&bytes[0..4], &[0x80, 0x00, 0x09, 0x0c]);
assert_eq!(&bytes[4..10], &mac);
assert_eq!(&bytes[10..16], &mask);
}
#[test]
fn encode_mac_masked_multicast() {
let mac = [0x01, 0x00, 0x00, 0x00, 0x00, 0x00];
let mask = [0x01, 0x00, 0x00, 0x00, 0x00, 0x00];
let bytes = encode_mac_masked_field(OxmField::EthDst, mac, mask);
assert_eq!(bytes.len(), 16);
assert_eq!(&bytes[0..4], &[0x80, 0x00, 0x07, 0x0c]);
assert_eq!(&bytes[4..10], &mac);
assert_eq!(&bytes[10..16], &mask);
}
#[test]
fn encode_u64_masked_metadata() {
let value: u64 = 0x1234_0000_0000_0000;
let mask: u64 = 0xffff_0000_0000_0000;
let bytes = encode_u64_masked_field(OxmField::Metadata, value, mask);
assert_eq!(bytes.len(), 20);
assert_eq!(&bytes[0..4], &[0x80, 0x00, 0x05, 0x10]);
assert_eq!(&bytes[4..12], &[0x12, 0x34, 0, 0, 0, 0, 0, 0]);
assert_eq!(&bytes[12..20], &[0xff, 0xff, 0, 0, 0, 0, 0, 0]);
}
#[test]
fn encode_nxm_register_masked() {
let bytes = encode_u32_masked(OxmClass::Nxm1, 0, 0xabcd_0000, 0xffff_0000);
assert_eq!(bytes.len(), 12);
assert_eq!(&bytes[0..4], &[0x00, 0x01, 0x01, 0x08]);
assert_eq!(&bytes[4..8], &[0xab, 0xcd, 0x00, 0x00]);
assert_eq!(&bytes[8..12], &[0xff, 0xff, 0x00, 0x00]);
}
#[test]
fn encode_reg_0() {
let bytes = encode_reg(0, 0x12345678);
assert_eq!(bytes.len(), 8);
assert_eq!(&bytes[0..4], &[0x00, 0x01, 0x00, 0x04]);
assert_eq!(&bytes[4..8], &[0x12, 0x34, 0x56, 0x78]);
}
#[test]
fn encode_reg_15() {
let bytes = encode_reg(15, 0xdeadbeef);
assert_eq!(bytes.len(), 8);
assert_eq!(&bytes[0..4], &[0x00, 0x01, 0x1e, 0x04]);
assert_eq!(&bytes[4..8], &[0xde, 0xad, 0xbe, 0xef]);
}
#[test]
fn encode_reg_masked_partial() {
let bytes = encode_reg_masked(5, 0x42, 0x000000ff);
assert_eq!(bytes.len(), 12);
assert_eq!(&bytes[0..4], &[0x00, 0x01, 0x0b, 0x08]);
assert_eq!(&bytes[4..8], &[0x00, 0x00, 0x00, 0x42]);
assert_eq!(&bytes[8..12], &[0x00, 0x00, 0x00, 0xff]);
}
#[test]
fn encode_tun_id_value() {
let bytes = encode_tun_id(1000);
assert_eq!(bytes.len(), 12);
assert_eq!(&bytes[0..4], &[0x00, 0x01, 0x20, 0x08]);
assert_eq!(&bytes[4..12], &[0, 0, 0, 0, 0, 0, 0x03, 0xe8]);
}
#[test]
fn encode_tun_id_masked_value() {
let bytes = encode_tun_id_masked(0x12345678_00000000, 0xffffffff_00000000);
assert_eq!(bytes.len(), 20);
assert_eq!(&bytes[0..4], &[0x00, 0x01, 0x21, 0x10]);
}
#[test]
fn encode_tun_ipv4_src_value() {
let addr: u32 = (10 << 24) | 1;
let bytes = encode_tun_ipv4_src(addr);
assert_eq!(bytes.len(), 8);
assert_eq!(&bytes[0..4], &[0x00, 0x01, 0x3e, 0x04]);
assert_eq!(&bytes[4..8], &[10, 0, 0, 1]);
}
#[test]
fn encode_tun_ipv4_dst_value() {
let addr: u32 = (192 << 24) | (168 << 16) | (1 << 8) | 1;
let bytes = encode_tun_ipv4_dst(addr);
assert_eq!(bytes.len(), 8);
assert_eq!(&bytes[0..4], &[0x00, 0x01, 0x40, 0x04]);
assert_eq!(&bytes[4..8], &[192, 168, 1, 1]);
}
#[test]
fn encode_pkt_mark_value() {
let bytes = encode_pkt_mark(0x100);
assert_eq!(bytes.len(), 8);
assert_eq!(&bytes[0..4], &[0x00, 0x01, 0x42, 0x04]);
assert_eq!(&bytes[4..8], &[0x00, 0x00, 0x01, 0x00]);
}
#[test]
fn encode_pkt_mark_masked_value() {
let bytes = encode_pkt_mark_masked(0xff00, 0xff00);
assert_eq!(bytes.len(), 12);
assert_eq!(&bytes[0..4], &[0x00, 0x01, 0x43, 0x08]);
}
#[test]
fn encode_ct_state_tracked_established() {
let state = ct_state::TRK | ct_state::EST;
let bytes = encode_ct_state(state);
assert_eq!(bytes.len(), 8);
assert_eq!(&bytes[0..4], &[0x00, 0x01, 0xd2, 0x04]);
assert_eq!(&bytes[4..8], &[0x00, 0x00, 0x00, 0x22]);
}
#[test]
fn encode_ct_state_masked_new() {
let state = ct_state::TRK | ct_state::NEW;
let mask = ct_state::TRK | ct_state::NEW;
let bytes = encode_ct_state_masked(state, mask);
assert_eq!(bytes.len(), 12);
assert_eq!(&bytes[0..4], &[0x00, 0x01, 0xd3, 0x08]);
assert_eq!(&bytes[4..8], &[0x00, 0x00, 0x00, 0x21]);
assert_eq!(&bytes[8..12], &[0x00, 0x00, 0x00, 0x21]);
}
#[test]
fn encode_ct_zone_value() {
let bytes = encode_ct_zone(100);
assert_eq!(bytes.len(), 6);
assert_eq!(&bytes[0..4], &[0x00, 0x01, 0xd4, 0x02]);
assert_eq!(&bytes[4..6], &[0x00, 0x64]);
}
#[test]
fn encode_ct_mark_value() {
let bytes = encode_ct_mark(0xaabbccdd);
assert_eq!(bytes.len(), 8);
assert_eq!(&bytes[0..4], &[0x00, 0x01, 0xd6, 0x04]);
assert_eq!(&bytes[4..8], &[0xaa, 0xbb, 0xcc, 0xdd]);
}
#[test]
fn encode_ct_mark_masked_value() {
let bytes = encode_ct_mark_masked(0xff000000, 0xff000000);
assert_eq!(bytes.len(), 12);
assert_eq!(&bytes[0..4], &[0x00, 0x01, 0xd7, 0x08]);
assert_eq!(&bytes[4..8], &[0xff, 0x00, 0x00, 0x00]);
assert_eq!(&bytes[8..12], &[0xff, 0x00, 0x00, 0x00]);
}
#[test]
fn encode_ct_label_value() {
let label: u128 = 0x123456789abcdef0_123456789abcdef0;
let bytes = encode_ct_label(label);
assert_eq!(bytes.len(), 20);
assert_eq!(&bytes[0..4], &[0x00, 0x01, 0xd8, 0x10]);
assert_eq!(
&bytes[4..20],
&[
0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc,
0xde, 0xf0
]
);
}
#[test]
fn encode_ct_label_masked_value() {
let label: u128 = 0xdeadbeef_00000000_00000000_00000000;
let mask: u128 = 0xffffffff_00000000_00000000_00000000;
let bytes = encode_ct_label_masked(label, mask);
assert_eq!(bytes.len(), 36);
assert_eq!(&bytes[0..4], &[0x00, 0x01, 0xd9, 0x20]);
}
#[test]
fn encode_nxm_u32_via_enum() {
let bytes = encode_nxm_u32(NxmField::PktMark, 0x42);
assert_eq!(bytes.len(), 8);
assert_eq!(&bytes[0..4], &[0x00, 0x01, 0x42, 0x04]);
assert_eq!(&bytes[4..8], &[0x00, 0x00, 0x00, 0x42]);
}
#[test]
fn ct_state_flags_correct() {
assert_eq!(ct_state::NEW, 0x01);
assert_eq!(ct_state::EST, 0x02);
assert_eq!(ct_state::REL, 0x04);
assert_eq!(ct_state::RPL, 0x08);
assert_eq!(ct_state::INV, 0x10);
assert_eq!(ct_state::TRK, 0x20);
assert_eq!(ct_state::SNAT, 0x40);
assert_eq!(ct_state::DNAT, 0x80);
}
#[test]
fn encode_xxreg_0() {
let value: u128 = 0x0123456789abcdef_fedcba9876543210;
let bytes = encode_xxreg(0, value);
assert_eq!(bytes.len(), 20);
assert_eq!(&bytes[0..4], &[0x00, 0x01, 0xde, 0x10]);
assert_eq!(
&bytes[4..20],
&[
0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54,
0x32, 0x10
]
);
}
#[test]
fn encode_xxreg_3() {
let value: u128 = 0xdeadbeef_cafebabe_12345678_9abcdef0;
let bytes = encode_xxreg(3, value);
assert_eq!(bytes.len(), 20);
assert_eq!(&bytes[0..4], &[0x00, 0x01, 0xe4, 0x10]);
}
#[test]
fn encode_xxreg_masked_high64() {
let value: u128 = 0xaabbccdd_eeff0011_00000000_00000000;
let mask: u128 = 0xffffffff_ffffffff_00000000_00000000;
let bytes = encode_xxreg_masked(1, value, mask);
assert_eq!(bytes.len(), 36);
assert_eq!(&bytes[0..4], &[0x00, 0x01, 0xe1, 0x20]);
}
#[test]
fn encode_xxreg_ipv6_address() {
use std::net::Ipv6Addr;
let addr: Ipv6Addr = "2001:db8::1".parse().unwrap();
let bytes = encode_xxreg_ipv6(0, addr);
assert_eq!(bytes.len(), 20);
assert_eq!(&bytes[0..4], &[0x00, 0x01, 0xde, 0x10]);
assert_eq!(
&bytes[4..20],
&[0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01]
);
}
#[test]
fn encode_xxreg_ipv6_masked_prefix() {
use std::net::Ipv6Addr;
let addr: Ipv6Addr = "2001:db8::".parse().unwrap();
let mask: Ipv6Addr = "ffff:ffff::".parse().unwrap();
let bytes = encode_xxreg_ipv6_masked(2, addr, mask);
assert_eq!(bytes.len(), 36);
assert_eq!(&bytes[0..4], &[0x00, 0x01, 0xe3, 0x20]);
}
#[test]
fn prefix_to_mask_v6_64() {
assert_eq!(
prefix_to_mask_v6(64),
0xffffffff_ffffffff_00000000_00000000
);
}
#[test]
fn prefix_to_mask_v6_128() {
assert_eq!(prefix_to_mask_v6(128), u128::MAX);
}
#[test]
fn prefix_to_mask_v6_0() {
assert_eq!(prefix_to_mask_v6(0), 0);
}
#[test]
fn prefix_to_mask_v6_various() {
assert_eq!(prefix_to_mask_v6(32), 0xffffffff_00000000_00000000_00000000);
assert_eq!(prefix_to_mask_v6(48), 0xffffffff_ffff0000_00000000_00000000);
assert_eq!(
prefix_to_mask_v6(96),
0xffffffff_ffffffff_ffffffff_00000000
);
assert_eq!(
prefix_to_mask_v6(120),
0xffffffff_ffffffff_ffffffff_ffffff00
);
}
#[test]
fn xxreg_field_numbers() {
assert_eq!(NxmField::XxReg0 as u8, 111);
assert_eq!(NxmField::XxReg1 as u8, 112);
assert_eq!(NxmField::XxReg2 as u8, 113);
assert_eq!(NxmField::XxReg3 as u8, 114);
}
#[test]
fn trait_oxm_in_port() {
let field = OxmMatchField::InPort(1);
let bytes = field.encode();
assert_eq!(bytes, encode_u32_field(OxmField::InPort, 1));
assert_eq!(bytes.len(), 8);
assert_eq!(&bytes[0..4], &[0x80, 0x00, 0x00, 0x04]);
}
#[test]
fn trait_oxm_eth_type() {
let field = OxmMatchField::EthType(0x0800);
let bytes = field.encode();
assert_eq!(bytes, encode_u16_field(OxmField::EthType, 0x0800));
assert_eq!(bytes.len(), 6);
}
#[test]
fn trait_oxm_ipv4_src_masked() {
let field = OxmMatchField::Ipv4SrcMasked(0x0a000000, 0xffffff00);
let bytes = field.encode();
assert_eq!(
bytes,
encode_u32_masked_field(OxmField::Ipv4Src, 0x0a000000, 0xffffff00)
);
assert_eq!(bytes.len(), 12);
}
#[test]
fn trait_oxm_eth_dst() {
let mac = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55];
let field = OxmMatchField::EthDst(mac);
let bytes = field.encode();
assert_eq!(bytes, encode_mac_field(OxmField::EthDst, mac));
assert_eq!(bytes.len(), 10);
}
#[test]
fn trait_oxm_eth_src_masked() {
let mac = [0x00, 0x11, 0x22, 0x00, 0x00, 0x00];
let mask = [0xff, 0xff, 0xff, 0x00, 0x00, 0x00];
let field = OxmMatchField::EthSrcMasked(mac, mask);
let bytes = field.encode();
assert_eq!(bytes, encode_mac_masked_field(OxmField::EthSrc, mac, mask));
assert_eq!(bytes.len(), 16);
}
#[test]
fn trait_oxm_tcp_dst() {
let field = OxmMatchField::TcpDst(80);
let bytes = field.encode();
assert_eq!(bytes, encode_u16_field(OxmField::TcpDst, 80));
}
#[test]
fn trait_oxm_ip_proto() {
let field = OxmMatchField::IpProto(6); let bytes = field.encode();
assert_eq!(bytes, encode_u8_field(OxmField::IpProto, 6));
assert_eq!(bytes.len(), 5);
}
#[test]
fn trait_nxm_reg() {
let field = NxmMatchField::Reg(0, 0x12345678);
let bytes = field.encode();
assert_eq!(bytes, encode_reg(0, 0x12345678));
assert_eq!(bytes.len(), 8);
}
#[test]
fn trait_nxm_reg_masked() {
let field = NxmMatchField::RegMasked(5, 0xff00, 0xff00);
let bytes = field.encode();
assert_eq!(bytes, encode_reg_masked(5, 0xff00, 0xff00));
assert_eq!(bytes.len(), 12);
}
#[test]
fn trait_nxm_tun_id() {
let field = NxmMatchField::TunId(1000);
let bytes = field.encode();
assert_eq!(bytes, encode_tun_id(1000));
assert_eq!(bytes.len(), 12);
}
#[test]
fn trait_nxm_ct_state() {
let state = ct_state::TRK | ct_state::EST;
let field = NxmMatchField::CtState(state);
let bytes = field.encode();
assert_eq!(bytes, encode_ct_state(state));
}
#[test]
fn trait_nxm_ct_zone() {
let field = NxmMatchField::CtZone(100);
let bytes = field.encode();
assert_eq!(bytes, encode_ct_zone(100));
assert_eq!(bytes.len(), 6);
}
#[test]
fn trait_nxm_xxreg() {
let field = NxmMatchField::XxReg(0, 0x123456789abcdef0_fedcba9876543210);
let bytes = field.encode();
assert_eq!(
bytes,
encode_xxreg(0, 0x123456789abcdef0_fedcba9876543210)
);
assert_eq!(bytes.len(), 20);
}
#[test]
fn trait_unified_match_field_oxm() {
let field: MatchField = OxmMatchField::EthType(0x0800).into();
let bytes = field.encode();
assert_eq!(bytes, encode_u16_field(OxmField::EthType, 0x0800));
}
#[test]
fn trait_unified_match_field_nxm() {
let field: MatchField = NxmMatchField::Reg(0, 42).into();
let bytes = field.encode();
assert_eq!(bytes, encode_reg(0, 42));
}
#[test]
fn trait_match_list_encoding() {
let fields: Vec<MatchField> = vec![
OxmMatchField::EthType(0x0800).into(),
OxmMatchField::IpProto(6).into(),
OxmMatchField::TcpDst(80).into(),
NxmMatchField::Reg(0, 1).into(),
];
let mut encoded = Vec::new();
for field in &fields {
encoded.extend(field.encode());
}
assert_eq!(encoded.len(), 25);
}
#[test]
fn trait_oxm_vlan_vid_masked() {
let field = OxmMatchField::VlanVidMasked(0x1064, 0x1fff); let bytes = field.encode();
assert_eq!(bytes.len(), 8);
assert_eq!(&bytes[0..4], &[0x80, 0x00, 0x0d, 0x04]);
}
#[test]
fn trait_oxm_metadata_masked() {
let field = OxmMatchField::MetadataMasked(0xff00_0000_0000_0000, 0xff00_0000_0000_0000);
let bytes = field.encode();
assert_eq!(bytes.len(), 20);
}
#[test]
fn trait_nxm_ct_label() {
let label: u128 = 0x12345678_9abcdef0_12345678_9abcdef0;
let field = NxmMatchField::CtLabel(label);
let bytes = field.encode();
assert_eq!(bytes, encode_ct_label(label));
assert_eq!(bytes.len(), 20);
}
}