Skip to main content

rovs_openflow/
oxm.rs

1//! `OpenFlow` Extensible Match (OXM) encoding.
2//!
3//! OXM is used in `OpenFlow` 1.2+ for flexible match field encoding.
4//!
5//! # Wire Format
6//!
7//! The OXM header is 4 bytes in network byte order:
8//! ```text
9//!  0                   1                   2                   3
10//!  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
11//! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
12//! |           class             |field|M|        length           |
13//! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
14//! ```
15//!
16//! - `class`: 16 bits - OXM class (e.g., OpenFlow Basic = 0x8000)
17//! - `field`: 7 bits - Field identifier within the class
18//! - `M` (hasmask): 1 bit - Whether a mask follows the value
19//! - `length`: 8 bits - Length of value bytes (doubled if masked)
20
21/// OXM class identifiers.
22#[allow(dead_code)] // Reserved for future OpenFlow implementation
23#[derive(Debug, Clone, Copy, PartialEq, Eq)]
24#[repr(u16)]
25pub enum OxmClass {
26    /// Basic OpenFlow match fields
27    OpenflowBasic = 0x8000,
28    /// Experimenter match fields
29    Experimenter = 0xffff,
30    /// Nicira extensions
31    Nxm0 = 0x0000,
32    /// Nicira extensions (class 1)
33    Nxm1 = 0x0001,
34}
35
36/// OXM field identifiers for `OpenFlow` Basic class.
37#[allow(dead_code)] // Reserved for future OpenFlow implementation
38#[derive(Debug, Clone, Copy, PartialEq, Eq)]
39#[repr(u8)]
40pub enum OxmField {
41    /// Input port
42    InPort = 0,
43    /// Physical input port
44    InPhyPort = 1,
45    /// Metadata
46    Metadata = 2,
47    /// Ethernet destination
48    EthDst = 3,
49    /// Ethernet source
50    EthSrc = 4,
51    /// Ethernet type
52    EthType = 5,
53    /// VLAN ID
54    VlanVid = 6,
55    /// VLAN PCP
56    VlanPcp = 7,
57    /// IP DSCP
58    IpDscp = 8,
59    /// IP ECN
60    IpEcn = 9,
61    /// IP protocol
62    IpProto = 10,
63    /// IPv4 source
64    Ipv4Src = 11,
65    /// IPv4 destination
66    Ipv4Dst = 12,
67    /// TCP source port
68    TcpSrc = 13,
69    /// TCP destination port
70    TcpDst = 14,
71    /// UDP source port
72    UdpSrc = 15,
73    /// UDP destination port
74    UdpDst = 16,
75    /// SCTP source port
76    SctpSrc = 17,
77    /// SCTP destination port
78    SctpDst = 18,
79    /// ICMP type
80    Icmpv4Type = 19,
81    /// ICMP code
82    Icmpv4Code = 20,
83    /// ARP opcode
84    ArpOp = 21,
85    /// ARP source IPv4
86    ArpSpa = 22,
87    /// ARP target IPv4
88    ArpTpa = 23,
89    /// ARP source MAC
90    ArpSha = 24,
91    /// ARP target MAC
92    ArpTha = 25,
93    /// IPv6 source
94    Ipv6Src = 26,
95    /// IPv6 destination
96    Ipv6Dst = 27,
97    /// IPv6 flow label
98    Ipv6Flabel = 28,
99    /// ICMPv6 type
100    Icmpv6Type = 29,
101    /// ICMPv6 code
102    Icmpv6Code = 30,
103    /// Tunnel ID
104    TunnelId = 38,
105}
106
107// =============================================================================
108// NXM Field Identifiers (Phase 1.4)
109// =============================================================================
110
111/// NXM (Nicira Extended Match) field identifiers.
112///
113/// NXM fields use classes `Nxm0` (0x0000) for OpenFlow-compatible fields
114/// and `Nxm1` (0x0001) for Nicira extensions.
115#[allow(dead_code)]
116#[derive(Debug, Clone, Copy, PartialEq, Eq)]
117#[repr(u8)]
118pub enum NxmField {
119    // -------------------------------------------------------------------------
120    // Registers (class 0x0001, fields 0-15, 4 bytes each)
121    // -------------------------------------------------------------------------
122    /// General purpose register 0
123    Reg0 = 0,
124    /// General purpose register 1
125    Reg1 = 1,
126    /// General purpose register 2
127    Reg2 = 2,
128    /// General purpose register 3
129    Reg3 = 3,
130    /// General purpose register 4
131    Reg4 = 4,
132    /// General purpose register 5
133    Reg5 = 5,
134    /// General purpose register 6
135    Reg6 = 6,
136    /// General purpose register 7
137    Reg7 = 7,
138    /// General purpose register 8
139    Reg8 = 8,
140    /// General purpose register 9
141    Reg9 = 9,
142    /// General purpose register 10
143    Reg10 = 10,
144    /// General purpose register 11
145    Reg11 = 11,
146    /// General purpose register 12
147    Reg12 = 12,
148    /// General purpose register 13
149    Reg13 = 13,
150    /// General purpose register 14
151    Reg14 = 14,
152    /// General purpose register 15
153    Reg15 = 15,
154
155    // -------------------------------------------------------------------------
156    // Tunnel fields (class 0x0001)
157    // -------------------------------------------------------------------------
158    /// Tunnel ID (field 16, 8 bytes)
159    TunId = 16,
160    /// Tunnel IPv4 source address (field 31, 4 bytes)
161    TunIpv4Src = 31,
162    /// Tunnel IPv4 destination address (field 32, 4 bytes)
163    TunIpv4Dst = 32,
164    /// Packet mark (field 33, 4 bytes)
165    PktMark = 33,
166
167    // -------------------------------------------------------------------------
168    // Connection tracking fields (class 0x0001)
169    // -------------------------------------------------------------------------
170    /// Connection tracking state (field 105, 4 bytes)
171    CtState = 105,
172    /// Connection tracking zone (field 106, 2 bytes)
173    CtZone = 106,
174    /// Connection tracking mark (field 107, 4 bytes)
175    CtMark = 107,
176    /// Connection tracking label (field 108, 16 bytes)
177    CtLabel = 108,
178
179    // -------------------------------------------------------------------------
180    // Extended registers (class 0x0001, fields 111-114, 16 bytes each)
181    // -------------------------------------------------------------------------
182    /// 128-bit extended register 0
183    XxReg0 = 111,
184    /// 128-bit extended register 1
185    XxReg1 = 112,
186    /// 128-bit extended register 2
187    XxReg2 = 113,
188    /// 128-bit extended register 3
189    XxReg3 = 114,
190}
191
192/// Connection tracking state flags for `NXM_NX_CT_STATE`.
193///
194/// These flags can be combined with bitwise OR.
195#[allow(dead_code)]
196pub mod ct_state {
197    /// Packet is tracked (ct has been executed)
198    pub const TRK: u32 = 0x20;
199    /// New connection
200    pub const NEW: u32 = 0x01;
201    /// Established connection
202    pub const EST: u32 = 0x02;
203    /// Related connection (e.g., FTP data)
204    pub const REL: u32 = 0x04;
205    /// Reply direction
206    pub const RPL: u32 = 0x08;
207    /// Invalid connection
208    pub const INV: u32 = 0x10;
209    /// Source NAT applied
210    pub const SNAT: u32 = 0x40;
211    /// Destination NAT applied
212    pub const DNAT: u32 = 0x80;
213}
214
215/// OXM header containing class, field, mask flag, and length.
216///
217/// This struct represents the 4-byte header that precedes OXM field values
218/// in OpenFlow 1.2+ match encoding.
219#[allow(dead_code)] // Foundation for OpenFlow wire encoding (Phase 1)
220#[derive(Debug, Clone, Copy, PartialEq, Eq)]
221pub struct OxmHeader {
222    /// OXM class (e.g., `OpenflowBasic`, `Nxm0`, `Nxm1`)
223    pub class: OxmClass,
224    /// Field identifier within the class (7 bits, 0-127)
225    pub field: u8,
226    /// Whether this field has a mask following the value
227    pub has_mask: bool,
228    /// Length of the value bytes (or value + mask if `has_mask` is true)
229    pub length: u8,
230}
231
232#[allow(dead_code)] // Foundation for OpenFlow wire encoding (Phase 1)
233impl OxmHeader {
234    /// Create a new OXM header with raw field value.
235    #[must_use]
236    pub const fn new(class: OxmClass, field: u8, has_mask: bool, length: u8) -> Self {
237        Self {
238            class,
239            field,
240            has_mask,
241            length,
242        }
243    }
244
245    /// Create an OXM header for an OpenFlow Basic field.
246    #[must_use]
247    pub const fn openflow_basic(field: OxmField, has_mask: bool, length: u8) -> Self {
248        Self {
249            class: OxmClass::OpenflowBasic,
250            field: field as u8,
251            has_mask,
252            length,
253        }
254    }
255
256    /// Encode the header to 4 bytes in network byte order.
257    ///
258    /// # Wire Format
259    ///
260    /// ```text
261    /// Byte 0: class high byte
262    /// Byte 1: class low byte
263    /// Byte 2: (field << 1) | has_mask
264    /// Byte 3: length
265    /// ```
266    #[must_use]
267    pub const fn encode(self) -> [u8; 4] {
268        let class_val = self.class as u16;
269        let field_and_mask = (self.field << 1) | (self.has_mask as u8);
270
271        [
272            (class_val >> 8) as u8, // class high byte
273            class_val as u8,        // class low byte
274            field_and_mask,         // field (7 bits) | hasmask (1 bit)
275            self.length,            // length
276        ]
277    }
278
279    /// Get the encoded header as a big-endian u32.
280    #[must_use]
281    pub const fn as_u32(self) -> u32 {
282        let bytes = self.encode();
283        u32::from_be_bytes(bytes)
284    }
285}
286
287// =============================================================================
288// Fixed-Size Field Encoding (Phase 1.2)
289// =============================================================================
290
291/// Encode a 1-byte OXM field value (e.g., `IpProto`, `IpDscp`, `IpEcn`).
292///
293/// Returns 5 bytes: 4-byte header + 1-byte value.
294#[allow(dead_code)]
295#[must_use]
296pub fn encode_u8(class: OxmClass, field: u8, value: u8) -> Vec<u8> {
297    let header = OxmHeader::new(class, field, false, 1);
298    let mut buf = Vec::with_capacity(5);
299    buf.extend_from_slice(&header.encode());
300    buf.push(value);
301    buf
302}
303
304/// Encode a 1-byte OpenFlow Basic field value.
305#[allow(dead_code)]
306#[must_use]
307pub fn encode_u8_field(field: OxmField, value: u8) -> Vec<u8> {
308    encode_u8(OxmClass::OpenflowBasic, field as u8, value)
309}
310
311/// Encode a 2-byte OXM field value (e.g., `EthType`, `TcpSrc`, `TcpDst`, `VlanVid`).
312///
313/// Returns 6 bytes: 4-byte header + 2-byte value (big-endian).
314#[allow(dead_code)]
315#[must_use]
316pub fn encode_u16(class: OxmClass, field: u8, value: u16) -> Vec<u8> {
317    let header = OxmHeader::new(class, field, false, 2);
318    let mut buf = Vec::with_capacity(6);
319    buf.extend_from_slice(&header.encode());
320    buf.extend_from_slice(&value.to_be_bytes());
321    buf
322}
323
324/// Encode a 2-byte OpenFlow Basic field value.
325#[allow(dead_code)]
326#[must_use]
327pub fn encode_u16_field(field: OxmField, value: u16) -> Vec<u8> {
328    encode_u16(OxmClass::OpenflowBasic, field as u8, value)
329}
330
331/// Encode a masked 2-byte OXM field value (e.g., `VlanVid` with mask).
332///
333/// Returns 8 bytes: 4-byte header + 2-byte value + 2-byte mask (big-endian).
334#[allow(dead_code)]
335#[must_use]
336pub fn encode_u16_masked(class: OxmClass, field: u8, value: u16, mask: u16) -> Vec<u8> {
337    let header = OxmHeader::new(class, field, true, 4);
338    let mut buf = Vec::with_capacity(8);
339    buf.extend_from_slice(&header.encode());
340    buf.extend_from_slice(&value.to_be_bytes());
341    buf.extend_from_slice(&mask.to_be_bytes());
342    buf
343}
344
345/// Encode a 4-byte OXM field value (e.g., `InPort`, `Ipv4Src`, `Ipv4Dst`).
346///
347/// Returns 8 bytes: 4-byte header + 4-byte value (big-endian).
348#[allow(dead_code)]
349#[must_use]
350pub fn encode_u32(class: OxmClass, field: u8, value: u32) -> Vec<u8> {
351    let header = OxmHeader::new(class, field, false, 4);
352    let mut buf = Vec::with_capacity(8);
353    buf.extend_from_slice(&header.encode());
354    buf.extend_from_slice(&value.to_be_bytes());
355    buf
356}
357
358/// Encode a 4-byte OpenFlow Basic field value.
359#[allow(dead_code)]
360#[must_use]
361pub fn encode_u32_field(field: OxmField, value: u32) -> Vec<u8> {
362    encode_u32(OxmClass::OpenflowBasic, field as u8, value)
363}
364
365/// Encode an 8-byte OXM field value (e.g., `Metadata`, `TunnelId`).
366///
367/// Returns 12 bytes: 4-byte header + 8-byte value (big-endian).
368#[allow(dead_code)]
369#[must_use]
370pub fn encode_u64(class: OxmClass, field: u8, value: u64) -> Vec<u8> {
371    let header = OxmHeader::new(class, field, false, 8);
372    let mut buf = Vec::with_capacity(12);
373    buf.extend_from_slice(&header.encode());
374    buf.extend_from_slice(&value.to_be_bytes());
375    buf
376}
377
378/// Encode an 8-byte OpenFlow Basic field value.
379#[allow(dead_code)]
380#[must_use]
381pub fn encode_u64_field(field: OxmField, value: u64) -> Vec<u8> {
382    encode_u64(OxmClass::OpenflowBasic, field as u8, value)
383}
384
385/// Encode a 6-byte MAC address OXM field (e.g., `EthSrc`, `EthDst`).
386///
387/// Returns 10 bytes: 4-byte header + 6-byte MAC address.
388#[allow(dead_code)]
389#[must_use]
390pub fn encode_mac(class: OxmClass, field: u8, value: [u8; 6]) -> Vec<u8> {
391    let header = OxmHeader::new(class, field, false, 6);
392    let mut buf = Vec::with_capacity(10);
393    buf.extend_from_slice(&header.encode());
394    buf.extend_from_slice(&value);
395    buf
396}
397
398/// Encode a 6-byte MAC address OpenFlow Basic field.
399#[allow(dead_code)]
400#[must_use]
401pub fn encode_mac_field(field: OxmField, value: [u8; 6]) -> Vec<u8> {
402    encode_mac(OxmClass::OpenflowBasic, field as u8, value)
403}
404
405// =============================================================================
406// Masked Field Encoding (Phase 1.3)
407// =============================================================================
408
409/// Encode a masked 4-byte OXM field value (e.g., `Ipv4Src/Dst` with subnet).
410///
411/// Returns 12 bytes: 4-byte header + 4-byte value + 4-byte mask.
412/// The header length field is set to 8 (value + mask bytes).
413#[allow(dead_code)]
414#[must_use]
415pub fn encode_u32_masked(class: OxmClass, field: u8, value: u32, mask: u32) -> Vec<u8> {
416    let header = OxmHeader::new(class, field, true, 8);
417    let mut buf = Vec::with_capacity(12);
418    buf.extend_from_slice(&header.encode());
419    buf.extend_from_slice(&value.to_be_bytes());
420    buf.extend_from_slice(&mask.to_be_bytes());
421    buf
422}
423
424/// Encode a masked 4-byte OpenFlow Basic field value.
425#[allow(dead_code)]
426#[must_use]
427pub fn encode_u32_masked_field(field: OxmField, value: u32, mask: u32) -> Vec<u8> {
428    encode_u32_masked(OxmClass::OpenflowBasic, field as u8, value, mask)
429}
430
431/// Encode a masked 6-byte MAC address OXM field.
432///
433/// Returns 16 bytes: 4-byte header + 6-byte MAC + 6-byte mask.
434/// The header length field is set to 12 (value + mask bytes).
435#[allow(dead_code)]
436#[must_use]
437pub fn encode_mac_masked(class: OxmClass, field: u8, value: [u8; 6], mask: [u8; 6]) -> Vec<u8> {
438    let header = OxmHeader::new(class, field, true, 12);
439    let mut buf = Vec::with_capacity(16);
440    buf.extend_from_slice(&header.encode());
441    buf.extend_from_slice(&value);
442    buf.extend_from_slice(&mask);
443    buf
444}
445
446/// Encode a masked 6-byte MAC address OpenFlow Basic field.
447#[allow(dead_code)]
448#[must_use]
449pub fn encode_mac_masked_field(field: OxmField, value: [u8; 6], mask: [u8; 6]) -> Vec<u8> {
450    encode_mac_masked(OxmClass::OpenflowBasic, field as u8, value, mask)
451}
452
453/// Encode a masked 8-byte OXM field value (e.g., `Metadata`, `TunnelId`).
454///
455/// Returns 20 bytes: 4-byte header + 8-byte value + 8-byte mask.
456/// The header length field is set to 16 (value + mask bytes).
457#[allow(dead_code)]
458#[must_use]
459pub fn encode_u64_masked(class: OxmClass, field: u8, value: u64, mask: u64) -> Vec<u8> {
460    let header = OxmHeader::new(class, field, true, 16);
461    let mut buf = Vec::with_capacity(20);
462    buf.extend_from_slice(&header.encode());
463    buf.extend_from_slice(&value.to_be_bytes());
464    buf.extend_from_slice(&mask.to_be_bytes());
465    buf
466}
467
468/// Encode a masked 8-byte OpenFlow Basic field value.
469#[allow(dead_code)]
470#[must_use]
471pub fn encode_u64_masked_field(field: OxmField, value: u64, mask: u64) -> Vec<u8> {
472    encode_u64_masked(OxmClass::OpenflowBasic, field as u8, value, mask)
473}
474
475// =============================================================================
476// NXM Field Encoding (Phase 1.4)
477// =============================================================================
478
479/// Encode an NXM register value (REG0-REG15).
480///
481/// Returns 8 bytes: 4-byte header + 4-byte value.
482/// All registers use class `Nxm1` (0x0001).
483#[allow(dead_code, clippy::missing_panics_doc)]
484#[must_use]
485pub fn encode_reg(reg_num: u8, value: u32) -> Vec<u8> {
486    assert!(reg_num <= 15, "Register number must be 0-15");
487    encode_u32(OxmClass::Nxm1, reg_num, value)
488}
489
490/// Encode a masked NXM register value.
491///
492/// Returns 12 bytes: 4-byte header + 4-byte value + 4-byte mask.
493#[allow(dead_code, clippy::missing_panics_doc)]
494#[must_use]
495pub fn encode_reg_masked(reg_num: u8, value: u32, mask: u32) -> Vec<u8> {
496    assert!(reg_num <= 15, "Register number must be 0-15");
497    encode_u32_masked(OxmClass::Nxm1, reg_num, value, mask)
498}
499
500/// Encode an NXM field value using the `NxmField` enum.
501#[allow(dead_code)]
502#[must_use]
503pub fn encode_nxm_u32(field: NxmField, value: u32) -> Vec<u8> {
504    encode_u32(OxmClass::Nxm1, field as u8, value)
505}
506
507/// Encode a masked NXM field value using the `NxmField` enum.
508#[allow(dead_code)]
509#[must_use]
510pub fn encode_nxm_u32_masked(field: NxmField, value: u32, mask: u32) -> Vec<u8> {
511    encode_u32_masked(OxmClass::Nxm1, field as u8, value, mask)
512}
513
514/// Encode NXM tunnel ID (`NXM_NX_TUN_ID`).
515///
516/// Returns 12 bytes: 4-byte header + 8-byte value.
517#[allow(dead_code)]
518#[must_use]
519pub fn encode_tun_id(value: u64) -> Vec<u8> {
520    encode_u64(OxmClass::Nxm1, NxmField::TunId as u8, value)
521}
522
523/// Encode masked NXM tunnel ID.
524///
525/// Returns 20 bytes: 4-byte header + 8-byte value + 8-byte mask.
526#[allow(dead_code)]
527#[must_use]
528pub fn encode_tun_id_masked(value: u64, mask: u64) -> Vec<u8> {
529    encode_u64_masked(OxmClass::Nxm1, NxmField::TunId as u8, value, mask)
530}
531
532/// Encode NXM tunnel IPv4 source address (`NXM_NX_TUN_IPV4_SRC`).
533///
534/// Returns 8 bytes: 4-byte header + 4-byte IPv4 address.
535#[allow(dead_code)]
536#[must_use]
537pub fn encode_tun_ipv4_src(addr: u32) -> Vec<u8> {
538    encode_u32(OxmClass::Nxm1, NxmField::TunIpv4Src as u8, addr)
539}
540
541/// Encode NXM tunnel IPv4 destination address (`NXM_NX_TUN_IPV4_DST`).
542///
543/// Returns 8 bytes: 4-byte header + 4-byte IPv4 address.
544#[allow(dead_code)]
545#[must_use]
546pub fn encode_tun_ipv4_dst(addr: u32) -> Vec<u8> {
547    encode_u32(OxmClass::Nxm1, NxmField::TunIpv4Dst as u8, addr)
548}
549
550/// Encode NXM packet mark (`NXM_NX_PKT_MARK`).
551///
552/// Returns 8 bytes: 4-byte header + 4-byte mark value.
553#[allow(dead_code)]
554#[must_use]
555pub fn encode_pkt_mark(value: u32) -> Vec<u8> {
556    encode_u32(OxmClass::Nxm1, NxmField::PktMark as u8, value)
557}
558
559/// Encode masked NXM packet mark.
560///
561/// Returns 12 bytes: 4-byte header + 4-byte value + 4-byte mask.
562#[allow(dead_code)]
563#[must_use]
564pub fn encode_pkt_mark_masked(value: u32, mask: u32) -> Vec<u8> {
565    encode_u32_masked(OxmClass::Nxm1, NxmField::PktMark as u8, value, mask)
566}
567
568/// Encode connection tracking state (`NXM_NX_CT_STATE`).
569///
570/// Use constants from `ct_state` module (e.g., `ct_state::TRK | ct_state::EST`).
571///
572/// Returns 8 bytes: 4-byte header + 4-byte state flags.
573#[allow(dead_code)]
574#[must_use]
575pub fn encode_ct_state(state: u32) -> Vec<u8> {
576    encode_u32(OxmClass::Nxm1, NxmField::CtState as u8, state)
577}
578
579/// Encode masked connection tracking state.
580///
581/// Returns 12 bytes: 4-byte header + 4-byte value + 4-byte mask.
582#[allow(dead_code)]
583#[must_use]
584pub fn encode_ct_state_masked(state: u32, mask: u32) -> Vec<u8> {
585    encode_u32_masked(OxmClass::Nxm1, NxmField::CtState as u8, state, mask)
586}
587
588/// Encode connection tracking zone (`NXM_NX_CT_ZONE`).
589///
590/// Returns 6 bytes: 4-byte header + 2-byte zone ID.
591#[allow(dead_code)]
592#[must_use]
593pub fn encode_ct_zone(zone: u16) -> Vec<u8> {
594    encode_u16(OxmClass::Nxm1, NxmField::CtZone as u8, zone)
595}
596
597/// Encode connection tracking mark (`NXM_NX_CT_MARK`).
598///
599/// Returns 8 bytes: 4-byte header + 4-byte mark value.
600#[allow(dead_code)]
601#[must_use]
602pub fn encode_ct_mark(mark: u32) -> Vec<u8> {
603    encode_u32(OxmClass::Nxm1, NxmField::CtMark as u8, mark)
604}
605
606/// Encode masked connection tracking mark.
607///
608/// Returns 12 bytes: 4-byte header + 4-byte value + 4-byte mask.
609#[allow(dead_code, clippy::similar_names)]
610#[must_use]
611pub fn encode_ct_mark_masked(mark: u32, mask: u32) -> Vec<u8> {
612    encode_u32_masked(OxmClass::Nxm1, NxmField::CtMark as u8, mark, mask)
613}
614
615/// Encode connection tracking label (`NXM_NX_CT_LABEL`).
616///
617/// Returns 20 bytes: 4-byte header + 16-byte label.
618#[allow(dead_code)]
619#[must_use]
620pub fn encode_ct_label(label: u128) -> Vec<u8> {
621    let header = OxmHeader::new(OxmClass::Nxm1, NxmField::CtLabel as u8, false, 16);
622    let mut buf = Vec::with_capacity(20);
623    buf.extend_from_slice(&header.encode());
624    buf.extend_from_slice(&label.to_be_bytes());
625    buf
626}
627
628/// Encode masked connection tracking label.
629///
630/// Returns 36 bytes: 4-byte header + 16-byte value + 16-byte mask.
631#[allow(dead_code)]
632#[must_use]
633pub fn encode_ct_label_masked(label: u128, mask: u128) -> Vec<u8> {
634    let header = OxmHeader::new(OxmClass::Nxm1, NxmField::CtLabel as u8, true, 32);
635    let mut buf = Vec::with_capacity(36);
636    buf.extend_from_slice(&header.encode());
637    buf.extend_from_slice(&label.to_be_bytes());
638    buf.extend_from_slice(&mask.to_be_bytes());
639    buf
640}
641
642// =============================================================================
643// Extended Register Encoding (Phase 1.5)
644// =============================================================================
645
646/// Encode a 128-bit extended register (`NXM_NX_XXREG0-3`).
647///
648/// Extended registers are 128-bit (16-byte) registers useful for storing
649/// IPv6 addresses or large metadata values.
650///
651/// Returns 20 bytes: 4-byte header + 16-byte value.
652///
653/// # Panics
654///
655/// Panics if `reg_num` is greater than 3.
656#[allow(dead_code)]
657#[must_use]
658pub fn encode_xxreg(reg_num: u8, value: u128) -> Vec<u8> {
659    assert!(reg_num <= 3, "Extended register number must be 0-3");
660    let field = NxmField::XxReg0 as u8 + reg_num;
661    let header = OxmHeader::new(OxmClass::Nxm1, field, false, 16);
662    let mut buf = Vec::with_capacity(20);
663    buf.extend_from_slice(&header.encode());
664    buf.extend_from_slice(&value.to_be_bytes());
665    buf
666}
667
668/// Encode a masked 128-bit extended register.
669///
670/// Returns 36 bytes: 4-byte header + 16-byte value + 16-byte mask.
671///
672/// # Panics
673///
674/// Panics if `reg_num` is greater than 3.
675#[allow(dead_code)]
676#[must_use]
677pub fn encode_xxreg_masked(reg_num: u8, value: u128, mask: u128) -> Vec<u8> {
678    assert!(reg_num <= 3, "Extended register number must be 0-3");
679    let field = NxmField::XxReg0 as u8 + reg_num;
680    let header = OxmHeader::new(OxmClass::Nxm1, field, true, 32);
681    let mut buf = Vec::with_capacity(36);
682    buf.extend_from_slice(&header.encode());
683    buf.extend_from_slice(&value.to_be_bytes());
684    buf.extend_from_slice(&mask.to_be_bytes());
685    buf
686}
687
688/// Encode an IPv6 address in an extended register.
689///
690/// This is a convenience function for storing IPv6 addresses in xxreg registers.
691///
692/// Returns 20 bytes: 4-byte header + 16-byte IPv6 address.
693#[allow(dead_code)]
694#[must_use]
695pub fn encode_xxreg_ipv6(reg_num: u8, addr: std::net::Ipv6Addr) -> Vec<u8> {
696    encode_xxreg(reg_num, u128::from(addr))
697}
698
699/// Encode a masked IPv6 address in an extended register.
700///
701/// Returns 36 bytes: 4-byte header + 16-byte address + 16-byte mask.
702#[allow(dead_code)]
703#[must_use]
704pub fn encode_xxreg_ipv6_masked(reg_num: u8, addr: std::net::Ipv6Addr, mask: std::net::Ipv6Addr) -> Vec<u8> {
705    encode_xxreg_masked(reg_num, u128::from(addr), u128::from(mask))
706}
707
708/// Convert an IPv6 prefix length to a 128-bit mask.
709///
710/// # Examples
711///
712/// ```ignore
713/// use rovs_openflow::oxm::prefix_to_mask_v6;
714///
715/// assert_eq!(prefix_to_mask_v6(64), 0xffffffff_ffffffff_00000000_00000000);
716/// assert_eq!(prefix_to_mask_v6(128), u128::MAX);
717/// assert_eq!(prefix_to_mask_v6(0), 0);
718/// ```
719#[allow(dead_code)]
720#[must_use]
721pub const fn prefix_to_mask_v6(prefix_len: u8) -> u128 {
722    if prefix_len == 0 {
723        0
724    } else if prefix_len >= 128 {
725        u128::MAX
726    } else {
727        u128::MAX << (128 - prefix_len)
728    }
729}
730
731// =============================================================================
732// Helper Functions
733// =============================================================================
734
735/// Convert an IPv4 prefix length to a subnet mask.
736///
737/// # Examples
738///
739/// ```ignore
740/// use rovs_openflow::oxm::prefix_to_mask;
741///
742/// assert_eq!(prefix_to_mask(24), 0xffffff00); // 255.255.255.0
743/// assert_eq!(prefix_to_mask(16), 0xffff0000); // 255.255.0.0
744/// assert_eq!(prefix_to_mask(32), 0xffffffff); // 255.255.255.255
745/// assert_eq!(prefix_to_mask(0), 0x00000000);  // 0.0.0.0
746/// ```
747#[allow(dead_code)]
748#[must_use]
749pub const fn prefix_to_mask(prefix_len: u8) -> u32 {
750    if prefix_len == 0 {
751        0
752    } else if prefix_len >= 32 {
753        0xffff_ffff
754    } else {
755        // Shift 1s into the high bits
756        0xffff_ffff << (32 - prefix_len)
757    }
758}
759
760/// Encode an IPv4 address with prefix length as a masked OXM field.
761///
762/// Convenience function that combines address and prefix into value/mask.
763///
764/// # Examples
765///
766/// ```ignore
767/// // Match 10.0.0.0/24
768/// let bytes = encode_ipv4_prefix(OxmField::Ipv4Dst, 0x0a000000, 24);
769/// ```
770#[allow(dead_code)]
771#[must_use]
772pub fn encode_ipv4_prefix(field: OxmField, addr: u32, prefix_len: u8) -> Vec<u8> {
773    let mask = prefix_to_mask(prefix_len);
774    encode_u32_masked_field(field, addr & mask, mask)
775}
776
777// =============================================================================
778// OxmEncode Trait (Phase 1.6)
779// =============================================================================
780
781/// A trait for types that can be encoded to OXM/NXM wire format.
782///
783/// This provides a unified interface for encoding match fields regardless
784/// of whether they are OpenFlow Basic (OXM) or Nicira Extension (NXM) fields.
785pub trait OxmEncode {
786    /// Encode this field to OXM wire format.
787    ///
788    /// Returns a `Vec<u8>` containing the 4-byte OXM header followed by
789    /// the field value (and mask, if applicable).
790    fn encode(&self) -> Vec<u8>;
791}
792
793/// OpenFlow Basic match field with value.
794///
795/// This enum represents all standard OpenFlow 1.3+ match fields from the
796/// `OFPXMC_OPENFLOW_BASIC` class (0x8000).
797#[allow(dead_code)]
798#[derive(Debug, Clone, PartialEq, Eq)]
799pub enum OxmMatchField {
800    /// Switch input port (4 bytes)
801    InPort(u32),
802    /// Switch physical input port (4 bytes)
803    InPhyPort(u32),
804    /// Metadata (8 bytes)
805    Metadata(u64),
806    /// Metadata with mask (8+8 bytes)
807    MetadataMasked(u64, u64),
808    /// Ethernet destination address (6 bytes)
809    EthDst([u8; 6]),
810    /// Ethernet destination with mask (6+6 bytes)
811    EthDstMasked([u8; 6], [u8; 6]),
812    /// Ethernet source address (6 bytes)
813    EthSrc([u8; 6]),
814    /// Ethernet source with mask (6+6 bytes)
815    EthSrcMasked([u8; 6], [u8; 6]),
816    /// Ethernet type (2 bytes)
817    EthType(u16),
818    /// VLAN ID (2 bytes)
819    VlanVid(u16),
820    /// VLAN ID with mask (2+2 bytes)
821    VlanVidMasked(u16, u16),
822    /// VLAN priority (1 byte)
823    VlanPcp(u8),
824    /// IP DSCP (1 byte)
825    IpDscp(u8),
826    /// IP ECN (1 byte)
827    IpEcn(u8),
828    /// IP protocol (1 byte)
829    IpProto(u8),
830    /// IPv4 source address (4 bytes)
831    Ipv4Src(u32),
832    /// IPv4 source with mask (4+4 bytes)
833    Ipv4SrcMasked(u32, u32),
834    /// IPv4 destination address (4 bytes)
835    Ipv4Dst(u32),
836    /// IPv4 destination with mask (4+4 bytes)
837    Ipv4DstMasked(u32, u32),
838    /// TCP source port (2 bytes)
839    TcpSrc(u16),
840    /// TCP destination port (2 bytes)
841    TcpDst(u16),
842    /// UDP source port (2 bytes)
843    UdpSrc(u16),
844    /// UDP destination port (2 bytes)
845    UdpDst(u16),
846    /// SCTP source port (2 bytes)
847    SctpSrc(u16),
848    /// SCTP destination port (2 bytes)
849    SctpDst(u16),
850    /// ICMPv4 type (1 byte)
851    Icmpv4Type(u8),
852    /// ICMPv4 code (1 byte)
853    Icmpv4Code(u8),
854    /// ARP opcode (2 bytes)
855    ArpOp(u16),
856    /// ARP source IPv4 (4 bytes)
857    ArpSpa(u32),
858    /// ARP source IPv4 with mask (4+4 bytes)
859    ArpSpaMasked(u32, u32),
860    /// ARP target IPv4 (4 bytes)
861    ArpTpa(u32),
862    /// ARP target IPv4 with mask (4+4 bytes)
863    ArpTpaMasked(u32, u32),
864    /// ARP source MAC (6 bytes)
865    ArpSha([u8; 6]),
866    /// ARP target MAC (6 bytes)
867    ArpTha([u8; 6]),
868    /// Tunnel ID (8 bytes)
869    TunnelId(u64),
870    /// Tunnel ID with mask (8+8 bytes)
871    TunnelIdMasked(u64, u64),
872}
873
874#[allow(dead_code)]
875impl OxmEncode for OxmMatchField {
876    fn encode(&self) -> Vec<u8> {
877        match self {
878            Self::InPort(v) => encode_u32_field(OxmField::InPort, *v),
879            Self::InPhyPort(v) => encode_u32_field(OxmField::InPhyPort, *v),
880            Self::Metadata(v) => encode_u64_field(OxmField::Metadata, *v),
881            Self::MetadataMasked(v, m) => encode_u64_masked_field(OxmField::Metadata, *v, *m),
882            Self::EthDst(v) => encode_mac_field(OxmField::EthDst, *v),
883            Self::EthDstMasked(v, m) => encode_mac_masked_field(OxmField::EthDst, *v, *m),
884            Self::EthSrc(v) => encode_mac_field(OxmField::EthSrc, *v),
885            Self::EthSrcMasked(v, m) => encode_mac_masked_field(OxmField::EthSrc, *v, *m),
886            Self::EthType(v) => encode_u16_field(OxmField::EthType, *v),
887            Self::VlanVid(v) => encode_u16_field(OxmField::VlanVid, *v),
888            Self::VlanVidMasked(v, m) => {
889                encode_u16_masked(OxmClass::OpenflowBasic, OxmField::VlanVid as u8, *v, *m)
890            }
891            Self::VlanPcp(v) => encode_u8_field(OxmField::VlanPcp, *v),
892            Self::IpDscp(v) => encode_u8_field(OxmField::IpDscp, *v),
893            Self::IpEcn(v) => encode_u8_field(OxmField::IpEcn, *v),
894            Self::IpProto(v) => encode_u8_field(OxmField::IpProto, *v),
895            Self::Ipv4Src(v) => encode_u32_field(OxmField::Ipv4Src, *v),
896            Self::Ipv4SrcMasked(v, m) => encode_u32_masked_field(OxmField::Ipv4Src, *v, *m),
897            Self::Ipv4Dst(v) => encode_u32_field(OxmField::Ipv4Dst, *v),
898            Self::Ipv4DstMasked(v, m) => encode_u32_masked_field(OxmField::Ipv4Dst, *v, *m),
899            Self::TcpSrc(v) => encode_u16_field(OxmField::TcpSrc, *v),
900            Self::TcpDst(v) => encode_u16_field(OxmField::TcpDst, *v),
901            Self::UdpSrc(v) => encode_u16_field(OxmField::UdpSrc, *v),
902            Self::UdpDst(v) => encode_u16_field(OxmField::UdpDst, *v),
903            Self::SctpSrc(v) => encode_u16_field(OxmField::SctpSrc, *v),
904            Self::SctpDst(v) => encode_u16_field(OxmField::SctpDst, *v),
905            Self::Icmpv4Type(v) => encode_u8_field(OxmField::Icmpv4Type, *v),
906            Self::Icmpv4Code(v) => encode_u8_field(OxmField::Icmpv4Code, *v),
907            Self::ArpOp(v) => encode_u16_field(OxmField::ArpOp, *v),
908            Self::ArpSpa(v) => encode_u32_field(OxmField::ArpSpa, *v),
909            Self::ArpSpaMasked(v, m) => encode_u32_masked_field(OxmField::ArpSpa, *v, *m),
910            Self::ArpTpa(v) => encode_u32_field(OxmField::ArpTpa, *v),
911            Self::ArpTpaMasked(v, m) => encode_u32_masked_field(OxmField::ArpTpa, *v, *m),
912            Self::ArpSha(v) => encode_mac_field(OxmField::ArpSha, *v),
913            Self::ArpTha(v) => encode_mac_field(OxmField::ArpTha, *v),
914            Self::TunnelId(v) => encode_u64_field(OxmField::TunnelId, *v),
915            Self::TunnelIdMasked(v, m) => encode_u64_masked_field(OxmField::TunnelId, *v, *m),
916        }
917    }
918}
919
920/// Nicira Extension (NXM) match field with value.
921///
922/// This enum represents Nicira-specific match fields from the `NXM_NX_*`
923/// class (0x0001).
924#[allow(dead_code)]
925#[derive(Debug, Clone, PartialEq, Eq)]
926pub enum NxmMatchField {
927    /// General purpose register 0-15 (4 bytes)
928    Reg(u8, u32),
929    /// General purpose register with mask (4+4 bytes)
930    RegMasked(u8, u32, u32),
931    /// Extended 128-bit register 0-3 (16 bytes)
932    XxReg(u8, u128),
933    /// Extended register with mask (16+16 bytes)
934    XxRegMasked(u8, u128, u128),
935    /// Tunnel ID (8 bytes)
936    TunId(u64),
937    /// Tunnel ID with mask (8+8 bytes)
938    TunIdMasked(u64, u64),
939    /// Tunnel IPv4 source (4 bytes)
940    TunIpv4Src(u32),
941    /// Tunnel IPv4 destination (4 bytes)
942    TunIpv4Dst(u32),
943    /// Packet mark (4 bytes)
944    PktMark(u32),
945    /// Packet mark with mask (4+4 bytes)
946    PktMarkMasked(u32, u32),
947    /// Connection tracking state (4 bytes)
948    CtState(u32),
949    /// Connection tracking state with mask (4+4 bytes)
950    CtStateMasked(u32, u32),
951    /// Connection tracking zone (2 bytes)
952    CtZone(u16),
953    /// Connection tracking mark (4 bytes)
954    CtMark(u32),
955    /// Connection tracking mark with mask (4+4 bytes)
956    CtMarkMasked(u32, u32),
957    /// Connection tracking label (16 bytes)
958    CtLabel(u128),
959    /// Connection tracking label with mask (16+16 bytes)
960    CtLabelMasked(u128, u128),
961}
962
963#[allow(dead_code)]
964impl OxmEncode for NxmMatchField {
965    fn encode(&self) -> Vec<u8> {
966        match self {
967            Self::Reg(n, v) => encode_reg(*n, *v),
968            Self::RegMasked(n, v, m) => encode_reg_masked(*n, *v, *m),
969            Self::XxReg(n, v) => encode_xxreg(*n, *v),
970            Self::XxRegMasked(n, v, m) => encode_xxreg_masked(*n, *v, *m),
971            Self::TunId(v) => encode_tun_id(*v),
972            Self::TunIdMasked(v, m) => encode_tun_id_masked(*v, *m),
973            Self::TunIpv4Src(v) => encode_tun_ipv4_src(*v),
974            Self::TunIpv4Dst(v) => encode_tun_ipv4_dst(*v),
975            Self::PktMark(v) => encode_pkt_mark(*v),
976            Self::PktMarkMasked(v, m) => encode_pkt_mark_masked(*v, *m),
977            Self::CtState(v) => encode_ct_state(*v),
978            Self::CtStateMasked(v, m) => encode_ct_state_masked(*v, *m),
979            Self::CtZone(v) => encode_ct_zone(*v),
980            Self::CtMark(v) => encode_ct_mark(*v),
981            Self::CtMarkMasked(v, m) => encode_ct_mark_masked(*v, *m),
982            Self::CtLabel(v) => encode_ct_label(*v),
983            Self::CtLabelMasked(v, m) => encode_ct_label_masked(*v, *m),
984        }
985    }
986}
987
988/// A unified match field that can be either OXM or NXM.
989///
990/// This enum provides a single type for all match fields, useful when
991/// building match lists that may contain both standard and extension fields.
992#[allow(dead_code)]
993#[derive(Debug, Clone, PartialEq, Eq)]
994pub enum MatchField {
995    /// OpenFlow Basic match field
996    Oxm(OxmMatchField),
997    /// Nicira Extension match field
998    Nxm(NxmMatchField),
999}
1000
1001#[allow(dead_code)]
1002impl OxmEncode for MatchField {
1003    fn encode(&self) -> Vec<u8> {
1004        match self {
1005            Self::Oxm(f) => f.encode(),
1006            Self::Nxm(f) => f.encode(),
1007        }
1008    }
1009}
1010
1011// Convenience From implementations
1012impl From<OxmMatchField> for MatchField {
1013    fn from(f: OxmMatchField) -> Self {
1014        Self::Oxm(f)
1015    }
1016}
1017
1018impl From<NxmMatchField> for MatchField {
1019    fn from(f: NxmMatchField) -> Self {
1020        Self::Nxm(f)
1021    }
1022}
1023
1024/// Build an OXM header.
1025///
1026/// Format: class (16 bits) | field (7 bits) | hasmask (1 bit) | length (8 bits)
1027#[allow(dead_code)] // Reserved for future OpenFlow implementation
1028pub fn oxm_header(class: OxmClass, field: OxmField, has_mask: bool, length: u8) -> u32 {
1029    let class_val = class as u32;
1030    let field_val = (field as u32) << 1;
1031    let mask_val = if has_mask { 1u32 } else { 0u32 };
1032    let len_val = length as u32;
1033
1034    (class_val << 16) | (field_val << 8) | (mask_val << 8) | len_val
1035}
1036
1037#[cfg(test)]
1038mod tests {
1039    use super::*;
1040
1041    #[test]
1042    fn oxm_header_in_port() {
1043        // InPort: class=0x8000, field=0, no mask, length=4
1044        let header = OxmHeader::openflow_basic(OxmField::InPort, false, 4);
1045        let bytes = header.encode();
1046
1047        // Expected: 0x80000004
1048        // byte 0: 0x80 (class high)
1049        // byte 1: 0x00 (class low)
1050        // byte 2: (0 << 1) | 0 = 0x00
1051        // byte 3: 4
1052        assert_eq!(bytes, [0x80, 0x00, 0x00, 0x04]);
1053        assert_eq!(header.as_u32(), 0x8000_0004);
1054    }
1055
1056    #[test]
1057    fn oxm_header_eth_type() {
1058        // EthType: class=0x8000, field=5, no mask, length=2
1059        let header = OxmHeader::openflow_basic(OxmField::EthType, false, 2);
1060        let bytes = header.encode();
1061
1062        // byte 2: (5 << 1) | 0 = 0x0a
1063        assert_eq!(bytes, [0x80, 0x00, 0x0a, 0x02]);
1064        assert_eq!(header.as_u32(), 0x8000_0a02);
1065    }
1066
1067    #[test]
1068    fn oxm_header_eth_dst_masked() {
1069        // EthDst with mask: class=0x8000, field=3, mask=true, length=12
1070        let header = OxmHeader::openflow_basic(OxmField::EthDst, true, 12);
1071        let bytes = header.encode();
1072
1073        // byte 2: (3 << 1) | 1 = 0x07
1074        assert_eq!(bytes, [0x80, 0x00, 0x07, 0x0c]);
1075        assert_eq!(header.as_u32(), 0x8000_070c);
1076    }
1077
1078    #[test]
1079    fn oxm_header_ipv4_src_no_mask() {
1080        // Ipv4Src: class=0x8000, field=11, no mask, length=4
1081        let header = OxmHeader::openflow_basic(OxmField::Ipv4Src, false, 4);
1082        let bytes = header.encode();
1083
1084        // byte 2: (11 << 1) | 0 = 0x16
1085        assert_eq!(bytes, [0x80, 0x00, 0x16, 0x04]);
1086        assert_eq!(header.as_u32(), 0x8000_1604);
1087    }
1088
1089    #[test]
1090    fn oxm_header_ipv4_src_masked() {
1091        // Ipv4Src with mask: class=0x8000, field=11, mask=true, length=8
1092        let header = OxmHeader::openflow_basic(OxmField::Ipv4Src, true, 8);
1093        let bytes = header.encode();
1094
1095        // byte 2: (11 << 1) | 1 = 0x17
1096        assert_eq!(bytes, [0x80, 0x00, 0x17, 0x08]);
1097        assert_eq!(header.as_u32(), 0x8000_1708);
1098    }
1099
1100    #[test]
1101    fn oxm_header_tcp_dst() {
1102        // TcpDst: class=0x8000, field=14, no mask, length=2
1103        let header = OxmHeader::openflow_basic(OxmField::TcpDst, false, 2);
1104        let bytes = header.encode();
1105
1106        // byte 2: (14 << 1) | 0 = 0x1c
1107        assert_eq!(bytes, [0x80, 0x00, 0x1c, 0x02]);
1108    }
1109
1110    #[test]
1111    fn oxm_header_metadata_masked() {
1112        // Metadata with mask: class=0x8000, field=2, mask=true, length=16
1113        let header = OxmHeader::openflow_basic(OxmField::Metadata, true, 16);
1114        let bytes = header.encode();
1115
1116        // byte 2: (2 << 1) | 1 = 0x05
1117        assert_eq!(bytes, [0x80, 0x00, 0x05, 0x10]);
1118    }
1119
1120    #[test]
1121    fn oxm_header_nxm_register() {
1122        // NXM register: class=0x0001 (Nxm1), field=0 (REG0), no mask, length=4
1123        let header = OxmHeader::new(OxmClass::Nxm1, 0, false, 4);
1124        let bytes = header.encode();
1125
1126        assert_eq!(bytes, [0x00, 0x01, 0x00, 0x04]);
1127        assert_eq!(header.as_u32(), 0x0001_0004);
1128    }
1129
1130    #[test]
1131    fn oxm_header_nxm_register_masked() {
1132        // NXM register with mask: class=0x0001, field=0 (REG0), mask=true, length=8
1133        let header = OxmHeader::new(OxmClass::Nxm1, 0, true, 8);
1134        let bytes = header.encode();
1135
1136        // byte 2: (0 << 1) | 1 = 0x01
1137        assert_eq!(bytes, [0x00, 0x01, 0x01, 0x08]);
1138    }
1139
1140    #[test]
1141    fn oxm_header_tunnel_id() {
1142        // TunnelId: class=0x8000, field=38, no mask, length=8
1143        let header = OxmHeader::openflow_basic(OxmField::TunnelId, false, 8);
1144        let bytes = header.encode();
1145
1146        // byte 2: (38 << 1) | 0 = 0x4c
1147        assert_eq!(bytes, [0x80, 0x00, 0x4c, 0x08]);
1148    }
1149
1150    #[test]
1151    fn oxm_header_compatible_with_legacy_function() {
1152        // Verify OxmHeader.as_u32() matches legacy oxm_header() function
1153        let header = OxmHeader::openflow_basic(OxmField::EthType, false, 2);
1154        let legacy = oxm_header(OxmClass::OpenflowBasic, OxmField::EthType, false, 2);
1155
1156        assert_eq!(header.as_u32(), legacy);
1157    }
1158
1159    // =========================================================================
1160    // Fixed-Size Encoding Tests (Phase 1.2)
1161    // =========================================================================
1162
1163    #[test]
1164    fn encode_u8_ip_proto_tcp() {
1165        // IP protocol = 6 (TCP)
1166        // IpProto: field=10
1167        let bytes = encode_u8_field(OxmField::IpProto, 6);
1168
1169        assert_eq!(bytes.len(), 5);
1170        // Header: class=0x8000, field=10, no mask, length=1
1171        // byte 2: (10 << 1) | 0 = 0x14
1172        assert_eq!(&bytes[0..4], &[0x80, 0x00, 0x14, 0x01]);
1173        // Value
1174        assert_eq!(bytes[4], 6);
1175    }
1176
1177    #[test]
1178    fn encode_u8_ip_dscp() {
1179        // IP DSCP = 46 (EF/Expedited Forwarding)
1180        // IpDscp: field=8
1181        let bytes = encode_u8_field(OxmField::IpDscp, 46);
1182
1183        assert_eq!(bytes.len(), 5);
1184        // byte 2: (8 << 1) | 0 = 0x10
1185        assert_eq!(&bytes[0..4], &[0x80, 0x00, 0x10, 0x01]);
1186        assert_eq!(bytes[4], 46);
1187    }
1188
1189    #[test]
1190    fn encode_u16_eth_type_ipv4() {
1191        // EthType = 0x0800 (IPv4)
1192        // EthType: field=5
1193        let bytes = encode_u16_field(OxmField::EthType, 0x0800);
1194
1195        assert_eq!(bytes.len(), 6);
1196        // byte 2: (5 << 1) | 0 = 0x0a
1197        assert_eq!(&bytes[0..4], &[0x80, 0x00, 0x0a, 0x02]);
1198        // Value in big-endian
1199        assert_eq!(&bytes[4..6], &[0x08, 0x00]);
1200    }
1201
1202    #[test]
1203    fn encode_u16_tcp_dst_http() {
1204        // TCP dst port = 80 (HTTP)
1205        // TcpDst: field=14
1206        let bytes = encode_u16_field(OxmField::TcpDst, 80);
1207
1208        assert_eq!(bytes.len(), 6);
1209        // byte 2: (14 << 1) | 0 = 0x1c
1210        assert_eq!(&bytes[0..4], &[0x80, 0x00, 0x1c, 0x02]);
1211        // 80 = 0x0050 in big-endian
1212        assert_eq!(&bytes[4..6], &[0x00, 0x50]);
1213    }
1214
1215    #[test]
1216    fn encode_u16_vlan_vid() {
1217        // VLAN ID = 100
1218        // VlanVid: field=6
1219        let bytes = encode_u16_field(OxmField::VlanVid, 100);
1220
1221        assert_eq!(bytes.len(), 6);
1222        // byte 2: (6 << 1) | 0 = 0x0c
1223        assert_eq!(&bytes[0..4], &[0x80, 0x00, 0x0c, 0x02]);
1224        // 100 = 0x0064 in big-endian
1225        assert_eq!(&bytes[4..6], &[0x00, 0x64]);
1226    }
1227
1228    #[test]
1229    fn encode_u32_in_port() {
1230        // InPort = 1
1231        // InPort: field=0
1232        let bytes = encode_u32_field(OxmField::InPort, 1);
1233
1234        assert_eq!(bytes.len(), 8);
1235        // byte 2: (0 << 1) | 0 = 0x00
1236        assert_eq!(&bytes[0..4], &[0x80, 0x00, 0x00, 0x04]);
1237        // 1 in big-endian
1238        assert_eq!(&bytes[4..8], &[0x00, 0x00, 0x00, 0x01]);
1239    }
1240
1241    #[test]
1242    fn encode_u32_ipv4_src() {
1243        // IPv4 src = 10.0.0.1 = 0x0a000001
1244        // Ipv4Src: field=11
1245        let ip: u32 = (10 << 24) | (0 << 16) | (0 << 8) | 1;
1246        let bytes = encode_u32_field(OxmField::Ipv4Src, ip);
1247
1248        assert_eq!(bytes.len(), 8);
1249        // byte 2: (11 << 1) | 0 = 0x16
1250        assert_eq!(&bytes[0..4], &[0x80, 0x00, 0x16, 0x04]);
1251        // IP address bytes
1252        assert_eq!(&bytes[4..8], &[10, 0, 0, 1]);
1253    }
1254
1255    #[test]
1256    fn encode_u32_ipv4_dst() {
1257        // IPv4 dst = 192.168.1.1
1258        // Ipv4Dst: field=12
1259        let ip: u32 = (192 << 24) | (168 << 16) | (1 << 8) | 1;
1260        let bytes = encode_u32_field(OxmField::Ipv4Dst, ip);
1261
1262        assert_eq!(bytes.len(), 8);
1263        // byte 2: (12 << 1) | 0 = 0x18
1264        assert_eq!(&bytes[0..4], &[0x80, 0x00, 0x18, 0x04]);
1265        assert_eq!(&bytes[4..8], &[192, 168, 1, 1]);
1266    }
1267
1268    #[test]
1269    fn encode_u64_metadata() {
1270        // Metadata = 0x123456789ABCDEF0
1271        // Metadata: field=2
1272        let bytes = encode_u64_field(OxmField::Metadata, 0x123456789ABCDEF0);
1273
1274        assert_eq!(bytes.len(), 12);
1275        // byte 2: (2 << 1) | 0 = 0x04
1276        assert_eq!(&bytes[0..4], &[0x80, 0x00, 0x04, 0x08]);
1277        // Value in big-endian
1278        assert_eq!(
1279            &bytes[4..12],
1280            &[0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0]
1281        );
1282    }
1283
1284    #[test]
1285    fn encode_u64_tunnel_id() {
1286        // TunnelId = 42
1287        // TunnelId: field=38
1288        let bytes = encode_u64_field(OxmField::TunnelId, 42);
1289
1290        assert_eq!(bytes.len(), 12);
1291        // byte 2: (38 << 1) | 0 = 0x4c
1292        assert_eq!(&bytes[0..4], &[0x80, 0x00, 0x4c, 0x08]);
1293        // 42 in big-endian (8 bytes)
1294        assert_eq!(&bytes[4..12], &[0, 0, 0, 0, 0, 0, 0, 42]);
1295    }
1296
1297    #[test]
1298    fn encode_mac_eth_src() {
1299        // EthSrc = 00:11:22:33:44:55
1300        // EthSrc: field=4
1301        let mac = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55];
1302        let bytes = encode_mac_field(OxmField::EthSrc, mac);
1303
1304        assert_eq!(bytes.len(), 10);
1305        // byte 2: (4 << 1) | 0 = 0x08
1306        assert_eq!(&bytes[0..4], &[0x80, 0x00, 0x08, 0x06]);
1307        assert_eq!(&bytes[4..10], &mac);
1308    }
1309
1310    #[test]
1311    fn encode_mac_eth_dst() {
1312        // EthDst = ff:ff:ff:ff:ff:ff (broadcast)
1313        // EthDst: field=3
1314        let mac = [0xff, 0xff, 0xff, 0xff, 0xff, 0xff];
1315        let bytes = encode_mac_field(OxmField::EthDst, mac);
1316
1317        assert_eq!(bytes.len(), 10);
1318        // byte 2: (3 << 1) | 0 = 0x06
1319        assert_eq!(&bytes[0..4], &[0x80, 0x00, 0x06, 0x06]);
1320        assert_eq!(&bytes[4..10], &mac);
1321    }
1322
1323    #[test]
1324    fn encode_mac_arp_sha() {
1325        // ARP source hardware address
1326        // ArpSha: field=24
1327        let mac = [0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe];
1328        let bytes = encode_mac_field(OxmField::ArpSha, mac);
1329
1330        assert_eq!(bytes.len(), 10);
1331        // byte 2: (24 << 1) | 0 = 0x30
1332        assert_eq!(&bytes[0..4], &[0x80, 0x00, 0x30, 0x06]);
1333        assert_eq!(&bytes[4..10], &mac);
1334    }
1335
1336    #[test]
1337    fn encode_nxm_register_raw() {
1338        // NXM_NX_REG0 (class=0x0001, field=0) with value 0x12345678
1339        let bytes = encode_u32(OxmClass::Nxm1, 0, 0x12345678);
1340
1341        assert_eq!(bytes.len(), 8);
1342        assert_eq!(&bytes[0..4], &[0x00, 0x01, 0x00, 0x04]);
1343        assert_eq!(&bytes[4..8], &[0x12, 0x34, 0x56, 0x78]);
1344    }
1345
1346    // =========================================================================
1347    // Masked Field Encoding Tests (Phase 1.3)
1348    // =========================================================================
1349
1350    #[test]
1351    fn prefix_to_mask_24() {
1352        // /24 = 255.255.255.0 = 0xffffff00
1353        assert_eq!(prefix_to_mask(24), 0xffff_ff00);
1354    }
1355
1356    #[test]
1357    fn prefix_to_mask_16() {
1358        // /16 = 255.255.0.0 = 0xffff0000
1359        assert_eq!(prefix_to_mask(16), 0xffff_0000);
1360    }
1361
1362    #[test]
1363    fn prefix_to_mask_8() {
1364        // /8 = 255.0.0.0 = 0xff000000
1365        assert_eq!(prefix_to_mask(8), 0xff00_0000);
1366    }
1367
1368    #[test]
1369    fn prefix_to_mask_32() {
1370        // /32 = 255.255.255.255 (host route)
1371        assert_eq!(prefix_to_mask(32), 0xffff_ffff);
1372    }
1373
1374    #[test]
1375    fn prefix_to_mask_0() {
1376        // /0 = 0.0.0.0 (default route)
1377        assert_eq!(prefix_to_mask(0), 0x0000_0000);
1378    }
1379
1380    #[test]
1381    fn prefix_to_mask_various() {
1382        assert_eq!(prefix_to_mask(1), 0x8000_0000);
1383        assert_eq!(prefix_to_mask(25), 0xffff_ff80);
1384        assert_eq!(prefix_to_mask(30), 0xffff_fffc); // /30 point-to-point
1385        assert_eq!(prefix_to_mask(31), 0xffff_fffe); // /31 point-to-point (RFC 3021)
1386    }
1387
1388    #[test]
1389    fn encode_u32_masked_ipv4_24() {
1390        // IPv4 dst = 10.0.0.0/24
1391        // Ipv4Dst: field=12
1392        let addr: u32 = 10 << 24; // 10.0.0.0
1393        let mask: u32 = 0xffff_ff00; // /24
1394        let bytes = encode_u32_masked_field(OxmField::Ipv4Dst, addr, mask);
1395
1396        assert_eq!(bytes.len(), 12);
1397        // Header: class=0x8000, field=12, has_mask=1, length=8
1398        // byte 2: (12 << 1) | 1 = 0x19
1399        assert_eq!(&bytes[0..4], &[0x80, 0x00, 0x19, 0x08]);
1400        // Value: 10.0.0.0
1401        assert_eq!(&bytes[4..8], &[10, 0, 0, 0]);
1402        // Mask: 255.255.255.0
1403        assert_eq!(&bytes[8..12], &[255, 255, 255, 0]);
1404    }
1405
1406    #[test]
1407    fn encode_u32_masked_ipv4_16() {
1408        // IPv4 src = 192.168.0.0/16
1409        // Ipv4Src: field=11
1410        let addr: u32 = (192 << 24) | (168 << 16); // 192.168.0.0
1411        let mask: u32 = 0xffff_0000; // /16
1412        let bytes = encode_u32_masked_field(OxmField::Ipv4Src, addr, mask);
1413
1414        assert_eq!(bytes.len(), 12);
1415        // byte 2: (11 << 1) | 1 = 0x17
1416        assert_eq!(&bytes[0..4], &[0x80, 0x00, 0x17, 0x08]);
1417        assert_eq!(&bytes[4..8], &[192, 168, 0, 0]);
1418        assert_eq!(&bytes[8..12], &[255, 255, 0, 0]);
1419    }
1420
1421    #[test]
1422    fn encode_ipv4_prefix_convenience() {
1423        // Test the convenience function
1424        // 10.0.0.0/24
1425        let addr: u32 = (10 << 24) | (0 << 16) | (0 << 8) | 1; // 10.0.0.1
1426        let bytes = encode_ipv4_prefix(OxmField::Ipv4Dst, addr, 24);
1427
1428        assert_eq!(bytes.len(), 12);
1429        // Value should be masked: 10.0.0.1 & 255.255.255.0 = 10.0.0.0
1430        assert_eq!(&bytes[4..8], &[10, 0, 0, 0]);
1431        assert_eq!(&bytes[8..12], &[255, 255, 255, 0]);
1432    }
1433
1434    #[test]
1435    fn encode_mac_masked_oui() {
1436        // Match by OUI (first 3 bytes)
1437        // EthSrc: field=4
1438        let mac = [0x00, 0x11, 0x22, 0x00, 0x00, 0x00];
1439        let mask = [0xff, 0xff, 0xff, 0x00, 0x00, 0x00];
1440        let bytes = encode_mac_masked_field(OxmField::EthSrc, mac, mask);
1441
1442        assert_eq!(bytes.len(), 16);
1443        // Header: class=0x8000, field=4, has_mask=1, length=12
1444        // byte 2: (4 << 1) | 1 = 0x09
1445        assert_eq!(&bytes[0..4], &[0x80, 0x00, 0x09, 0x0c]);
1446        assert_eq!(&bytes[4..10], &mac);
1447        assert_eq!(&bytes[10..16], &mask);
1448    }
1449
1450    #[test]
1451    fn encode_mac_masked_multicast() {
1452        // Match multicast bit (LSB of first byte = 1)
1453        // EthDst: field=3
1454        let mac = [0x01, 0x00, 0x00, 0x00, 0x00, 0x00];
1455        let mask = [0x01, 0x00, 0x00, 0x00, 0x00, 0x00];
1456        let bytes = encode_mac_masked_field(OxmField::EthDst, mac, mask);
1457
1458        assert_eq!(bytes.len(), 16);
1459        // byte 2: (3 << 1) | 1 = 0x07
1460        assert_eq!(&bytes[0..4], &[0x80, 0x00, 0x07, 0x0c]);
1461        assert_eq!(&bytes[4..10], &mac);
1462        assert_eq!(&bytes[10..16], &mask);
1463    }
1464
1465    #[test]
1466    fn encode_u64_masked_metadata() {
1467        // Metadata with partial mask
1468        // Metadata: field=2
1469        let value: u64 = 0x1234_0000_0000_0000;
1470        let mask: u64 = 0xffff_0000_0000_0000;
1471        let bytes = encode_u64_masked_field(OxmField::Metadata, value, mask);
1472
1473        assert_eq!(bytes.len(), 20);
1474        // Header: class=0x8000, field=2, has_mask=1, length=16
1475        // byte 2: (2 << 1) | 1 = 0x05
1476        assert_eq!(&bytes[0..4], &[0x80, 0x00, 0x05, 0x10]);
1477        assert_eq!(&bytes[4..12], &[0x12, 0x34, 0, 0, 0, 0, 0, 0]);
1478        assert_eq!(&bytes[12..20], &[0xff, 0xff, 0, 0, 0, 0, 0, 0]);
1479    }
1480
1481    #[test]
1482    fn encode_nxm_register_masked() {
1483        // NXM_NX_REG0 with mask (match only high 16 bits)
1484        let bytes = encode_u32_masked(OxmClass::Nxm1, 0, 0xabcd_0000, 0xffff_0000);
1485
1486        assert_eq!(bytes.len(), 12);
1487        // Header: class=0x0001, field=0, has_mask=1, length=8
1488        // byte 2: (0 << 1) | 1 = 0x01
1489        assert_eq!(&bytes[0..4], &[0x00, 0x01, 0x01, 0x08]);
1490        assert_eq!(&bytes[4..8], &[0xab, 0xcd, 0x00, 0x00]);
1491        assert_eq!(&bytes[8..12], &[0xff, 0xff, 0x00, 0x00]);
1492    }
1493
1494    // =========================================================================
1495    // NXM Field Encoding Tests (Phase 1.4)
1496    // =========================================================================
1497
1498    #[test]
1499    fn encode_reg_0() {
1500        // NXM_NX_REG0 = 0x12345678
1501        let bytes = encode_reg(0, 0x12345678);
1502
1503        assert_eq!(bytes.len(), 8);
1504        // Header: class=0x0001, field=0, no mask, length=4
1505        assert_eq!(&bytes[0..4], &[0x00, 0x01, 0x00, 0x04]);
1506        assert_eq!(&bytes[4..8], &[0x12, 0x34, 0x56, 0x78]);
1507    }
1508
1509    #[test]
1510    fn encode_reg_15() {
1511        // NXM_NX_REG15 = 0xdeadbeef
1512        let bytes = encode_reg(15, 0xdeadbeef);
1513
1514        assert_eq!(bytes.len(), 8);
1515        // Header: class=0x0001, field=15, no mask, length=4
1516        // byte 2: (15 << 1) | 0 = 0x1e
1517        assert_eq!(&bytes[0..4], &[0x00, 0x01, 0x1e, 0x04]);
1518        assert_eq!(&bytes[4..8], &[0xde, 0xad, 0xbe, 0xef]);
1519    }
1520
1521    #[test]
1522    fn encode_reg_masked_partial() {
1523        // NXM_NX_REG5 with mask (match only low 8 bits)
1524        let bytes = encode_reg_masked(5, 0x42, 0x000000ff);
1525
1526        assert_eq!(bytes.len(), 12);
1527        // Header: class=0x0001, field=5, has_mask=1, length=8
1528        // byte 2: (5 << 1) | 1 = 0x0b
1529        assert_eq!(&bytes[0..4], &[0x00, 0x01, 0x0b, 0x08]);
1530        assert_eq!(&bytes[4..8], &[0x00, 0x00, 0x00, 0x42]);
1531        assert_eq!(&bytes[8..12], &[0x00, 0x00, 0x00, 0xff]);
1532    }
1533
1534    #[test]
1535    fn encode_tun_id_value() {
1536        // NXM_NX_TUN_ID = 1000
1537        let bytes = encode_tun_id(1000);
1538
1539        assert_eq!(bytes.len(), 12);
1540        // Header: class=0x0001, field=16, no mask, length=8
1541        // byte 2: (16 << 1) | 0 = 0x20
1542        assert_eq!(&bytes[0..4], &[0x00, 0x01, 0x20, 0x08]);
1543        // 1000 = 0x3e8 in big-endian 8 bytes
1544        assert_eq!(&bytes[4..12], &[0, 0, 0, 0, 0, 0, 0x03, 0xe8]);
1545    }
1546
1547    #[test]
1548    fn encode_tun_id_masked_value() {
1549        // NXM_NX_TUN_ID with high 32-bit mask
1550        let bytes = encode_tun_id_masked(0x12345678_00000000, 0xffffffff_00000000);
1551
1552        assert_eq!(bytes.len(), 20);
1553        // Header: class=0x0001, field=16, has_mask=1, length=16
1554        // byte 2: (16 << 1) | 1 = 0x21
1555        assert_eq!(&bytes[0..4], &[0x00, 0x01, 0x21, 0x10]);
1556    }
1557
1558    #[test]
1559    fn encode_tun_ipv4_src_value() {
1560        // NXM_NX_TUN_IPV4_SRC = 10.0.0.1
1561        let addr: u32 = (10 << 24) | 1;
1562        let bytes = encode_tun_ipv4_src(addr);
1563
1564        assert_eq!(bytes.len(), 8);
1565        // Header: class=0x0001, field=31, no mask, length=4
1566        // byte 2: (31 << 1) | 0 = 0x3e
1567        assert_eq!(&bytes[0..4], &[0x00, 0x01, 0x3e, 0x04]);
1568        assert_eq!(&bytes[4..8], &[10, 0, 0, 1]);
1569    }
1570
1571    #[test]
1572    fn encode_tun_ipv4_dst_value() {
1573        // NXM_NX_TUN_IPV4_DST = 192.168.1.1
1574        let addr: u32 = (192 << 24) | (168 << 16) | (1 << 8) | 1;
1575        let bytes = encode_tun_ipv4_dst(addr);
1576
1577        assert_eq!(bytes.len(), 8);
1578        // Header: class=0x0001, field=32, no mask, length=4
1579        // byte 2: (32 << 1) | 0 = 0x40
1580        assert_eq!(&bytes[0..4], &[0x00, 0x01, 0x40, 0x04]);
1581        assert_eq!(&bytes[4..8], &[192, 168, 1, 1]);
1582    }
1583
1584    #[test]
1585    fn encode_pkt_mark_value() {
1586        // NXM_NX_PKT_MARK = 0x100
1587        let bytes = encode_pkt_mark(0x100);
1588
1589        assert_eq!(bytes.len(), 8);
1590        // Header: class=0x0001, field=33, no mask, length=4
1591        // byte 2: (33 << 1) | 0 = 0x42
1592        assert_eq!(&bytes[0..4], &[0x00, 0x01, 0x42, 0x04]);
1593        assert_eq!(&bytes[4..8], &[0x00, 0x00, 0x01, 0x00]);
1594    }
1595
1596    #[test]
1597    fn encode_pkt_mark_masked_value() {
1598        // NXM_NX_PKT_MARK with partial mask
1599        let bytes = encode_pkt_mark_masked(0xff00, 0xff00);
1600
1601        assert_eq!(bytes.len(), 12);
1602        // byte 2: (33 << 1) | 1 = 0x43
1603        assert_eq!(&bytes[0..4], &[0x00, 0x01, 0x43, 0x08]);
1604    }
1605
1606    #[test]
1607    fn encode_ct_state_tracked_established() {
1608        // ct_state=+trk+est
1609        let state = ct_state::TRK | ct_state::EST;
1610        let bytes = encode_ct_state(state);
1611
1612        assert_eq!(bytes.len(), 8);
1613        // Header: class=0x0001, field=105, no mask, length=4
1614        // byte 2: (105 << 1) | 0 = 0xd2
1615        assert_eq!(&bytes[0..4], &[0x00, 0x01, 0xd2, 0x04]);
1616        // TRK (0x20) | EST (0x02) = 0x22
1617        assert_eq!(&bytes[4..8], &[0x00, 0x00, 0x00, 0x22]);
1618    }
1619
1620    #[test]
1621    fn encode_ct_state_masked_new() {
1622        // ct_state=+trk+new with mask
1623        let state = ct_state::TRK | ct_state::NEW;
1624        let mask = ct_state::TRK | ct_state::NEW;
1625        let bytes = encode_ct_state_masked(state, mask);
1626
1627        assert_eq!(bytes.len(), 12);
1628        // byte 2: (105 << 1) | 1 = 0xd3
1629        assert_eq!(&bytes[0..4], &[0x00, 0x01, 0xd3, 0x08]);
1630        // TRK (0x20) | NEW (0x01) = 0x21
1631        assert_eq!(&bytes[4..8], &[0x00, 0x00, 0x00, 0x21]);
1632        assert_eq!(&bytes[8..12], &[0x00, 0x00, 0x00, 0x21]);
1633    }
1634
1635    #[test]
1636    fn encode_ct_zone_value() {
1637        // NXM_NX_CT_ZONE = 100
1638        let bytes = encode_ct_zone(100);
1639
1640        assert_eq!(bytes.len(), 6);
1641        // Header: class=0x0001, field=106, no mask, length=2
1642        // byte 2: (106 << 1) | 0 = 0xd4
1643        assert_eq!(&bytes[0..4], &[0x00, 0x01, 0xd4, 0x02]);
1644        // 100 = 0x0064 in big-endian
1645        assert_eq!(&bytes[4..6], &[0x00, 0x64]);
1646    }
1647
1648    #[test]
1649    fn encode_ct_mark_value() {
1650        // NXM_NX_CT_MARK = 0xaabbccdd
1651        let bytes = encode_ct_mark(0xaabbccdd);
1652
1653        assert_eq!(bytes.len(), 8);
1654        // Header: class=0x0001, field=107, no mask, length=4
1655        // byte 2: (107 << 1) | 0 = 0xd6
1656        assert_eq!(&bytes[0..4], &[0x00, 0x01, 0xd6, 0x04]);
1657        assert_eq!(&bytes[4..8], &[0xaa, 0xbb, 0xcc, 0xdd]);
1658    }
1659
1660    #[test]
1661    fn encode_ct_mark_masked_value() {
1662        // NXM_NX_CT_MARK with high byte mask
1663        let bytes = encode_ct_mark_masked(0xff000000, 0xff000000);
1664
1665        assert_eq!(bytes.len(), 12);
1666        // byte 2: (107 << 1) | 1 = 0xd7
1667        assert_eq!(&bytes[0..4], &[0x00, 0x01, 0xd7, 0x08]);
1668        assert_eq!(&bytes[4..8], &[0xff, 0x00, 0x00, 0x00]);
1669        assert_eq!(&bytes[8..12], &[0xff, 0x00, 0x00, 0x00]);
1670    }
1671
1672    #[test]
1673    fn encode_ct_label_value() {
1674        // NXM_NX_CT_LABEL = 0x12345678_9abcdef0_12345678_9abcdef0
1675        let label: u128 = 0x123456789abcdef0_123456789abcdef0;
1676        let bytes = encode_ct_label(label);
1677
1678        assert_eq!(bytes.len(), 20);
1679        // Header: class=0x0001, field=108, no mask, length=16
1680        // byte 2: (108 << 1) | 0 = 0xd8
1681        assert_eq!(&bytes[0..4], &[0x00, 0x01, 0xd8, 0x10]);
1682        assert_eq!(
1683            &bytes[4..20],
1684            &[
1685                0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc,
1686                0xde, 0xf0
1687            ]
1688        );
1689    }
1690
1691    #[test]
1692    fn encode_ct_label_masked_value() {
1693        // NXM_NX_CT_LABEL with high 64-bit mask
1694        let label: u128 = 0xdeadbeef_00000000_00000000_00000000;
1695        let mask: u128 = 0xffffffff_00000000_00000000_00000000;
1696        let bytes = encode_ct_label_masked(label, mask);
1697
1698        assert_eq!(bytes.len(), 36);
1699        // Header: class=0x0001, field=108, has_mask=1, length=32
1700        // byte 2: (108 << 1) | 1 = 0xd9
1701        assert_eq!(&bytes[0..4], &[0x00, 0x01, 0xd9, 0x20]);
1702    }
1703
1704    #[test]
1705    fn encode_nxm_u32_via_enum() {
1706        // Test encode_nxm_u32 helper with NxmField enum
1707        let bytes = encode_nxm_u32(NxmField::PktMark, 0x42);
1708
1709        assert_eq!(bytes.len(), 8);
1710        // Same as encode_pkt_mark
1711        assert_eq!(&bytes[0..4], &[0x00, 0x01, 0x42, 0x04]);
1712        assert_eq!(&bytes[4..8], &[0x00, 0x00, 0x00, 0x42]);
1713    }
1714
1715    #[test]
1716    fn ct_state_flags_correct() {
1717        // Verify ct_state flag values match OVS definitions
1718        assert_eq!(ct_state::NEW, 0x01);
1719        assert_eq!(ct_state::EST, 0x02);
1720        assert_eq!(ct_state::REL, 0x04);
1721        assert_eq!(ct_state::RPL, 0x08);
1722        assert_eq!(ct_state::INV, 0x10);
1723        assert_eq!(ct_state::TRK, 0x20);
1724        assert_eq!(ct_state::SNAT, 0x40);
1725        assert_eq!(ct_state::DNAT, 0x80);
1726    }
1727
1728    // =========================================================================
1729    // Extended Register Encoding Tests (Phase 1.5)
1730    // =========================================================================
1731
1732    #[test]
1733    fn encode_xxreg_0() {
1734        // NXM_NX_XXREG0 = 0x0123456789abcdef_fedcba9876543210
1735        let value: u128 = 0x0123456789abcdef_fedcba9876543210;
1736        let bytes = encode_xxreg(0, value);
1737
1738        assert_eq!(bytes.len(), 20);
1739        // Header: class=0x0001, field=111, no mask, length=16
1740        // byte 2: (111 << 1) | 0 = 0xde
1741        assert_eq!(&bytes[0..4], &[0x00, 0x01, 0xde, 0x10]);
1742        assert_eq!(
1743            &bytes[4..20],
1744            &[
1745                0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54,
1746                0x32, 0x10
1747            ]
1748        );
1749    }
1750
1751    #[test]
1752    fn encode_xxreg_3() {
1753        // NXM_NX_XXREG3
1754        let value: u128 = 0xdeadbeef_cafebabe_12345678_9abcdef0;
1755        let bytes = encode_xxreg(3, value);
1756
1757        assert_eq!(bytes.len(), 20);
1758        // Header: class=0x0001, field=114, no mask, length=16
1759        // byte 2: (114 << 1) | 0 = 0xe4
1760        assert_eq!(&bytes[0..4], &[0x00, 0x01, 0xe4, 0x10]);
1761    }
1762
1763    #[test]
1764    fn encode_xxreg_masked_high64() {
1765        // NXM_NX_XXREG1 with high 64-bit mask
1766        let value: u128 = 0xaabbccdd_eeff0011_00000000_00000000;
1767        let mask: u128 = 0xffffffff_ffffffff_00000000_00000000;
1768        let bytes = encode_xxreg_masked(1, value, mask);
1769
1770        assert_eq!(bytes.len(), 36);
1771        // Header: class=0x0001, field=112, has_mask=1, length=32
1772        // byte 2: (112 << 1) | 1 = 0xe1
1773        assert_eq!(&bytes[0..4], &[0x00, 0x01, 0xe1, 0x20]);
1774    }
1775
1776    #[test]
1777    fn encode_xxreg_ipv6_address() {
1778        use std::net::Ipv6Addr;
1779
1780        // Store IPv6 address 2001:db8::1 in XXREG0
1781        let addr: Ipv6Addr = "2001:db8::1".parse().unwrap();
1782        let bytes = encode_xxreg_ipv6(0, addr);
1783
1784        assert_eq!(bytes.len(), 20);
1785        // Header: field=111
1786        assert_eq!(&bytes[0..4], &[0x00, 0x01, 0xde, 0x10]);
1787        // IPv6 address bytes: 2001:0db8:0000:0000:0000:0000:0000:0001
1788        assert_eq!(
1789            &bytes[4..20],
1790            &[0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01]
1791        );
1792    }
1793
1794    #[test]
1795    fn encode_xxreg_ipv6_masked_prefix() {
1796        use std::net::Ipv6Addr;
1797
1798        // Store IPv6 prefix 2001:db8::/32 in XXREG2
1799        let addr: Ipv6Addr = "2001:db8::".parse().unwrap();
1800        let mask: Ipv6Addr = "ffff:ffff::".parse().unwrap();
1801        let bytes = encode_xxreg_ipv6_masked(2, addr, mask);
1802
1803        assert_eq!(bytes.len(), 36);
1804        // Header: class=0x0001, field=113, has_mask=1, length=32
1805        // byte 2: (113 << 1) | 1 = 0xe3
1806        assert_eq!(&bytes[0..4], &[0x00, 0x01, 0xe3, 0x20]);
1807    }
1808
1809    #[test]
1810    fn prefix_to_mask_v6_64() {
1811        // /64 prefix
1812        assert_eq!(
1813            prefix_to_mask_v6(64),
1814            0xffffffff_ffffffff_00000000_00000000
1815        );
1816    }
1817
1818    #[test]
1819    fn prefix_to_mask_v6_128() {
1820        // /128 = full match
1821        assert_eq!(prefix_to_mask_v6(128), u128::MAX);
1822    }
1823
1824    #[test]
1825    fn prefix_to_mask_v6_0() {
1826        // /0 = match all
1827        assert_eq!(prefix_to_mask_v6(0), 0);
1828    }
1829
1830    #[test]
1831    fn prefix_to_mask_v6_various() {
1832        assert_eq!(prefix_to_mask_v6(32), 0xffffffff_00000000_00000000_00000000);
1833        assert_eq!(prefix_to_mask_v6(48), 0xffffffff_ffff0000_00000000_00000000);
1834        assert_eq!(
1835            prefix_to_mask_v6(96),
1836            0xffffffff_ffffffff_ffffffff_00000000
1837        );
1838        assert_eq!(
1839            prefix_to_mask_v6(120),
1840            0xffffffff_ffffffff_ffffffff_ffffff00
1841        );
1842    }
1843
1844    #[test]
1845    fn xxreg_field_numbers() {
1846        // Verify field numbers for xxreg0-3
1847        assert_eq!(NxmField::XxReg0 as u8, 111);
1848        assert_eq!(NxmField::XxReg1 as u8, 112);
1849        assert_eq!(NxmField::XxReg2 as u8, 113);
1850        assert_eq!(NxmField::XxReg3 as u8, 114);
1851    }
1852
1853    // =========================================================================
1854    // OxmEncode Trait Tests (Phase 1.6)
1855    // =========================================================================
1856
1857    #[test]
1858    fn trait_oxm_in_port() {
1859        let field = OxmMatchField::InPort(1);
1860        let bytes = field.encode();
1861
1862        // Should match encode_u32_field(OxmField::InPort, 1)
1863        assert_eq!(bytes, encode_u32_field(OxmField::InPort, 1));
1864        assert_eq!(bytes.len(), 8);
1865        assert_eq!(&bytes[0..4], &[0x80, 0x00, 0x00, 0x04]);
1866    }
1867
1868    #[test]
1869    fn trait_oxm_eth_type() {
1870        let field = OxmMatchField::EthType(0x0800);
1871        let bytes = field.encode();
1872
1873        assert_eq!(bytes, encode_u16_field(OxmField::EthType, 0x0800));
1874        assert_eq!(bytes.len(), 6);
1875    }
1876
1877    #[test]
1878    fn trait_oxm_ipv4_src_masked() {
1879        // 10.0.0.0/24
1880        let field = OxmMatchField::Ipv4SrcMasked(0x0a000000, 0xffffff00);
1881        let bytes = field.encode();
1882
1883        assert_eq!(
1884            bytes,
1885            encode_u32_masked_field(OxmField::Ipv4Src, 0x0a000000, 0xffffff00)
1886        );
1887        assert_eq!(bytes.len(), 12);
1888    }
1889
1890    #[test]
1891    fn trait_oxm_eth_dst() {
1892        let mac = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55];
1893        let field = OxmMatchField::EthDst(mac);
1894        let bytes = field.encode();
1895
1896        assert_eq!(bytes, encode_mac_field(OxmField::EthDst, mac));
1897        assert_eq!(bytes.len(), 10);
1898    }
1899
1900    #[test]
1901    fn trait_oxm_eth_src_masked() {
1902        // Match by OUI
1903        let mac = [0x00, 0x11, 0x22, 0x00, 0x00, 0x00];
1904        let mask = [0xff, 0xff, 0xff, 0x00, 0x00, 0x00];
1905        let field = OxmMatchField::EthSrcMasked(mac, mask);
1906        let bytes = field.encode();
1907
1908        assert_eq!(bytes, encode_mac_masked_field(OxmField::EthSrc, mac, mask));
1909        assert_eq!(bytes.len(), 16);
1910    }
1911
1912    #[test]
1913    fn trait_oxm_tcp_dst() {
1914        let field = OxmMatchField::TcpDst(80);
1915        let bytes = field.encode();
1916
1917        assert_eq!(bytes, encode_u16_field(OxmField::TcpDst, 80));
1918    }
1919
1920    #[test]
1921    fn trait_oxm_ip_proto() {
1922        let field = OxmMatchField::IpProto(6); // TCP
1923        let bytes = field.encode();
1924
1925        assert_eq!(bytes, encode_u8_field(OxmField::IpProto, 6));
1926        assert_eq!(bytes.len(), 5);
1927    }
1928
1929    #[test]
1930    fn trait_nxm_reg() {
1931        let field = NxmMatchField::Reg(0, 0x12345678);
1932        let bytes = field.encode();
1933
1934        assert_eq!(bytes, encode_reg(0, 0x12345678));
1935        assert_eq!(bytes.len(), 8);
1936    }
1937
1938    #[test]
1939    fn trait_nxm_reg_masked() {
1940        let field = NxmMatchField::RegMasked(5, 0xff00, 0xff00);
1941        let bytes = field.encode();
1942
1943        assert_eq!(bytes, encode_reg_masked(5, 0xff00, 0xff00));
1944        assert_eq!(bytes.len(), 12);
1945    }
1946
1947    #[test]
1948    fn trait_nxm_tun_id() {
1949        let field = NxmMatchField::TunId(1000);
1950        let bytes = field.encode();
1951
1952        assert_eq!(bytes, encode_tun_id(1000));
1953        assert_eq!(bytes.len(), 12);
1954    }
1955
1956    #[test]
1957    fn trait_nxm_ct_state() {
1958        let state = ct_state::TRK | ct_state::EST;
1959        let field = NxmMatchField::CtState(state);
1960        let bytes = field.encode();
1961
1962        assert_eq!(bytes, encode_ct_state(state));
1963    }
1964
1965    #[test]
1966    fn trait_nxm_ct_zone() {
1967        let field = NxmMatchField::CtZone(100);
1968        let bytes = field.encode();
1969
1970        assert_eq!(bytes, encode_ct_zone(100));
1971        assert_eq!(bytes.len(), 6);
1972    }
1973
1974    #[test]
1975    fn trait_nxm_xxreg() {
1976        let field = NxmMatchField::XxReg(0, 0x123456789abcdef0_fedcba9876543210);
1977        let bytes = field.encode();
1978
1979        assert_eq!(
1980            bytes,
1981            encode_xxreg(0, 0x123456789abcdef0_fedcba9876543210)
1982        );
1983        assert_eq!(bytes.len(), 20);
1984    }
1985
1986    #[test]
1987    fn trait_unified_match_field_oxm() {
1988        let field: MatchField = OxmMatchField::EthType(0x0800).into();
1989        let bytes = field.encode();
1990
1991        assert_eq!(bytes, encode_u16_field(OxmField::EthType, 0x0800));
1992    }
1993
1994    #[test]
1995    fn trait_unified_match_field_nxm() {
1996        let field: MatchField = NxmMatchField::Reg(0, 42).into();
1997        let bytes = field.encode();
1998
1999        assert_eq!(bytes, encode_reg(0, 42));
2000    }
2001
2002    #[test]
2003    fn trait_match_list_encoding() {
2004        // Test encoding a list of mixed fields
2005        let fields: Vec<MatchField> = vec![
2006            OxmMatchField::EthType(0x0800).into(),
2007            OxmMatchField::IpProto(6).into(),
2008            OxmMatchField::TcpDst(80).into(),
2009            NxmMatchField::Reg(0, 1).into(),
2010        ];
2011
2012        let mut encoded = Vec::new();
2013        for field in &fields {
2014            encoded.extend(field.encode());
2015        }
2016
2017        // Verify total length: 6 + 5 + 6 + 8 = 25 bytes
2018        assert_eq!(encoded.len(), 25);
2019    }
2020
2021    #[test]
2022    fn trait_oxm_vlan_vid_masked() {
2023        // VLAN ID with CFI bit mask
2024        let field = OxmMatchField::VlanVidMasked(0x1064, 0x1fff); // VID=100 with CFI
2025        let bytes = field.encode();
2026
2027        assert_eq!(bytes.len(), 8);
2028        // Header: field=6, has_mask=1, length=4
2029        // byte 2: (6 << 1) | 1 = 0x0d
2030        assert_eq!(&bytes[0..4], &[0x80, 0x00, 0x0d, 0x04]);
2031    }
2032
2033    #[test]
2034    fn trait_oxm_metadata_masked() {
2035        let field = OxmMatchField::MetadataMasked(0xff00_0000_0000_0000, 0xff00_0000_0000_0000);
2036        let bytes = field.encode();
2037
2038        assert_eq!(bytes.len(), 20);
2039    }
2040
2041    #[test]
2042    fn trait_nxm_ct_label() {
2043        let label: u128 = 0x12345678_9abcdef0_12345678_9abcdef0;
2044        let field = NxmMatchField::CtLabel(label);
2045        let bytes = field.encode();
2046
2047        assert_eq!(bytes, encode_ct_label(label));
2048        assert_eq!(bytes.len(), 20);
2049    }
2050}