use byteorder::{NetworkEndian, WriteBytesExt};
use bypass_csv::BypassRecord;
use openflow::messages::*;
use std::convert::From;
use std::io;
use std::io::Write;
use std::mem::size_of;
impl OfpHeader {
pub fn new(typ: OfpType, xid: u32) -> OfpHeader {
OfpHeader {
version: OFP_VERSION,
typ: typ as u8,
length: OfpHeader::header_length() as u16,
xid,
}
}
pub fn header_length() -> usize {
size_of::<OfpHeader>()
}
pub fn serialize<S: Write>(&self, stream: &mut S) -> io::Result<()> {
stream.write_all(&[self.version, self.typ])?;
stream.write_u16::<NetworkEndian>(self.length)?;
stream.write_u32::<NetworkEndian>(self.xid)
}
}
impl OfpMatch {
pub fn new() -> OfpMatch {
OfpMatch {
typ: OfpMatchType::Oxm as u16,
oxm_fields: vec![],
}
}
pub fn add_tlv(&mut self, oxm_tlv: OfpOxmTlv) -> &mut OfpMatch {
self.oxm_fields.push(oxm_tlv);
self
}
fn length(&self) -> usize {
let mut length = 4;
for oxm in &self.oxm_fields {
length += oxm.length();
}
length
}
fn pad_len(&self) -> usize {
let len = self.length();
(len + 7) / 8 * 8 - len
}
fn serialize<S: Write>(&self, stream: &mut S) -> io::Result<()> {
stream.write_u16::<NetworkEndian>(self.typ)?;
stream.write_u16::<NetworkEndian>(self.length() as u16)?;
for oxm in &self.oxm_fields {
oxm.serialize(stream)?;
}
stream.write_all(&vec![0; self.pad_len()])
}
}
impl<'a> From<&'a BypassRecord> for OfpMatch {
fn from(rec: &'a BypassRecord) -> Self {
let mut mat = OfpMatch::new();
mat.add_tlv(OfpOxmTlv::new_eth_type_ipv4());
if let Some(ref ip) = rec.src_ip() {
let src_ip = OfpOxmTlv::new_ipv4(ip, &ProtocolEndpoint::Src);
mat.add_tlv(src_ip);
}
if let Some(ref ip) = rec.dst_ip() {
let dst_ip = OfpOxmTlv::new_ipv4(ip, &ProtocolEndpoint::Dst);
mat.add_tlv(dst_ip);
}
if let Some(ref proto) = rec.proto() {
let oxm_proto = OfpOxmTlv::new_ip_proto(proto);
mat.add_tlv(oxm_proto);
if let Some(port) = rec.src_port() {
if let Some(oxm_port) = OfpOxmTlv::new_port(proto, port, &ProtocolEndpoint::Src) {
mat.add_tlv(oxm_port);
}
}
if let Some(port) = rec.dst_port() {
if let Some(oxm_port) = OfpOxmTlv::new_port(proto, port, &ProtocolEndpoint::Dst) {
mat.add_tlv(oxm_port);
}
}
}
mat
}
}
impl OfpOxmTlv {
fn length(&self) -> usize {
4 + self.body.len()
}
fn serialize<S: Write>(&self, stream: &mut S) -> io::Result<()> {
let class = self.class as u32;
let hasmask_u32 = if self.hasmask { 1 } else { 0 };
let header = ((class) << 16) | ((self.field as u32) << 9) | (hasmask_u32 << 8)
| self.body.len() as u32;
stream.write_u32::<NetworkEndian>(header)?;
stream.write_all(&self.body)
}
}
impl OfpActionOutput {
pub fn new(port: u32) -> OfpActionOutput {
OfpActionOutput {
typ: OfpActionType::Output as u16,
len: size_of::<OfpActionOutput>() as u16,
port,
max_len: 0,
pad: [0; 6],
}
}
fn serialize<S: Write>(&self, stream: &mut S) -> io::Result<()> {
stream.write_u16::<NetworkEndian>(self.typ)?;
stream.write_u16::<NetworkEndian>(self.len)?;
stream.write_u32::<NetworkEndian>(self.port)?;
stream.write_u16::<NetworkEndian>(self.max_len)?;
stream.write_all(&self.pad)
}
}
impl OfpInstructionActions {
pub fn new(actions: Vec<OfpActionOutput>) -> OfpInstructionActions {
OfpInstructionActions {
typ: OfpInstructionType::ApplyActions as u16,
pad: [0; 4],
actions,
}
}
fn serialize<S: Write>(&self, stream: &mut S) -> io::Result<()> {
let actions = &mut vec![];
for action in &self.actions {
action.serialize(actions)?;
}
stream.write_u16::<NetworkEndian>(self.typ)?;
stream.write_u16::<NetworkEndian>(8 + actions.len() as u16)?;
stream.write_all(&self.pad)?;
stream.write_all(actions)
}
}
impl OfpFlowMod {
pub fn new(
command: OfpFlowModCommand,
table_id: u8,
priority: u16,
out_port: u32,
match_field: OfpMatch,
instructions: Vec<OfpInstructionActions>,
) -> OfpFlowMod {
OfpFlowMod {
cookie: 0,
cookie_mask: 0,
table_id,
command: command as u8,
idle_timeout: OFP_FLOW_PERMANENT,
hard_timeout: OFP_FLOW_PERMANENT,
priority,
buffer_id: OFP_NO_BUFFER,
out_port,
out_group: OFPP_ANY,
flags: 0,
pad: [0; 2],
match_field,
instructions,
}
}
}
pub trait OfpPacket {
fn header(&self, body_length: usize, xid: u32) -> OfpHeader {
OfpHeader {
version: OFP_VERSION,
typ: Self::typ() as u8,
length: (OfpHeader::header_length() + body_length) as u16,
xid,
}
}
fn typ() -> OfpType;
fn serialize<S: Write>(&self, stream: &mut S, xid: u32) -> io::Result<()> {
let mut body = vec![];
self.serialize_body(&mut body)?;
let header = self.header(body.len(), xid);
debug!("Outgoing message: {:?}", header);
header.serialize(stream)?;
stream.write_all(&body)
}
fn serialize_body<S: Write>(&self, stream: &mut S) -> io::Result<()>;
}
impl OfpEchoReply {
pub fn new(arbitrary: Vec<u8>) -> OfpEchoReply {
OfpEchoReply { arbitrary }
}
}
impl OfpPacket for OfpEchoReply {
fn typ() -> OfpType {
OfpType::EchoReply
}
fn serialize_body<S: Write>(&self, stream: &mut S) -> io::Result<()> {
stream.write_all(&self.arbitrary)
}
}
impl OfpPacket for OfpErrorMsg {
fn typ() -> OfpType {
OfpType::Error
}
fn serialize_body<S: Write>(&self, stream: &mut S) -> io::Result<()> {
stream.write_u16::<NetworkEndian>(self.typ)?;
stream.write_u16::<NetworkEndian>(self.code)?;
stream.write_all(&self.data)
}
}
impl OfpPacket for OfpFlowMod {
fn typ() -> OfpType {
OfpType::FlowMod
}
fn serialize_body<S: Write>(&self, stream: &mut S) -> io::Result<()> {
stream.write_u64::<NetworkEndian>(self.cookie)?;
stream.write_u64::<NetworkEndian>(self.cookie_mask)?;
stream.write_all(&[self.table_id, self.command])?;
stream.write_u16::<NetworkEndian>(self.idle_timeout)?;
stream.write_u16::<NetworkEndian>(self.hard_timeout)?;
stream.write_u16::<NetworkEndian>(self.priority)?;
stream.write_u32::<NetworkEndian>(self.buffer_id)?;
stream.write_u32::<NetworkEndian>(self.out_port)?;
stream.write_u32::<NetworkEndian>(self.out_group)?;
stream.write_u16::<NetworkEndian>(self.flags)?;
stream.write_all(&self.pad)?;
self.match_field.serialize(stream)?;
for instr in &self.instructions {
instr.serialize(stream)?;
}
Ok(())
}
}
impl OfpPacket for OfpAsyncConfig {
fn typ() -> OfpType {
OfpType::SetAsync
}
fn serialize_body<S: Write>(&self, stream: &mut S) -> io::Result<()> {
stream.write_u32::<NetworkEndian>(self.packet_in_mask[0])?;
stream.write_u32::<NetworkEndian>(self.packet_in_mask[1])?;
stream.write_u32::<NetworkEndian>(self.port_status_mask[0])?;
stream.write_u32::<NetworkEndian>(self.port_status_mask[1])?;
stream.write_u32::<NetworkEndian>(self.flow_removed_mask[0])?;
stream.write_u32::<NetworkEndian>(self.flow_removed_mask[1])
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn echo_reply_header() {
let xid = 42;
let expected = OfpHeader {
version: 4,
typ: 3,
length: 8,
xid,
};
let testee = OfpEchoReply { arbitrary: vec![] };
assert_eq!(expected, testee.header(0, xid));
}
#[test]
fn echo_reply_body_serialization() {
let arbitrary = vec![1, 2, 3, 4];
let testee = OfpEchoReply { arbitrary };
let mut ser = vec![];
testee.serialize_body(&mut ser).unwrap();
assert_eq!(vec![1, 2, 3, 4], ser);
assert_eq!(12, testee.header(ser.len(), 1).length);
}
#[test]
fn oxm_tlv_serialization() {
let testee = OfpOxmTlv::new_in_port(0x11223344);
assert_eq!(8, testee.length());
assert_eq!(vec![0x11, 0x22, 0x33, 0x44], testee.body);
}
#[test]
fn match_serialization() {
let tlv = OfpOxmTlv::new_in_port(1);
let mut testee = OfpMatch::new();
testee.add_tlv(tlv);
assert_eq!(12, testee.length());
assert_eq!(4, testee.pad_len());
let mut ser = vec![];
testee.serialize(&mut ser).unwrap();
assert_eq!(16, ser.len());
}
#[test]
fn action_output_serialization() {
let testee = OfpActionOutput::new(0x11223344);
let mut ser = vec![];
testee.serialize(&mut ser).unwrap();
assert_eq!(16, ser.len());
assert_eq!(
vec![0, 0, 0, 16, 0x11, 0x22, 0x33, 0x44, 0, 0, 0, 0, 0, 0, 0, 0],
ser
);
}
}