| use crate::err::packet::BuildWriteError; |
| |
| use super::*; |
| |
| use std::{io, marker}; |
| |
| /// Helper for building packets. |
| /// |
| /// The packet builder allows the easy construction of a packet from the |
| /// ethernet II layer downwards including ipv6, ipv4, the udp header and the |
| /// actual payload. The packet builder automatically calculates lengths & checksums |
| /// for ip & udp and set type identifiers for ethernetII and ip. This makes it |
| /// easy and less error prone to construct custom packets. |
| /// |
| /// # Example: |
| /// |
| /// Generating a packet that starts with an Ethernet II header: |
| /// |
| /// ``` |
| /// use etherparse::PacketBuilder; |
| /// |
| /// let builder = PacketBuilder:: |
| /// ethernet2([1,2,3,4,5,6], //source mac |
| /// [7,8,9,10,11,12]) //destination mac |
| /// .ipv4([192,168,1,1], //source ip |
| /// [192,168,1,2], //destination ip |
| /// 20) //time to life |
| /// .udp(21, //source port |
| /// 1234); //destination port |
| /// |
| /// //payload of the udp packet |
| /// let payload = [1,2,3,4,5,6,7,8]; |
| /// |
| /// //get some memory to store the result |
| /// let mut result = Vec::<u8>::with_capacity( |
| /// builder.size(payload.len())); |
| /// |
| /// //serialize |
| /// builder.write(&mut result, &payload).unwrap(); |
| /// println!("{:?}", result); |
| /// ``` |
| /// |
| /// # Options |
| /// |
| /// * Starting Options: |
| /// * [`PacketBuilder::ethernet2`] |
| /// * [`PacketBuilder::linux_sll`] |
| /// * [`PacketBuilder::ip`] |
| /// * [`PacketBuilder::ipv4`] |
| /// * [`PacketBuilder::ipv6`] |
| /// * Options after an Ethernet2 header was added: |
| /// * [`PacketBuilderStep<Ethernet2Header>::vlan`] |
| /// * [`PacketBuilderStep<Ethernet2Header>::single_vlan`] |
| /// * [`PacketBuilderStep<Ethernet2Header>::double_vlan`] |
| /// * [`PacketBuilderStep<Ethernet2Header>::ip`] |
| /// * [`PacketBuilderStep<Ethernet2Header>::ipv4`] |
| /// * [`PacketBuilderStep<Ethernet2Header>::ipv6`] |
| /// * Options after a Linux Cooked Capture v1 (SLL) was added: |
| /// * [`PacketBuilderStep<LinuxSllHeader>::ip`] |
| /// * [`PacketBuilderStep<LinuxSllHeader>::ipv4`] |
| /// * [`PacketBuilderStep<LinuxSllHeader>::ipv6`] |
| /// * Options after an Vlan header was added: |
| /// * [`PacketBuilderStep<VlanHeader>::ip`] |
| /// * [`PacketBuilderStep<VlanHeader>::ipv4`] |
| /// * [`PacketBuilderStep<VlanHeader>::ipv6`] |
| /// * Options after an IP header was added: |
| /// * [`PacketBuilderStep<IpHeaders>::write`] |
| /// * [`PacketBuilderStep<IpHeaders>::tcp`] |
| /// * [`PacketBuilderStep<IpHeaders>::udp`] |
| /// * [`PacketBuilderStep<IpHeaders>::icmpv4`] |
| /// * [`PacketBuilderStep<IpHeaders>::icmpv4_raw`] |
| /// * [`PacketBuilderStep<IpHeaders>::icmpv4_echo_request`] |
| /// * [`PacketBuilderStep<IpHeaders>::icmpv4_echo_reply`] |
| /// * [`PacketBuilderStep<IpHeaders>::icmpv6`] |
| /// * [`PacketBuilderStep<IpHeaders>::icmpv6_raw`] |
| /// * [`PacketBuilderStep<IpHeaders>::icmpv6_echo_request`] |
| /// * [`PacketBuilderStep<IpHeaders>::icmpv6_echo_reply`] |
| /// * Options after an TCP header was added: |
| /// * [`PacketBuilderStep<TcpHeader>::write`] |
| /// * [`PacketBuilderStep<TcpHeader>::size`] |
| /// * [`PacketBuilderStep<TcpHeader>::ns`] |
| /// * [`PacketBuilderStep<TcpHeader>::fin`] |
| /// * [`PacketBuilderStep<TcpHeader>::syn`] |
| /// * [`PacketBuilderStep<TcpHeader>::rst`] |
| /// * [`PacketBuilderStep<TcpHeader>::psh`] |
| /// * [`PacketBuilderStep<TcpHeader>::ack`] |
| /// * [`PacketBuilderStep<TcpHeader>::urg`] |
| /// * [`PacketBuilderStep<TcpHeader>::ece`] |
| /// * [`PacketBuilderStep<TcpHeader>::cwr`] |
| /// * [`PacketBuilderStep<TcpHeader>::options`] |
| /// * [`PacketBuilderStep<TcpHeader>::options_raw`] |
| /// * Options after an UDP header was added: |
| /// * [`PacketBuilderStep<UdpHeader>::write`] |
| /// * [`PacketBuilderStep<UdpHeader>::size`] |
| /// * Options after an ICMPv4 header was added: |
| /// * [`PacketBuilderStep<Icmpv4Header>::write`] |
| /// * [`PacketBuilderStep<Icmpv4Header>::size`] |
| /// * Options after an ICMPv6 header was added: |
| /// * [`PacketBuilderStep<Icmpv6Header>::write`] |
| /// * [`PacketBuilderStep<Icmpv6Header>::size`] |
| /// |
| #[cfg_attr(docsrs, doc(cfg(feature = "std")))] |
| pub struct PacketBuilder {} |
| |
| impl PacketBuilder { |
| /// Start an packet with an ethernetII header. |
| /// |
| /// # Example |
| /// |
| /// Basic usage: |
| /// |
| /// ``` |
| /// # use etherparse::PacketBuilder; |
| /// # |
| /// let builder = PacketBuilder:: |
| /// ethernet2([1,2,3,4,5,6], //source mac |
| /// [7,8,9,10,11,12]) //destination mac |
| /// .ipv4([192,168,1,1], //source ip |
| /// [192,168,1,2], //destination ip |
| /// 20) //time to life |
| /// .udp(21, //source port |
| /// 1234); //destination port |
| /// |
| /// //payload of the udp packet |
| /// let payload = [1,2,3,4,5,6,7,8]; |
| /// |
| /// //get some memory to store the result |
| /// let mut result = Vec::<u8>::with_capacity( |
| /// builder.size(payload.len())); |
| /// |
| /// //serialize |
| /// builder.write(&mut result, &payload).unwrap(); |
| /// ``` |
| pub fn ethernet2(source: [u8; 6], destination: [u8; 6]) -> PacketBuilderStep<Ethernet2Header> { |
| PacketBuilderStep { |
| state: PacketImpl { |
| link_header: Some(LinkHeader::Ethernet2(Ethernet2Header { |
| source, |
| destination, |
| ether_type: EtherType(0), //the type identifier |
| })), |
| vlan_header: None, |
| ip_header: None, |
| transport_header: None, |
| }, |
| _marker: marker::PhantomData::<Ethernet2Header> {}, |
| } |
| } |
| |
| /// Start an packet with an Linux Cooked Catpure (v1) header. |
| /// |
| /// # Example |
| /// |
| /// Basic usage: |
| /// |
| /// ``` |
| /// # use etherparse::{PacketBuilder, LinuxSllPacketType}; |
| /// # |
| /// let builder = PacketBuilder:: |
| /// linux_sll(LinuxSllPacketType::OTHERHOST, //packet type |
| /// 6, //sender address valid length |
| /// [1,2,3,4,5,6,0,0]) //sender address with padding |
| /// .ipv4([192,168,1,1], //source ip |
| /// [192,168,1,2], //destination ip |
| /// 20) //time to life |
| /// .udp(21, //source port |
| /// 1234); //destination port |
| /// |
| /// //payload of the udp packet |
| /// let payload = [1,2,3,4,5,6,7,8]; |
| /// |
| /// //get some memory to store the result |
| /// let mut result = Vec::<u8>::with_capacity( |
| /// builder.size(payload.len())); |
| /// |
| /// //serialize |
| /// builder.write(&mut result, &payload).unwrap(); |
| /// ``` |
| pub fn linux_sll( |
| packet_type: LinuxSllPacketType, |
| sender_address_valid_length: u16, |
| sender_address: [u8; 8], |
| ) -> PacketBuilderStep<LinuxSllHeader> { |
| PacketBuilderStep { |
| state: PacketImpl { |
| link_header: Some(LinkHeader::LinuxSll(LinuxSllHeader { |
| packet_type, |
| arp_hrd_type: ArpHardwareId::ETHER, |
| sender_address_valid_length, |
| sender_address, |
| protocol_type: LinuxSllProtocolType::EtherType(EtherType(0)), // Will be overwitten when writing depending on the net layer |
| })), |
| vlan_header: None, |
| ip_header: None, |
| transport_header: None, |
| }, |
| _marker: marker::PhantomData::<LinuxSllHeader> {}, |
| } |
| } |
| |
| /// Starts a packet with an IPv4 header. |
| /// |
| /// # Example |
| /// |
| /// Basic usage: |
| /// |
| /// ``` |
| /// # use etherparse::PacketBuilder; |
| /// # |
| /// let builder = PacketBuilder:: |
| /// ipv4([192,168,1,1], //source ip |
| /// [192,168,1,2], //destination ip |
| /// 20) //time to life |
| /// .udp(21, //source port |
| /// 1234); //destination port |
| /// |
| /// //payload of the udp packet |
| /// let payload = [1,2,3,4,5,6,7,8]; |
| /// |
| /// //get some memory to store the result |
| /// let mut result = Vec::<u8>::with_capacity( |
| /// builder.size(payload.len())); |
| /// |
| /// //serialize |
| /// builder.write(&mut result, &payload).unwrap(); |
| /// ``` |
| pub fn ipv4( |
| source: [u8; 4], |
| destination: [u8; 4], |
| time_to_live: u8, |
| ) -> PacketBuilderStep<IpHeaders> { |
| PacketBuilderStep { |
| state: PacketImpl { |
| link_header: None, |
| vlan_header: None, |
| ip_header: None, |
| transport_header: None, |
| }, |
| _marker: marker::PhantomData::<Ethernet2Header> {}, |
| } |
| .ipv4(source, destination, time_to_live) |
| } |
| |
| /// Start a packet with an IPv6 header. |
| /// |
| /// # Example |
| /// |
| /// Basic usage: |
| /// |
| /// ``` |
| /// # use etherparse::PacketBuilder; |
| /// # |
| /// let builder = PacketBuilder:: |
| /// ipv6( |
| /// //source |
| /// [11,12,13,14,15,16,17,18,19,10,21,22,23,24,25,26], |
| /// //destination |
| /// [31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46], |
| /// //hop_limit |
| /// 47) |
| /// .udp(21, //source port |
| /// 1234); //destination port |
| /// |
| /// //payload of the udp packet |
| /// let payload = [1,2,3,4,5,6,7,8]; |
| /// |
| /// //get some memory to store the result |
| /// let mut result = Vec::<u8>::with_capacity( |
| /// builder.size(payload.len())); |
| /// |
| /// //serialize |
| /// builder.write(&mut result, &payload).unwrap(); |
| /// ``` |
| pub fn ipv6( |
| source: [u8; 16], |
| destination: [u8; 16], |
| hop_limit: u8, |
| ) -> PacketBuilderStep<IpHeaders> { |
| PacketBuilderStep { |
| state: PacketImpl { |
| link_header: None, |
| vlan_header: None, |
| ip_header: None, |
| transport_header: None, |
| }, |
| _marker: marker::PhantomData::<Ethernet2Header> {}, |
| } |
| .ipv6(source, destination, hop_limit) |
| } |
| |
| /// Starts a packet with an arbitrary IP header (length, protocol/next_header & checksum fields will be overwritten based on the rest of the packet). |
| /// |
| /// # Examples |
| /// |
| /// With an IPv4 header: |
| /// |
| /// ``` |
| /// # use etherparse::*; |
| /// # |
| /// let builder = PacketBuilder:: |
| /// //payload_len, protocol & checksum will be replaced during write |
| /// ip(IpHeaders::Ipv4( |
| /// Ipv4Header::new( |
| /// 0, //payload_len will be replaced during write |
| /// 12, //time_to_live |
| /// ip_number::UDP, //will be replaced during write |
| /// [0,1,2,3], //source |
| /// [4,5,6,7] //destination |
| /// ).unwrap(), |
| /// Default::default())) |
| /// .udp(21, //source port |
| /// 1234); //destination port |
| /// |
| /// //payload of the udp packet |
| /// let payload = [1,2,3,4,5,6,7,8]; |
| /// |
| /// //get some memory to store the result |
| /// let mut result = Vec::<u8>::with_capacity( |
| /// builder.size(payload.len())); |
| /// |
| /// //serialize |
| /// builder.write(&mut result, &payload).unwrap(); |
| /// ``` |
| /// |
| /// With an IPv6 header: |
| /// |
| /// ``` |
| /// # use etherparse::*; |
| /// # |
| /// let builder = PacketBuilder:: |
| /// ip(IpHeaders::Ipv6( |
| /// Ipv6Header{ |
| /// traffic_class: 0, |
| /// flow_label: 0.try_into().unwrap(), |
| /// hop_limit: 4.try_into().unwrap(), |
| /// source: [0;16], |
| /// destination: [0;16], |
| /// // payload_length & next_header will be replaced during write |
| /// ..Default::default() |
| /// }, |
| /// Default::default())) |
| /// .udp(21, //source port |
| /// 1234); //destination port |
| /// |
| /// //payload of the udp packet |
| /// let payload = [1,2,3,4,5,6,7,8]; |
| /// |
| /// //get some memory to store the result |
| /// let mut result = Vec::<u8>::with_capacity( |
| /// builder.size(payload.len())); |
| /// |
| /// //serialize |
| /// builder.write(&mut result, &payload).unwrap(); |
| /// ``` |
| pub fn ip(ip_header: IpHeaders) -> PacketBuilderStep<IpHeaders> { |
| PacketBuilderStep { |
| state: PacketImpl { |
| link_header: None, |
| vlan_header: None, |
| ip_header: None, |
| transport_header: None, |
| }, |
| _marker: marker::PhantomData::<Ethernet2Header> {}, |
| } |
| .ip(ip_header) |
| } |
| } |
| |
| struct PacketImpl { |
| link_header: Option<LinkHeader>, |
| ip_header: Option<IpHeaders>, |
| vlan_header: Option<VlanHeader>, |
| transport_header: Option<TransportHeader>, |
| } |
| |
| ///An unfinished packet that is build with the packet builder |
| #[cfg_attr(docsrs, doc(cfg(feature = "std")))] |
| pub struct PacketBuilderStep<LastStep> { |
| state: PacketImpl, |
| _marker: marker::PhantomData<LastStep>, |
| } |
| |
| #[cfg_attr(docsrs, doc(cfg(feature = "std")))] |
| impl PacketBuilderStep<Ethernet2Header> { |
| /// Add an IPv4 header |
| /// |
| /// # Example |
| /// |
| /// Basic usage: |
| /// |
| /// ``` |
| /// # use etherparse::PacketBuilder; |
| /// # |
| /// let builder = PacketBuilder:: |
| /// ethernet2([1,2,3,4,5,6], //source mac |
| /// [7,8,9,10,11,12]) //destination mac |
| /// .ipv4([192,168,1,1], //source ip |
| /// [192,168,1,2], //destination ip |
| /// 20) //time to life |
| /// .udp(21, //source port |
| /// 1234); //destination port |
| /// |
| /// //payload of the udp packet |
| /// let payload = [1,2,3,4,5,6,7,8]; |
| /// |
| /// //get some memory to store the result |
| /// let mut result = Vec::<u8>::with_capacity( |
| /// builder.size(payload.len())); |
| /// |
| /// //serialize |
| /// builder.write(&mut result, &payload).unwrap(); |
| /// ``` |
| pub fn ipv4( |
| mut self, |
| source: [u8; 4], |
| destination: [u8; 4], |
| time_to_live: u8, |
| ) -> PacketBuilderStep<IpHeaders> { |
| //add ip header |
| self.state.ip_header = Some(IpHeaders::Ipv4( |
| Ipv4Header { |
| source, |
| destination, |
| time_to_live, |
| ..Default::default() |
| }, |
| Default::default(), |
| )); |
| //return for next step |
| PacketBuilderStep { |
| state: self.state, |
| _marker: marker::PhantomData::<IpHeaders> {}, |
| } |
| } |
| |
| /// Add an IP header (length, protocol/next_header & checksum fields will be overwritten based on the rest of the packet). |
| /// |
| /// # Examples |
| /// |
| /// With an IPv4 header: |
| /// |
| /// ``` |
| /// # use etherparse::*; |
| /// # |
| /// let builder = PacketBuilder:: |
| /// ethernet2([1,2,3,4,5,6], |
| /// [7,8,9,10,11,12]) |
| /// //payload_len, protocol & checksum will be replaced during write |
| /// .ip(IpHeaders::Ipv4( |
| /// Ipv4Header::new( |
| /// 0, //payload_len will be replaced during write |
| /// 12, //time_to_live |
| /// ip_number::UDP, //will be replaced during write |
| /// [0,1,2,3], //source |
| /// [4,5,6,7] //destination |
| /// ).unwrap(), |
| /// Default::default())); |
| /// ``` |
| /// |
| /// With an IPv6 header: |
| /// |
| /// ``` |
| /// # use etherparse::*; |
| /// # |
| /// let builder = PacketBuilder:: |
| /// ethernet2([1,2,3,4,5,6], |
| /// [7,8,9,10,11,12]) |
| /// .ip(IpHeaders::Ipv6( |
| /// Ipv6Header{ |
| /// traffic_class: 0, |
| /// flow_label: 0.try_into().unwrap(), |
| /// hop_limit: 4, |
| /// source: [0;16], |
| /// destination: [0;16], |
| /// // payload_length & next_header will be replaced during write |
| /// ..Default::default() |
| /// }, |
| /// Default::default())); |
| /// ``` |
| pub fn ip(mut self, ip_header: IpHeaders) -> PacketBuilderStep<IpHeaders> { |
| //add ip header |
| self.state.ip_header = Some(ip_header); |
| //return for next step |
| PacketBuilderStep { |
| state: self.state, |
| _marker: marker::PhantomData::<IpHeaders> {}, |
| } |
| } |
| |
| /// Add an IPv6 header |
| /// |
| /// # Example |
| /// |
| /// Basic usage: |
| /// |
| /// ``` |
| /// # use etherparse::PacketBuilder; |
| /// # |
| /// let builder = PacketBuilder:: |
| /// ethernet2([1,2,3,4,5,6], |
| /// [7,8,9,10,11,12]) |
| /// .ipv6( |
| /// //source |
| /// [11,12,13,14,15,16,17,18,19,10,21,22,23,24,25,26], |
| /// //destination |
| /// [31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46], |
| /// //hop_limit |
| /// 47) |
| /// .udp(21, //source port |
| /// 1234); //destination port |
| /// |
| /// //payload of the udp packet |
| /// let payload = [1,2,3,4,5,6,7,8]; |
| /// |
| /// //get some memory to store the result |
| /// let mut result = Vec::<u8>::with_capacity( |
| /// builder.size(payload.len())); |
| /// |
| /// //serialize |
| /// builder.write(&mut result, &payload).unwrap(); |
| /// ``` |
| pub fn ipv6( |
| mut self, |
| source: [u8; 16], |
| destination: [u8; 16], |
| hop_limit: u8, |
| ) -> PacketBuilderStep<IpHeaders> { |
| self.state.ip_header = Some(IpHeaders::Ipv6( |
| Ipv6Header { |
| traffic_class: 0, |
| flow_label: Ipv6FlowLabel::ZERO, |
| payload_length: 0, //filled in on write |
| next_header: IpNumber(255), //filled in on write |
| hop_limit, |
| source, |
| destination, |
| }, |
| Default::default(), |
| )); |
| |
| //return for next step |
| PacketBuilderStep { |
| state: self.state, |
| _marker: marker::PhantomData::<IpHeaders> {}, |
| } |
| } |
| |
| /// Adds a vlan tagging header with the given vlan identifier |
| /// |
| /// # Example |
| /// |
| /// Basic usage: |
| /// |
| /// ``` |
| /// # use etherparse::{PacketBuilder, SingleVlanHeader, VlanHeader}; |
| /// # |
| /// let builder = PacketBuilder:: |
| /// ethernet2([1,2,3,4,5,6], //source mac |
| /// [7,8,9,10,11,12]) //destination mac |
| /// .vlan(VlanHeader::Single( |
| /// SingleVlanHeader{ |
| /// pcp: 1.try_into().unwrap(), |
| /// drop_eligible_indicator: false, |
| /// vlan_id: 0x123.try_into().unwrap(), |
| /// ether_type: 0.into() // will be overwritten during write |
| /// })) |
| /// .ipv4([192,168,1,1], //source ip |
| /// [192,168,1,2], //destination ip |
| /// 20) //time to life |
| /// .udp(21, //source port |
| /// 1234); //destination port |
| /// |
| /// //payload of the udp packet |
| /// let payload = [1,2,3,4,5,6,7,8]; |
| /// |
| /// //get some memory to store the result |
| /// let mut result = Vec::<u8>::with_capacity( |
| /// builder.size(payload.len())); |
| /// |
| /// //serialize |
| /// builder.write(&mut result, &payload).unwrap(); |
| /// ``` |
| pub fn vlan(mut self, vlan: VlanHeader) -> PacketBuilderStep<VlanHeader> { |
| self.state.vlan_header = Some(vlan); |
| //return for next step |
| PacketBuilderStep { |
| state: self.state, |
| _marker: marker::PhantomData::<VlanHeader> {}, |
| } |
| } |
| |
| /// Adds a vlan tagging header with the given vlan identifier |
| /// |
| /// # Example |
| /// |
| /// Basic usage: |
| /// |
| /// ``` |
| /// # use etherparse::{PacketBuilder, SingleVlanHeader, VlanHeader}; |
| /// # |
| /// let builder = PacketBuilder:: |
| /// ethernet2([1,2,3,4,5,6], //source mac |
| /// [7,8,9,10,11,12]) //destination mac |
| /// .single_vlan(0x123.try_into().unwrap()) // vlan identifier |
| /// .ipv4([192,168,1,1], //source ip |
| /// [192,168,1,2], //destination ip |
| /// 20) //time to life |
| /// .udp(21, //source port |
| /// 1234); //destination port |
| /// |
| /// //payload of the udp packet |
| /// let payload = [1,2,3,4,5,6,7,8]; |
| /// |
| /// //get some memory to store the result |
| /// let mut result = Vec::<u8>::with_capacity( |
| /// builder.size(payload.len())); |
| /// |
| /// //serialize |
| /// builder.write(&mut result, &payload).unwrap(); |
| /// ``` |
| pub fn single_vlan(mut self, vlan_identifier: VlanId) -> PacketBuilderStep<VlanHeader> { |
| self.state.vlan_header = Some(VlanHeader::Single(SingleVlanHeader { |
| pcp: VlanPcp::ZERO, |
| drop_eligible_indicator: false, |
| vlan_id: vlan_identifier, |
| ether_type: EtherType(0), //will be set automatically during write |
| })); |
| //return for next step |
| PacketBuilderStep { |
| state: self.state, |
| _marker: marker::PhantomData::<VlanHeader> {}, |
| } |
| } |
| |
| /// Adds two vlan tagging header with the given vlan identifiers (also known as double vlan tagging). |
| /// |
| /// # Example |
| /// |
| /// Basic usage: |
| /// |
| /// ``` |
| /// # use etherparse::{PacketBuilder, SingleVlanHeader, VlanHeader}; |
| /// # |
| /// let builder = PacketBuilder:: |
| /// ethernet2([1,2,3,4,5,6], //source mac |
| /// [7,8,9,10,11,12]) //destination mac |
| /// .double_vlan(0x123.try_into().unwrap(), // outer vlan identifier |
| /// 0x234.try_into().unwrap()) // inner vlan identifier |
| /// .ipv4([192,168,1,1], //source ip |
| /// [192,168,1,2], //destination ip |
| /// 20) //time to life |
| /// .udp(21, //source port |
| /// 1234); //destination port |
| /// |
| /// //payload of the udp packet |
| /// let payload = [1,2,3,4,5,6,7,8]; |
| /// |
| /// //get some memory to store the result |
| /// let mut result = Vec::<u8>::with_capacity( |
| /// builder.size(payload.len())); |
| /// |
| /// //serialize |
| /// builder.write(&mut result, &payload).unwrap(); |
| /// ``` |
| pub fn double_vlan( |
| mut self, |
| outer_vlan_identifier: VlanId, |
| inner_vlan_identifier: VlanId, |
| ) -> PacketBuilderStep<VlanHeader> { |
| self.state.vlan_header = Some(VlanHeader::Double(DoubleVlanHeader { |
| outer: SingleVlanHeader { |
| pcp: VlanPcp::ZERO, |
| drop_eligible_indicator: false, |
| vlan_id: outer_vlan_identifier, |
| ether_type: EtherType(0), //will be set automatically during write |
| }, |
| inner: SingleVlanHeader { |
| pcp: VlanPcp::ZERO, |
| drop_eligible_indicator: false, |
| vlan_id: inner_vlan_identifier, |
| ether_type: EtherType(0), //will be set automatically during write |
| }, |
| })); |
| //return for next step |
| PacketBuilderStep { |
| state: self.state, |
| _marker: marker::PhantomData::<VlanHeader> {}, |
| } |
| } |
| } |
| |
| #[cfg_attr(docsrs, doc(cfg(feature = "std")))] |
| impl PacketBuilderStep<LinuxSllHeader> { |
| /// Add an ip header (length, protocol/next_header & checksum fields will be overwritten based on the rest of the packet). |
| /// |
| /// # Example IPv4 |
| /// ``` |
| /// # use etherparse::*; |
| /// # |
| /// let builder = PacketBuilder:: |
| /// linux_sll(LinuxSllPacketType::OTHERHOST, //packet type |
| /// 6, //sender address valid length |
| /// [1,2,3,4,5,6,0,0]) //sender address with padding |
| /// //payload_len, protocol & checksum will be replaced during write |
| /// .ip(IpHeaders::Ipv4( |
| /// Ipv4Header::new( |
| /// 0, //payload_len will be replaced during write |
| /// 12, //time_to_live |
| /// ip_number::UDP, //will be replaced during write |
| /// [0,1,2,3], //source |
| /// [4,5,6,7] //destination |
| /// ).unwrap(), |
| /// Default::default() // IPv4 extension headers (default is none) |
| /// )); |
| /// ``` |
| /// |
| /// # Example IPv6 |
| /// ``` |
| /// # use etherparse::*; |
| /// # |
| /// let builder = PacketBuilder:: |
| /// linux_sll(LinuxSllPacketType::OTHERHOST, //packet type |
| /// 6, //sender address valid length |
| /// [1,2,3,4,5,6,0,0]) //sender address with padding |
| /// .ip(IpHeaders::Ipv6( |
| /// Ipv6Header{ |
| /// traffic_class: 0, |
| /// flow_label: 0.try_into().unwrap(), |
| /// hop_limit: 4, |
| /// source: [0;16], |
| /// destination: [0;16], |
| /// // payload_length & next_header will be replaced during write |
| /// ..Default::default() |
| /// }, |
| /// Default::default() // IPv6 extension headers (default is none) |
| /// )); |
| /// ``` |
| pub fn ip(self, ip_header: IpHeaders) -> PacketBuilderStep<IpHeaders> { |
| //use the method from the Ethernet2Header implementation |
| PacketBuilderStep { |
| state: self.state, |
| _marker: marker::PhantomData::<Ethernet2Header> {}, |
| } |
| .ip(ip_header) |
| } |
| |
| /// Add an IPv6 header |
| /// |
| /// # Example |
| /// |
| /// Basic usage: |
| /// |
| /// ``` |
| /// # use etherparse::{PacketBuilder, LinuxSllPacketType, ArpHardwareId, LinuxSllProtocolType, EtherType}; |
| /// # |
| /// let builder = PacketBuilder:: |
| /// linux_sll(LinuxSllPacketType::OTHERHOST, //packet type |
| /// 6, //sender address valid length |
| /// [1,2,3,4,5,6,0,0]) //sender address with padding |
| /// .ipv6( |
| /// //source |
| /// [11,12,13,14,15,16,17,18,19,10,21,22,23,24,25,26], |
| /// //destination |
| /// [31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46], |
| /// //hop_limit |
| /// 47) |
| /// .udp(21, //source port |
| /// 1234); //destination port |
| /// |
| /// //payload of the udp packet |
| /// let payload = [1,2,3,4,5,6,7,8]; |
| /// |
| /// //get some memory to store the result |
| /// let mut result = Vec::<u8>::with_capacity( |
| /// builder.size(payload.len())); |
| /// |
| /// //serialize |
| /// builder.write(&mut result, &payload).unwrap(); |
| /// ``` |
| pub fn ipv6( |
| self, |
| source: [u8; 16], |
| destination: [u8; 16], |
| hop_limit: u8, |
| ) -> PacketBuilderStep<IpHeaders> { |
| //use the method from the Ethernet2Header implementation |
| PacketBuilderStep { |
| state: self.state, |
| _marker: marker::PhantomData::<Ethernet2Header> {}, |
| } |
| .ipv6(source, destination, hop_limit) |
| } |
| |
| /// Add an IPv4 header |
| /// |
| /// # Example |
| /// |
| /// Basic usage: |
| /// |
| /// ``` |
| /// # use etherparse::{PacketBuilder, LinuxSllPacketType, ArpHardwareId, LinuxSllProtocolType, EtherType}; |
| /// # |
| /// let builder = PacketBuilder:: |
| /// linux_sll(LinuxSllPacketType::OTHERHOST, //packet type |
| /// 6, //sender address valid length |
| /// [1,2,3,4,5,6,0,0]) //sender address with padding |
| /// .ipv4([192,168,1,1], //source ip |
| /// [192,168,1,2], //destination ip |
| /// 20) //time to life |
| /// .udp(21, //source port |
| /// 1234); //destination port |
| /// |
| /// //payload of the udp packet |
| /// let payload = [1,2,3,4,5,6,7,8]; |
| /// |
| /// //get some memory to store the result |
| /// let mut result = Vec::<u8>::with_capacity( |
| /// builder.size(payload.len())); |
| /// |
| /// //serialize |
| /// builder.write(&mut result, &payload).unwrap(); |
| /// ``` |
| pub fn ipv4( |
| self, |
| source: [u8; 4], |
| destination: [u8; 4], |
| time_to_live: u8, |
| ) -> PacketBuilderStep<IpHeaders> { |
| //use the method from the Ethernet2Header implementation |
| PacketBuilderStep { |
| state: self.state, |
| _marker: marker::PhantomData::<Ethernet2Header> {}, |
| } |
| .ipv4(source, destination, time_to_live) |
| } |
| } |
| |
| #[cfg_attr(docsrs, doc(cfg(feature = "std")))] |
| impl PacketBuilderStep<VlanHeader> { |
| ///Add an ip header (length, protocol/next_header & checksum fields will be overwritten based on the rest of the packet). |
| /// |
| /// # Example IPv4 |
| /// ``` |
| /// # use etherparse::*; |
| /// # |
| /// let builder = PacketBuilder:: |
| /// ethernet2([1,2,3,4,5,6], |
| /// [7,8,9,10,11,12]) |
| /// .single_vlan(0x132.try_into().unwrap()) |
| /// //payload_len, protocol & checksum will be replaced during write |
| /// .ip(IpHeaders::Ipv4( |
| /// Ipv4Header::new( |
| /// 0, //payload_len will be replaced during write |
| /// 12, //time_to_live |
| /// ip_number::UDP, //will be replaced during write |
| /// [0,1,2,3], //source |
| /// [4,5,6,7] //destination |
| /// ).unwrap(), |
| /// Default::default() // IPv4 extension headers (default is none) |
| /// )); |
| /// ``` |
| /// |
| /// # Example IPv6 |
| /// ``` |
| /// # use etherparse::*; |
| /// # |
| /// let builder = PacketBuilder:: |
| /// ethernet2([1,2,3,4,5,6], |
| /// [7,8,9,10,11,12]) |
| /// .single_vlan(0x132.try_into().unwrap()) |
| /// .ip(IpHeaders::Ipv6( |
| /// Ipv6Header{ |
| /// traffic_class: 0, |
| /// flow_label: 0.try_into().unwrap(), |
| /// hop_limit: 4, |
| /// source: [0;16], |
| /// destination: [0;16], |
| /// // payload_length & next_header will be replaced during write |
| /// ..Default::default() |
| /// }, |
| /// Default::default() // IPv6 extension headers (default is none) |
| /// )); |
| /// ``` |
| pub fn ip(self, ip_header: IpHeaders) -> PacketBuilderStep<IpHeaders> { |
| //use the method from the Ethernet2Header implementation |
| PacketBuilderStep { |
| state: self.state, |
| _marker: marker::PhantomData::<Ethernet2Header> {}, |
| } |
| .ip(ip_header) |
| } |
| |
| /// Add an IPv6 header |
| /// |
| /// # Example |
| /// |
| /// Basic usage: |
| /// |
| /// ``` |
| /// # use etherparse::{PacketBuilder, SingleVlanHeader, VlanHeader}; |
| /// # |
| /// let builder = PacketBuilder:: |
| /// ethernet2([1,2,3,4,5,6], //source mac |
| /// [7,8,9,10,11,12]) //destination mac |
| /// .single_vlan(0x123.try_into().unwrap()) // vlan identifier |
| /// .ipv6( |
| /// //source |
| /// [11,12,13,14,15,16,17,18,19,10,21,22,23,24,25,26], |
| /// //destination |
| /// [31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46], |
| /// //hop_limit |
| /// 47) |
| /// .udp(21, //source port |
| /// 1234); //destination port |
| /// |
| /// //payload of the udp packet |
| /// let payload = [1,2,3,4,5,6,7,8]; |
| /// |
| /// //get some memory to store the result |
| /// let mut result = Vec::<u8>::with_capacity( |
| /// builder.size(payload.len())); |
| /// |
| /// //serialize |
| /// builder.write(&mut result, &payload).unwrap(); |
| /// ``` |
| pub fn ipv6( |
| self, |
| source: [u8; 16], |
| destination: [u8; 16], |
| hop_limit: u8, |
| ) -> PacketBuilderStep<IpHeaders> { |
| //use the method from the Ethernet2Header implementation |
| PacketBuilderStep { |
| state: self.state, |
| _marker: marker::PhantomData::<Ethernet2Header> {}, |
| } |
| .ipv6(source, destination, hop_limit) |
| } |
| |
| /// Add an IPv4 header |
| /// |
| /// # Example |
| /// |
| /// Basic usage: |
| /// |
| /// ``` |
| /// # use etherparse::{PacketBuilder, SingleVlanHeader, VlanHeader}; |
| /// # |
| /// let builder = PacketBuilder:: |
| /// ethernet2([1,2,3,4,5,6], //source mac |
| /// [7,8,9,10,11,12]) //destination mac |
| /// .single_vlan(0x123.try_into().unwrap()) // vlan identifier |
| /// .ipv4([192,168,1,1], //source ip |
| /// [192,168,1,2], //destination ip |
| /// 20) //time to life |
| /// .udp(21, //source port |
| /// 1234); //destination port |
| /// |
| /// //payload of the udp packet |
| /// let payload = [1,2,3,4,5,6,7,8]; |
| /// |
| /// //get some memory to store the result |
| /// let mut result = Vec::<u8>::with_capacity( |
| /// builder.size(payload.len())); |
| /// |
| /// //serialize |
| /// builder.write(&mut result, &payload).unwrap(); |
| /// ``` |
| pub fn ipv4( |
| self, |
| source: [u8; 4], |
| destination: [u8; 4], |
| time_to_live: u8, |
| ) -> PacketBuilderStep<IpHeaders> { |
| //use the method from the Ethernet2Header implementation |
| PacketBuilderStep { |
| state: self.state, |
| _marker: marker::PhantomData::<Ethernet2Header> {}, |
| } |
| .ipv4(source, destination, time_to_live) |
| } |
| } |
| |
| #[cfg_attr(docsrs, doc(cfg(feature = "std")))] |
| impl PacketBuilderStep<IpHeaders> { |
| /// Adds an ICMPv4 header of the given [`Icmpv4Type`] to the packet. |
| /// |
| /// If an ICMPv4 header gets added the payload used during the builders `write` |
| /// call contains the bytes after the header and has different meanings |
| /// and contents based on the type. Usually all statically sized values |
| /// known based on the ICMPv4 type & code are part of the header and the |
| /// payload is used to store contains the dynamic parts of the ICMPv4 packet. |
| /// |
| /// Check [`Icmpv4Type`] for a documentation which values are part of the |
| /// header and what is stored as part of the payload. |
| /// |
| /// # Example |
| /// |
| /// Basic usage: |
| /// |
| /// ``` |
| /// # use etherparse::{PacketBuilder, Icmpv4Type, icmpv4}; |
| /// # |
| /// let builder = PacketBuilder:: |
| /// ipv4([192,168,1,1], //source ip |
| /// [192,168,1,2], //destination ip |
| /// 20) //time to life |
| /// .icmpv4( |
| /// Icmpv4Type::TimeExceeded( |
| /// icmpv4::TimeExceededCode::TtlExceededInTransit |
| /// ) |
| /// ); |
| /// |
| /// // what is part of the payload depends on the Icmpv4Type |
| /// // |
| /// // In case of `Icmpv4Type::TimeExceeded` the "internet header |
| /// // + 64 bits of the original data datagram" should be given as |
| /// // the payload |
| /// let payload = [1,2,3,4,5,6,7,8]; |
| /// |
| /// //get some memory to store the result |
| /// let mut result = Vec::<u8>::with_capacity( |
| /// builder.size(payload.len())); |
| /// |
| /// //serialize |
| /// builder.write(&mut result, &payload).unwrap(); |
| /// ``` |
| pub fn icmpv4(mut self, icmp_type: Icmpv4Type) -> PacketBuilderStep<Icmpv4Header> { |
| self.state.transport_header = Some(TransportHeader::Icmpv4(Icmpv4Header { |
| icmp_type, |
| checksum: 0, // calculated later |
| })); |
| //return for next step |
| PacketBuilderStep { |
| state: self.state, |
| _marker: marker::PhantomData::<Icmpv4Header> {}, |
| } |
| } |
| |
| /// Adds an ICMPv4 header based on raw numbers. |
| /// |
| /// This can be useful when trying to build an ICMPv4 packet |
| /// which is not fully supported by etherparse and is the equivalent |
| /// of using [`Icmpv4Type::Unknown`] together with |
| /// [`PacketBuilderStep<IpHeaders>::icmpv4`]. |
| /// |
| /// # Example |
| /// |
| /// Basic usage: |
| /// |
| /// ``` |
| /// # use etherparse::PacketBuilder; |
| /// # |
| /// let builder = PacketBuilder:: |
| /// ipv4([192,168,1,1], //source ip |
| /// [192,168,1,2], //destination ip |
| /// 20) //time to life |
| /// .icmpv4_raw( |
| /// 253, // ICMPv4 type (e.g. 253 is RFC3692-style Experiment 1) |
| /// 0, // ICMPv4 code |
| /// [1,2,3,4] // bytes 5-8 in the ICMPv4 header |
| /// ); |
| /// |
| /// // the payload is written after the 8 byte raw ICMPv4 header |
| /// let payload = [1,2,3,4,5,6,7,8]; |
| /// |
| /// // get some memory to store the result |
| /// let mut result = Vec::<u8>::with_capacity( |
| /// builder.size(payload.len())); |
| /// |
| /// // serialize |
| /// builder.write(&mut result, &payload).unwrap(); |
| /// ``` |
| pub fn icmpv4_raw( |
| mut self, |
| type_u8: u8, |
| code_u8: u8, |
| bytes5to8: [u8; 4], |
| ) -> PacketBuilderStep<Icmpv4Header> { |
| let icmp_type = Icmpv4Type::Unknown { |
| type_u8, |
| code_u8, |
| bytes5to8, |
| }; |
| self.state.transport_header = Some(TransportHeader::Icmpv4(Icmpv4Header { |
| icmp_type, |
| checksum: 0, // calculated later |
| })); |
| //return for next step |
| PacketBuilderStep { |
| state: self.state, |
| _marker: marker::PhantomData::<Icmpv4Header> {}, |
| } |
| } |
| |
| /// Adds an ICMPv4 echo request packet. |
| /// |
| /// # Example |
| /// |
| /// Basic usage: |
| /// |
| /// ``` |
| /// # use etherparse::PacketBuilder; |
| /// # |
| /// let builder = PacketBuilder:: |
| /// ipv4([192,168,1,1], //source ip |
| /// [192,168,1,2], //destination ip |
| /// 20) //time to life |
| /// .icmpv4_echo_request( |
| /// 123, // identifier |
| /// 456, // sequence number |
| /// ); |
| /// |
| /// // payload of the echo request |
| /// let payload = [1,2,3,4,5,6,7,8]; |
| /// |
| /// // get some memory to store the result |
| /// let mut result = Vec::<u8>::with_capacity( |
| /// builder.size(payload.len())); |
| /// |
| /// // serialize |
| /// builder.write(&mut result, &payload).unwrap(); |
| /// ``` |
| pub fn icmpv4_echo_request(mut self, id: u16, seq: u16) -> PacketBuilderStep<Icmpv4Header> { |
| let echo_header = IcmpEchoHeader { id, seq }; |
| let icmpv4_echo = Icmpv4Header::new(Icmpv4Type::EchoRequest(echo_header)); |
| self.state.transport_header = Some(TransportHeader::Icmpv4(icmpv4_echo)); |
| //return for next step |
| PacketBuilderStep { |
| state: self.state, |
| _marker: marker::PhantomData::<Icmpv4Header> {}, |
| } |
| } |
| |
| /// Adds an ICMPv4 echo reply packet. |
| /// |
| /// # Example |
| /// |
| /// Basic usage: |
| /// |
| /// ``` |
| /// # use etherparse::PacketBuilder; |
| /// # |
| /// let builder = PacketBuilder:: |
| /// ipv4([192,168,1,1], //source ip |
| /// [192,168,1,2], //destination ip |
| /// 20) //time to life |
| /// .icmpv4_echo_reply( |
| /// 123, // identifier |
| /// 456, // sequence number |
| /// ); |
| /// |
| /// // payload of the echo reply |
| /// let payload = [1,2,3,4,5,6,7,8]; |
| /// |
| /// // get some memory to store the result |
| /// let mut result = Vec::<u8>::with_capacity( |
| /// builder.size(payload.len())); |
| /// |
| /// // serialize |
| /// builder.write(&mut result, &payload).unwrap(); |
| /// ``` |
| pub fn icmpv4_echo_reply(mut self, id: u16, seq: u16) -> PacketBuilderStep<Icmpv4Header> { |
| let echo_header = IcmpEchoHeader { id, seq }; |
| let icmpv4_echo = Icmpv4Header::new(Icmpv4Type::EchoReply(echo_header)); |
| self.state.transport_header = Some(TransportHeader::Icmpv4(icmpv4_echo)); |
| //return for next step |
| PacketBuilderStep { |
| state: self.state, |
| _marker: marker::PhantomData::<Icmpv4Header> {}, |
| } |
| } |
| |
| /// Adds an ICMPv6 header of the given [`Icmpv6Type`] to the packet. |
| /// |
| /// If an ICMPv6 header gets added the payload used during the builders `write` |
| /// call contains the bytes after the header and has different meanings |
| /// and contents based on the type. Usually all statically sized values |
| /// known based on the ICMPv6 type & code are part of the header and the |
| /// payload is used to store contains the dynamic parts of the ICMPv6 packet. |
| /// |
| /// Check [`Icmpv6Type`] for a documentation which values are part of the |
| /// header and what is stored as part of the payload. |
| /// |
| /// # Example |
| /// |
| /// Basic usage: |
| /// |
| /// ``` |
| /// # use etherparse::{PacketBuilder, Icmpv6Type, icmpv6}; |
| /// # |
| /// let builder = PacketBuilder:: |
| /// ipv6( |
| /// //source |
| /// [11,12,13,14,15,16,17,18,19,10,21,22,23,24,25,26], |
| /// //destination |
| /// [31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46], |
| /// //hop_limit |
| /// 47) |
| /// .icmpv6( |
| /// Icmpv6Type::TimeExceeded( |
| /// icmpv6::TimeExceededCode::HopLimitExceeded |
| /// ) |
| /// ); |
| /// |
| /// // what is part of the payload depends on the Icmpv6Type |
| /// // |
| /// // In case of `Icmpv6Type::TimeExceeded` "As much of invoking packet |
| /// // as possible without the ICMPv6 packet exceeding the minimum IPv6 MTU" |
| /// // should be given as the payload. |
| /// let payload = [1,2,3,4,5,6,7,8]; |
| /// |
| /// //get some memory to store the result |
| /// let mut result = Vec::<u8>::with_capacity( |
| /// builder.size(payload.len())); |
| /// |
| /// //serialize |
| /// builder.write(&mut result, &payload).unwrap(); |
| /// ``` |
| pub fn icmpv6(mut self, icmp_type: Icmpv6Type) -> PacketBuilderStep<Icmpv6Header> { |
| self.state.transport_header = Some(TransportHeader::Icmpv6(Icmpv6Header { |
| icmp_type, |
| checksum: 0, // calculated later |
| })); |
| //return for next step |
| PacketBuilderStep { |
| state: self.state, |
| _marker: marker::PhantomData::<Icmpv6Header> {}, |
| } |
| } |
| |
| /// Adds an ICMPv6 header based on raw values. |
| /// |
| /// This can be useful when trying to build an ICMPv6 packet |
| /// which is not fully supported by etherparse and is the equivalent |
| /// of using [`Icmpv6Type::Unknown`] together with |
| /// [`PacketBuilderStep<IpHeaders>::icmpv6`]. |
| /// |
| /// # Example |
| /// |
| /// Basic usage: |
| /// |
| /// ``` |
| /// # use etherparse::PacketBuilder; |
| /// # |
| /// let builder = PacketBuilder:: |
| /// ipv6( |
| /// //source |
| /// [11,12,13,14,15,16,17,18,19,10,21,22,23,24,25,26], |
| /// //destination |
| /// [31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46], |
| /// //hop_limit |
| /// 47) |
| /// .icmpv4_raw( |
| /// 200, // ICMPv6 type (e.g. 200 is for "private experimentation") |
| /// 0, // ICMPv6 code |
| /// [1,2,3,4] // bytes 5-8 in the ICMPv6 header |
| /// ); |
| /// |
| /// // the payload is written after the 8 byte raw ICMPv6 header |
| /// let payload = [1,2,3,4,5,6,7,8]; |
| /// |
| /// //get some memory to store the result |
| /// let mut result = Vec::<u8>::with_capacity( |
| /// builder.size(payload.len())); |
| /// |
| /// //serialize |
| /// builder.write(&mut result, &payload).unwrap(); |
| /// ``` |
| pub fn icmpv6_raw( |
| mut self, |
| type_u8: u8, |
| code_u8: u8, |
| bytes5to8: [u8; 4], |
| ) -> PacketBuilderStep<Icmpv6Header> { |
| let icmp_type = Icmpv6Type::Unknown { |
| type_u8, |
| code_u8, |
| bytes5to8, |
| }; |
| self.state.transport_header = Some(TransportHeader::Icmpv6(Icmpv6Header { |
| icmp_type, |
| checksum: 0, // calculated later |
| })); |
| //return for next step |
| PacketBuilderStep { |
| state: self.state, |
| _marker: marker::PhantomData::<Icmpv6Header> {}, |
| } |
| } |
| |
| /// Adds an ICMPv6 echo reply packet. |
| /// |
| /// # Example |
| /// |
| /// Basic usage: |
| /// |
| /// ``` |
| /// # use etherparse::PacketBuilder; |
| /// # |
| /// let builder = PacketBuilder:: |
| /// ipv6( |
| /// //source |
| /// [11,12,13,14,15,16,17,18,19,10,21,22,23,24,25,26], |
| /// //destination |
| /// [31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46], |
| /// //hop_limit |
| /// 47) |
| /// .icmpv6_echo_request( |
| /// 123, // identifier |
| /// 456, // sequence number |
| /// ); |
| /// |
| /// // payload of the echo request |
| /// let payload = [1,2,3,4,5,6,7,8]; |
| /// |
| /// //get some memory to store the result |
| /// let mut result = Vec::<u8>::with_capacity( |
| /// builder.size(payload.len())); |
| /// |
| /// //serialize |
| /// builder.write(&mut result, &payload).unwrap(); |
| /// ``` |
| pub fn icmpv6_echo_request(mut self, id: u16, seq: u16) -> PacketBuilderStep<Icmpv6Header> { |
| let echo_header = IcmpEchoHeader { id, seq }; |
| let icmpv6_echo = Icmpv6Header::new(Icmpv6Type::EchoRequest(echo_header)); |
| self.state.transport_header = Some(TransportHeader::Icmpv6(icmpv6_echo)); |
| //return for next step |
| PacketBuilderStep { |
| state: self.state, |
| _marker: marker::PhantomData::<Icmpv6Header> {}, |
| } |
| } |
| |
| /// Adds an ICMPv6 echo request packet. |
| /// |
| /// # Example |
| /// |
| /// Basic usage: |
| /// |
| /// ``` |
| /// # use etherparse::PacketBuilder; |
| /// # |
| /// let builder = PacketBuilder:: |
| /// ipv6( |
| /// //source |
| /// [11,12,13,14,15,16,17,18,19,10,21,22,23,24,25,26], |
| /// //destination |
| /// [31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46], |
| /// //hop_limit |
| /// 47) |
| /// .icmpv6_echo_reply( |
| /// 123, // identifier |
| /// 456, // sequence number |
| /// ); |
| /// |
| /// // payload of the echo reply |
| /// let payload = [1,2,3,4,5,6,7,8]; |
| /// |
| /// //get some memory to store the result |
| /// let mut result = Vec::<u8>::with_capacity( |
| /// builder.size(payload.len())); |
| /// |
| /// //serialize |
| /// builder.write(&mut result, &payload).unwrap(); |
| /// ``` |
| pub fn icmpv6_echo_reply(mut self, id: u16, seq: u16) -> PacketBuilderStep<Icmpv6Header> { |
| let echo_header = IcmpEchoHeader { seq, id }; |
| let icmpv6_echo = Icmpv6Header::new(Icmpv6Type::EchoReply(echo_header)); |
| self.state.transport_header = Some(TransportHeader::Icmpv6(icmpv6_echo)); |
| //return for next step |
| PacketBuilderStep { |
| state: self.state, |
| _marker: marker::PhantomData::<Icmpv6Header> {}, |
| } |
| } |
| |
| /// Adds an UDP header. |
| /// |
| /// # Example |
| /// |
| /// Basic usage: |
| /// |
| /// ``` |
| /// # use etherparse::PacketBuilder; |
| /// # |
| /// let builder = PacketBuilder:: |
| /// ethernet2([1,2,3,4,5,6], //source mac |
| /// [7,8,9,10,11,12]) //destination mac |
| /// .ipv4([192,168,1,1], //source ip |
| /// [192,168,1,2], //destination ip |
| /// 20) //time to life |
| /// .udp(21, //source port |
| /// 1234); //destination port |
| /// |
| /// //payload of the udp packet |
| /// let payload = [1,2,3,4,5,6,7,8]; |
| /// |
| /// //get some memory to store the result |
| /// let mut result = Vec::<u8>::with_capacity( |
| /// builder.size(payload.len())); |
| /// |
| /// //serialize |
| /// builder.write(&mut result, &payload).unwrap(); |
| /// ``` |
| pub fn udp(mut self, source_port: u16, destination_port: u16) -> PacketBuilderStep<UdpHeader> { |
| self.state.transport_header = Some(TransportHeader::Udp(UdpHeader { |
| source_port, |
| destination_port, |
| length: 0, //calculated later |
| checksum: 0, //calculated later |
| })); |
| //return for next step |
| PacketBuilderStep { |
| state: self.state, |
| _marker: marker::PhantomData::<UdpHeader> {}, |
| } |
| } |
| |
| /// Adds a simple TCP header. |
| /// |
| /// # Example |
| /// |
| /// Basic usage: |
| /// |
| /// ``` |
| /// # use etherparse::PacketBuilder; |
| /// # |
| /// let builder = PacketBuilder:: |
| /// ethernet2([1,2,3,4,5,6], // source mac |
| /// [7,8,9,10,11,12]) // destination mac |
| /// .ipv4([192,168,1,1], // source ip |
| /// [192,168,1,2], // destination ip |
| /// 20) // time to life |
| /// .tcp(21, // source port |
| /// 12, // destination port |
| /// 12345, // sequence number |
| /// 4000); // window size |
| /// |
| /// //payload of the udp packet |
| /// let payload = [1,2,3,4,5,6,7,8]; |
| /// |
| /// //get some memory to store the result |
| /// let mut result = Vec::<u8>::with_capacity( |
| /// builder.size(payload.len())); |
| /// |
| /// //serialize |
| /// builder.write(&mut result, &payload).unwrap(); |
| /// ``` |
| pub fn tcp( |
| mut self, |
| source_port: u16, |
| destination_port: u16, |
| sequence_number: u32, |
| window_size: u16, |
| ) -> PacketBuilderStep<TcpHeader> { |
| self.state.transport_header = Some(TransportHeader::Tcp(TcpHeader::new( |
| source_port, |
| destination_port, |
| sequence_number, |
| window_size, |
| ))); |
| //return for next step |
| PacketBuilderStep { |
| state: self.state, |
| _marker: marker::PhantomData::<TcpHeader> {}, |
| } |
| } |
| |
| /// Adds a more complicated TCP header. |
| /// |
| /// # Example |
| /// |
| /// Basic usage: |
| /// |
| /// ``` |
| /// # use etherparse::PacketBuilder; |
| /// use etherparse::TcpHeader; |
| /// |
| /// let mut tcp_header = TcpHeader::new( |
| /// 21, // source port |
| /// 12, // destination port |
| /// 12345, // sequence number |
| /// 4000, // window size |
| /// ); |
| /// tcp_header.psh = true; |
| /// tcp_header.ack = true; |
| /// tcp_header.acknowledgment_number = 1; |
| /// |
| /// let builder = PacketBuilder:: |
| /// ethernet2([1,2,3,4,5,6], // source mac |
| /// [7,8,9,10,11,12]) // destination mac |
| /// .ipv4([192,168,1,1], // source ip |
| /// [192,168,1,2], // destination ip |
| /// 20) // time to life |
| /// .tcp_header(tcp_header); |
| /// |
| /// //payload of the udp packet |
| /// let payload = [1,2,3,4,5,6,7,8]; |
| /// |
| /// //get some memory to store the result |
| /// let mut result = Vec::<u8>::with_capacity( |
| /// builder.size(payload.len())); |
| /// |
| /// //serialize |
| /// builder.write(&mut result, &payload).unwrap(); |
| /// ``` |
| pub fn tcp_header(mut self, tcp_header: TcpHeader) -> PacketBuilderStep<TcpHeader> { |
| self.state.transport_header = Some(TransportHeader::Tcp(tcp_header)); |
| //return for next step |
| PacketBuilderStep { |
| state: self.state, |
| _marker: marker::PhantomData::<TcpHeader> {}, |
| } |
| } |
| |
| /// Write all the headers and the payload with the given ip number. |
| /// |
| /// `last_next_header_ip_number` will be set in the last extension header |
| /// or if no extension header exists the ip header as the "next header" or |
| /// "protocol number". |
| pub fn write<T: io::Write + Sized>( |
| mut self, |
| writer: &mut T, |
| last_next_header_ip_number: IpNumber, |
| payload: &[u8], |
| ) -> Result<(), BuildWriteError> { |
| self.state |
| .ip_header |
| .as_mut() |
| .unwrap() |
| .set_next_headers(last_next_header_ip_number); |
| final_write(self, writer, payload) |
| } |
| |
| ///Returns the size of the packet when it is serialized |
| pub fn size(&self, payload_size: usize) -> usize { |
| final_size(self, payload_size) |
| } |
| } |
| |
| #[cfg_attr(docsrs, doc(cfg(feature = "std")))] |
| impl PacketBuilderStep<Icmpv4Header> { |
| /// Write all the headers and the payload. |
| pub fn write<T: io::Write + Sized>( |
| self, |
| writer: &mut T, |
| payload: &[u8], |
| ) -> Result<(), BuildWriteError> { |
| final_write(self, writer, payload) |
| } |
| |
| /// Returns the size of the packet when it is serialized |
| pub fn size(&self, payload_size: usize) -> usize { |
| final_size(self, payload_size) |
| } |
| } |
| |
| #[cfg_attr(docsrs, doc(cfg(feature = "std")))] |
| impl PacketBuilderStep<Icmpv6Header> { |
| ///Write all the headers and the payload. |
| pub fn write<T: io::Write + Sized>( |
| self, |
| writer: &mut T, |
| payload: &[u8], |
| ) -> Result<(), BuildWriteError> { |
| final_write(self, writer, payload) |
| } |
| |
| ///Returns the size of the packet when it is serialized |
| pub fn size(&self, payload_size: usize) -> usize { |
| final_size(self, payload_size) |
| } |
| } |
| |
| #[cfg_attr(docsrs, doc(cfg(feature = "std")))] |
| impl PacketBuilderStep<UdpHeader> { |
| ///Write all the headers and the payload. |
| pub fn write<T: io::Write + Sized>( |
| self, |
| writer: &mut T, |
| payload: &[u8], |
| ) -> Result<(), BuildWriteError> { |
| final_write(self, writer, payload) |
| } |
| |
| ///Returns the size of the packet when it is serialized |
| pub fn size(&self, payload_size: usize) -> usize { |
| final_size(self, payload_size) |
| } |
| } |
| |
| #[cfg_attr(docsrs, doc(cfg(feature = "std")))] |
| impl PacketBuilderStep<TcpHeader> { |
| ///Set ns flag (ECN-nonce - concealment protection; experimental: see RFC 3540) |
| pub fn ns(mut self) -> PacketBuilderStep<TcpHeader> { |
| self.state |
| .transport_header |
| .as_mut() |
| .unwrap() |
| .mut_tcp() |
| .unwrap() |
| .ns = true; |
| self |
| } |
| ///Set fin flag (No more data from sender) |
| pub fn fin(mut self) -> PacketBuilderStep<TcpHeader> { |
| self.state |
| .transport_header |
| .as_mut() |
| .unwrap() |
| .mut_tcp() |
| .unwrap() |
| .fin = true; |
| self |
| } |
| ///Set the syn flag (synchronize sequence numbers) |
| pub fn syn(mut self) -> PacketBuilderStep<TcpHeader> { |
| self.state |
| .transport_header |
| .as_mut() |
| .unwrap() |
| .mut_tcp() |
| .unwrap() |
| .syn = true; |
| self |
| } |
| ///Sets the rst flag (reset the connection) |
| pub fn rst(mut self) -> PacketBuilderStep<TcpHeader> { |
| self.state |
| .transport_header |
| .as_mut() |
| .unwrap() |
| .mut_tcp() |
| .unwrap() |
| .rst = true; |
| self |
| } |
| ///Sets the psh flag (push function) |
| pub fn psh(mut self) -> PacketBuilderStep<TcpHeader> { |
| self.state |
| .transport_header |
| .as_mut() |
| .unwrap() |
| .mut_tcp() |
| .unwrap() |
| .psh = true; |
| self |
| } |
| ///Sets the ack flag and the acknowledgment_number. |
| pub fn ack(mut self, acknowledgment_number: u32) -> PacketBuilderStep<TcpHeader> { |
| { |
| let header = self |
| .state |
| .transport_header |
| .as_mut() |
| .unwrap() |
| .mut_tcp() |
| .unwrap(); |
| header.ack = true; |
| header.acknowledgment_number = acknowledgment_number; |
| } |
| self |
| } |
| ///Set the urg flag & the urgent pointer field. |
| /// |
| ///The urgent pointer points to the sequence number of the octet following |
| ///the urgent data. |
| pub fn urg(mut self, urgent_pointer: u16) -> PacketBuilderStep<TcpHeader> { |
| { |
| let header = self |
| .state |
| .transport_header |
| .as_mut() |
| .unwrap() |
| .mut_tcp() |
| .unwrap(); |
| header.urg = true; |
| header.urgent_pointer = urgent_pointer; |
| } |
| self |
| } |
| ///Sets ece flag (ECN-Echo, RFC 3168) |
| pub fn ece(mut self) -> PacketBuilderStep<TcpHeader> { |
| self.state |
| .transport_header |
| .as_mut() |
| .unwrap() |
| .mut_tcp() |
| .unwrap() |
| .ece = true; |
| self |
| } |
| |
| ///Set cwr flag (Congestion Window Reduced) |
| /// |
| ///This flag is set by the sending host to indicate that it received a TCP segment with the ECE flag set and had responded in congestion control mechanism (added to header by RFC 3168). |
| pub fn cwr(mut self) -> PacketBuilderStep<TcpHeader> { |
| self.state |
| .transport_header |
| .as_mut() |
| .unwrap() |
| .mut_tcp() |
| .unwrap() |
| .cwr = true; |
| self |
| } |
| |
| ///Set the tcp options of the header. |
| pub fn options( |
| mut self, |
| options: &[TcpOptionElement], |
| ) -> Result<PacketBuilderStep<TcpHeader>, TcpOptionWriteError> { |
| self.state |
| .transport_header |
| .as_mut() |
| .unwrap() |
| .mut_tcp() |
| .unwrap() |
| .set_options(options)?; |
| Ok(self) |
| } |
| |
| ///Set the tcp options of the header (setting the bytes directly). |
| pub fn options_raw( |
| mut self, |
| options: &[u8], |
| ) -> Result<PacketBuilderStep<TcpHeader>, TcpOptionWriteError> { |
| self.state |
| .transport_header |
| .as_mut() |
| .unwrap() |
| .mut_tcp() |
| .unwrap() |
| .set_options_raw(options)?; |
| Ok(self) |
| } |
| |
| ///Write all the headers and the payload. |
| pub fn write<T: io::Write + Sized>( |
| self, |
| writer: &mut T, |
| payload: &[u8], |
| ) -> Result<(), BuildWriteError> { |
| final_write(self, writer, payload) |
| } |
| |
| ///Returns the size of the packet when it is serialized |
| pub fn size(&self, payload_size: usize) -> usize { |
| final_size(self, payload_size) |
| } |
| } |
| |
| /// Write all the headers and the payload. |
| fn final_write<T: io::Write + Sized, B>( |
| builder: PacketBuilderStep<B>, |
| writer: &mut T, |
| payload: &[u8], |
| ) -> Result<(), BuildWriteError> { |
| use BuildWriteError::*; |
| |
| let ip_ether_type = { |
| use crate::IpHeaders::*; |
| match builder.state.ip_header { |
| Some(Ipv4(_, _)) => ether_type::IPV4, |
| Some(Ipv6(_, _)) => ether_type::IPV6, |
| None => panic!("Missing ip header"), |
| } |
| }; |
| |
| //link header |
| if let Some(link) = builder.state.link_header { |
| match link { |
| LinkHeader::Ethernet2(mut eth) => { |
| eth.ether_type = { |
| use crate::VlanHeader::*; |
| //determine the ether type depending on if there is a vlan tagging header |
| match builder.state.vlan_header { |
| Some(Single(_)) => ether_type::VLAN_TAGGED_FRAME, |
| Some(Double(_)) => ether_type::PROVIDER_BRIDGING, |
| //if no vlan header exists, the id is purely defined by the ip type |
| None => ip_ether_type, |
| } |
| }; |
| eth.write(writer).map_err(Io)?; |
| } |
| LinkHeader::LinuxSll(mut linux_sll) => { |
| // Assumes that next layers are ether based. If more types of |
| // layers are supported, this should be updated |
| debug_assert_eq!(linux_sll.arp_hrd_type, ArpHardwareId::ETHER); |
| |
| linux_sll.protocol_type.change_value(ip_ether_type.into()); |
| linux_sll.write(writer).map_err(Io)?; |
| } |
| } |
| } |
| |
| //write the vlan header if it exists |
| use crate::VlanHeader::*; |
| match builder.state.vlan_header { |
| Some(Single(mut value)) => { |
| //set ether types |
| value.ether_type = ip_ether_type; |
| //serialize |
| value.write(writer).map_err(Io)?; |
| } |
| Some(Double(mut value)) => { |
| //set ether types |
| value.outer.ether_type = ether_type::VLAN_TAGGED_FRAME; |
| value.inner.ether_type = ip_ether_type; |
| //serialize |
| value.write(writer).map_err(Io)?; |
| } |
| None => {} |
| } |
| |
| //ip header |
| use crate::IpHeaders::*; |
| let ip_header = builder.state.ip_header.unwrap(); |
| |
| //transport header |
| let transport = builder.state.transport_header; |
| match transport { |
| None => { |
| // in case no transport header is present the protocol |
| // number and next_header fields are set in the write call |
| // directly and don't need to be set here again. |
| match ip_header { |
| Ipv4(mut ip, ext) => { |
| ip.set_payload_len(ext.header_len() + payload.len()) |
| .map_err(PayloadLen)?; |
| ip.write(writer).map_err(Io)?; |
| ext.write(writer, ip.protocol).map_err(|err| { |
| use err::ipv4_exts::HeaderWriteError as I; |
| match err { |
| I::Io(err) => Io(err), |
| I::Content(err) => Ipv4Exts(err), |
| } |
| })?; |
| } |
| Ipv6(mut ip, ext) => { |
| ip.set_payload_length(ext.header_len() + payload.len()) |
| .map_err(PayloadLen)?; |
| ip.write(writer).map_err(Io)?; |
| ext.write(writer, ip.next_header).map_err(|err| { |
| use err::ipv6_exts::HeaderWriteError as I; |
| match err { |
| I::Io(err) => Io(err), |
| I::Content(err) => Ipv6Exts(err), |
| } |
| })?; |
| } |
| } |
| } |
| Some(mut transport) => { |
| match ip_header { |
| Ipv4(mut ip, mut ext) => { |
| //set total length & udp payload length (ip checks that the payload length is ok) |
| let transport_size = transport.header_len() + payload.len(); |
| ip.set_payload_len(ext.header_len() + transport_size) |
| .map_err(PayloadLen)?; |
| use crate::TransportHeader::*; |
| match transport { |
| Icmpv4(_) => {} |
| Icmpv6(_) => {} |
| Udp(ref mut udp) => { |
| udp.length = transport_size as u16; |
| } |
| Tcp(_) => {} |
| } |
| |
| //ip protocol number & next header values of the extension header |
| ip.protocol = ext.set_next_headers(match transport { |
| Icmpv4(_) => ip_number::ICMP, |
| Icmpv6(_) => ip_number::IPV6_ICMP, |
| Udp(_) => ip_number::UDP, |
| Tcp(_) => ip_number::TCP, |
| }); |
| |
| //calculate the udp checksum |
| transport |
| .update_checksum_ipv4(&ip, payload) |
| .map_err(|err| { |
| use err::packet::TransportChecksumError as I; |
| match err { |
| I::PayloadLen(err) => PayloadLen(err), |
| I::Icmpv6InIpv4 => Icmpv6InIpv4, |
| } |
| })?; |
| |
| //write (will automatically calculate the checksum) |
| ip.write(writer).map_err(Io)?; |
| ext.write(writer, ip.protocol).map_err(|err| { |
| use err::ipv4_exts::HeaderWriteError as I; |
| match err { |
| I::Io(err) => Io(err), |
| I::Content(err) => Ipv4Exts(err), |
| } |
| })?; |
| } |
| Ipv6(mut ip, mut ext) => { |
| //set total length |
| let transport_size = transport.header_len() + payload.len(); |
| ip.set_payload_length(ext.header_len() + transport_size) |
| .map_err(PayloadLen)?; |
| use crate::TransportHeader::*; |
| match transport { |
| Icmpv4(_) => {} |
| Icmpv6(_) => {} |
| Udp(ref mut udp) => { |
| udp.length = transport_size as u16; |
| } |
| Tcp(_) => {} |
| } |
| |
| //set the protocol |
| ip.next_header = ext.set_next_headers(match transport { |
| Icmpv4(_) => ip_number::ICMP, |
| Icmpv6(_) => ip_number::IPV6_ICMP, |
| Udp(_) => ip_number::UDP, |
| Tcp(_) => ip_number::TCP, |
| }); |
| |
| //calculate the udp checksum |
| transport |
| .update_checksum_ipv6(&ip, payload) |
| .map_err(PayloadLen)?; |
| |
| //write (will automatically calculate the checksum) |
| ip.write(writer).map_err(Io)?; |
| ext.write(writer, ip.next_header).map_err(|err| { |
| use err::ipv6_exts::HeaderWriteError as I; |
| match err { |
| I::Io(err) => Io(err), |
| I::Content(err) => Ipv6Exts(err), |
| } |
| })?; |
| } |
| } |
| |
| //finally write the udp header & payload |
| transport.write(writer).map_err(Io)?; |
| } |
| } |
| writer.write_all(payload).map_err(Io)?; |
| Ok(()) |
| } |
| |
| ///Returns the size of the packet when it is serialized |
| fn final_size<B>(builder: &PacketBuilderStep<B>, payload_size: usize) -> usize { |
| use crate::IpHeaders::*; |
| use crate::TransportHeader::*; |
| use crate::VlanHeader::*; |
| (match builder.state.link_header { |
| Some(ref header) => header.header_len(), |
| None => 0, |
| }) + match builder.state.vlan_header { |
| Some(Single(_)) => SingleVlanHeader::LEN, |
| Some(Double(_)) => DoubleVlanHeader::LEN, |
| None => 0, |
| } + match builder.state.ip_header { |
| Some(Ipv4(ref value, ref ext)) => value.header_len() + ext.header_len(), |
| Some(Ipv6(_, ref ext)) => Ipv6Header::LEN + ext.header_len(), |
| None => 0, |
| } + match builder.state.transport_header { |
| Some(Icmpv4(ref value)) => value.header_len(), |
| Some(Icmpv6(ref value)) => value.header_len(), |
| Some(Udp(_)) => UdpHeader::LEN, |
| Some(Tcp(ref value)) => value.header_len(), |
| None => 0, |
| } + payload_size |
| } |
| |
| #[cfg(test)] |
| mod white_box_tests { |
| use super::*; |
| use alloc::vec::Vec; |
| |
| //white box tests that need internal access |
| #[test] |
| fn size() { |
| assert_eq!( |
| 0, |
| PacketBuilderStep::<UdpHeader> { |
| state: PacketImpl { |
| link_header: None, |
| ip_header: None, |
| vlan_header: None, |
| transport_header: None |
| }, |
| _marker: marker::PhantomData::<UdpHeader> {} |
| } |
| .size(0) |
| ); |
| } |
| |
| #[test] |
| #[should_panic] |
| fn final_write_panic_missing_ip() { |
| let mut writer = Vec::new(); |
| final_write( |
| PacketBuilderStep::<UdpHeader> { |
| state: PacketImpl { |
| link_header: None, |
| ip_header: None, |
| vlan_header: None, |
| transport_header: None, |
| }, |
| _marker: marker::PhantomData::<UdpHeader> {}, |
| }, |
| &mut writer, |
| &[], |
| ) |
| .unwrap(); |
| } |
| } |
| |
| #[cfg(test)] |
| mod test { |
| use super::*; |
| use crate::test_gens::*; |
| use alloc::{vec, vec::Vec}; |
| use proptest::prelude::*; |
| use std::io::Read; |
| |
| #[test] |
| fn eth_ipv4_udp() { |
| //generate |
| let in_payload = [24, 25, 26, 27]; |
| let mut serialized = Vec::new(); |
| PacketBuilder::ethernet2([1, 2, 3, 4, 5, 6], [7, 8, 9, 10, 11, 12]) |
| .ipv4([13, 14, 15, 16], [17, 18, 19, 20], 21) |
| .udp(22, 23) |
| .write(&mut serialized, &in_payload) |
| .unwrap(); |
| |
| //check the deserialized size |
| let expected_ip_size: usize = UdpHeader::LEN + in_payload.len(); |
| assert_eq!( |
| expected_ip_size + Ethernet2Header::LEN + Ipv4Header::MIN_LEN, |
| serialized.len() |
| ); |
| |
| //deserialize and check that everything is as expected |
| use std::io::Cursor; |
| //deserialize each part of the message and check it |
| let mut cursor = Cursor::new(&serialized); |
| |
| //ethernet 2 header |
| assert_eq!( |
| Ethernet2Header::read(&mut cursor).unwrap(), |
| Ethernet2Header { |
| source: [1, 2, 3, 4, 5, 6], |
| destination: [7, 8, 9, 10, 11, 12], |
| ether_type: ether_type::IPV4 |
| } |
| ); |
| |
| //ip header |
| let ip_actual = Ipv4Header::read(&mut cursor).unwrap(); |
| let mut ip_expected = Ipv4Header::new( |
| expected_ip_size as u16, |
| 21, //ttl |
| ip_number::UDP, |
| [13, 14, 15, 16], |
| [17, 18, 19, 20], |
| ) |
| .unwrap(); |
| ip_expected.header_checksum = ip_expected.calc_header_checksum(); |
| assert_eq!(ip_actual, ip_expected); |
| |
| //udp header |
| let udp_actual = UdpHeader::read(&mut cursor).unwrap(); |
| let udp_expected = |
| UdpHeader::with_ipv4_checksum(22, 23, &ip_expected, &in_payload).unwrap(); |
| assert_eq!(udp_actual, udp_expected); |
| |
| //payload |
| let mut actual_payload: [u8; 4] = [0; 4]; |
| cursor.read_exact(&mut actual_payload).unwrap(); |
| assert_eq!(actual_payload, in_payload); |
| } |
| |
| #[test] |
| fn linuxsll_ipv4_udp() { |
| //generate |
| let in_payload = [24, 25, 26, 27]; |
| let mut serialized = Vec::new(); |
| PacketBuilder::linux_sll(LinuxSllPacketType::OUTGOING, 6, [7, 8, 9, 10, 11, 12, 0, 0]) |
| .ipv4([13, 14, 15, 16], [17, 18, 19, 20], 21) |
| .udp(22, 23) |
| .write(&mut serialized, &in_payload) |
| .unwrap(); |
| |
| //check the deserialized size |
| let expected_ip_size: usize = UdpHeader::LEN + in_payload.len(); |
| assert_eq!( |
| expected_ip_size + LinuxSllHeader::LEN + Ipv4Header::MIN_LEN, |
| serialized.len() |
| ); |
| |
| //deserialize and check that everything is as expected |
| use std::io::Cursor; |
| //deserialize each part of the message and check it |
| let mut cursor = Cursor::new(&serialized); |
| |
| //ethernet 2 header |
| assert_eq!( |
| LinuxSllHeader::read(&mut cursor).unwrap(), |
| LinuxSllHeader { |
| packet_type: LinuxSllPacketType::OUTGOING, |
| arp_hrd_type: ArpHardwareId::ETHER, |
| sender_address_valid_length: 6, |
| sender_address: [7, 8, 9, 10, 11, 12, 0, 0], |
| protocol_type: LinuxSllProtocolType::EtherType(EtherType::IPV4) |
| } |
| ); |
| |
| //ip header |
| let ip_actual = Ipv4Header::read(&mut cursor).unwrap(); |
| let mut ip_expected = Ipv4Header::new( |
| expected_ip_size as u16, |
| 21, //ttl |
| ip_number::UDP, |
| [13, 14, 15, 16], |
| [17, 18, 19, 20], |
| ) |
| .unwrap(); |
| ip_expected.header_checksum = ip_expected.calc_header_checksum(); |
| assert_eq!(ip_actual, ip_expected); |
| |
| //udp header |
| let udp_actual = UdpHeader::read(&mut cursor).unwrap(); |
| let udp_expected = |
| UdpHeader::with_ipv4_checksum(22, 23, &ip_expected, &in_payload).unwrap(); |
| assert_eq!(udp_actual, udp_expected); |
| |
| //payload |
| let mut actual_payload: [u8; 4] = [0; 4]; |
| cursor.read_exact(&mut actual_payload).unwrap(); |
| assert_eq!(actual_payload, in_payload); |
| } |
| |
| #[test] |
| fn ipv4() { |
| let auth_ext = IpAuthHeader::new(0.into(), 1, 2, &[3, 4, 5, 6]).unwrap(); |
| |
| //generate |
| let in_payload = [22, 23, 24, 25]; |
| let mut serialized = Vec::new(); |
| let builder = PacketBuilder::ip(IpHeaders::Ipv4( |
| Ipv4Header::new( |
| in_payload.len() as u16, |
| 21, |
| 0.into(), |
| [13, 14, 15, 16], |
| [17, 18, 19, 20], |
| ) |
| .unwrap(), |
| Ipv4Extensions { |
| auth: Some(auth_ext.clone()), |
| }, |
| )); |
| |
| // check size |
| assert_eq!( |
| builder.size(in_payload.len()), |
| Ipv4Header::MIN_LEN + auth_ext.header_len() + in_payload.len() |
| ); |
| |
| // write |
| serialized.reserve(builder.size(in_payload.len())); |
| builder |
| .write(&mut serialized, 200.into(), &in_payload) |
| .unwrap(); |
| |
| //check the deserialized size |
| assert_eq!( |
| Ipv4Header::MIN_LEN + auth_ext.header_len() + in_payload.len(), |
| serialized.len() |
| ); |
| |
| //deserialize and check that everything is as expected |
| use std::io::{Cursor, Read}; |
| |
| //deserialize each part of the message and check it |
| let mut cursor = Cursor::new(&serialized); |
| |
| //ip header |
| let ip_actual = Ipv4Header::read(&mut cursor).unwrap(); |
| let mut ip_expected = Ipv4Header::new( |
| (auth_ext.header_len() + in_payload.len()) as u16, |
| 21, //ttl |
| ip_number::AUTH, // should have been set |
| [13, 14, 15, 16], |
| [17, 18, 19, 20], |
| ) |
| .unwrap(); |
| ip_expected.header_checksum = ip_expected.calc_header_checksum(); |
| assert_eq!(ip_actual, ip_expected); |
| |
| // auth header |
| let auth_actual = IpAuthHeader::read(&mut cursor).unwrap(); |
| assert_eq!( |
| auth_actual, |
| IpAuthHeader::new( |
| 200.into(), // ip number should have been set |
| 1, |
| 2, |
| &[3, 4, 5, 6] |
| ) |
| .unwrap() |
| ); |
| |
| //payload |
| let mut actual_payload: [u8; 4] = [0; 4]; |
| cursor.read_exact(&mut actual_payload).unwrap(); |
| assert_eq!(actual_payload, in_payload); |
| } |
| |
| #[test] |
| fn ipv6() { |
| let auth_ext = IpAuthHeader::new(0.into(), 1, 2, &[3, 4, 5, 6]).unwrap(); |
| |
| //generate |
| let in_payload = [48, 49, 50, 51]; |
| let mut serialized = Vec::new(); |
| let builder = PacketBuilder::ip(IpHeaders::Ipv6( |
| Ipv6Header { |
| traffic_class: 0, |
| flow_label: Ipv6FlowLabel::ZERO, |
| payload_length: in_payload.len() as u16, |
| next_header: 0.into(), |
| hop_limit: 47, |
| source: [ |
| 11, 12, 13, 14, 15, 16, 17, 18, 19, 10, 21, 22, 23, 24, 25, 26, |
| ], |
| destination: [ |
| 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, |
| ], |
| }, |
| Ipv6Extensions { |
| hop_by_hop_options: None, |
| destination_options: None, |
| routing: None, |
| fragment: None, |
| auth: Some(auth_ext.clone()), |
| }, |
| )); |
| |
| // check size |
| assert_eq!( |
| builder.size(in_payload.len()), |
| Ipv6Header::LEN + auth_ext.header_len() + in_payload.len() |
| ); |
| |
| // write |
| builder |
| .write(&mut serialized, 200.into(), &in_payload) |
| .unwrap(); |
| |
| //check the deserialized size |
| assert_eq!( |
| Ipv6Header::LEN + auth_ext.header_len() + in_payload.len(), |
| serialized.len() |
| ); |
| |
| //deserialize and check that everything is as expected |
| use std::io::{Cursor, Read}; |
| |
| //deserialize each part of the message and check it |
| let mut cursor = Cursor::new(&serialized); |
| |
| //ip header |
| let ip_actual = Ipv6Header::read(&mut cursor).unwrap(); |
| let ip_expected = Ipv6Header { |
| traffic_class: 0, |
| flow_label: Ipv6FlowLabel::ZERO, |
| payload_length: (auth_ext.header_len() + in_payload.len()) as u16, |
| next_header: ip_number::AUTH, // should have been set |
| hop_limit: 47, |
| source: [ |
| 11, 12, 13, 14, 15, 16, 17, 18, 19, 10, 21, 22, 23, 24, 25, 26, |
| ], |
| destination: [ |
| 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, |
| ], |
| }; |
| |
| assert_eq!(ip_actual, ip_expected); |
| |
| // auth header |
| let auth_actual = IpAuthHeader::read(&mut cursor).unwrap(); |
| assert_eq!( |
| auth_actual, |
| IpAuthHeader::new( |
| 200.into(), // ip number should have been set |
| 1, |
| 2, |
| &[3, 4, 5, 6] |
| ) |
| .unwrap() |
| ); |
| |
| //payload |
| let mut actual_payload: [u8; 4] = [0; 4]; |
| cursor.read_exact(&mut actual_payload).unwrap(); |
| assert_eq!(actual_payload, in_payload); |
| } |
| |
| #[test] |
| fn ipv4_udp() { |
| //generate |
| let in_payload = [24, 25, 26, 27]; |
| let mut serialized = Vec::new(); |
| PacketBuilder::ipv4([13, 14, 15, 16], [17, 18, 19, 20], 21) |
| .udp(22, 23) |
| .write(&mut serialized, &in_payload) |
| .unwrap(); |
| |
| //check the deserialized size |
| let expected_ip_size: usize = UdpHeader::LEN + in_payload.len(); |
| assert_eq!(expected_ip_size + Ipv4Header::MIN_LEN, serialized.len()); |
| |
| //deserialize and check that everything is as expected |
| use std::io::{Cursor, Read}; |
| |
| //deserialize each part of the message and check it |
| let mut cursor = Cursor::new(&serialized); |
| |
| //ip header |
| let ip_actual = Ipv4Header::read(&mut cursor).unwrap(); |
| let mut ip_expected = Ipv4Header::new( |
| expected_ip_size as u16, |
| 21, //ttl |
| ip_number::UDP, |
| [13, 14, 15, 16], |
| [17, 18, 19, 20], |
| ) |
| .unwrap(); |
| ip_expected.header_checksum = ip_expected.calc_header_checksum(); |
| assert_eq!(ip_actual, ip_expected); |
| |
| //udp header |
| let udp_actual = UdpHeader::read(&mut cursor).unwrap(); |
| let udp_expected = |
| UdpHeader::with_ipv4_checksum(22, 23, &ip_expected, &in_payload).unwrap(); |
| assert_eq!(udp_actual, udp_expected); |
| |
| //payload |
| let mut actual_payload: [u8; 4] = [0; 4]; |
| cursor.read_exact(&mut actual_payload).unwrap(); |
| assert_eq!(actual_payload, in_payload); |
| } |
| |
| #[test] |
| fn ipv6_udp() { |
| //generate |
| let in_payload = [24, 25, 26, 27]; |
| let mut serialized = Vec::new(); |
| PacketBuilder::ipv6( |
| //source |
| [ |
| 11, 12, 13, 14, 15, 16, 17, 18, 19, 10, 21, 22, 23, 24, 25, 26, |
| ], |
| //destination |
| [ |
| 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, |
| ], |
| //hop_limit |
| 47, |
| ) |
| .udp(22, 23) |
| .write(&mut serialized, &in_payload) |
| .unwrap(); |
| |
| //check the deserialized size |
| let expected_ip_size: usize = UdpHeader::LEN + in_payload.len(); |
| assert_eq!(expected_ip_size + Ipv6Header::LEN, serialized.len()); |
| |
| //deserialize and check that everything is as expected |
| use std::io::{Cursor, Read}; |
| |
| //deserialize each part of the message and check it |
| let mut cursor = Cursor::new(&serialized); |
| |
| //ip header |
| let ip_actual = Ipv6Header::read(&mut cursor).unwrap(); |
| let ip_expected = Ipv6Header { |
| traffic_class: 0, |
| flow_label: Ipv6FlowLabel::ZERO, |
| payload_length: (UdpHeader::LEN + in_payload.len()) as u16, |
| next_header: ip_number::UDP, |
| hop_limit: 47, |
| source: [ |
| 11, 12, 13, 14, 15, 16, 17, 18, 19, 10, 21, 22, 23, 24, 25, 26, |
| ], |
| destination: [ |
| 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, |
| ], |
| }; |
| |
| assert_eq!(ip_actual, ip_expected); |
| |
| //udp header |
| let udp_actual = UdpHeader::read(&mut cursor).unwrap(); |
| let udp_expected = |
| UdpHeader::with_ipv6_checksum(22, 23, &ip_expected, &in_payload).unwrap(); |
| assert_eq!(udp_actual, udp_expected); |
| |
| //payload |
| let mut actual_payload: [u8; 4] = [0; 4]; |
| cursor.read_exact(&mut actual_payload).unwrap(); |
| assert_eq!(actual_payload, in_payload); |
| } |
| |
| #[test] |
| fn ipv4_custom_udp() { |
| //generate |
| let in_payload = [24, 25, 26, 27]; |
| let mut serialized = Vec::new(); |
| PacketBuilder::ip(IpHeaders::Ipv4( |
| Ipv4Header::new( |
| 0, //payload_len will be replaced during write |
| 12, //time_to_live |
| ip_number::TCP, //will be replaced during write |
| [13, 14, 15, 16], //source |
| [17, 18, 19, 20], //destination |
| ) |
| .unwrap(), |
| Default::default(), |
| )) |
| .udp(22, 23) |
| .write(&mut serialized, &in_payload) |
| .unwrap(); |
| |
| //check the deserialized size |
| let expected_ip_size: usize = UdpHeader::LEN + in_payload.len(); |
| assert_eq!(expected_ip_size + Ipv4Header::MIN_LEN, serialized.len()); |
| |
| //deserialize and check that everything is as expected |
| use std::io::{Cursor, Read}; |
| |
| //deserialize each part of the message and check it |
| let mut cursor = Cursor::new(&serialized); |
| |
| //ip header |
| let ip_actual = Ipv4Header::read(&mut cursor).unwrap(); |
| let mut ip_expected = Ipv4Header::new( |
| expected_ip_size as u16, |
| 12, //ttl |
| ip_number::UDP, |
| [13, 14, 15, 16], |
| [17, 18, 19, 20], |
| ) |
| .unwrap(); |
| ip_expected.header_checksum = ip_expected.calc_header_checksum(); |
| assert_eq!(ip_actual, ip_expected); |
| |
| //udp header |
| let udp_actual = UdpHeader::read(&mut cursor).unwrap(); |
| let udp_expected = |
| UdpHeader::with_ipv4_checksum(22, 23, &ip_expected, &in_payload).unwrap(); |
| assert_eq!(udp_actual, udp_expected); |
| |
| //payload |
| let mut actual_payload: [u8; 4] = [0; 4]; |
| cursor.read_exact(&mut actual_payload).unwrap(); |
| assert_eq!(actual_payload, in_payload); |
| } |
| |
| #[test] |
| fn udp_builder_eth_ipv6_udp() { |
| //generate |
| let in_payload = [50, 51, 52, 53]; |
| let mut serialized = Vec::new(); |
| PacketBuilder::ethernet2([1, 2, 3, 4, 5, 6], [7, 8, 9, 10, 11, 12]) |
| .ipv6( |
| [ |
| 11, 12, 13, 14, 15, 16, 17, 18, 19, 10, 21, 22, 23, 24, 25, 26, |
| ], |
| [ |
| 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, |
| ], |
| 47, |
| ) |
| .udp(48, 49) |
| .write(&mut serialized, &in_payload) |
| .unwrap(); |
| |
| //check the deserialized size |
| assert_eq!( |
| Ethernet2Header::LEN + Ipv6Header::LEN + UdpHeader::LEN + in_payload.len(), |
| serialized.len() |
| ); |
| |
| //deserialize and check that everything is as expected |
| use std::io::Cursor; |
| use std::io::Read; |
| //deserialize each part of the message and check it |
| let mut cursor = Cursor::new(&serialized); |
| |
| //ethernet 2 header |
| assert_eq!( |
| Ethernet2Header::read(&mut cursor).unwrap(), |
| Ethernet2Header { |
| source: [1, 2, 3, 4, 5, 6], |
| destination: [7, 8, 9, 10, 11, 12], |
| ether_type: ether_type::IPV6 |
| } |
| ); |
| |
| //ip header |
| let ip_actual = Ipv6Header::read(&mut cursor).unwrap(); |
| let ip_expected = Ipv6Header { |
| traffic_class: 0, |
| flow_label: Ipv6FlowLabel::ZERO, |
| payload_length: (UdpHeader::LEN + in_payload.len()) as u16, |
| next_header: ip_number::UDP, |
| hop_limit: 47, |
| source: [ |
| 11, 12, 13, 14, 15, 16, 17, 18, 19, 10, 21, 22, 23, 24, 25, 26, |
| ], |
| destination: [ |
| 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, |
| ], |
| }; |
| assert_eq!(ip_actual, ip_expected); |
| |
| //udp header |
| let udp_actual = UdpHeader::read(&mut cursor).unwrap(); |
| let udp_expected = |
| UdpHeader::with_ipv6_checksum(48, 49, &ip_expected, &in_payload).unwrap(); |
| assert_eq!(udp_actual, udp_expected); |
| |
| //payload |
| let mut actual_payload: [u8; 4] = [0; 4]; |
| cursor.read_exact(&mut actual_payload).unwrap(); |
| assert_eq!(actual_payload, in_payload); |
| } |
| |
| #[test] |
| fn udp_builder_linuxsll_ipv6_udp() { |
| //generate |
| let in_payload = [50, 51, 52, 53]; |
| let mut serialized = Vec::new(); |
| PacketBuilder::linux_sll(LinuxSllPacketType::OUTGOING, 6, [7, 8, 9, 10, 11, 12, 0, 0]) |
| .ipv6( |
| [ |
| 11, 12, 13, 14, 15, 16, 17, 18, 19, 10, 21, 22, 23, 24, 25, 26, |
| ], |
| [ |
| 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, |
| ], |
| 47, |
| ) |
| .udp(48, 49) |
| .write(&mut serialized, &in_payload) |
| .unwrap(); |
| |
| //check the deserialized size |
| assert_eq!( |
| LinuxSllHeader::LEN + Ipv6Header::LEN + UdpHeader::LEN + in_payload.len(), |
| serialized.len() |
| ); |
| |
| //deserialize and check that everything is as expected |
| use std::io::Cursor; |
| use std::io::Read; |
| //deserialize each part of the message and check it |
| let mut cursor = Cursor::new(&serialized); |
| |
| //ethernet 2 header |
| assert_eq!( |
| LinuxSllHeader::read(&mut cursor).unwrap(), |
| LinuxSllHeader { |
| packet_type: LinuxSllPacketType::OUTGOING, |
| arp_hrd_type: ArpHardwareId::ETHER, |
| sender_address_valid_length: 6, |
| sender_address: [7, 8, 9, 10, 11, 12, 0, 0], |
| protocol_type: LinuxSllProtocolType::EtherType(EtherType::IPV6) |
| } |
| ); |
| |
| //ip header |
| let ip_actual = Ipv6Header::read(&mut cursor).unwrap(); |
| let ip_expected = Ipv6Header { |
| traffic_class: 0, |
| flow_label: Ipv6FlowLabel::ZERO, |
| payload_length: (UdpHeader::LEN + in_payload.len()) as u16, |
| next_header: ip_number::UDP, |
| hop_limit: 47, |
| source: [ |
| 11, 12, 13, 14, 15, 16, 17, 18, 19, 10, 21, 22, 23, 24, 25, 26, |
| ], |
| destination: [ |
| 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, |
| ], |
| }; |
| assert_eq!(ip_actual, ip_expected); |
| |
| //udp header |
| let udp_actual = UdpHeader::read(&mut cursor).unwrap(); |
| let udp_expected = |
| UdpHeader::with_ipv6_checksum(48, 49, &ip_expected, &in_payload).unwrap(); |
| assert_eq!(udp_actual, udp_expected); |
| |
| //payload |
| let mut actual_payload: [u8; 4] = [0; 4]; |
| cursor.read_exact(&mut actual_payload).unwrap(); |
| assert_eq!(actual_payload, in_payload); |
| } |
| |
| #[test] |
| fn udp_builder_eth_single_vlan_ipv4_udp() { |
| //generate |
| let in_payload = [50, 51, 52, 53]; |
| let mut serialized = Vec::new(); |
| PacketBuilder::ethernet2([1, 2, 3, 4, 5, 6], [7, 8, 9, 10, 11, 12]) |
| .single_vlan(0x123.try_into().unwrap()) |
| .ipv4([13, 14, 15, 16], [17, 18, 19, 20], 21) |
| .udp(48, 49) |
| .write(&mut serialized, &in_payload) |
| .unwrap(); |
| |
| //check the deserialized size |
| |
| //check the deserialized size |
| let expected_ip_size: usize = UdpHeader::LEN + in_payload.len(); |
| assert_eq!( |
| expected_ip_size + Ethernet2Header::LEN + Ipv4Header::MIN_LEN + SingleVlanHeader::LEN, |
| serialized.len() |
| ); |
| |
| //deserialize and check that everything is as expected |
| use std::io::Cursor; |
| use std::io::Read; |
| //deserialize each part of the message and check it |
| let mut cursor = Cursor::new(&serialized); |
| |
| //ethernet 2 header |
| assert_eq!( |
| Ethernet2Header::read(&mut cursor).unwrap(), |
| Ethernet2Header { |
| source: [1, 2, 3, 4, 5, 6], |
| destination: [7, 8, 9, 10, 11, 12], |
| ether_type: ether_type::VLAN_TAGGED_FRAME |
| } |
| ); |
| |
| //vlan header |
| assert_eq!( |
| SingleVlanHeader::read(&mut cursor).unwrap(), |
| SingleVlanHeader { |
| pcp: VlanPcp::ZERO, |
| drop_eligible_indicator: false, |
| vlan_id: 0x123.try_into().unwrap(), |
| ether_type: ether_type::IPV4 |
| } |
| ); |
| |
| //ip header |
| let ip_actual = Ipv4Header::read(&mut cursor).unwrap(); |
| let mut ip_expected = Ipv4Header::new( |
| expected_ip_size as u16, //payload_len |
| 21, //ttl |
| ip_number::UDP, |
| [13, 14, 15, 16], |
| [17, 18, 19, 20], |
| ) |
| .unwrap(); |
| ip_expected.header_checksum = ip_expected.calc_header_checksum(); |
| assert_eq!(ip_actual, ip_expected); |
| |
| //udp header |
| let udp_actual = UdpHeader::read(&mut cursor).unwrap(); |
| let udp_expected = |
| UdpHeader::with_ipv4_checksum(48, 49, &ip_expected, &in_payload).unwrap(); |
| assert_eq!(udp_actual, udp_expected); |
| |
| //payload |
| let mut actual_payload: [u8; 4] = [0; 4]; |
| cursor.read_exact(&mut actual_payload).unwrap(); |
| assert_eq!(actual_payload, in_payload); |
| } |
| |
| #[test] |
| fn udp_builder_eth_double_vlan_ipv6_udp() { |
| //generate |
| let in_payload = [50, 51, 52, 53]; |
| let mut serialized = Vec::new(); |
| PacketBuilder::ethernet2([1, 2, 3, 4, 5, 6], [7, 8, 9, 10, 11, 12]) |
| .double_vlan(0x123.try_into().unwrap(), 0x234.try_into().unwrap()) |
| .ipv6( |
| [ |
| 11, 12, 13, 14, 15, 16, 17, 18, 19, 10, 21, 22, 23, 24, 25, 26, |
| ], |
| [ |
| 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, |
| ], |
| 47, |
| ) |
| .udp(48, 49) |
| .write(&mut serialized, &in_payload) |
| .unwrap(); |
| |
| //check the deserialized size |
| assert_eq!( |
| Ethernet2Header::LEN |
| + DoubleVlanHeader::LEN |
| + Ipv6Header::LEN |
| + UdpHeader::LEN |
| + in_payload.len(), |
| serialized.len() |
| ); |
| |
| //deserialize and check that everything is as expected |
| use std::io::Cursor; |
| use std::io::Read; |
| |
| //deserialize each part of the message and check it |
| let mut cursor = Cursor::new(&serialized); |
| |
| //ethernet 2 header |
| assert_eq!( |
| Ethernet2Header::read(&mut cursor).unwrap(), |
| Ethernet2Header { |
| source: [1, 2, 3, 4, 5, 6], |
| destination: [7, 8, 9, 10, 11, 12], |
| ether_type: ether_type::PROVIDER_BRIDGING, |
| } |
| ); |
| |
| //outer vlan header |
| assert_eq!( |
| SingleVlanHeader::read(&mut cursor).unwrap(), |
| SingleVlanHeader { |
| pcp: VlanPcp::ZERO, |
| drop_eligible_indicator: false, |
| vlan_id: 0x123.try_into().unwrap(), |
| ether_type: ether_type::VLAN_TAGGED_FRAME |
| } |
| ); |
| |
| //inner vlan header |
| assert_eq!( |
| SingleVlanHeader::read(&mut cursor).unwrap(), |
| SingleVlanHeader { |
| pcp: VlanPcp::ZERO, |
| drop_eligible_indicator: false, |
| vlan_id: 0x234.try_into().unwrap(), |
| ether_type: ether_type::IPV6 |
| } |
| ); |
| |
| //ip header |
| let ip_actual = Ipv6Header::read(&mut cursor).unwrap(); |
| let ip_expected = Ipv6Header { |
| traffic_class: 0, |
| flow_label: Ipv6FlowLabel::ZERO, |
| payload_length: (UdpHeader::LEN + in_payload.len()) as u16, |
| next_header: ip_number::UDP, |
| hop_limit: 47, |
| source: [ |
| 11, 12, 13, 14, 15, 16, 17, 18, 19, 10, 21, 22, 23, 24, 25, 26, |
| ], |
| destination: [ |
| 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, |
| ], |
| }; |
| assert_eq!(ip_actual, ip_expected); |
| |
| //udp header |
| let udp_actual = UdpHeader::read(&mut cursor).unwrap(); |
| let udp_expected = |
| UdpHeader::with_ipv6_checksum(48, 49, &ip_expected, &in_payload).unwrap(); |
| assert_eq!(udp_actual, udp_expected); |
| |
| //payload |
| let mut actual_payload: [u8; 4] = [0; 4]; |
| cursor.read_exact(&mut actual_payload).unwrap(); |
| assert_eq!(actual_payload, in_payload); |
| } |
| |
| #[test] |
| fn udp_builder_eth_ip_udp() { |
| //generate |
| let in_payload = [50, 51, 52, 53]; |
| let mut serialized = Vec::new(); |
| PacketBuilder::ethernet2([1, 2, 3, 4, 5, 6], [7, 8, 9, 10, 11, 12]) |
| .ip(IpHeaders::Ipv6( |
| Ipv6Header { |
| traffic_class: 1, |
| flow_label: 2.try_into().unwrap(), |
| payload_length: (UdpHeader::LEN + in_payload.len()) as u16, |
| next_header: ip_number::UDP, |
| hop_limit: 47, |
| source: [ |
| 11, 12, 13, 14, 15, 16, 17, 18, 19, 10, 21, 22, 23, 24, 25, 26, |
| ], |
| destination: [ |
| 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, |
| ], |
| }, |
| Default::default(), |
| )) |
| .udp(48, 49) |
| .write(&mut serialized, &in_payload) |
| .unwrap(); |
| |
| //check the deserialized size |
| assert_eq!( |
| Ethernet2Header::LEN + Ipv6Header::LEN + UdpHeader::LEN + in_payload.len(), |
| serialized.len() |
| ); |
| |
| //deserialize and check that everything is as expected |
| use std::io::Cursor; |
| use std::io::Read; |
| |
| //deserialize each part of the message and check it |
| let mut cursor = Cursor::new(&serialized); |
| |
| //ethernet 2 header |
| assert_eq!( |
| Ethernet2Header::read(&mut cursor).unwrap(), |
| Ethernet2Header { |
| source: [1, 2, 3, 4, 5, 6], |
| destination: [7, 8, 9, 10, 11, 12], |
| ether_type: ether_type::IPV6 |
| } |
| ); |
| |
| //ip header |
| let ip_actual = Ipv6Header::read(&mut cursor).unwrap(); |
| let ip_expected = Ipv6Header { |
| traffic_class: 1, |
| flow_label: 2.try_into().unwrap(), |
| payload_length: (UdpHeader::LEN + in_payload.len()) as u16, |
| next_header: ip_number::UDP, |
| hop_limit: 47, |
| source: [ |
| 11, 12, 13, 14, 15, 16, 17, 18, 19, 10, 21, 22, 23, 24, 25, 26, |
| ], |
| destination: [ |
| 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, |
| ], |
| }; |
| assert_eq!(ip_actual, ip_expected); |
| |
| //udp header |
| let udp_actual = UdpHeader::read(&mut cursor).unwrap(); |
| let udp_expected = |
| UdpHeader::with_ipv6_checksum(48, 49, &ip_expected, &in_payload).unwrap(); |
| assert_eq!(udp_actual, udp_expected); |
| |
| //payload |
| let mut actual_payload: [u8; 4] = [0; 4]; |
| cursor.read_exact(&mut actual_payload).unwrap(); |
| assert_eq!(actual_payload, in_payload); |
| } |
| |
| #[test] |
| fn udp_builder_linuxsll_ip_udp() { |
| //generate |
| let in_payload = [50, 51, 52, 53]; |
| let mut serialized = Vec::new(); |
| PacketBuilder::linux_sll(LinuxSllPacketType::OUTGOING, 6, [7, 8, 9, 10, 11, 12, 0, 0]) |
| .ip(IpHeaders::Ipv6( |
| Ipv6Header { |
| traffic_class: 1, |
| flow_label: 2.try_into().unwrap(), |
| payload_length: (UdpHeader::LEN + in_payload.len()) as u16, |
| next_header: ip_number::UDP, |
| hop_limit: 47, |
| source: [ |
| 11, 12, 13, 14, 15, 16, 17, 18, 19, 10, 21, 22, 23, 24, 25, 26, |
| ], |
| destination: [ |
| 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, |
| ], |
| }, |
| Default::default(), |
| )) |
| .udp(48, 49) |
| .write(&mut serialized, &in_payload) |
| .unwrap(); |
| |
| //check the deserialized size |
| assert_eq!( |
| LinuxSllHeader::LEN + Ipv6Header::LEN + UdpHeader::LEN + in_payload.len(), |
| serialized.len() |
| ); |
| |
| //deserialize and check that everything is as expected |
| use std::io::Cursor; |
| use std::io::Read; |
| |
| //deserialize each part of the message and check it |
| let mut cursor = Cursor::new(&serialized); |
| |
| //ethernet 2 header |
| assert_eq!( |
| LinuxSllHeader::read(&mut cursor).unwrap(), |
| LinuxSllHeader { |
| packet_type: LinuxSllPacketType::OUTGOING, |
| arp_hrd_type: ArpHardwareId::ETHER, |
| sender_address_valid_length: 6, |
| sender_address: [7, 8, 9, 10, 11, 12, 0, 0], |
| protocol_type: LinuxSllProtocolType::EtherType(EtherType::IPV6) |
| } |
| ); |
| |
| //ip header |
| let ip_actual = Ipv6Header::read(&mut cursor).unwrap(); |
| let ip_expected = Ipv6Header { |
| traffic_class: 1, |
| flow_label: 2.try_into().unwrap(), |
| payload_length: (UdpHeader::LEN + in_payload.len()) as u16, |
| next_header: ip_number::UDP, |
| hop_limit: 47, |
| source: [ |
| 11, 12, 13, 14, 15, 16, 17, 18, 19, 10, 21, 22, 23, 24, 25, 26, |
| ], |
| destination: [ |
| 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, |
| ], |
| }; |
| assert_eq!(ip_actual, ip_expected); |
| |
| //udp header |
| let udp_actual = UdpHeader::read(&mut cursor).unwrap(); |
| let udp_expected = |
| UdpHeader::with_ipv6_checksum(48, 49, &ip_expected, &in_payload).unwrap(); |
| assert_eq!(udp_actual, udp_expected); |
| |
| //payload |
| let mut actual_payload: [u8; 4] = [0; 4]; |
| cursor.read_exact(&mut actual_payload).unwrap(); |
| assert_eq!(actual_payload, in_payload); |
| } |
| |
| #[test] |
| fn udp_builder_eth_vlan_ip_udp() { |
| //generate |
| let in_payload = [50, 51, 52, 53]; |
| let mut serialized = Vec::new(); |
| PacketBuilder::ethernet2([1, 2, 3, 4, 5, 6], [7, 8, 9, 10, 11, 12]) |
| .vlan(VlanHeader::Single(SingleVlanHeader { |
| pcp: 1.try_into().unwrap(), |
| drop_eligible_indicator: true, |
| vlan_id: 0x123.try_into().unwrap(), |
| ether_type: 0.into(), //should be overwritten |
| })) |
| .ip(IpHeaders::Ipv6( |
| Ipv6Header { |
| traffic_class: 1, |
| flow_label: 2.try_into().unwrap(), |
| payload_length: (UdpHeader::LEN + in_payload.len()) as u16, |
| next_header: ip_number::UDP, |
| hop_limit: 47, |
| source: [ |
| 11, 12, 13, 14, 15, 16, 17, 18, 19, 10, 21, 22, 23, 24, 25, 26, |
| ], |
| destination: [ |
| 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, |
| ], |
| }, |
| Default::default(), |
| )) |
| .udp(48, 49) |
| .write(&mut serialized, &in_payload) |
| .unwrap(); |
| |
| //check the deserialized size |
| assert_eq!( |
| Ethernet2Header::LEN |
| + SingleVlanHeader::LEN |
| + Ipv6Header::LEN |
| + UdpHeader::LEN |
| + in_payload.len(), |
| serialized.len() |
| ); |
| |
| //deserialize and check that everything is as expected |
| use std::io::Cursor; |
| use std::io::Read; |
| |
| //deserialize each part of the message and check it |
| let mut cursor = Cursor::new(&serialized); |
| |
| //ethernet 2 header |
| assert_eq!( |
| Ethernet2Header::read(&mut cursor).unwrap(), |
| Ethernet2Header { |
| source: [1, 2, 3, 4, 5, 6], |
| destination: [7, 8, 9, 10, 11, 12], |
| ether_type: ether_type::VLAN_TAGGED_FRAME |
| } |
| ); |
| |
| //outer vlan header |
| assert_eq!( |
| SingleVlanHeader::read(&mut cursor).unwrap(), |
| SingleVlanHeader { |
| pcp: 1.try_into().unwrap(), |
| drop_eligible_indicator: true, |
| vlan_id: 0x123.try_into().unwrap(), |
| ether_type: ether_type::IPV6 |
| } |
| ); |
| |
| //ip header |
| let ip_actual = Ipv6Header::read(&mut cursor).unwrap(); |
| let ip_expected = Ipv6Header { |
| traffic_class: 1, |
| flow_label: 2.try_into().unwrap(), |
| payload_length: (UdpHeader::LEN + in_payload.len()) as u16, |
| next_header: ip_number::UDP, |
| hop_limit: 47, |
| source: [ |
| 11, 12, 13, 14, 15, 16, 17, 18, 19, 10, 21, 22, 23, 24, 25, 26, |
| ], |
| destination: [ |
| 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, |
| ], |
| }; |
| assert_eq!(ip_actual, ip_expected); |
| |
| //udp header |
| let udp_actual = UdpHeader::read(&mut cursor).unwrap(); |
| let udp_expected = |
| UdpHeader::with_ipv6_checksum(48, 49, &ip_expected, &in_payload).unwrap(); |
| assert_eq!(udp_actual, udp_expected); |
| |
| //payload |
| let mut actual_payload: [u8; 4] = [0; 4]; |
| cursor.read_exact(&mut actual_payload).unwrap(); |
| assert_eq!(actual_payload, in_payload); |
| } |
| |
| proptest! { |
| #[test] |
| fn tcp_ipv4(ref input in tcp_any()) { |
| |
| //payload |
| let in_payload = [24,25,26,27]; |
| |
| //ip v4 header |
| let mut ip_expected = Ipv4Header::new( |
| in_payload.len() as u16 + input.header_len_u16(), |
| 21, //ttl |
| ip_number::TCP, |
| [13,14,15,16], |
| [17,18,19,20] |
| ).unwrap(); |
| ip_expected.header_checksum = ip_expected.calc_header_checksum(); |
| |
| //generated the expected output |
| let expected = { |
| let mut expected = input.clone(); |
| //replace urg & ack if the flags are not set |
| if !expected.ack { |
| expected.acknowledgment_number = 0; |
| } |
| if !expected.urg { |
| expected.urgent_pointer = 0; |
| } |
| //calculate the checksum |
| expected.checksum = expected.calc_checksum_ipv4(&ip_expected, &in_payload[..]).unwrap(); |
| //done |
| expected |
| }; |
| |
| //generate |
| let serialized = { |
| |
| //create builder |
| let mut builder = PacketBuilder::ethernet2([1,2,3,4,5,6],[7,8,9,10,11,12]) |
| .ipv4([13,14,15,16], [17,18,19,20], 21) |
| .tcp(input.source_port, |
| input.destination_port, |
| input.sequence_number, |
| input.window_size) |
| .options_raw(input.options.as_slice()).unwrap(); |
| //set the flags |
| if input.ns { |
| builder = builder.ns(); |
| } |
| if input.fin { |
| builder = builder.fin(); |
| } |
| if input.syn { |
| builder = builder.syn(); |
| } |
| if input.rst { |
| builder = builder.rst(); |
| } |
| if input.psh { |
| builder = builder.psh(); |
| } |
| if input.ack { |
| builder = builder.ack(input.acknowledgment_number); |
| } |
| if input.urg { |
| builder = builder.urg(input.urgent_pointer); |
| } |
| if input.ece { |
| builder = builder.ece(); |
| } |
| if input.cwr { |
| builder = builder.cwr(); |
| } |
| |
| let mut serialized = Vec::new(); |
| builder.write(&mut serialized, &in_payload).unwrap(); |
| serialized |
| }; |
| |
| //deserialize and check that everything is as expected |
| use std::io::Cursor; |
| use std::io::Read; |
| //deserialize each part of the message and check it |
| let mut cursor = Cursor::new(&serialized); |
| |
| //ethernet 2 header |
| assert_eq!(Ethernet2Header::read(&mut cursor).unwrap(), |
| Ethernet2Header{ |
| source: [1,2,3,4,5,6], |
| destination: [7,8,9,10,11,12], |
| ether_type: ether_type::IPV4 |
| }); |
| |
| //ip header |
| let ip_actual = Ipv4Header::read(&mut cursor).unwrap(); |
| assert_eq!(ip_actual, |
| ip_expected); |
| |
| //tcp header |
| assert_eq!(TcpHeader::read(&mut cursor).unwrap(), |
| expected); |
| |
| //payload |
| let mut actual_payload: [u8;4] = [0;4]; |
| cursor.read_exact(&mut actual_payload).unwrap(); |
| assert_eq!(actual_payload, in_payload); |
| } |
| } |
| |
| proptest! { |
| #[test] |
| fn tcp_ipv6(ref input in tcp_any()) { |
| |
| //payload |
| let in_payload = [24,25,26,27]; |
| |
| //ip v4 header |
| let ip_expected = Ipv6Header{ |
| traffic_class: 0, |
| flow_label: Ipv6FlowLabel::ZERO, |
| payload_length: (input.header_len() as usize + in_payload.len()) as u16, |
| next_header: ip_number::TCP, |
| hop_limit: 47, |
| source: [11,12,13,14,15,16,17,18,19,10,21,22,23,24,25,26], |
| destination: [31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46] |
| }; |
| |
| //generated the expected output |
| let expected = { |
| let mut expected = input.clone(); |
| //replace urg & ack if the flags are not set |
| if !expected.ack { |
| expected.acknowledgment_number = 0; |
| } |
| if !expected.urg { |
| expected.urgent_pointer = 0; |
| } |
| //calculate the checksum |
| expected.checksum = expected.calc_checksum_ipv6(&ip_expected, &in_payload[..]).unwrap(); |
| //done |
| expected |
| }; |
| |
| //generate |
| let serialized = { |
| |
| //create builder |
| let mut builder = PacketBuilder::ethernet2([1,2,3,4,5,6],[7,8,9,10,11,12]) |
| .ipv6([11,12,13,14,15,16,17,18,19,10,21,22,23,24,25,26], |
| [31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46], |
| 47, |
| ) |
| .tcp(input.source_port, |
| input.destination_port, |
| input.sequence_number, |
| input.window_size) |
| .options_raw(input.options.as_slice()).unwrap(); |
| //set the flags |
| if input.ns { |
| builder = builder.ns(); |
| } |
| if input.fin { |
| builder = builder.fin(); |
| } |
| if input.syn { |
| builder = builder.syn(); |
| } |
| if input.rst { |
| builder = builder.rst(); |
| } |
| if input.psh { |
| builder = builder.psh(); |
| } |
| if input.ack { |
| builder = builder.ack(input.acknowledgment_number); |
| } |
| if input.urg { |
| builder = builder.urg(input.urgent_pointer); |
| } |
| if input.ece { |
| builder = builder.ece(); |
| } |
| if input.cwr { |
| builder = builder.cwr(); |
| } |
| |
| let mut serialized = Vec::new(); |
| builder.write(&mut serialized, &in_payload).unwrap(); |
| serialized |
| }; |
| |
| //deserialize and check that everything is as expected |
| use std::io::Cursor; |
| use std::io::Read; |
| //deserialize each part of the message and check it |
| let mut cursor = Cursor::new(&serialized); |
| |
| //ethernet 2 header |
| assert_eq!(Ethernet2Header::read(&mut cursor).unwrap(), |
| Ethernet2Header{ |
| source: [1,2,3,4,5,6], |
| destination: [7,8,9,10,11,12], |
| ether_type: ether_type::IPV6 |
| }); |
| |
| //ip header |
| let ip_actual = Ipv6Header::read(&mut cursor).unwrap(); |
| assert_eq!(ip_actual, |
| ip_expected); |
| |
| //tcp header |
| assert_eq!(TcpHeader::read(&mut cursor).unwrap(), |
| expected); |
| |
| //payload |
| let mut actual_payload: [u8;4] = [0;4]; |
| cursor.read_exact(&mut actual_payload).unwrap(); |
| assert_eq!(actual_payload, in_payload); |
| } |
| } |
| |
| #[test] |
| fn tcp_options() { |
| let mut serialized = Vec::new(); |
| |
| use crate::TcpOptionElement::*; |
| let options = vec![MaximumSegmentSize(1234), Noop]; |
| |
| PacketBuilder::ethernet2([1, 2, 3, 4, 5, 6], [7, 8, 9, 10, 11, 12]) |
| .ipv4([13, 14, 15, 16], [17, 18, 19, 20], 21) |
| .tcp(1, 2, 3, 4) |
| .options(&options) |
| .unwrap() |
| .write(&mut serialized, &[]) |
| .unwrap(); |
| |
| let decoded = PacketHeaders::from_ethernet_slice(&serialized[..]).unwrap(); |
| let dec_options: Vec<Result<TcpOptionElement, TcpOptionReadError>> = decoded |
| .transport |
| .unwrap() |
| .tcp() |
| .unwrap() |
| .options_iterator() |
| .collect(); |
| assert_eq!(&[Ok(MaximumSegmentSize(1234)), Ok(Noop)], &dec_options[..]); |
| } |
| |
| #[test] |
| fn size() { |
| //ipv4 no vlan ethernet |
| assert_eq!( |
| Ethernet2Header::LEN + Ipv4Header::MIN_LEN + UdpHeader::LEN + 123, |
| PacketBuilder::ethernet2([1, 2, 3, 4, 5, 6], [7, 8, 9, 10, 11, 12]) |
| .ipv4([13, 14, 15, 16], [17, 18, 19, 20], 21) |
| .udp(22, 23) |
| .size(123) |
| ); |
| |
| //ipv6 no vlan ethernet |
| assert_eq!( |
| Ethernet2Header::LEN + Ipv6Header::LEN + UdpHeader::LEN + 123, |
| PacketBuilder::ethernet2([1, 2, 3, 4, 5, 6], [7, 8, 9, 10, 11, 12]) |
| .ipv6( |
| [11, 12, 13, 14, 15, 16, 17, 18, 19, 10, 21, 22, 23, 24, 25, 26], |
| [31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46], |
| 47, |
| ) |
| .udp(22, 23) |
| .size(123) |
| ); |
| |
| //ipv4 linux_sll |
| assert_eq!( |
| LinuxSllHeader::LEN + Ipv4Header::MIN_LEN + UdpHeader::LEN + 123, |
| PacketBuilder::linux_sll(LinuxSllPacketType::OUTGOING, 6, [7, 8, 9, 10, 11, 12, 0, 0]) |
| .ipv4([13, 14, 15, 16], [17, 18, 19, 20], 21) |
| .udp(22, 23) |
| .size(123) |
| ); |
| |
| //ipv6 linux_sll |
| assert_eq!( |
| LinuxSllHeader::LEN + Ipv6Header::LEN + UdpHeader::LEN + 123, |
| PacketBuilder::linux_sll(LinuxSllPacketType::OUTGOING, 6, [7, 8, 9, 10, 11, 12, 0, 0]) |
| .ipv6( |
| [11, 12, 13, 14, 15, 16, 17, 18, 19, 10, 21, 22, 23, 24, 25, 26], |
| [31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46], |
| 47, |
| ) |
| .udp(22, 23) |
| .size(123) |
| ); |
| |
| //ipv4 single vlan ethernet |
| assert_eq!( |
| Ethernet2Header::LEN |
| + SingleVlanHeader::LEN |
| + Ipv4Header::MIN_LEN |
| + UdpHeader::LEN |
| + 123, |
| PacketBuilder::ethernet2([1, 2, 3, 4, 5, 6], [7, 8, 9, 10, 11, 12]) |
| .single_vlan(0x123.try_into().unwrap()) |
| .ipv4([13, 14, 15, 16], [17, 18, 19, 20], 21) |
| .udp(22, 23) |
| .size(123) |
| ); |
| |
| //ipv6 double vlan ethernet |
| assert_eq!( |
| Ethernet2Header::LEN + DoubleVlanHeader::LEN + Ipv6Header::LEN + UdpHeader::LEN + 123, |
| PacketBuilder::ethernet2([1, 2, 3, 4, 5, 6], [7, 8, 9, 10, 11, 12]) |
| .double_vlan(0x123.try_into().unwrap(), 0x234.try_into().unwrap()) |
| .ipv6( |
| [11, 12, 13, 14, 15, 16, 17, 18, 19, 10, 21, 22, 23, 24, 25, 26], |
| [31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46], |
| 47, |
| ) |
| .udp(22, 23) |
| .size(123) |
| ); |
| } |
| |
| proptest! { |
| #[test] |
| fn size_tcp(ref input in tcp_any()) { |
| |
| assert_eq!(Ethernet2Header::LEN + |
| Ipv4Header::MIN_LEN + |
| input.header_len() as usize + |
| 123, |
| |
| PacketBuilder::ethernet2([1,2,3,4,5,6],[7,8,9,10,11,12]) |
| .ipv4([13,14,15,16], [17,18,19,20], 21) |
| .tcp(input.source_port, |
| input.destination_port, |
| input.sequence_number, |
| input.window_size) |
| .options_raw(input.options.as_slice()).unwrap() |
| .size(123)); |
| } |
| } |
| |
| proptest! { |
| #[test] |
| fn ipv4_icmpv4( |
| ipv4_source in any::<[u8;4]>(), |
| ipv4_dest in any::<[u8;4]>(), |
| ipv4_time_to_live in any::<u8>(), |
| icmpv4_type_u8 in 15u8..u8::MAX, |
| icmpv4_code_u8 in any::<u8>(), |
| icmpv4_bytes5to8 in any::<[u8;4]>(), |
| icmpv4 in icmpv4_type_any(), |
| echo_id in any::<u16>(), |
| echo_seq in any::<u16>(), |
| payload in proptest::collection::vec(any::<u8>(), 0..64), |
| ) { |
| let test_builder = |builder: PacketBuilderStep<Icmpv4Header>, icmpv4_type: Icmpv4Type| { |
| use crate::Icmpv4Type::*; |
| let adapted_payload = match &icmpv4_type { |
| TimestampRequest(_) | |
| TimestampReply(_) => &[], |
| _ => &payload[..], |
| }; |
| let icmp_expected = Icmpv4Header::with_checksum(icmpv4_type, &adapted_payload); |
| let ip_expected = { |
| let mut expected_ipv4 = Ipv4Header::new( |
| (icmp_expected.header_len() + adapted_payload.len()) as u16, |
| ipv4_time_to_live, |
| ip_number::ICMP, |
| ipv4_source, |
| ipv4_dest |
| ).unwrap(); |
| expected_ipv4.header_checksum = expected_ipv4.calc_header_checksum(); |
| expected_ipv4 |
| }; |
| |
| // test builder.size() |
| assert_eq!( |
| builder.size(adapted_payload.len()), |
| Ethernet2Header::LEN + |
| Ipv4Header::MIN_LEN + |
| icmp_expected.header_len() + |
| adapted_payload.len() |
| ); |
| |
| // test builder.write() |
| let mut buffer = Vec::<u8>::with_capacity(builder.size(adapted_payload.len())); |
| builder.write(&mut buffer, adapted_payload).unwrap(); |
| |
| // decode packets |
| let actual = PacketHeaders::from_ethernet_slice(&buffer).unwrap(); |
| |
| // check the packets could be decoded |
| assert_eq!( |
| Some(LinkHeader::Ethernet2(Ethernet2Header{ |
| source: [1,2,3,4,5,6], |
| destination: [7,8,9,10,11,12], |
| ether_type: ether_type::IPV4 |
| })), |
| actual.link |
| ); |
| assert_eq!( |
| Some(NetHeaders::Ipv4(ip_expected, Default::default())), |
| actual.net |
| ); |
| assert_eq!( |
| Some(TransportHeader::Icmpv4(icmp_expected)), |
| actual.transport |
| ); |
| assert_eq!(actual.payload.slice(), adapted_payload); |
| }; |
| |
| // icmpv4 |
| { |
| let builder = PacketBuilder::ethernet2([1,2,3,4,5,6],[7,8,9,10,11,12]) |
| .ipv4(ipv4_source, ipv4_dest, ipv4_time_to_live) |
| .icmpv4(icmpv4.clone()); |
| |
| test_builder( |
| builder, |
| icmpv4 |
| ); |
| } |
| |
| // icmpv4_raw |
| { |
| let builder = PacketBuilder::ethernet2([1,2,3,4,5,6],[7,8,9,10,11,12]) |
| .ipv4(ipv4_source, ipv4_dest, ipv4_time_to_live) |
| .icmpv4_raw(icmpv4_type_u8, icmpv4_code_u8, icmpv4_bytes5to8); |
| |
| test_builder( |
| builder, |
| Icmpv4Type::Unknown{ |
| type_u8: icmpv4_type_u8, |
| code_u8: icmpv4_code_u8, |
| bytes5to8: icmpv4_bytes5to8, |
| } |
| ); |
| } |
| |
| // icmpv4_echo_request |
| { |
| let builder = PacketBuilder::ethernet2([1,2,3,4,5,6],[7,8,9,10,11,12]) |
| .ipv4(ipv4_source, ipv4_dest, ipv4_time_to_live) |
| .icmpv4_echo_request(echo_id, echo_seq); |
| |
| test_builder( |
| builder, |
| Icmpv4Type::EchoRequest(IcmpEchoHeader{ |
| id: echo_id, |
| seq: echo_seq, |
| }) |
| ); |
| } |
| |
| // icmp4_echo_reply |
| { |
| let builder = PacketBuilder::ethernet2([1,2,3,4,5,6],[7,8,9,10,11,12]) |
| .ipv4(ipv4_source, ipv4_dest, ipv4_time_to_live) |
| .icmpv4_echo_reply(echo_id, echo_seq); |
| |
| test_builder( |
| builder, |
| Icmpv4Type::EchoReply(IcmpEchoHeader{ |
| id: echo_id, |
| seq: echo_seq, |
| }) |
| ); |
| } |
| } |
| } |
| |
| proptest! { |
| #[test] |
| fn ipv4_icmpv6( |
| ipv4_source in any::<[u8;4]>(), |
| ipv4_dest in any::<[u8;4]>(), |
| ipv4_time_to_live in any::<u8>(), |
| icmpv6_type_u8 in 162u8..u8::MAX, |
| icmpv6_code_u8 in any::<u8>(), |
| icmpv6_bytes5to8 in any::<[u8;4]>(), |
| icmpv6 in icmpv6_type_any(), |
| echo_id in any::<u16>(), |
| echo_seq in any::<u16>(), |
| payload in proptest::collection::vec(any::<u8>(), 0..64), |
| ) { |
| let test_builder = |builder: PacketBuilderStep<Icmpv6Header>, icmpv6_type: Icmpv6Type| { |
| // test builder.size() |
| assert_eq!( |
| builder.size(payload.len()), |
| Ethernet2Header::LEN + |
| Ipv4Header::MIN_LEN + |
| icmpv6_type.header_len() + |
| payload.len() |
| ); |
| |
| // test builder.write() |
| let mut buffer = Vec::<u8>::with_capacity(builder.size(payload.len())); |
| // should trigger an error, was it is not possible to calculate the checksum |
| assert!(builder.write(&mut buffer, &payload).is_err()); |
| }; |
| |
| // icmpv6 |
| { |
| let builder = PacketBuilder::ethernet2([1,2,3,4,5,6],[7,8,9,10,11,12]) |
| .ipv4(ipv4_source, ipv4_dest, ipv4_time_to_live) |
| .icmpv6(icmpv6.clone()); |
| |
| test_builder( |
| builder, |
| icmpv6 |
| ); |
| } |
| |
| // icmpv6_raw |
| { |
| let builder = PacketBuilder::ethernet2([1,2,3,4,5,6],[7,8,9,10,11,12]) |
| .ipv4(ipv4_source, ipv4_dest, ipv4_time_to_live) |
| .icmpv6_raw(icmpv6_type_u8, icmpv6_code_u8, icmpv6_bytes5to8); |
| |
| test_builder( |
| builder, |
| Icmpv6Type::Unknown{ |
| type_u8: icmpv6_type_u8, |
| code_u8: icmpv6_code_u8, |
| bytes5to8: icmpv6_bytes5to8, |
| } |
| ); |
| } |
| |
| // icmpv6_echo_request |
| { |
| let builder = PacketBuilder::ethernet2([1,2,3,4,5,6],[7,8,9,10,11,12]) |
| .ipv4(ipv4_source, ipv4_dest, ipv4_time_to_live) |
| .icmpv6_echo_request(echo_id, echo_seq); |
| |
| test_builder( |
| builder, |
| Icmpv6Type::EchoRequest(IcmpEchoHeader{ |
| id: echo_id, |
| seq: echo_seq, |
| }) |
| ); |
| } |
| |
| // icmp4_echo_reply |
| { |
| let builder = PacketBuilder::ethernet2([1,2,3,4,5,6],[7,8,9,10,11,12]) |
| .ipv4(ipv4_source, ipv4_dest, ipv4_time_to_live) |
| .icmpv6_echo_reply(echo_id, echo_seq); |
| |
| test_builder( |
| builder, |
| Icmpv6Type::EchoReply(IcmpEchoHeader{ |
| id: echo_id, |
| seq: echo_seq, |
| }) |
| ); |
| } |
| } |
| } |
| |
| proptest! { |
| #[test] |
| fn ipv6_icmpv4( |
| ipv6_source in any::<[u8;16]>(), |
| ipv6_dest in any::<[u8;16]>(), |
| ipv6_hop_limit in any::<u8>(), |
| icmpv4_type_u8 in 15u8..u8::MAX, |
| icmpv4_code_u8 in any::<u8>(), |
| icmpv4_bytes5to8 in any::<[u8;4]>(), |
| icmpv4 in icmpv4_type_any(), |
| echo_id in any::<u16>(), |
| echo_seq in any::<u16>(), |
| payload in proptest::collection::vec(any::<u8>(), 0..64), |
| ) { |
| let test_builder = |builder: PacketBuilderStep<Icmpv4Header>, icmpv4_type: Icmpv4Type| { |
| |
| use Icmpv4Type::*; |
| let adapted_payload = match icmpv4_type { |
| TimestampRequest(_) | TimestampReply(_) => &[], |
| _ => &payload[..], |
| }; |
| |
| let icmp_expected = Icmpv4Header::with_checksum(icmpv4_type, &adapted_payload); |
| let ip_expected = Ipv6Header{ |
| traffic_class: 0, |
| flow_label: Ipv6FlowLabel::ZERO, |
| payload_length: (icmp_expected.header_len() + adapted_payload.len()) as u16, |
| next_header: ip_number::ICMP, |
| hop_limit: ipv6_hop_limit, |
| source: ipv6_source, |
| destination: ipv6_dest |
| }; |
| |
| // test builder.size() |
| assert_eq!( |
| builder.size(adapted_payload.len()), |
| Ethernet2Header::LEN + |
| Ipv6Header::LEN + |
| icmp_expected.header_len() + |
| adapted_payload.len() |
| ); |
| |
| // test builder.write() |
| let mut buffer = Vec::<u8>::with_capacity(builder.size(adapted_payload.len())); |
| builder.write(&mut buffer, adapted_payload).unwrap(); |
| |
| // decode packets |
| let actual = PacketHeaders::from_ethernet_slice(&buffer).unwrap(); |
| |
| // check the packets could be decoded |
| assert_eq!( |
| Some(LinkHeader::Ethernet2(Ethernet2Header{ |
| source: [1,2,3,4,5,6], |
| destination: [7,8,9,10,11,12], |
| ether_type: ether_type::IPV6 |
| })), |
| actual.link |
| ); |
| assert_eq!( |
| Some(NetHeaders::Ipv6(ip_expected, Default::default())), |
| actual.net |
| ); |
| assert_eq!( |
| Some(TransportHeader::Icmpv4(icmp_expected)), |
| actual.transport |
| ); |
| assert_eq!(actual.payload.slice(), adapted_payload); |
| }; |
| |
| // icmpv4 |
| { |
| let builder = PacketBuilder::ethernet2([1,2,3,4,5,6],[7,8,9,10,11,12]) |
| .ipv6(ipv6_source, ipv6_dest, ipv6_hop_limit) |
| .icmpv4(icmpv4.clone()); |
| |
| test_builder( |
| builder, |
| icmpv4 |
| ); |
| } |
| |
| // icmpv4_raw |
| { |
| let builder = PacketBuilder::ethernet2([1,2,3,4,5,6],[7,8,9,10,11,12]) |
| .ipv6(ipv6_source, ipv6_dest, ipv6_hop_limit) |
| .icmpv4_raw(icmpv4_type_u8, icmpv4_code_u8, icmpv4_bytes5to8); |
| |
| test_builder( |
| builder, |
| Icmpv4Type::Unknown{ |
| type_u8: icmpv4_type_u8, |
| code_u8: icmpv4_code_u8, |
| bytes5to8: icmpv4_bytes5to8, |
| } |
| ); |
| } |
| |
| // icmpv4_echo_request |
| { |
| let builder = PacketBuilder::ethernet2([1,2,3,4,5,6],[7,8,9,10,11,12]) |
| .ipv6(ipv6_source, ipv6_dest, ipv6_hop_limit) |
| .icmpv4_echo_request(echo_id, echo_seq); |
| |
| test_builder( |
| builder, |
| Icmpv4Type::EchoRequest(IcmpEchoHeader{ |
| id: echo_id, |
| seq: echo_seq, |
| }) |
| ); |
| } |
| |
| // icmp4_echo_reply |
| { |
| let builder = PacketBuilder::ethernet2([1,2,3,4,5,6],[7,8,9,10,11,12]) |
| .ipv6(ipv6_source, ipv6_dest, ipv6_hop_limit) |
| .icmpv4_echo_reply(echo_id, echo_seq); |
| |
| test_builder( |
| builder, |
| Icmpv4Type::EchoReply(IcmpEchoHeader{ |
| id: echo_id, |
| seq: echo_seq, |
| }) |
| ); |
| } |
| } |
| } |
| |
| proptest! { |
| #[test] |
| fn ipv6_icmpv6( |
| ipv6_source in any::<[u8;16]>(), |
| ipv6_dest in any::<[u8;16]>(), |
| ipv6_hop_limit in any::<u8>(), |
| icmpv6_type_u8 in 162u8..u8::MAX, |
| icmpv6_code_u8 in any::<u8>(), |
| icmpv6_bytes5to8 in any::<[u8;4]>(), |
| icmpv6 in icmpv6_type_any(), |
| echo_id in any::<u16>(), |
| echo_seq in any::<u16>(), |
| payload in proptest::collection::vec(any::<u8>(), 0..64), |
| ) { |
| let test_builder = |builder: PacketBuilderStep<Icmpv6Header>, icmpv6_type: Icmpv6Type| { |
| let icmp_expected = Icmpv6Header::with_checksum( |
| icmpv6_type, |
| ipv6_source, |
| ipv6_dest, |
| &payload |
| ).unwrap(); |
| let ip_expected = Ipv6Header{ |
| traffic_class: 0, |
| flow_label: Ipv6FlowLabel::ZERO, |
| payload_length: (icmp_expected.header_len() + payload.len()) as u16, |
| next_header: ip_number::IPV6_ICMP, |
| hop_limit: ipv6_hop_limit, |
| source: ipv6_source, |
| destination: ipv6_dest |
| }; |
| |
| // test builder.size() |
| assert_eq!( |
| builder.size(payload.len()), |
| Ethernet2Header::LEN + |
| Ipv6Header::LEN + |
| icmp_expected.header_len() + |
| payload.len() |
| ); |
| |
| // test builder.write() |
| let mut buffer = Vec::<u8>::with_capacity(builder.size(payload.len())); |
| builder.write(&mut buffer, &payload).unwrap(); |
| |
| // decode packets |
| let actual = PacketHeaders::from_ethernet_slice(&buffer).unwrap(); |
| |
| // check the packets could be decoded |
| assert_eq!( |
| Some(LinkHeader::Ethernet2(Ethernet2Header{ |
| source: [1,2,3,4,5,6], |
| destination: [7,8,9,10,11,12], |
| ether_type: ether_type::IPV6 |
| })), |
| actual.link |
| ); |
| assert_eq!( |
| Some(NetHeaders::Ipv6(ip_expected, Default::default())), |
| actual.net |
| ); |
| assert_eq!( |
| Some(TransportHeader::Icmpv6(icmp_expected)), |
| actual.transport |
| ); |
| assert_eq!(actual.payload.slice(), &payload); |
| }; |
| |
| // icmpv6 |
| { |
| let builder = PacketBuilder::ethernet2([1,2,3,4,5,6],[7,8,9,10,11,12]) |
| .ipv6(ipv6_source, ipv6_dest, ipv6_hop_limit) |
| .icmpv6(icmpv6.clone()); |
| |
| test_builder( |
| builder, |
| icmpv6 |
| ); |
| } |
| |
| // icmpv6_raw |
| { |
| let builder = PacketBuilder::ethernet2([1,2,3,4,5,6],[7,8,9,10,11,12]) |
| .ipv6(ipv6_source, ipv6_dest, ipv6_hop_limit) |
| .icmpv6_raw(icmpv6_type_u8, icmpv6_code_u8, icmpv6_bytes5to8); |
| |
| test_builder( |
| builder, |
| Icmpv6Type::Unknown{ |
| type_u8: icmpv6_type_u8, |
| code_u8: icmpv6_code_u8, |
| bytes5to8: icmpv6_bytes5to8, |
| } |
| ); |
| } |
| |
| // icmpv6_echo_request |
| { |
| let builder = PacketBuilder::ethernet2([1,2,3,4,5,6],[7,8,9,10,11,12]) |
| .ipv6(ipv6_source, ipv6_dest, ipv6_hop_limit) |
| .icmpv6_echo_request(echo_id, echo_seq); |
| |
| test_builder( |
| builder, |
| Icmpv6Type::EchoRequest(IcmpEchoHeader{ |
| id: echo_id, |
| seq: echo_seq, |
| }) |
| ); |
| } |
| |
| // icmp4_echo_reply |
| { |
| let builder = PacketBuilder::ethernet2([1,2,3,4,5,6],[7,8,9,10,11,12]) |
| .ipv6(ipv6_source, ipv6_dest, ipv6_hop_limit) |
| .icmpv6_echo_reply(echo_id, echo_seq); |
| |
| test_builder( |
| builder, |
| Icmpv6Type::EchoReply(IcmpEchoHeader{ |
| id: echo_id, |
| seq: echo_seq, |
| }) |
| ); |
| } |
| } |
| } |
| } |