Skip to main content

rovs_openflow/
match_fields.rs

1//! OpenFlow match field builder.
2
3use std::fmt;
4use std::net::{Ipv4Addr, Ipv6Addr};
5
6use crate::oxm::{self, ct_state, OxmClass, OxmField};
7
8/// MAC address type.
9pub type MacAddr = [u8; 6];
10
11/// OpenFlow match type (OF 1.2+).
12#[allow(dead_code)]
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14#[repr(u16)]
15pub enum MatchType {
16    /// Standard match (deprecated in OF 1.2+)
17    Standard = 0,
18    /// OXM (OpenFlow Extensible Match)
19    Oxm = 1,
20}
21
22/// Match fields for flow matching.
23///
24/// Uses a builder pattern for ergonomic construction.
25#[derive(Debug, Clone, Default)]
26pub struct Match {
27    /// Input port
28    pub in_port: Option<u32>,
29    /// Input physical port
30    pub in_phy_port: Option<u32>,
31    /// Metadata
32    pub metadata: Option<u64>,
33    /// Metadata mask
34    pub metadata_mask: Option<u64>,
35
36    // L2
37    /// Source MAC address
38    pub eth_src: Option<MacAddr>,
39    /// Source MAC mask
40    pub eth_src_mask: Option<MacAddr>,
41    /// Destination MAC address
42    pub eth_dst: Option<MacAddr>,
43    /// Destination MAC mask
44    pub eth_dst_mask: Option<MacAddr>,
45    /// Ethernet type
46    pub eth_type: Option<u16>,
47    /// VLAN ID
48    pub vlan_vid: Option<u16>,
49    /// VLAN PCP
50    pub vlan_pcp: Option<u8>,
51
52    // L3 IPv4
53    /// IPv4 source address
54    pub ipv4_src: Option<Ipv4Addr>,
55    /// IPv4 source mask (prefix length)
56    pub ipv4_src_mask: Option<u8>,
57    /// IPv4 destination address
58    pub ipv4_dst: Option<Ipv4Addr>,
59    /// IPv4 destination mask (prefix length)
60    pub ipv4_dst_mask: Option<u8>,
61    /// IP protocol
62    pub ip_proto: Option<u8>,
63    /// IP DSCP
64    pub ip_dscp: Option<u8>,
65    /// IP ECN
66    pub ip_ecn: Option<u8>,
67
68    // L3 IPv6
69    /// IPv6 source address
70    pub ipv6_src: Option<Ipv6Addr>,
71    /// IPv6 source mask (prefix length)
72    pub ipv6_src_mask: Option<u8>,
73    /// IPv6 destination address
74    pub ipv6_dst: Option<Ipv6Addr>,
75    /// IPv6 destination mask (prefix length)
76    pub ipv6_dst_mask: Option<u8>,
77    /// IPv6 flow label
78    pub ipv6_flabel: Option<u32>,
79
80    // L4 TCP
81    /// TCP source port
82    pub tcp_src: Option<u16>,
83    /// TCP destination port
84    pub tcp_dst: Option<u16>,
85    /// TCP flags
86    pub tcp_flags: Option<u16>,
87
88    // L4 UDP
89    /// UDP source port
90    pub udp_src: Option<u16>,
91    /// UDP destination port
92    pub udp_dst: Option<u16>,
93
94    // L4 ICMPv4
95    /// ICMPv4 type
96    pub icmp_type: Option<u8>,
97    /// ICMPv4 code
98    pub icmp_code: Option<u8>,
99
100    // L4 ICMPv6
101    /// ICMPv6 type
102    pub icmpv6_type: Option<u8>,
103    /// ICMPv6 code
104    pub icmpv6_code: Option<u8>,
105
106    // ARP
107    /// ARP opcode
108    pub arp_op: Option<u16>,
109    /// ARP source IPv4
110    pub arp_spa: Option<Ipv4Addr>,
111    /// ARP target IPv4
112    pub arp_tpa: Option<Ipv4Addr>,
113    /// ARP source MAC
114    pub arp_sha: Option<MacAddr>,
115    /// ARP target MAC
116    pub arp_tha: Option<MacAddr>,
117
118    // Tunnel
119    /// Tunnel ID
120    pub tunnel_id: Option<u64>,
121
122    // Connection tracking (Nicira extensions)
123    /// Connection tracking state (use ct_state:: constants)
124    pub ct_state: Option<u32>,
125    /// Connection tracking state mask
126    pub ct_state_mask: Option<u32>,
127    /// Connection tracking zone
128    pub ct_zone: Option<u16>,
129    /// Connection tracking mark
130    pub ct_mark: Option<u32>,
131    /// Connection tracking mark mask
132    pub ct_mark_mask: Option<u32>,
133}
134
135impl Match {
136    /// Create a new empty match (matches all packets).
137    pub fn new() -> Self {
138        Self::default()
139    }
140
141    /// Match on input port.
142    pub fn in_port(mut self, port: u32) -> Self {
143        self.in_port = Some(port);
144        self
145    }
146
147    /// Match on source MAC address.
148    pub fn eth_src(mut self, addr: MacAddr) -> Self {
149        self.eth_src = Some(addr);
150        self
151    }
152
153    /// Match on destination MAC address.
154    pub fn eth_dst(mut self, addr: MacAddr) -> Self {
155        self.eth_dst = Some(addr);
156        self
157    }
158
159    /// Match on Ethernet type.
160    pub fn eth_type(mut self, etype: u16) -> Self {
161        self.eth_type = Some(etype);
162        self
163    }
164
165    /// Match on VLAN ID.
166    pub fn vlan_vid(mut self, vid: u16) -> Self {
167        self.vlan_vid = Some(vid);
168        self
169    }
170
171    /// Match on IPv4 source address with prefix length.
172    pub fn ipv4_src(mut self, addr: Ipv4Addr, prefix_len: u8) -> Self {
173        self.eth_type = Some(0x0800); // IP
174        self.ipv4_src = Some(addr);
175        self.ipv4_src_mask = Some(prefix_len);
176        self
177    }
178
179    /// Match on IPv4 destination address with prefix length.
180    pub fn ipv4_dst(mut self, addr: Ipv4Addr, prefix_len: u8) -> Self {
181        self.eth_type = Some(0x0800); // IP
182        self.ipv4_dst = Some(addr);
183        self.ipv4_dst_mask = Some(prefix_len);
184        self
185    }
186
187    /// Match on IPv6 source address with prefix length.
188    pub fn ipv6_src(mut self, addr: Ipv6Addr, prefix_len: u8) -> Self {
189        self.eth_type = Some(0x86dd); // IPv6
190        self.ipv6_src = Some(addr);
191        self.ipv6_src_mask = Some(prefix_len);
192        self
193    }
194
195    /// Match on IPv6 destination address with prefix length.
196    pub fn ipv6_dst(mut self, addr: Ipv6Addr, prefix_len: u8) -> Self {
197        self.eth_type = Some(0x86dd); // IPv6
198        self.ipv6_dst = Some(addr);
199        self.ipv6_dst_mask = Some(prefix_len);
200        self
201    }
202
203    /// Match on IP protocol.
204    pub fn ip_proto(mut self, proto: u8) -> Self {
205        self.ip_proto = Some(proto);
206        self
207    }
208
209    /// Match on TCP source port.
210    pub fn tcp_src(mut self, port: u16) -> Self {
211        self.ip_proto = Some(6); // TCP
212        self.tcp_src = Some(port);
213        self
214    }
215
216    /// Match on TCP destination port.
217    pub fn tcp_dst(mut self, port: u16) -> Self {
218        self.ip_proto = Some(6); // TCP
219        self.tcp_dst = Some(port);
220        self
221    }
222
223    /// Match on UDP source port.
224    pub fn udp_src(mut self, port: u16) -> Self {
225        self.ip_proto = Some(17); // UDP
226        self.udp_src = Some(port);
227        self
228    }
229
230    /// Match on UDP destination port.
231    pub fn udp_dst(mut self, port: u16) -> Self {
232        self.ip_proto = Some(17); // UDP
233        self.udp_dst = Some(port);
234        self
235    }
236
237    /// Match on tunnel ID.
238    pub fn tunnel_id(mut self, id: u64) -> Self {
239        self.tunnel_id = Some(id);
240        self
241    }
242
243    /// Match on connection tracking state (Nicira extension).
244    ///
245    /// Use constants from `oxm::ct_state` module, e.g.:
246    /// - `ct_state::TRK` - packet has been tracked
247    /// - `ct_state::NEW` - new connection
248    /// - `ct_state::EST` - established connection
249    /// - `ct_state::REL` - related connection
250    /// - `ct_state::INV` - invalid connection
251    ///
252    /// # Example
253    ///
254    /// ```ignore
255    /// use rovs_openflow::oxm::ct_state;
256    ///
257    /// // Match established connections
258    /// Match::new().ct_state(ct_state::TRK | ct_state::EST)
259    /// ```
260    pub fn ct_state(mut self, state: u32) -> Self {
261        self.ct_state = Some(state);
262        self.ct_state_mask = Some(state); // Default mask = match all set bits
263        self
264    }
265
266    /// Match on connection tracking state with explicit mask.
267    ///
268    /// The mask specifies which bits of the state to match.
269    ///
270    /// # Example
271    ///
272    /// ```ignore
273    /// use rovs_openflow::oxm::ct_state;
274    ///
275    /// // Match tracked + new, ignoring other flags
276    /// Match::new().ct_state_masked(
277    ///     ct_state::TRK | ct_state::NEW,
278    ///     ct_state::TRK | ct_state::NEW
279    /// )
280    /// ```
281    pub fn ct_state_masked(mut self, state: u32, mask: u32) -> Self {
282        self.ct_state = Some(state);
283        self.ct_state_mask = Some(mask);
284        self
285    }
286
287    /// Match on connection tracking zone (Nicira extension).
288    ///
289    /// Zones allow multiple independent connection tracking tables.
290    pub fn ct_zone(mut self, zone: u16) -> Self {
291        self.ct_zone = Some(zone);
292        self
293    }
294
295    /// Match on connection tracking mark (Nicira extension).
296    ///
297    /// The ct_mark is a 32-bit value that can be set by the ct action
298    /// and matched on later.
299    pub fn ct_mark(mut self, mark: u32) -> Self {
300        self.ct_mark = Some(mark);
301        self
302    }
303
304    /// Match on connection tracking mark with mask (Nicira extension).
305    #[allow(clippy::similar_names)]
306    pub fn ct_mark_masked(mut self, mark: u32, mask: u32) -> Self {
307        self.ct_mark = Some(mark);
308        self.ct_mark_mask = Some(mask);
309        self
310    }
311
312    /// Match on ARP opcode.
313    ///
314    /// Common values: 1 = request, 2 = reply
315    pub fn arp_op(mut self, opcode: u16) -> Self {
316        self.eth_type = Some(0x0806); // ARP
317        self.arp_op = Some(opcode);
318        self
319    }
320
321    /// Match on ARP source protocol address (sender IP).
322    pub fn arp_spa(mut self, addr: impl Into<Ipv4Addr>) -> Self {
323        self.eth_type = Some(0x0806); // ARP
324        self.arp_spa = Some(addr.into());
325        self
326    }
327
328    /// Match on ARP target protocol address (target IP).
329    pub fn arp_tpa(mut self, addr: impl Into<Ipv4Addr>) -> Self {
330        self.eth_type = Some(0x0806); // ARP
331        self.arp_tpa = Some(addr.into());
332        self
333    }
334
335    /// Match on ARP source hardware address (sender MAC).
336    pub fn arp_sha(mut self, addr: MacAddr) -> Self {
337        self.eth_type = Some(0x0806); // ARP
338        self.arp_sha = Some(addr);
339        self
340    }
341
342    /// Match on ARP target hardware address (target MAC).
343    pub fn arp_tha(mut self, addr: MacAddr) -> Self {
344        self.eth_type = Some(0x0806); // ARP
345        self.arp_tha = Some(addr);
346        self
347    }
348
349    /// Match on ICMPv4 type.
350    ///
351    /// Common values: 0 = echo reply, 8 = echo request
352    pub fn icmp_type(mut self, icmp_type: u8) -> Self {
353        self.eth_type = Some(0x0800); // IPv4
354        self.ip_proto = Some(1); // ICMP
355        self.icmp_type = Some(icmp_type);
356        self
357    }
358
359    /// Match on ICMPv4 code.
360    pub fn icmp_code(mut self, code: u8) -> Self {
361        self.eth_type = Some(0x0800); // IPv4
362        self.ip_proto = Some(1); // ICMP
363        self.icmp_code = Some(code);
364        self
365    }
366
367    /// Match on ICMPv6 type.
368    ///
369    /// Common values: 128 = echo request, 129 = echo reply,
370    /// 133 = router solicitation, 134 = router advertisement,
371    /// 135 = neighbor solicitation, 136 = neighbor advertisement
372    pub fn icmpv6_type(mut self, icmp_type: u8) -> Self {
373        self.eth_type = Some(0x86dd); // IPv6
374        self.ip_proto = Some(58); // ICMPv6
375        self.icmpv6_type = Some(icmp_type);
376        self
377    }
378
379    /// Match on ICMPv6 code.
380    pub fn icmpv6_code(mut self, code: u8) -> Self {
381        self.eth_type = Some(0x86dd); // IPv6
382        self.ip_proto = Some(58); // ICMPv6
383        self.icmpv6_code = Some(code);
384        self
385    }
386
387    /// Check if this match is empty (matches all).
388    pub fn is_empty(&self) -> bool {
389        self.in_port.is_none()
390            && self.eth_src.is_none()
391            && self.eth_dst.is_none()
392            && self.eth_type.is_none()
393            && self.vlan_vid.is_none()
394            && self.ipv4_src.is_none()
395            && self.ipv4_dst.is_none()
396            && self.ip_proto.is_none()
397            && self.tcp_src.is_none()
398            && self.tcp_dst.is_none()
399            && self.udp_src.is_none()
400            && self.udp_dst.is_none()
401            && self.icmp_type.is_none()
402            && self.icmp_code.is_none()
403            && self.icmpv6_type.is_none()
404            && self.icmpv6_code.is_none()
405            && self.tunnel_id.is_none()
406            && self.arp_op.is_none()
407            && self.arp_spa.is_none()
408            && self.arp_tpa.is_none()
409            && self.arp_sha.is_none()
410            && self.arp_tha.is_none()
411            && self.ct_state.is_none()
412            && self.ct_zone.is_none()
413            && self.ct_mark.is_none()
414    }
415}
416
417/// Format a MAC address as `aa:bb:cc:dd:ee:ff`.
418fn format_mac(mac: MacAddr) -> String {
419    format!(
420        "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
421        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]
422    )
423}
424
425/// Format ct_state flags using OVS `+flag` notation.
426fn format_ct_state(state: u32) -> String {
427    let mut parts = Vec::new();
428    if state & ct_state::TRK != 0 { parts.push("+trk"); }
429    if state & ct_state::NEW != 0 { parts.push("+new"); }
430    if state & ct_state::EST != 0 { parts.push("+est"); }
431    if state & ct_state::REL != 0 { parts.push("+rel"); }
432    if state & ct_state::RPL != 0 { parts.push("+rpl"); }
433    if state & ct_state::INV != 0 { parts.push("+inv"); }
434    if state & ct_state::SNAT != 0 { parts.push("+snat"); }
435    if state & ct_state::DNAT != 0 { parts.push("+dnat"); }
436    if parts.is_empty() {
437        format!("0x{state:x}")
438    } else {
439        parts.join("")
440    }
441}
442
443impl fmt::Display for Match {
444    #[allow(clippy::too_many_lines)]
445    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
446        if self.is_empty() {
447            return write!(f, "*");
448        }
449
450        let mut parts: Vec<String> = Vec::new();
451
452        if let Some(p) = self.in_port {
453            parts.push(format!("in_port={p}"));
454        }
455        if let Some(p) = self.in_phy_port {
456            parts.push(format!("in_phy_port={p}"));
457        }
458        if let Some(m) = self.metadata {
459            if let Some(mask) = self.metadata_mask {
460                if mask == u64::MAX {
461                    parts.push(format!("metadata=0x{m:x}"));
462                } else {
463                    parts.push(format!("metadata=0x{m:x}/0x{mask:x}"));
464                }
465            } else {
466                parts.push(format!("metadata=0x{m:x}"));
467            }
468        }
469        if let Some(mac) = self.eth_src {
470            let s = format_mac(mac);
471            if let Some(mask) = self.eth_src_mask {
472                if mask == [0xff; 6] {
473                    parts.push(format!("eth_src={s}"));
474                } else {
475                    parts.push(format!("eth_src={s}/{}", format_mac(mask)));
476                }
477            } else {
478                parts.push(format!("eth_src={s}"));
479            }
480        }
481        if let Some(mac) = self.eth_dst {
482            let s = format_mac(mac);
483            if let Some(mask) = self.eth_dst_mask {
484                if mask == [0xff; 6] {
485                    parts.push(format!("eth_dst={s}"));
486                } else {
487                    parts.push(format!("eth_dst={s}/{}", format_mac(mask)));
488                }
489            } else {
490                parts.push(format!("eth_dst={s}"));
491            }
492        }
493        if let Some(et) = self.eth_type {
494            parts.push(format!("eth_type=0x{et:04x}"));
495        }
496        if let Some(vid) = self.vlan_vid {
497            parts.push(format!("vlan_vid={vid}"));
498        }
499        if let Some(pcp) = self.vlan_pcp {
500            parts.push(format!("vlan_pcp={pcp}"));
501        }
502        if let Some(proto) = self.ip_proto {
503            parts.push(format!("ip_proto={proto}"));
504        }
505        if let Some(dscp) = self.ip_dscp {
506            parts.push(format!("ip_dscp={dscp}"));
507        }
508        if let Some(ecn) = self.ip_ecn {
509            parts.push(format!("ip_ecn={ecn}"));
510        }
511        if let Some(ip) = self.ipv4_src {
512            let prefix = self.ipv4_src_mask.unwrap_or(32);
513            if prefix < 32 {
514                parts.push(format!("ipv4_src={ip}/{prefix}"));
515            } else {
516                parts.push(format!("ipv4_src={ip}"));
517            }
518        }
519        if let Some(ip) = self.ipv4_dst {
520            let prefix = self.ipv4_dst_mask.unwrap_or(32);
521            if prefix < 32 {
522                parts.push(format!("ipv4_dst={ip}/{prefix}"));
523            } else {
524                parts.push(format!("ipv4_dst={ip}"));
525            }
526        }
527        if let Some(ip) = self.ipv6_src {
528            let prefix = self.ipv6_src_mask.unwrap_or(128);
529            if prefix < 128 {
530                parts.push(format!("ipv6_src={ip}/{prefix}"));
531            } else {
532                parts.push(format!("ipv6_src={ip}"));
533            }
534        }
535        if let Some(ip) = self.ipv6_dst {
536            let prefix = self.ipv6_dst_mask.unwrap_or(128);
537            if prefix < 128 {
538                parts.push(format!("ipv6_dst={ip}/{prefix}"));
539            } else {
540                parts.push(format!("ipv6_dst={ip}"));
541            }
542        }
543        if let Some(fl) = self.ipv6_flabel {
544            parts.push(format!("ipv6_flabel=0x{fl:x}"));
545        }
546        if let Some(p) = self.tcp_src {
547            parts.push(format!("tcp_src={p}"));
548        }
549        if let Some(p) = self.tcp_dst {
550            parts.push(format!("tcp_dst={p}"));
551        }
552        if let Some(flags) = self.tcp_flags {
553            parts.push(format!("tcp_flags=0x{flags:x}"));
554        }
555        if let Some(p) = self.udp_src {
556            parts.push(format!("udp_src={p}"));
557        }
558        if let Some(p) = self.udp_dst {
559            parts.push(format!("udp_dst={p}"));
560        }
561        if let Some(t) = self.icmp_type {
562            parts.push(format!("icmp_type={t}"));
563        }
564        if let Some(c) = self.icmp_code {
565            parts.push(format!("icmp_code={c}"));
566        }
567        if let Some(t) = self.icmpv6_type {
568            parts.push(format!("icmpv6_type={t}"));
569        }
570        if let Some(c) = self.icmpv6_code {
571            parts.push(format!("icmpv6_code={c}"));
572        }
573        if let Some(op) = self.arp_op {
574            parts.push(format!("arp_op={op}"));
575        }
576        if let Some(ip) = self.arp_spa {
577            parts.push(format!("arp_spa={ip}"));
578        }
579        if let Some(ip) = self.arp_tpa {
580            parts.push(format!("arp_tpa={ip}"));
581        }
582        if let Some(mac) = self.arp_sha {
583            parts.push(format!("arp_sha={}", format_mac(mac)));
584        }
585        if let Some(mac) = self.arp_tha {
586            parts.push(format!("arp_tha={}", format_mac(mac)));
587        }
588        if let Some(id) = self.tunnel_id {
589            parts.push(format!("tunnel_id={id:#x}"));
590        }
591        if let Some(state) = self.ct_state {
592            let s = format_ct_state(state);
593            if let Some(mask) = self.ct_state_mask {
594                if mask == state {
595                    parts.push(format!("ct_state={s}"));
596                } else {
597                    parts.push(format!("ct_state={s}/{}", format_ct_state(mask)));
598                }
599            } else {
600                parts.push(format!("ct_state={s}"));
601            }
602        }
603        if let Some(z) = self.ct_zone {
604            parts.push(format!("ct_zone={z}"));
605        }
606        if let Some(mark) = self.ct_mark {
607            if let Some(mask) = self.ct_mark_mask {
608                if mask == u32::MAX {
609                    parts.push(format!("ct_mark=0x{mark:x}"));
610                } else {
611                    parts.push(format!("ct_mark=0x{mark:x}/0x{mask:x}"));
612                }
613            } else {
614                parts.push(format!("ct_mark=0x{mark:x}"));
615            }
616        }
617
618        write!(f, "{}", parts.join(","))
619    }
620}
621
622impl Match {
623    /// Decode OXM fields from raw bytes (without match header).
624    ///
625    /// This is useful for parsing match fields from Packet-In messages
626    /// where the match header has already been processed.
627    pub fn decode_oxm(oxm_data: &[u8]) -> crate::Result<Self> {
628        let mut m = Match::new();
629        let mut offset = 0;
630
631        while offset + 4 <= oxm_data.len() {
632            let header = u32::from_be_bytes([
633                oxm_data[offset],
634                oxm_data[offset + 1],
635                oxm_data[offset + 2],
636                oxm_data[offset + 3],
637            ]);
638
639            let oxm_class = (header >> 16) as u16;
640            let field = ((header >> 9) & 0x7f) as u8;
641            let has_mask = ((header >> 8) & 1) != 0;
642            let length = (header & 0xff) as usize;
643
644            offset += 4; // Skip header
645
646            if offset + length > oxm_data.len() {
647                break; // Not enough data
648            }
649
650            let value = &oxm_data[offset..offset + length];
651            let value_len = if has_mask { length / 2 } else { length };
652
653            // Decode based on class and field
654            if oxm_class == OxmClass::OpenflowBasic as u16 {
655                Self::decode_oxm_field(&mut m, field, has_mask, value, value_len);
656            } else if oxm_class == OxmClass::Nxm1 as u16 {
657                Self::decode_nxm_field(&mut m, field, has_mask, value, value_len);
658            } else if oxm_class == OxmClass::Nxm0 as u16 {
659                Self::decode_nxm0_field(&mut m, field, has_mask, value, value_len);
660            }
661            // Skip unknown classes
662
663            offset += length;
664        }
665
666        Ok(m)
667    }
668
669    /// Decode a match from OpenFlow wire format.
670    ///
671    /// Returns the decoded match and the total number of bytes consumed
672    /// (including padding to 8-byte boundary).
673    #[allow(clippy::too_many_lines)]
674    pub fn decode(data: &[u8]) -> crate::Result<(Self, usize)> {
675        if data.len() < 4 {
676            return Err(crate::Error::Parse("match header too short".into()));
677        }
678
679        let match_type = u16::from_be_bytes([data[0], data[1]]);
680        let match_len = u16::from_be_bytes([data[2], data[3]]) as usize;
681
682        if match_type != MatchType::Oxm as u16 {
683            return Err(crate::Error::Parse(format!(
684                "unsupported match type: {match_type}"
685            )));
686        }
687
688        if data.len() < match_len {
689            return Err(crate::Error::Parse("match data truncated".into()));
690        }
691
692        let mut m = Match::new();
693        let oxm_data = &data[4..match_len];
694        let mut offset = 0;
695
696        while offset + 4 <= oxm_data.len() {
697            let header = u32::from_be_bytes([
698                oxm_data[offset],
699                oxm_data[offset + 1],
700                oxm_data[offset + 2],
701                oxm_data[offset + 3],
702            ]);
703
704            let oxm_class = (header >> 16) as u16;
705            let field = ((header >> 9) & 0x7f) as u8;
706            let has_mask = ((header >> 8) & 1) != 0;
707            let length = (header & 0xff) as usize;
708
709            offset += 4; // Skip header
710
711            if offset + length > oxm_data.len() {
712                break; // Not enough data
713            }
714
715            let value = &oxm_data[offset..offset + length];
716            let value_len = if has_mask { length / 2 } else { length };
717
718            // Decode based on class and field
719            if oxm_class == OxmClass::OpenflowBasic as u16 {
720                Self::decode_oxm_field(&mut m, field, has_mask, value, value_len);
721            } else if oxm_class == OxmClass::Nxm1 as u16 {
722                Self::decode_nxm_field(&mut m, field, has_mask, value, value_len);
723            } else if oxm_class == OxmClass::Nxm0 as u16 {
724                Self::decode_nxm0_field(&mut m, field, has_mask, value, value_len);
725            }
726            // Skip unknown classes
727
728            offset += length;
729        }
730
731        // Calculate padded length (8-byte boundary)
732        let padded_len = (match_len + 7) & !7;
733
734        Ok((m, padded_len))
735    }
736
737    /// Decode an OXM field (OpenFlow Basic class).
738    #[allow(clippy::too_many_lines)]
739    fn decode_oxm_field(
740        m: &mut Match,
741        field: u8,
742        has_mask: bool,
743        value: &[u8],
744        value_len: usize,
745    ) {
746        match field {
747            f if f == OxmField::InPort as u8 => {
748                if value_len >= 4 {
749                    m.in_port = Some(u32::from_be_bytes([value[0], value[1], value[2], value[3]]));
750                }
751            }
752            f if f == OxmField::InPhyPort as u8 => {
753                if value_len >= 4 {
754                    m.in_phy_port = Some(u32::from_be_bytes([value[0], value[1], value[2], value[3]]));
755                }
756            }
757            f if f == OxmField::Metadata as u8 => {
758                if value_len >= 8 {
759                    m.metadata = Some(u64::from_be_bytes([
760                        value[0], value[1], value[2], value[3],
761                        value[4], value[5], value[6], value[7],
762                    ]));
763                    if has_mask && value.len() >= 16 {
764                        m.metadata_mask = Some(u64::from_be_bytes([
765                            value[8], value[9], value[10], value[11],
766                            value[12], value[13], value[14], value[15],
767                        ]));
768                    }
769                }
770            }
771            f if f == OxmField::EthDst as u8 => {
772                if value_len >= 6 {
773                    let mut mac = [0u8; 6];
774                    mac.copy_from_slice(&value[..6]);
775                    m.eth_dst = Some(mac);
776                    if has_mask && value.len() >= 12 {
777                        let mut mask = [0u8; 6];
778                        mask.copy_from_slice(&value[6..12]);
779                        m.eth_dst_mask = Some(mask);
780                    }
781                }
782            }
783            f if f == OxmField::EthSrc as u8 => {
784                if value_len >= 6 {
785                    let mut mac = [0u8; 6];
786                    mac.copy_from_slice(&value[..6]);
787                    m.eth_src = Some(mac);
788                    if has_mask && value.len() >= 12 {
789                        let mut mask = [0u8; 6];
790                        mask.copy_from_slice(&value[6..12]);
791                        m.eth_src_mask = Some(mask);
792                    }
793                }
794            }
795            f if f == OxmField::EthType as u8 => {
796                if value_len >= 2 {
797                    m.eth_type = Some(u16::from_be_bytes([value[0], value[1]]));
798                }
799            }
800            f if f == OxmField::VlanVid as u8 => {
801                if value_len >= 2 {
802                    let vid = u16::from_be_bytes([value[0], value[1]]);
803                    // Remove CFI bit (0x1000)
804                    m.vlan_vid = Some(vid & 0x0fff);
805                }
806            }
807            f if f == OxmField::VlanPcp as u8 => {
808                if value_len >= 1 {
809                    m.vlan_pcp = Some(value[0]);
810                }
811            }
812            f if f == OxmField::IpDscp as u8 => {
813                if value_len >= 1 {
814                    m.ip_dscp = Some(value[0]);
815                }
816            }
817            f if f == OxmField::IpEcn as u8 => {
818                if value_len >= 1 {
819                    m.ip_ecn = Some(value[0]);
820                }
821            }
822            f if f == OxmField::IpProto as u8 => {
823                if value_len >= 1 {
824                    m.ip_proto = Some(value[0]);
825                }
826            }
827            f if f == OxmField::Ipv4Src as u8 => {
828                if value_len >= 4 {
829                    let addr = Ipv4Addr::new(value[0], value[1], value[2], value[3]);
830                    m.ipv4_src = Some(addr);
831                    if has_mask && value.len() >= 8 {
832                        let mask = u32::from_be_bytes([value[4], value[5], value[6], value[7]]);
833                        m.ipv4_src_mask = Some(mask_to_prefix(mask));
834                    } else {
835                        m.ipv4_src_mask = Some(32);
836                    }
837                }
838            }
839            f if f == OxmField::Ipv4Dst as u8 => {
840                if value_len >= 4 {
841                    let addr = Ipv4Addr::new(value[0], value[1], value[2], value[3]);
842                    m.ipv4_dst = Some(addr);
843                    if has_mask && value.len() >= 8 {
844                        let mask = u32::from_be_bytes([value[4], value[5], value[6], value[7]]);
845                        m.ipv4_dst_mask = Some(mask_to_prefix(mask));
846                    } else {
847                        m.ipv4_dst_mask = Some(32);
848                    }
849                }
850            }
851            f if f == OxmField::TcpSrc as u8 => {
852                if value_len >= 2 {
853                    m.tcp_src = Some(u16::from_be_bytes([value[0], value[1]]));
854                }
855            }
856            f if f == OxmField::TcpDst as u8 => {
857                if value_len >= 2 {
858                    m.tcp_dst = Some(u16::from_be_bytes([value[0], value[1]]));
859                }
860            }
861            f if f == OxmField::UdpSrc as u8 => {
862                if value_len >= 2 {
863                    m.udp_src = Some(u16::from_be_bytes([value[0], value[1]]));
864                }
865            }
866            f if f == OxmField::UdpDst as u8 => {
867                if value_len >= 2 {
868                    m.udp_dst = Some(u16::from_be_bytes([value[0], value[1]]));
869                }
870            }
871            f if f == OxmField::Icmpv4Type as u8 => {
872                if value_len >= 1 {
873                    m.icmp_type = Some(value[0]);
874                }
875            }
876            f if f == OxmField::Icmpv4Code as u8 => {
877                if value_len >= 1 {
878                    m.icmp_code = Some(value[0]);
879                }
880            }
881            f if f == OxmField::Icmpv6Type as u8 => {
882                if value_len >= 1 {
883                    m.icmpv6_type = Some(value[0]);
884                }
885            }
886            f if f == OxmField::Icmpv6Code as u8 => {
887                if value_len >= 1 {
888                    m.icmpv6_code = Some(value[0]);
889                }
890            }
891            f if f == OxmField::ArpOp as u8 => {
892                if value_len >= 2 {
893                    m.arp_op = Some(u16::from_be_bytes([value[0], value[1]]));
894                }
895            }
896            f if f == OxmField::ArpSpa as u8 => {
897                if value_len >= 4 {
898                    m.arp_spa = Some(Ipv4Addr::new(value[0], value[1], value[2], value[3]));
899                }
900            }
901            f if f == OxmField::ArpTpa as u8 => {
902                if value_len >= 4 {
903                    m.arp_tpa = Some(Ipv4Addr::new(value[0], value[1], value[2], value[3]));
904                }
905            }
906            f if f == OxmField::ArpSha as u8 => {
907                if value_len >= 6 {
908                    let mut mac = [0u8; 6];
909                    mac.copy_from_slice(&value[..6]);
910                    m.arp_sha = Some(mac);
911                }
912            }
913            f if f == OxmField::ArpTha as u8 => {
914                if value_len >= 6 {
915                    let mut mac = [0u8; 6];
916                    mac.copy_from_slice(&value[..6]);
917                    m.arp_tha = Some(mac);
918                }
919            }
920            f if f == OxmField::Ipv6Src as u8 => {
921                if value_len >= 16 {
922                    let mut octets = [0u8; 16];
923                    octets.copy_from_slice(&value[..16]);
924                    m.ipv6_src = Some(Ipv6Addr::from(octets));
925                    if has_mask && value.len() >= 32 {
926                        let mut mask_bytes = [0u8; 16];
927                        mask_bytes.copy_from_slice(&value[16..32]);
928                        let mask = u128::from_be_bytes(mask_bytes);
929                        m.ipv6_src_mask = Some(mask_to_prefix_v6(mask));
930                    } else {
931                        m.ipv6_src_mask = Some(128);
932                    }
933                }
934            }
935            f if f == OxmField::Ipv6Dst as u8 => {
936                if value_len >= 16 {
937                    let mut octets = [0u8; 16];
938                    octets.copy_from_slice(&value[..16]);
939                    m.ipv6_dst = Some(Ipv6Addr::from(octets));
940                    if has_mask && value.len() >= 32 {
941                        let mut mask_bytes = [0u8; 16];
942                        mask_bytes.copy_from_slice(&value[16..32]);
943                        let mask = u128::from_be_bytes(mask_bytes);
944                        m.ipv6_dst_mask = Some(mask_to_prefix_v6(mask));
945                    } else {
946                        m.ipv6_dst_mask = Some(128);
947                    }
948                }
949            }
950            f if f == OxmField::Ipv6Flabel as u8 => {
951                if value_len >= 4 {
952                    m.ipv6_flabel = Some(u32::from_be_bytes([value[0], value[1], value[2], value[3]]));
953                }
954            }
955            f if f == OxmField::TunnelId as u8 => {
956                if value_len >= 8 {
957                    m.tunnel_id = Some(u64::from_be_bytes([
958                        value[0], value[1], value[2], value[3],
959                        value[4], value[5], value[6], value[7],
960                    ]));
961                }
962            }
963            _ => {
964                // Unknown field, skip
965            }
966        }
967    }
968
969    /// Decode an NXM field (Nicira extensions).
970    /// Decode an NXM0 field (Nicira class 0x0000 — OpenFlow 1.0 compatible fields).
971    ///
972    /// NXM0 field numbering differs from OXM (OpenFlow Basic class):
973    ///   0=IN_PORT, 1=ETH_DST, 2=ETH_SRC, 3=ETH_TYPE, 4=VLAN_TCI,
974    ///   5=IP_TOS, 6=IP_PROTO, 7=IP_SRC, 8=IP_DST, 9=TCP_SRC,
975    ///   10=TCP_DST, 11=UDP_SRC, 12=UDP_DST, 13=ICMP_TYPE, 14=ICMP_CODE,
976    ///   15=ARP_OP, 16=ARP_SPA, 17=ARP_TPA
977    fn decode_nxm0_field(
978        m: &mut Match,
979        field: u8,
980        has_mask: bool,
981        value: &[u8],
982        value_len: usize,
983    ) {
984        match field {
985            // NXM_OF_IN_PORT (2 bytes, stored as u32 in Match)
986            0 if value_len >= 2 => {
987                m.in_port = Some(u16::from_be_bytes([value[0], value[1]]) as u32);
988            }
989            // NXM_OF_ETH_DST (6 bytes)
990            1 if value_len >= 6 => {
991                let mut mac = [0u8; 6];
992                mac.copy_from_slice(&value[..6]);
993                m.eth_dst = Some(mac);
994                if has_mask && value.len() >= 12 {
995                    let mut mask = [0u8; 6];
996                    mask.copy_from_slice(&value[6..12]);
997                    m.eth_dst_mask = Some(mask);
998                }
999            }
1000            // NXM_OF_ETH_SRC (6 bytes)
1001            2 if value_len >= 6 => {
1002                let mut mac = [0u8; 6];
1003                mac.copy_from_slice(&value[..6]);
1004                m.eth_src = Some(mac);
1005                if has_mask && value.len() >= 12 {
1006                    let mut mask = [0u8; 6];
1007                    mask.copy_from_slice(&value[6..12]);
1008                    m.eth_src_mask = Some(mask);
1009                }
1010            }
1011            // NXM_OF_ETH_TYPE (2 bytes)
1012            3 if value_len >= 2 => {
1013                m.eth_type = Some(u16::from_be_bytes([value[0], value[1]]));
1014            }
1015            // NXM_OF_VLAN_TCI (2 bytes)
1016            4 if value_len >= 2 => {
1017                let tci = u16::from_be_bytes([value[0], value[1]]);
1018                // VLAN VID is the lower 12 bits with OFPVID_PRESENT (0x1000)
1019                m.vlan_vid = Some(tci);
1020            }
1021            // NXM_OF_IP_TOS (1 byte — contains DSCP in upper 6 bits)
1022            5 if value_len >= 1 => {
1023                m.ip_dscp = Some(value[0] >> 2);
1024            }
1025            // NXM_OF_IP_PROTO (1 byte)
1026            6 if value_len >= 1 => {
1027                m.ip_proto = Some(value[0]);
1028            }
1029            // NXM_OF_IP_SRC (4 bytes)
1030            7 if value_len >= 4 => {
1031                m.ipv4_src = Some(Ipv4Addr::new(value[0], value[1], value[2], value[3]));
1032                if has_mask && value.len() >= 8 {
1033                    let mask = u32::from_be_bytes([value[4], value[5], value[6], value[7]]);
1034                    m.ipv4_src_mask = Some(mask_to_prefix(mask));
1035                } else {
1036                    m.ipv4_src_mask = Some(32);
1037                }
1038            }
1039            // NXM_OF_IP_DST (4 bytes)
1040            8 if value_len >= 4 => {
1041                m.ipv4_dst = Some(Ipv4Addr::new(value[0], value[1], value[2], value[3]));
1042                if has_mask && value.len() >= 8 {
1043                    let mask = u32::from_be_bytes([value[4], value[5], value[6], value[7]]);
1044                    m.ipv4_dst_mask = Some(mask_to_prefix(mask));
1045                } else {
1046                    m.ipv4_dst_mask = Some(32);
1047                }
1048            }
1049            // NXM_OF_TCP_SRC (2 bytes)
1050            9 if value_len >= 2 => {
1051                m.tcp_src = Some(u16::from_be_bytes([value[0], value[1]]));
1052            }
1053            // NXM_OF_TCP_DST (2 bytes)
1054            10 if value_len >= 2 => {
1055                m.tcp_dst = Some(u16::from_be_bytes([value[0], value[1]]));
1056            }
1057            // NXM_OF_UDP_SRC (2 bytes)
1058            11 if value_len >= 2 => {
1059                m.udp_src = Some(u16::from_be_bytes([value[0], value[1]]));
1060            }
1061            // NXM_OF_UDP_DST (2 bytes)
1062            12 if value_len >= 2 => {
1063                m.udp_dst = Some(u16::from_be_bytes([value[0], value[1]]));
1064            }
1065            // NXM_OF_ICMP_TYPE (1 byte)
1066            13 if value_len >= 1 => {
1067                m.icmp_type = Some(value[0]);
1068            }
1069            // NXM_OF_ICMP_CODE (1 byte)
1070            14 if value_len >= 1 => {
1071                m.icmp_code = Some(value[0]);
1072            }
1073            // NXM_OF_ARP_OP (2 bytes)
1074            15 if value_len >= 2 => {
1075                m.arp_op = Some(u16::from_be_bytes([value[0], value[1]]));
1076            }
1077            // NXM_OF_ARP_SPA (4 bytes)
1078            16 if value_len >= 4 => {
1079                m.arp_spa = Some(Ipv4Addr::new(value[0], value[1], value[2], value[3]));
1080            }
1081            // NXM_OF_ARP_TPA (4 bytes)
1082            17 if value_len >= 4 => {
1083                m.arp_tpa = Some(Ipv4Addr::new(value[0], value[1], value[2], value[3]));
1084            }
1085            _ => {
1086                // Unknown NXM0 field, skip
1087            }
1088        }
1089    }
1090
1091    fn decode_nxm_field(
1092        m: &mut Match,
1093        field: u8,
1094        has_mask: bool,
1095        value: &[u8],
1096        value_len: usize,
1097    ) {
1098        match field {
1099            // NXM1 field 16 is TUN_ID
1100            16 if value_len >= 8 => {
1101                m.tunnel_id = Some(u64::from_be_bytes([
1102                    value[0], value[1], value[2], value[3],
1103                    value[4], value[5], value[6], value[7],
1104                ]));
1105            }
1106            // CT_STATE = 105
1107            105 if value_len >= 4 => {
1108                m.ct_state = Some(u32::from_be_bytes([
1109                    value[0], value[1], value[2], value[3],
1110                ]));
1111                if has_mask && value.len() >= 8 {
1112                    m.ct_state_mask = Some(u32::from_be_bytes([
1113                        value[4], value[5], value[6], value[7],
1114                    ]));
1115                }
1116            }
1117            // CT_ZONE = 106
1118            106 if value_len >= 2 => {
1119                m.ct_zone = Some(u16::from_be_bytes([value[0], value[1]]));
1120            }
1121            // CT_MARK = 107
1122            107 if value_len >= 4 => {
1123                m.ct_mark = Some(u32::from_be_bytes([
1124                    value[0], value[1], value[2], value[3],
1125                ]));
1126                if has_mask && value.len() >= 8 {
1127                    m.ct_mark_mask = Some(u32::from_be_bytes([
1128                        value[4], value[5], value[6], value[7],
1129                    ]));
1130                }
1131            }
1132            _ => {
1133                // Unknown NXM field, skip
1134            }
1135        }
1136    }
1137
1138    /// Encode the match to OpenFlow wire format (OXM).
1139    ///
1140    /// The match is encoded as:
1141    /// - Match header: type (2 bytes) + length (2 bytes)
1142    /// - OXM fields (variable)
1143    /// - Padding to 8-byte boundary
1144    ///
1145    /// Fields are encoded in OpenFlow-specified order with prerequisites
1146    /// automatically satisfied by the builder methods.
1147    #[allow(clippy::too_many_lines)]
1148    pub fn encode(&self) -> Vec<u8> {
1149        // Encode all OXM fields first
1150        let mut oxm_fields = Vec::new();
1151
1152        // Encode fields in OpenFlow-specified order
1153        // This ordering ensures prerequisites come before dependent fields
1154
1155        // Port fields
1156        if let Some(port) = self.in_port {
1157            oxm_fields.extend(oxm::encode_u32(
1158                OxmClass::OpenflowBasic,
1159                OxmField::InPort as u8,
1160                port,
1161            ));
1162        }
1163        if let Some(port) = self.in_phy_port {
1164            oxm_fields.extend(oxm::encode_u32(
1165                OxmClass::OpenflowBasic,
1166                OxmField::InPhyPort as u8,
1167                port,
1168            ));
1169        }
1170
1171        // Metadata
1172        if let Some(metadata) = self.metadata {
1173            if let Some(mask) = self.metadata_mask {
1174                oxm_fields.extend(oxm::encode_u64_masked(
1175                    OxmClass::OpenflowBasic,
1176                    OxmField::Metadata as u8,
1177                    metadata,
1178                    mask,
1179                ));
1180            } else {
1181                oxm_fields.extend(oxm::encode_u64(
1182                    OxmClass::OpenflowBasic,
1183                    OxmField::Metadata as u8,
1184                    metadata,
1185                ));
1186            }
1187        }
1188
1189        // L2 fields
1190        if let Some(mac) = self.eth_dst {
1191            if let Some(mask) = self.eth_dst_mask {
1192                oxm_fields.extend(oxm::encode_mac_masked(
1193                    OxmClass::OpenflowBasic,
1194                    OxmField::EthDst as u8,
1195                    mac,
1196                    mask,
1197                ));
1198            } else {
1199                oxm_fields.extend(oxm::encode_mac(
1200                    OxmClass::OpenflowBasic,
1201                    OxmField::EthDst as u8,
1202                    mac,
1203                ));
1204            }
1205        }
1206        if let Some(mac) = self.eth_src {
1207            if let Some(mask) = self.eth_src_mask {
1208                oxm_fields.extend(oxm::encode_mac_masked(
1209                    OxmClass::OpenflowBasic,
1210                    OxmField::EthSrc as u8,
1211                    mac,
1212                    mask,
1213                ));
1214            } else {
1215                oxm_fields.extend(oxm::encode_mac(
1216                    OxmClass::OpenflowBasic,
1217                    OxmField::EthSrc as u8,
1218                    mac,
1219                ));
1220            }
1221        }
1222        if let Some(eth_type) = self.eth_type {
1223            oxm_fields.extend(oxm::encode_u16(
1224                OxmClass::OpenflowBasic,
1225                OxmField::EthType as u8,
1226                eth_type,
1227            ));
1228        }
1229        if let Some(vid) = self.vlan_vid {
1230            // VLAN VID has CFI bit (0x1000) set when present
1231            oxm_fields.extend(oxm::encode_u16(
1232                OxmClass::OpenflowBasic,
1233                OxmField::VlanVid as u8,
1234                vid | 0x1000,
1235            ));
1236        }
1237        if let Some(pcp) = self.vlan_pcp {
1238            oxm_fields.extend(oxm::encode_u8(
1239                OxmClass::OpenflowBasic,
1240                OxmField::VlanPcp as u8,
1241                pcp,
1242            ));
1243        }
1244
1245        // L3 IPv4 fields
1246        if let Some(dscp) = self.ip_dscp {
1247            oxm_fields.extend(oxm::encode_u8(
1248                OxmClass::OpenflowBasic,
1249                OxmField::IpDscp as u8,
1250                dscp,
1251            ));
1252        }
1253        if let Some(ecn) = self.ip_ecn {
1254            oxm_fields.extend(oxm::encode_u8(
1255                OxmClass::OpenflowBasic,
1256                OxmField::IpEcn as u8,
1257                ecn,
1258            ));
1259        }
1260        if let Some(proto) = self.ip_proto {
1261            oxm_fields.extend(oxm::encode_u8(
1262                OxmClass::OpenflowBasic,
1263                OxmField::IpProto as u8,
1264                proto,
1265            ));
1266        }
1267        if let Some(addr) = self.ipv4_src {
1268            let addr_u32: u32 = addr.into();
1269            if let Some(prefix) = self.ipv4_src_mask {
1270                if prefix < 32 {
1271                    let mask = oxm::prefix_to_mask(prefix);
1272                    oxm_fields.extend(oxm::encode_u32_masked(
1273                        OxmClass::OpenflowBasic,
1274                        OxmField::Ipv4Src as u8,
1275                        addr_u32,
1276                        mask,
1277                    ));
1278                } else {
1279                    oxm_fields.extend(oxm::encode_u32(
1280                        OxmClass::OpenflowBasic,
1281                        OxmField::Ipv4Src as u8,
1282                        addr_u32,
1283                    ));
1284                }
1285            } else {
1286                oxm_fields.extend(oxm::encode_u32(
1287                    OxmClass::OpenflowBasic,
1288                    OxmField::Ipv4Src as u8,
1289                    addr_u32,
1290                ));
1291            }
1292        }
1293        if let Some(addr) = self.ipv4_dst {
1294            let addr_u32: u32 = addr.into();
1295            if let Some(prefix) = self.ipv4_dst_mask {
1296                if prefix < 32 {
1297                    let mask = oxm::prefix_to_mask(prefix);
1298                    oxm_fields.extend(oxm::encode_u32_masked(
1299                        OxmClass::OpenflowBasic,
1300                        OxmField::Ipv4Dst as u8,
1301                        addr_u32,
1302                        mask,
1303                    ));
1304                } else {
1305                    oxm_fields.extend(oxm::encode_u32(
1306                        OxmClass::OpenflowBasic,
1307                        OxmField::Ipv4Dst as u8,
1308                        addr_u32,
1309                    ));
1310                }
1311            } else {
1312                oxm_fields.extend(oxm::encode_u32(
1313                    OxmClass::OpenflowBasic,
1314                    OxmField::Ipv4Dst as u8,
1315                    addr_u32,
1316                ));
1317            }
1318        }
1319
1320        // L4 TCP fields
1321        if let Some(port) = self.tcp_src {
1322            oxm_fields.extend(oxm::encode_u16(
1323                OxmClass::OpenflowBasic,
1324                OxmField::TcpSrc as u8,
1325                port,
1326            ));
1327        }
1328        if let Some(port) = self.tcp_dst {
1329            oxm_fields.extend(oxm::encode_u16(
1330                OxmClass::OpenflowBasic,
1331                OxmField::TcpDst as u8,
1332                port,
1333            ));
1334        }
1335
1336        // L4 UDP fields
1337        if let Some(port) = self.udp_src {
1338            oxm_fields.extend(oxm::encode_u16(
1339                OxmClass::OpenflowBasic,
1340                OxmField::UdpSrc as u8,
1341                port,
1342            ));
1343        }
1344        if let Some(port) = self.udp_dst {
1345            oxm_fields.extend(oxm::encode_u16(
1346                OxmClass::OpenflowBasic,
1347                OxmField::UdpDst as u8,
1348                port,
1349            ));
1350        }
1351
1352        // ICMPv4 fields
1353        if let Some(icmp_type) = self.icmp_type {
1354            oxm_fields.extend(oxm::encode_u8(
1355                OxmClass::OpenflowBasic,
1356                OxmField::Icmpv4Type as u8,
1357                icmp_type,
1358            ));
1359        }
1360        if let Some(icmp_code) = self.icmp_code {
1361            oxm_fields.extend(oxm::encode_u8(
1362                OxmClass::OpenflowBasic,
1363                OxmField::Icmpv4Code as u8,
1364                icmp_code,
1365            ));
1366        }
1367
1368        // ICMPv6 fields
1369        if let Some(icmpv6_type) = self.icmpv6_type {
1370            oxm_fields.extend(oxm::encode_u8(
1371                OxmClass::OpenflowBasic,
1372                OxmField::Icmpv6Type as u8,
1373                icmpv6_type,
1374            ));
1375        }
1376        if let Some(icmpv6_code) = self.icmpv6_code {
1377            oxm_fields.extend(oxm::encode_u8(
1378                OxmClass::OpenflowBasic,
1379                OxmField::Icmpv6Code as u8,
1380                icmpv6_code,
1381            ));
1382        }
1383
1384        // ARP fields
1385        if let Some(op) = self.arp_op {
1386            oxm_fields.extend(oxm::encode_u16(
1387                OxmClass::OpenflowBasic,
1388                OxmField::ArpOp as u8,
1389                op,
1390            ));
1391        }
1392        if let Some(addr) = self.arp_spa {
1393            oxm_fields.extend(oxm::encode_u32(
1394                OxmClass::OpenflowBasic,
1395                OxmField::ArpSpa as u8,
1396                addr.into(),
1397            ));
1398        }
1399        if let Some(addr) = self.arp_tpa {
1400            oxm_fields.extend(oxm::encode_u32(
1401                OxmClass::OpenflowBasic,
1402                OxmField::ArpTpa as u8,
1403                addr.into(),
1404            ));
1405        }
1406        if let Some(mac) = self.arp_sha {
1407            oxm_fields.extend(oxm::encode_mac(
1408                OxmClass::OpenflowBasic,
1409                OxmField::ArpSha as u8,
1410                mac,
1411            ));
1412        }
1413        if let Some(mac) = self.arp_tha {
1414            oxm_fields.extend(oxm::encode_mac(
1415                OxmClass::OpenflowBasic,
1416                OxmField::ArpTha as u8,
1417                mac,
1418            ));
1419        }
1420
1421        // IPv6 fields
1422        if let Some(addr) = self.ipv6_src {
1423            let octets = addr.octets();
1424            let value = u128::from_be_bytes(octets);
1425            if let Some(prefix) = self.ipv6_src_mask {
1426                if prefix < 128 {
1427                    let mask = oxm::prefix_to_mask_v6(prefix);
1428                    oxm_fields.extend(encode_ipv6_masked(OxmField::Ipv6Src as u8, value, mask));
1429                } else {
1430                    oxm_fields.extend(encode_ipv6(OxmField::Ipv6Src as u8, value));
1431                }
1432            } else {
1433                oxm_fields.extend(encode_ipv6(OxmField::Ipv6Src as u8, value));
1434            }
1435        }
1436        if let Some(addr) = self.ipv6_dst {
1437            let octets = addr.octets();
1438            let value = u128::from_be_bytes(octets);
1439            if let Some(prefix) = self.ipv6_dst_mask {
1440                if prefix < 128 {
1441                    let mask = oxm::prefix_to_mask_v6(prefix);
1442                    oxm_fields.extend(encode_ipv6_masked(OxmField::Ipv6Dst as u8, value, mask));
1443                } else {
1444                    oxm_fields.extend(encode_ipv6(OxmField::Ipv6Dst as u8, value));
1445                }
1446            } else {
1447                oxm_fields.extend(encode_ipv6(OxmField::Ipv6Dst as u8, value));
1448            }
1449        }
1450        if let Some(flabel) = self.ipv6_flabel {
1451            oxm_fields.extend(oxm::encode_u32(
1452                OxmClass::OpenflowBasic,
1453                OxmField::Ipv6Flabel as u8,
1454                flabel,
1455            ));
1456        }
1457
1458        // Tunnel ID (NXM field)
1459        if let Some(tun_id) = self.tunnel_id {
1460            oxm_fields.extend(oxm::encode_tun_id(tun_id));
1461        }
1462
1463        // Connection tracking (NXM fields)
1464        if let Some(state) = self.ct_state {
1465            if let Some(mask) = self.ct_state_mask {
1466                if mask == 0xffff_ffff {
1467                    oxm_fields.extend(oxm::encode_ct_state(state));
1468                } else {
1469                    oxm_fields.extend(oxm::encode_ct_state_masked(state, mask));
1470                }
1471            } else {
1472                oxm_fields.extend(oxm::encode_ct_state(state));
1473            }
1474        }
1475        if let Some(zone) = self.ct_zone {
1476            oxm_fields.extend(oxm::encode_ct_zone(zone));
1477        }
1478        if let Some(mark) = self.ct_mark {
1479            if let Some(mask) = self.ct_mark_mask {
1480                oxm_fields.extend(oxm::encode_ct_mark_masked(mark, mask));
1481            } else {
1482                oxm_fields.extend(oxm::encode_ct_mark(mark));
1483            }
1484        }
1485
1486        // Build match structure
1487        // Match header: type (2) + length (2) + OXM fields + padding
1488        // Length includes header (4 bytes) + OXM fields length
1489        let oxm_len = oxm_fields.len();
1490        let match_len = 4 + oxm_len; // header + OXM fields
1491        let padded_len = (match_len + 7) & !7; // Round up to 8-byte boundary
1492        let padding = padded_len - match_len;
1493
1494        let mut buf = Vec::with_capacity(padded_len);
1495        buf.extend((MatchType::Oxm as u16).to_be_bytes()); // type = 1 (OXM)
1496        buf.extend((match_len as u16).to_be_bytes()); // length (includes header)
1497        buf.extend(oxm_fields);
1498        buf.extend(std::iter::repeat_n(0u8, padding));
1499        buf
1500    }
1501
1502    /// Encode just the OXM/NXM field TLVs without the match header.
1503    ///
1504    /// This is used by protocols that embed raw OXM fields without the
1505    /// standard OpenFlow match header (e.g., Nicira flow monitor requests).
1506    pub fn encode_oxm_fields(&self) -> Vec<u8> {
1507        // Re-use the full encode, then strip the 4-byte header and padding
1508        let full = self.encode();
1509        if full.len() <= 4 {
1510            return Vec::new();
1511        }
1512        let match_len = u16::from_be_bytes([full[2], full[3]]) as usize;
1513        // OXM fields are bytes 4..match_len (before padding)
1514        full[4..match_len].to_vec()
1515    }
1516}
1517
1518/// Convert a 32-bit network mask to prefix length.
1519fn mask_to_prefix(mask: u32) -> u8 {
1520    mask.leading_ones() as u8
1521}
1522
1523/// Convert a 128-bit network mask to prefix length.
1524fn mask_to_prefix_v6(mask: u128) -> u8 {
1525    mask.leading_ones() as u8
1526}
1527
1528// Helper functions for IPv6 encoding (16 bytes)
1529fn encode_ipv6(field: u8, value: u128) -> Vec<u8> {
1530    let mut buf = Vec::with_capacity(20);
1531    // OXM header: class=0x8000, field, has_mask=false, length=16
1532    let oxm_header = ((OxmClass::OpenflowBasic as u32) << 16) | ((field as u32) << 9) | 16;
1533    buf.extend(oxm_header.to_be_bytes());
1534    buf.extend(value.to_be_bytes());
1535    buf
1536}
1537
1538fn encode_ipv6_masked(field: u8, value: u128, mask: u128) -> Vec<u8> {
1539    let mut buf = Vec::with_capacity(36);
1540    // OXM header: class=0x8000, field, has_mask=true, length=32
1541    let oxm_header = ((OxmClass::OpenflowBasic as u32) << 16) | ((field as u32) << 9) | (1 << 8) | 32;
1542    buf.extend(oxm_header.to_be_bytes());
1543    buf.extend(value.to_be_bytes());
1544    buf.extend(mask.to_be_bytes());
1545    buf
1546}
1547
1548#[cfg(test)]
1549mod tests {
1550    use super::*;
1551
1552    #[test]
1553    fn match_type_values() {
1554        assert_eq!(MatchType::Standard as u16, 0);
1555        assert_eq!(MatchType::Oxm as u16, 1);
1556    }
1557
1558    #[test]
1559    fn encode_empty_match() {
1560        let m = Match::new();
1561        let bytes = m.encode();
1562        // Empty match: header (4) + padding (4) = 8 bytes
1563        assert_eq!(bytes.len(), 8);
1564        // type = 1 (OXM)
1565        assert_eq!(&bytes[0..2], &[0x00, 0x01]);
1566        // length = 4 (just header, no fields)
1567        assert_eq!(&bytes[2..4], &[0x00, 0x04]);
1568        // padding to 8 bytes
1569        assert_eq!(&bytes[4..8], &[0, 0, 0, 0]);
1570    }
1571
1572    #[test]
1573    fn encode_in_port_match() {
1574        let m = Match::new().in_port(1);
1575        let bytes = m.encode();
1576        // Header (4) + InPort OXM (4 header + 4 value) = 12, padded to 16
1577        assert_eq!(bytes.len(), 16);
1578        // type = 1 (OXM)
1579        assert_eq!(&bytes[0..2], &[0x00, 0x01]);
1580        // length = 12 (header + OXM)
1581        assert_eq!(&bytes[2..4], &[0x00, 0x0c]);
1582        // OXM header: class=0x8000, field=0 (InPort), has_mask=0, length=4
1583        let expected_oxm: u32 = (0x8000 << 16) | (0 << 9) | 4;
1584        assert_eq!(&bytes[4..8], &expected_oxm.to_be_bytes());
1585        // InPort value = 1
1586        assert_eq!(&bytes[8..12], &[0x00, 0x00, 0x00, 0x01]);
1587    }
1588
1589    #[test]
1590    fn encode_eth_type_match() {
1591        let m = Match::new().eth_type(0x0800);
1592        let bytes = m.encode();
1593        // Header (4) + EthType OXM (4 header + 2 value) = 10, padded to 16
1594        assert_eq!(bytes.len(), 16);
1595        // length = 10
1596        assert_eq!(&bytes[2..4], &[0x00, 0x0a]);
1597        // OXM header: class=0x8000, field=5 (EthType), has_mask=0, length=2
1598        let expected_oxm: u32 = (0x8000 << 16) | (5 << 9) | 2;
1599        assert_eq!(&bytes[4..8], &expected_oxm.to_be_bytes());
1600        // EthType value = 0x0800
1601        assert_eq!(&bytes[8..10], &[0x08, 0x00]);
1602    }
1603
1604    #[test]
1605    fn encode_ipv4_dst_with_prefix() {
1606        let m = Match::new().ipv4_dst("10.0.0.0".parse().unwrap(), 24);
1607        let bytes = m.encode();
1608        // Header (4) + EthType (6) + Ipv4Dst masked (12) = 22, padded to 24
1609        assert_eq!(bytes.len(), 24);
1610        // EthType should be auto-set to 0x0800
1611        // Check EthType OXM at offset 4
1612        let eth_type_oxm: u32 = (0x8000 << 16) | (5 << 9) | 2;
1613        assert_eq!(&bytes[4..8], &eth_type_oxm.to_be_bytes());
1614        assert_eq!(&bytes[8..10], &[0x08, 0x00]); // EthType = IPv4
1615    }
1616
1617    #[test]
1618    fn encode_tcp_dst_match() {
1619        let m = Match::new().eth_type(0x0800).ip_proto(6).tcp_dst(80);
1620        let bytes = m.encode();
1621        // Header (4) + EthType (6) + IpProto (5) + TcpDst (6) = 21, padded to 24
1622        assert_eq!(bytes.len(), 24);
1623    }
1624
1625    #[test]
1626    fn encode_eth_dst_match() {
1627        let mac: MacAddr = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55];
1628        let m = Match::new().eth_dst(mac);
1629        let bytes = m.encode();
1630        // Header (4) + EthDst OXM (4 header + 6 value) = 14, padded to 16
1631        assert_eq!(bytes.len(), 16);
1632        // OXM header: class=0x8000, field=3 (EthDst), has_mask=0, length=6
1633        let expected_oxm: u32 = (0x8000 << 16) | (3 << 9) | 6;
1634        assert_eq!(&bytes[4..8], &expected_oxm.to_be_bytes());
1635        // MAC address
1636        assert_eq!(&bytes[8..14], &mac);
1637    }
1638
1639    #[test]
1640    fn encode_vlan_vid_match() {
1641        let m = Match::new().vlan_vid(100);
1642        let bytes = m.encode();
1643        // Header (4) + VlanVid OXM (4 header + 2 value) = 10, padded to 16
1644        assert_eq!(bytes.len(), 16);
1645        // OXM header: class=0x8000, field=6 (VlanVid), has_mask=0, length=2
1646        let expected_oxm: u32 = (0x8000 << 16) | (6 << 9) | 2;
1647        assert_eq!(&bytes[4..8], &expected_oxm.to_be_bytes());
1648        // VLAN VID with CFI bit = 100 | 0x1000 = 0x1064
1649        assert_eq!(&bytes[8..10], &[0x10, 0x64]);
1650    }
1651
1652    #[test]
1653    fn encode_tunnel_id_match() {
1654        let m = Match::new().tunnel_id(0x1234);
1655        let bytes = m.encode();
1656        // Header (4) + TunId NXM (4 header + 8 value) = 16, already aligned
1657        assert_eq!(bytes.len(), 16);
1658    }
1659
1660    #[test]
1661    fn encode_multiple_fields() {
1662        let m = Match::new()
1663            .in_port(1)
1664            .eth_type(0x0800)
1665            .ipv4_dst("192.168.1.0".parse().unwrap(), 24);
1666        let bytes = m.encode();
1667        // Verify it's 8-byte aligned
1668        assert_eq!(bytes.len() % 8, 0);
1669        // Verify type = OXM
1670        assert_eq!(&bytes[0..2], &[0x00, 0x01]);
1671    }
1672
1673    #[test]
1674    fn match_8_byte_alignment() {
1675        // Test various field combinations ensure 8-byte alignment
1676        let m1 = Match::new().in_port(1);
1677        assert_eq!(m1.encode().len() % 8, 0);
1678
1679        let m2 = Match::new().eth_type(0x0800);
1680        assert_eq!(m2.encode().len() % 8, 0);
1681
1682        let m3 = Match::new().ip_proto(6);
1683        assert_eq!(m3.encode().len() % 8, 0);
1684
1685        let m4 = Match::new()
1686            .in_port(1)
1687            .eth_type(0x0800)
1688            .ip_proto(6)
1689            .tcp_dst(80);
1690        assert_eq!(m4.encode().len() % 8, 0);
1691    }
1692
1693    // Decode tests
1694
1695    #[test]
1696    fn decode_empty_match() {
1697        // Empty match: type=1 (OXM), length=4, padding
1698        let data = [0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00];
1699        let (m, len) = Match::decode(&data).unwrap();
1700        assert_eq!(len, 8);
1701        assert!(m.is_empty());
1702    }
1703
1704    #[test]
1705    fn decode_in_port() {
1706        let original = Match::new().in_port(42);
1707        let encoded = original.encode();
1708        let (decoded, _) = Match::decode(&encoded).unwrap();
1709        assert_eq!(decoded.in_port, Some(42));
1710    }
1711
1712    #[test]
1713    fn decode_eth_type() {
1714        let original = Match::new().eth_type(0x0800);
1715        let encoded = original.encode();
1716        let (decoded, _) = Match::decode(&encoded).unwrap();
1717        assert_eq!(decoded.eth_type, Some(0x0800));
1718    }
1719
1720    #[test]
1721    fn decode_vlan_vid() {
1722        let original = Match::new().vlan_vid(100);
1723        let encoded = original.encode();
1724        let (decoded, _) = Match::decode(&encoded).unwrap();
1725        assert_eq!(decoded.vlan_vid, Some(100));
1726    }
1727
1728    #[test]
1729    fn decode_ipv4_dst() {
1730        let original = Match::new().ipv4_dst("10.0.0.0".parse().unwrap(), 24);
1731        let encoded = original.encode();
1732        let (decoded, _) = Match::decode(&encoded).unwrap();
1733        assert_eq!(decoded.ipv4_dst, Some("10.0.0.0".parse().unwrap()));
1734        assert_eq!(decoded.ipv4_dst_mask, Some(24));
1735    }
1736
1737    #[test]
1738    fn decode_tcp_dst() {
1739        let original = Match::new().eth_type(0x0800).ip_proto(6).tcp_dst(80);
1740        let encoded = original.encode();
1741        let (decoded, _) = Match::decode(&encoded).unwrap();
1742        assert_eq!(decoded.eth_type, Some(0x0800));
1743        assert_eq!(decoded.ip_proto, Some(6));
1744        assert_eq!(decoded.tcp_dst, Some(80));
1745    }
1746
1747    #[test]
1748    fn decode_eth_dst() {
1749        let mac: MacAddr = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55];
1750        let original = Match::new().eth_dst(mac);
1751        let encoded = original.encode();
1752        let (decoded, _) = Match::decode(&encoded).unwrap();
1753        assert_eq!(decoded.eth_dst, Some(mac));
1754    }
1755
1756    #[test]
1757    fn decode_multiple_fields() {
1758        let original = Match::new()
1759            .in_port(1)
1760            .eth_type(0x0800)
1761            .ip_proto(6)
1762            .tcp_dst(443);
1763        let encoded = original.encode();
1764        let (decoded, _) = Match::decode(&encoded).unwrap();
1765        assert_eq!(decoded.in_port, Some(1));
1766        assert_eq!(decoded.eth_type, Some(0x0800));
1767        assert_eq!(decoded.ip_proto, Some(6));
1768        assert_eq!(decoded.tcp_dst, Some(443));
1769    }
1770
1771    #[test]
1772    fn roundtrip_encode_decode() {
1773        // Create a complex match and verify roundtrip
1774        let original = Match::new()
1775            .in_port(5)
1776            .eth_dst([0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff])
1777            .eth_type(0x0800)
1778            .ipv4_dst("192.168.1.0".parse().unwrap(), 24)
1779            .ip_proto(17)
1780            .udp_dst(53);
1781
1782        let encoded = original.encode();
1783        let (decoded, len) = Match::decode(&encoded).unwrap();
1784
1785        assert_eq!(len, encoded.len());
1786        assert_eq!(decoded.in_port, Some(5));
1787        assert_eq!(decoded.eth_dst, Some([0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff]));
1788        assert_eq!(decoded.eth_type, Some(0x0800));
1789        assert_eq!(decoded.ipv4_dst, Some("192.168.1.0".parse().unwrap()));
1790        assert_eq!(decoded.ipv4_dst_mask, Some(24));
1791        assert_eq!(decoded.ip_proto, Some(17));
1792        assert_eq!(decoded.udp_dst, Some(53));
1793    }
1794
1795    #[test]
1796    fn mask_to_prefix_conversion() {
1797        assert_eq!(mask_to_prefix(0xffff_ffff), 32);
1798        assert_eq!(mask_to_prefix(0xffff_ff00), 24);
1799        assert_eq!(mask_to_prefix(0xffff_0000), 16);
1800        assert_eq!(mask_to_prefix(0xff00_0000), 8);
1801        assert_eq!(mask_to_prefix(0x0000_0000), 0);
1802    }
1803
1804    #[test]
1805    fn mask_to_prefix_v6_conversion() {
1806        assert_eq!(mask_to_prefix_v6(u128::MAX), 128);
1807        assert_eq!(mask_to_prefix_v6(u128::MAX << 64), 64);
1808        assert_eq!(mask_to_prefix_v6(0), 0);
1809    }
1810
1811    // Connection tracking tests
1812
1813    #[test]
1814    fn encode_ct_state() {
1815        use crate::oxm::ct_state;
1816        let m = Match::new().ct_state(ct_state::TRK | ct_state::EST);
1817        let bytes = m.encode();
1818        // Should have match header + ct_state OXM
1819        assert!(bytes.len() >= 8);
1820        // Verify it's 8-byte aligned
1821        assert_eq!(bytes.len() % 8, 0);
1822    }
1823
1824    #[test]
1825    fn encode_ct_state_masked() {
1826        use crate::oxm::ct_state;
1827        let m = Match::new().ct_state_masked(
1828            ct_state::TRK | ct_state::NEW,
1829            ct_state::TRK | ct_state::NEW,
1830        );
1831        let bytes = m.encode();
1832        assert!(bytes.len() >= 8);
1833        assert_eq!(bytes.len() % 8, 0);
1834    }
1835
1836    #[test]
1837    fn encode_ct_zone() {
1838        let m = Match::new().ct_zone(100);
1839        let bytes = m.encode();
1840        assert!(bytes.len() >= 8);
1841        assert_eq!(bytes.len() % 8, 0);
1842    }
1843
1844    #[test]
1845    fn encode_ct_mark() {
1846        let m = Match::new().ct_mark(0xaabbccdd);
1847        let bytes = m.encode();
1848        assert!(bytes.len() >= 8);
1849        assert_eq!(bytes.len() % 8, 0);
1850    }
1851
1852    #[test]
1853    fn encode_ct_mark_masked() {
1854        let m = Match::new().ct_mark_masked(0xff000000, 0xff000000);
1855        let bytes = m.encode();
1856        assert!(bytes.len() >= 8);
1857        assert_eq!(bytes.len() % 8, 0);
1858    }
1859
1860    #[test]
1861    fn roundtrip_ct_state() {
1862        use crate::oxm::ct_state;
1863        let state = ct_state::TRK | ct_state::EST;
1864        let original = Match::new().ct_state(state);
1865        let encoded = original.encode();
1866        let (decoded, _) = Match::decode(&encoded).unwrap();
1867        assert_eq!(decoded.ct_state, Some(state));
1868    }
1869
1870    #[test]
1871    fn roundtrip_ct_zone() {
1872        let original = Match::new().ct_zone(42);
1873        let encoded = original.encode();
1874        let (decoded, _) = Match::decode(&encoded).unwrap();
1875        assert_eq!(decoded.ct_zone, Some(42));
1876    }
1877
1878    #[test]
1879    fn roundtrip_ct_mark() {
1880        let original = Match::new().ct_mark(0x12345678);
1881        let encoded = original.encode();
1882        let (decoded, _) = Match::decode(&encoded).unwrap();
1883        assert_eq!(decoded.ct_mark, Some(0x12345678));
1884    }
1885
1886    #[test]
1887    fn stateful_firewall_match() {
1888        use crate::oxm::ct_state;
1889        // Test a typical stateful firewall match pattern
1890        let m = Match::new()
1891            .eth_type(0x0800)
1892            .ct_state(ct_state::TRK | ct_state::EST);
1893
1894        let bytes = m.encode();
1895        let (decoded, _) = Match::decode(&bytes).unwrap();
1896
1897        assert_eq!(decoded.eth_type, Some(0x0800));
1898        assert_eq!(decoded.ct_state, Some(ct_state::TRK | ct_state::EST));
1899    }
1900}