rovs_openflow/
packet_in.rs1use bytes::{Buf, Bytes};
7
8use crate::match_fields::Match;
9use crate::{Error, Result};
10
11pub const OFP_NO_BUFFER: u32 = 0xffff_ffff;
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16#[repr(u8)]
17pub enum PacketInReason {
18 NoMatch = 0,
20 Action = 1,
22 InvalidTtl = 2,
24 ActionSet = 3,
26 Group = 4,
28 PacketOut = 5,
30}
31
32impl TryFrom<u8> for PacketInReason {
33 type Error = Error;
34
35 fn try_from(v: u8) -> Result<Self> {
36 match v {
37 0 => Ok(Self::NoMatch),
38 1 => Ok(Self::Action),
39 2 => Ok(Self::InvalidTtl),
40 3 => Ok(Self::ActionSet),
41 4 => Ok(Self::Group),
42 5 => Ok(Self::PacketOut),
43 _ => Err(Error::Parse(format!("unknown packet-in reason: {v}"))),
44 }
45 }
46}
47
48#[derive(Debug, Clone)]
50pub struct PacketIn {
51 pub buffer_id: u32,
53 pub total_len: u16,
55 pub reason: PacketInReason,
57 pub table_id: u8,
59 pub cookie: u64,
61 pub match_fields: Match,
63 pub data: Vec<u8>,
65}
66
67impl PacketIn {
68 pub fn parse(mut buf: Bytes) -> Result<Self> {
70 if buf.remaining() < 20 {
73 return Err(Error::Parse("packet-in too short".into()));
74 }
75
76 let buffer_id = buf.get_u32();
77 let total_len = buf.get_u16();
78 let reason = PacketInReason::try_from(buf.get_u8())?;
79 let table_id = buf.get_u8();
80 let cookie = buf.get_u64();
81
82 let match_type = buf.get_u16();
85 let match_len = buf.get_u16();
86
87 if match_type != 1 {
88 return Err(Error::Parse(format!(
90 "unsupported match type: {match_type}",
91 )));
92 }
93
94 let oxm_len = match_len.saturating_sub(4) as usize;
96 if buf.remaining() < oxm_len {
97 return Err(Error::Parse("packet-in match truncated".into()));
98 }
99
100 let oxm_bytes = buf.copy_to_bytes(oxm_len);
101 let match_fields = Match::decode_oxm(&oxm_bytes)?;
102
103 let padded_match_len = (match_len as usize + 7) & !7;
106 let padding = padded_match_len - match_len as usize;
107 if buf.remaining() < padding {
108 return Err(Error::Parse("packet-in padding missing".into()));
109 }
110 buf.advance(padding);
111
112 if buf.remaining() < 2 {
114 return Err(Error::Parse("packet-in data padding missing".into()));
115 }
116 buf.advance(2);
117
118 let data = buf.to_vec();
120
121 Ok(Self {
122 buffer_id,
123 total_len,
124 reason,
125 table_id,
126 cookie,
127 match_fields,
128 data,
129 })
130 }
131
132 pub fn is_buffered(&self) -> bool {
134 self.buffer_id != OFP_NO_BUFFER
135 }
136
137 pub fn in_port(&self) -> u32 {
139 self.match_fields.in_port.unwrap_or(0)
140 }
141
142 pub fn buffer_id(&self) -> Option<u32> {
144 if self.buffer_id == OFP_NO_BUFFER {
145 None
146 } else {
147 Some(self.buffer_id)
148 }
149 }
150
151 pub fn data(&self) -> &[u8] {
153 &self.data
154 }
155
156 #[cfg(any(test, feature = "test-support"))]
158 pub fn new_for_test(buffer_id: u32, in_port: u32, data: Vec<u8>) -> Self {
159 Self {
160 buffer_id,
161 total_len: data.len() as u16,
162 reason: PacketInReason::Action,
163 table_id: 0,
164 cookie: 0,
165 match_fields: Match::new().in_port(in_port),
166 data,
167 }
168 }
169}
170
171#[cfg(test)]
172mod tests {
173 use super::*;
174
175 #[test]
176 fn parse_packet_in_reason() {
177 assert_eq!(PacketInReason::try_from(0).unwrap(), PacketInReason::NoMatch);
178 assert_eq!(PacketInReason::try_from(1).unwrap(), PacketInReason::Action);
179 assert!(PacketInReason::try_from(99).is_err());
180 }
181}