Skip to main content

rovs_openflow/
packet_out.rs

1//! OpenFlow Packet-Out message encoding.
2//!
3//! Packet-Out messages are sent from the controller to the switch to inject
4//! a packet into the datapath or to release a buffered packet.
5
6use bytes::{BufMut, BytesMut};
7
8use crate::action::ActionList;
9use crate::message::{Header, Message, MessageType};
10use crate::packet_in::OFP_NO_BUFFER;
11use crate::Version;
12
13/// Special input port value meaning "controller".
14pub const OFPP_CONTROLLER: u32 = 0xffff_fffd;
15
16/// Special input port value meaning "any" (no specific port).
17pub const OFPP_ANY: u32 = 0xffff_ffff;
18
19/// Packet-Out message builder.
20#[derive(Debug, Clone)]
21pub struct PacketOut {
22    /// Buffer ID from Packet-In, or OFP_NO_BUFFER to send packet data.
23    buffer_id: u32,
24    /// Input port to use for action processing (affects output:IN_PORT).
25    in_port: u32,
26    /// Actions to apply to the packet.
27    actions: ActionList,
28    /// Packet data (required if buffer_id is OFP_NO_BUFFER).
29    data: Vec<u8>,
30}
31
32impl PacketOut {
33    /// Create a new Packet-Out with no buffer (packet data will be provided).
34    pub fn new() -> Self {
35        Self {
36            buffer_id: OFP_NO_BUFFER,
37            in_port: OFPP_CONTROLLER,
38            actions: ActionList::new(),
39            data: Vec::new(),
40        }
41    }
42
43    /// Create a Packet-Out that releases a buffered packet.
44    pub fn from_buffer(buffer_id: u32) -> Self {
45        Self {
46            buffer_id,
47            in_port: OFPP_CONTROLLER,
48            actions: ActionList::new(),
49            data: Vec::new(),
50        }
51    }
52
53    /// Set the buffer ID (use OFP_NO_BUFFER to include packet data).
54    pub fn buffer_id(mut self, id: u32) -> Self {
55        self.buffer_id = id;
56        self
57    }
58
59    /// Set the input port for action context.
60    pub fn in_port(mut self, port: u32) -> Self {
61        self.in_port = port;
62        self
63    }
64
65    /// Set the actions to apply to the packet.
66    pub fn actions(mut self, actions: ActionList) -> Self {
67        self.actions = actions;
68        self
69    }
70
71    /// Set the packet data (only used if buffer_id is OFP_NO_BUFFER).
72    pub fn data(mut self, data: Vec<u8>) -> Self {
73        self.data = data;
74        self
75    }
76
77    /// Encode the Packet-Out to an OpenFlow message.
78    pub fn to_message(&self, version: Version, xid: u32) -> Message {
79        let mut buf = BytesMut::new();
80
81        // Encode actions
82        let actions_bytes = self.actions.encode();
83        let actions_len = actions_bytes.len() as u16;
84
85        // Packet-Out body:
86        // - buffer_id: 4 bytes
87        // - in_port: 4 bytes
88        // - actions_len: 2 bytes
89        // - pad: 6 bytes
90        // - actions: variable
91        // - data: variable (if buffer_id == OFP_NO_BUFFER)
92
93        buf.put_u32(self.buffer_id);
94        buf.put_u32(self.in_port);
95        buf.put_u16(actions_len);
96        buf.put_slice(&[0u8; 6]); // padding
97
98        buf.extend_from_slice(&actions_bytes);
99
100        // Include packet data if not using a buffer
101        if self.buffer_id == OFP_NO_BUFFER {
102            buf.extend_from_slice(&self.data);
103        }
104
105        let total_len = (Header::SIZE + buf.len()) as u16;
106
107        let header = Header {
108            version,
109            msg_type: MessageType::PacketOut,
110            length: total_len,
111            xid,
112        };
113
114        Message {
115            header,
116            body: buf.freeze(),
117        }
118    }
119}
120
121impl Default for PacketOut {
122    fn default() -> Self {
123        Self::new()
124    }
125}
126
127#[cfg(test)]
128mod tests {
129    use super::*;
130
131    #[test]
132    fn packet_out_basic() {
133        let pkt = PacketOut::new()
134            .in_port(1)
135            .actions(ActionList::new().output(2))
136            .data(vec![0x00, 0x01, 0x02, 0x03]);
137
138        let msg = pkt.to_message(Version::Of13, 42);
139        assert_eq!(msg.header.msg_type, MessageType::PacketOut);
140        assert_eq!(msg.header.xid, 42);
141    }
142
143    #[test]
144    fn packet_out_from_buffer() {
145        let pkt = PacketOut::from_buffer(123)
146            .in_port(5)
147            .actions(ActionList::new().output(10));
148
149        assert_eq!(pkt.buffer_id, 123);
150        assert_eq!(pkt.in_port, 5);
151    }
152}