use super::*;
extern crate byteorder;
use std::marker;
pub struct PacketBuilder {}
impl PacketBuilder {
pub fn ethernet2(source: [u8;6], destination: [u8;6]) -> PacketBuilderStep<Ethernet2Header> {
PacketBuilderStep {
state: PacketImpl {
ethernet2_header: Some(Ethernet2Header{
source: source,
destination: destination,
ether_type: 0 }),
vlan_header: None,
ip_header: None,
udp_header: None
},
_marker: marker::PhantomData::<Ethernet2Header>{}
}
}
}
struct PacketImpl {
ethernet2_header: Option<Ethernet2Header>,
ip_header: Option<IpHeader>,
vlan_header: Option<VlanHeader>,
udp_header: Option<UdpHeader>
}
pub struct PacketBuilderStep<LastStep> {
state: PacketImpl,
_marker: marker::PhantomData<LastStep>
}
impl PacketBuilderStep<Ethernet2Header> {
pub fn ipv4(mut self, source: [u8;4], destination: [u8;4], time_to_live: u8) -> PacketBuilderStep<IpHeader> {
self.state.ip_header = Some(IpHeader::Version4(Ipv4Header{
header_length: 5,
differentiated_services_code_point: 0,
explicit_congestion_notification: 0,
total_length: 0, identification: 0,
dont_fragment: true,
more_fragments: false,
fragments_offset: 0,
time_to_live: time_to_live,
protocol: 0, header_checksum: 0, source: source,
destination: destination
}));
PacketBuilderStep {
state: self.state,
_marker: marker::PhantomData::<IpHeader>{}
}
}
pub fn ip(mut self, ip_header: IpHeader) -> PacketBuilderStep<IpHeader> {
self.state.ip_header = Some(ip_header);
PacketBuilderStep {
state: self.state,
_marker: marker::PhantomData::<IpHeader>{}
}
}
pub fn ipv6(mut self, source: [u8;16], destination: [u8;16], hop_limit: u8) -> PacketBuilderStep<IpHeader> {
self.state.ip_header = Some(IpHeader::Version6(Ipv6Header{
traffic_class: 0,
flow_label: 0,
payload_length: 0, next_header: 0, hop_limit: hop_limit,
source: source,
destination: destination
}));
PacketBuilderStep {
state: self.state,
_marker: marker::PhantomData::<IpHeader>{}
}
}
pub fn vlan(mut self, vlan: VlanHeader) -> PacketBuilderStep<VlanHeader> {
self.state.vlan_header = Some(vlan);
PacketBuilderStep {
state: self.state,
_marker: marker::PhantomData::<VlanHeader>{}
}
}
pub fn single_vlan(mut self, vlan_identifier: u16) -> PacketBuilderStep<VlanHeader> {
self.state.vlan_header = Some(VlanHeader::Single(SingleVlanHeader {
priority_code_point: 0,
drop_eligible_indicator: false,
vlan_identifier: vlan_identifier,
ether_type: 0, }));
PacketBuilderStep {
state: self.state,
_marker: marker::PhantomData::<VlanHeader>{}
}
}
pub fn double_vlan(mut self, outer_vlan_identifier: u16, inner_vlan_identifier: u16) -> PacketBuilderStep<VlanHeader> {
self.state.vlan_header = Some(VlanHeader::Double(DoubleVlanHeader {
outer: SingleVlanHeader {
priority_code_point: 0,
drop_eligible_indicator: false,
vlan_identifier: outer_vlan_identifier,
ether_type: 0, },
inner: SingleVlanHeader {
priority_code_point: 0,
drop_eligible_indicator: false,
vlan_identifier: inner_vlan_identifier,
ether_type: 0, }
}));
PacketBuilderStep {
state: self.state,
_marker: marker::PhantomData::<VlanHeader>{}
}
}
}
impl PacketBuilderStep<VlanHeader> {
pub fn ip(self, ip_header: IpHeader) -> PacketBuilderStep<IpHeader> {
PacketBuilderStep {
state: self.state,
_marker: marker::PhantomData::<Ethernet2Header>{}
}.ip(ip_header)
}
pub fn ipv6(self, source: [u8;16], destination: [u8;16], hop_limit: u8) -> PacketBuilderStep<IpHeader> {
PacketBuilderStep {
state: self.state,
_marker: marker::PhantomData::<Ethernet2Header>{}
}.ipv6(source, destination, hop_limit)
}
pub fn ipv4(self, source: [u8;4], destination: [u8;4], time_to_live: u8) -> PacketBuilderStep<IpHeader> {
PacketBuilderStep {
state: self.state,
_marker: marker::PhantomData::<Ethernet2Header>{}
}.ipv4(source, destination, time_to_live)
}
}
impl PacketBuilderStep<IpHeader> {
pub fn udp(mut self, source_port: u16, destination_port: u16) -> PacketBuilderStep<UdpHeader> {
self.state.udp_header = Some(UdpHeader{
source_port: source_port,
destination_port: destination_port,
length: 0, checksum: 0 });
PacketBuilderStep {
state: self.state,
_marker: marker::PhantomData::<UdpHeader>{}
}
}
}
impl PacketBuilderStep<UdpHeader> {
pub fn write<T: io::Write + Sized>(self, writer: &mut T, payload: &[u8]) -> Result<(),WriteError> {
let ip_ether_type = {
use IpHeader::*;
match self.state.ip_header {
Some(Version4(_)) => EtherType::Ipv4 as u16,
Some(Version6(_)) => EtherType::Ipv6 as u16,
None => panic!("Missing ip header")
}
};
match self.state.ethernet2_header {
Some(mut eth) => {
eth.ether_type = {
use VlanHeader::*;
match self.state.vlan_header {
Some(Single(_)) => EtherType::VlanTaggedFrame as u16,
Some(Double(_)) => EtherType::ProviderBridging as u16,
None => ip_ether_type
}
};
eth.write(writer)?;
},
None => {}
}
use VlanHeader::*;
match self.state.vlan_header {
Some(Single(mut value)) => {
value.ether_type = ip_ether_type;
value.write(writer)?;
},
Some(Double(mut value)) => {
value.outer.ether_type = EtherType::VlanTaggedFrame as u16;
value.inner.ether_type = ip_ether_type;
value.write(writer)?;
},
None => {}
}
let mut udp = self.state.udp_header.unwrap();
use IpHeader::*;
let ip_header = self.state.ip_header.unwrap();
match ip_header {
Version4(mut ip) => {
let size = UdpHeader::SERIALIZED_SIZE + payload.len();
ip.set_payload_and_options_length(size)?;
udp.length = size as u16;
ip.protocol = IpTrafficClass::Udp as u8;
udp.checksum = udp.calc_checksum_ipv4(&ip, payload)?;
ip.write(writer, &[])?
},
Version6(mut ip) => {
let size = UdpHeader::SERIALIZED_SIZE + payload.len();
ip.set_payload_length(size)?;
udp.length = size as u16;
ip.next_header = IpTrafficClass::Udp as u8;
udp.checksum = udp.calc_checksum_ipv6(&ip, payload)?;
ip.write(writer)?
}
}
udp.write(writer)?;
writer.write_all(payload)?;
Ok(())
}
pub fn size(&self, payload_size: usize) -> usize {
use IpHeader::*;
use VlanHeader::*;
let result = match self.state.ethernet2_header {
Some(_) => Ethernet2Header::SERIALIZED_SIZE,
None => 0
} + match self.state.vlan_header {
Some(Single(_)) => SingleVlanHeader::SERIALIZED_SIZE,
Some(Double(_)) => DoubleVlanHeader::SERIALIZED_SIZE,
None => 0
} + match self.state.ip_header {
Some(Version4(_)) => Ipv4Header::SERIALIZED_SIZE,
Some(Version6(_)) => Ipv6Header::SERIALIZED_SIZE,
None => 0
} + match self.state.udp_header {
Some(_) => UdpHeader::SERIALIZED_SIZE,
None => 0
} + payload_size;
result
}
}
#[cfg(test)]
mod whitebox_tests {
#[test]
fn size() {
use super::*;
assert_eq!(0,
PacketBuilderStep::<UdpHeader> {
state: PacketImpl {
ethernet2_header: None,
ip_header: None,
vlan_header: None,
udp_header: None
},
_marker: marker::PhantomData::<UdpHeader>{}
}.size(0));
}
}