use bytes::{BufMut, BytesMut};
use crate::action::ActionList;
use crate::message::{Header, Message, MessageType};
use crate::packet_in::OFP_NO_BUFFER;
use crate::Version;
pub const OFPP_CONTROLLER: u32 = 0xffff_fffd;
pub const OFPP_ANY: u32 = 0xffff_ffff;
#[derive(Debug, Clone)]
pub struct PacketOut {
buffer_id: u32,
in_port: u32,
actions: ActionList,
data: Vec<u8>,
}
impl PacketOut {
pub fn new() -> Self {
Self {
buffer_id: OFP_NO_BUFFER,
in_port: OFPP_CONTROLLER,
actions: ActionList::new(),
data: Vec::new(),
}
}
pub fn from_buffer(buffer_id: u32) -> Self {
Self {
buffer_id,
in_port: OFPP_CONTROLLER,
actions: ActionList::new(),
data: Vec::new(),
}
}
pub fn buffer_id(mut self, id: u32) -> Self {
self.buffer_id = id;
self
}
pub fn in_port(mut self, port: u32) -> Self {
self.in_port = port;
self
}
pub fn actions(mut self, actions: ActionList) -> Self {
self.actions = actions;
self
}
pub fn data(mut self, data: Vec<u8>) -> Self {
self.data = data;
self
}
pub fn to_message(&self, version: Version, xid: u32) -> Message {
let mut buf = BytesMut::new();
let actions_bytes = self.actions.encode();
let actions_len = actions_bytes.len() as u16;
buf.put_u32(self.buffer_id);
buf.put_u32(self.in_port);
buf.put_u16(actions_len);
buf.put_slice(&[0u8; 6]);
buf.extend_from_slice(&actions_bytes);
if self.buffer_id == OFP_NO_BUFFER {
buf.extend_from_slice(&self.data);
}
let total_len = (Header::SIZE + buf.len()) as u16;
let header = Header {
version,
msg_type: MessageType::PacketOut,
length: total_len,
xid,
};
Message {
header,
body: buf.freeze(),
}
}
}
impl Default for PacketOut {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn packet_out_basic() {
let pkt = PacketOut::new()
.in_port(1)
.actions(ActionList::new().output(2))
.data(vec![0x00, 0x01, 0x02, 0x03]);
let msg = pkt.to_message(Version::Of13, 42);
assert_eq!(msg.header.msg_type, MessageType::PacketOut);
assert_eq!(msg.header.xid, 42);
}
#[test]
fn packet_out_from_buffer() {
let pkt = PacketOut::from_buffer(123)
.in_port(5)
.actions(ActionList::new().output(10));
assert_eq!(pkt.buffer_id, 123);
assert_eq!(pkt.in_port, 5);
}
}