diff options
| author | luwenpeng <[email protected]> | 2023-09-07 18:05:25 +0800 |
|---|---|---|
| committer | luwenpeng <[email protected]> | 2023-09-08 11:34:48 +0800 |
| commit | 884a3d2bee16ddd96501a7d669077870bbd237e1 (patch) | |
| tree | e57d27d085e307b70672b0504af01478a9a0c16f | |
| parent | e16b028be675ee35f4d08521accbc52ee6e6b182 (diff) | |
[feature] Support VLAN Decode
| -rw-r--r-- | src/packet/packet.rs | 479 | ||||
| -rw-r--r-- | src/protocol/ethernet.rs | 2 | ||||
| -rw-r--r-- | src/protocol/mod.rs | 3 | ||||
| -rw-r--r-- | src/protocol/vlan.rs | 93 |
4 files changed, 319 insertions, 258 deletions
diff --git a/src/packet/packet.rs b/src/packet/packet.rs index 2336946..57d871e 100644 --- a/src/packet/packet.rs +++ b/src/packet/packet.rs @@ -7,12 +7,14 @@ use crate::protocol::ipv4::IPv4Header; use crate::protocol::ipv6::IPv6Header; use crate::protocol::tcp::TcpHeader; use crate::protocol::udp::UdpHeader; +use crate::protocol::vlan::VlanHeader; #[allow(non_camel_case_types)] #[derive(Clone, Debug, PartialEq)] pub enum Encapsulation<'a> { L2_ETH(EthernetFrame, &'a [u8]), + L3_VLAN(VlanHeader, &'a [u8]), L3_IPV4(IPv4Header, &'a [u8]), L3_IPV6(IPv6Header, &'a [u8]), @@ -371,6 +373,20 @@ fn handle_l3<'a>( ) -> Result<(), PacketError> { packet.event.push(PacketEvent::L3_EVENT); match next_proto { + EtherType::VLAN => { + let result = VlanHeader::decode(input); + if let Ok((payload, header)) = result { + dbg!(&header); + + packet + .encapsulation + .push(Encapsulation::L3_VLAN(header, payload)); + + return handle_l3(packet, payload, header.ether_type); + } else { + return Err(PacketError::IncompleteEthernetFrame); + } + } EtherType::IPv4 => { let result = IPv4Header::decode(input); if let Ok((payload, header)) = result { @@ -471,271 +487,20 @@ fn handle_l4<'a>( mod tests { use super::Encapsulation; use super::Packet; + use crate::protocol::ethernet::EtherType; + use crate::protocol::ethernet::EthernetFrame; + use crate::protocol::ethernet::MacAddress; use crate::protocol::ip::IPProtocol; use crate::protocol::ipv4::IPv4Header; use crate::protocol::ipv6::IPv6Header; use crate::protocol::tcp::TcpHeader; use crate::protocol::udp::UdpHeader; + use crate::protocol::vlan::VlanHeader; use std::net::Ipv4Addr; use std::net::Ipv6Addr; #[test] - fn test_packet_handle1() { - /* - * Frame 49: 74 bytes on wire (592 bits), 74 bytes captured (592 bits) on interface en0, id 0 - * Section number: 1 - * Interface id: 0 (en0) - * Interface name: en0 - * Interface description: Wi-Fi - * Encapsulation type: Ethernet (1) - * Arrival Time: Aug 2, 2023 11:36:35.739393000 CST - * [Time shift for this packet: 0.000000000 seconds] - * Epoch Time: 1690947395.739393000 seconds - * [Time delta from previous captured frame: 0.496904000 seconds] - * [Time delta from previous displayed frame: 0.000000000 seconds] - * [Time since reference or first frame: 7.146758000 seconds] - * Frame Number: 49 - * Frame Length: 74 bytes (592 bits) - * Capture Length: 74 bytes (592 bits) - * [Frame is marked: False] - * [Frame is ignored: False] - * [Protocols in frame: eth:ethertype:ip:udp:dns] - * [Coloring Rule Name: UDP] - * [Coloring Rule String: udp] - * Ethernet II, Src: Apple_0a:c5:ea (3c:a6:f6:0a:c5:ea), Dst: NewH3CTe_96:38:0e (48:73:97:96:38:0e) - * Destination: NewH3CTe_96:38:0e (48:73:97:96:38:0e) - * Address: NewH3CTe_96:38:0e (48:73:97:96:38:0e) - * .... ..0. .... .... .... .... = LG bit: Globally unique address (factory default) - * .... ...0 .... .... .... .... = IG bit: Individual address (unicast) - * Source: Apple_0a:c5:ea (3c:a6:f6:0a:c5:ea) - * Address: Apple_0a:c5:ea (3c:a6:f6:0a:c5:ea) - * .... ..0. .... .... .... .... = LG bit: Globally unique address (factory default) - * .... ...0 .... .... .... .... = IG bit: Individual address (unicast) - * Type: IPv4 (0x0800) - * Internet Protocol Version 4, Src: 192.168.38.63, Dst: 192.168.44.40 - * 0100 .... = Version: 4 - * .... 0101 = Header Length: 20 bytes (5) - * Differentiated Services Field: 0x00 (DSCP: CS0, ECN: Not-ECT) - * 0000 00.. = Differentiated Services Codepoint: Default (0) - * .... ..00 = Explicit Congestion Notification: Not ECN-Capable Transport (0) - * Total Length: 60 - * Identification: 0x040c (1036) - * 000. .... = Flags: 0x0 - * 0... .... = Reserved bit: Not set - * .0.. .... = Don't fragment: Not set - * ..0. .... = More fragments: Not set - * ...0 0000 0000 0000 = Fragment Offset: 0 - * Time to Live: 64 - * Protocol: UDP (17) - * Header Checksum: 0xa2ed [correct] - * [Header checksum status: Good] - * [Calculated Checksum: 0xa2ed] - * Source Address: 192.168.38.63 - * Destination Address: 192.168.44.40 - * User Datagram Protocol, Src Port: 55298, Dst Port: 53 - * Source Port: 55298 - * Destination Port: 53 - * Length: 40 - * Checksum: 0xd3e5 [correct] - * [Calculated Checksum: 0xd3e5] - * [Checksum Status: Good] - * [Stream index: 13] - * [Timestamps] - * [Time since first frame: 0.000000000 seconds] - * [Time since previous frame: 0.000000000 seconds] - * UDP payload (32 bytes) - * Domain Name System (query) - * Transaction ID: 0xfc60 - * Flags: 0x0100 Standard query - * 0... .... .... .... = Response: Message is a query - * .000 0... .... .... = Opcode: Standard query (0) - * .... ..0. .... .... = Truncated: Message is not truncated - * .... ...1 .... .... = Recursion desired: Do query recursively - * .... .... .0.. .... = Z: reserved (0) - * .... .... ...0 .... = Non-authenticated data: Unacceptable - * Questions: 1 - * Answer RRs: 0 - * Authority RRs: 0 - * Additional RRs: 0 - * Queries - * deb.debian.org: type A, class IN - * Name: deb.debian.org - * [Name Length: 14] - * [Label Count: 3] - * Type: A (Host Address) (1) - * Class: IN (0x0001) - * [Response In: 51] - */ - let bytes = [ - 0x48, 0x73, 0x97, 0x96, 0x38, 0x0e, 0x3c, 0xa6, 0xf6, 0x0a, 0xc5, 0xea, 0x08, 0x00, - 0x45, 0x00, 0x00, 0x3c, 0x04, 0x0c, 0x00, 0x00, 0x40, 0x11, 0xa2, 0xed, 0xc0, 0xa8, - 0x26, 0x3f, 0xc0, 0xa8, 0x2c, 0x28, 0xd8, 0x02, 0x00, 0x35, 0x00, 0x28, 0xd3, 0xe5, - 0xfc, 0x60, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x64, - 0x65, 0x62, 0x06, 0x64, 0x65, 0x62, 0x69, 0x61, 0x6e, 0x03, 0x6f, 0x72, 0x67, 0x00, - 0x00, 0x01, 0x00, 0x01, /* DNS END */ - ]; - - let mut packet = Packet::new(&bytes, bytes.len() as u32); - let result = packet.handle(); - - match result { - Ok(v) => { - println!("SUCCESS: {:?}, {:?}", packet, v); - // println!("SUCCESS: {:#?}, {:?}", packet, v); - // dbg!(packet); - } - Err(e) => { - println!("ERROR Data: {:?}", packet); - println!("ERROR Code: {:?}", e); - println!("ERROR Desc: {}", e); - assert_eq!(0, 1); - } - } - } - - #[test] - fn test_packet_handle2() { - /* - * Frame 217: 131 bytes on wire (1048 bits), 131 bytes captured (1048 bits) on interface en0, id 0 - * Section number: 1 - * Interface id: 0 (en0) - * Interface name: en0 - * Interface description: Wi-Fi - * Encapsulation type: Ethernet (1) - * Arrival Time: Aug 2, 2023 11:49:21.582237000 CST - * [Time shift for this packet: 0.000000000 seconds] - * Epoch Time: 1690948161.582237000 seconds - * [Time delta from previous captured frame: 0.000042000 seconds] - * [Time delta from previous displayed frame: 0.000000000 seconds] - * [Time since reference or first frame: 4.905717000 seconds] - * Frame Number: 217 - * Frame Length: 131 bytes (1048 bits) - * Capture Length: 131 bytes (1048 bits) - * [Frame is marked: False] - * [Frame is ignored: False] - * [Protocols in frame: eth:ethertype:ip:tcp:http] - * [Coloring Rule Name: HTTP] - * [Coloring Rule String: http || tcp.port == 80 || http2] - * Ethernet II, Src: Apple_0a:c5:ea (3c:a6:f6:0a:c5:ea), Dst: NewH3CTe_96:38:0e (48:73:97:96:38:0e) - * Destination: NewH3CTe_96:38:0e (48:73:97:96:38:0e) - * Address: NewH3CTe_96:38:0e (48:73:97:96:38:0e) - * .... ..0. .... .... .... .... = LG bit: Globally unique address (factory default) - * .... ...0 .... .... .... .... = IG bit: Individual address (unicast) - * Source: Apple_0a:c5:ea (3c:a6:f6:0a:c5:ea) - * Address: Apple_0a:c5:ea (3c:a6:f6:0a:c5:ea) - * .... ..0. .... .... .... .... = LG bit: Globally unique address (factory default) - * .... ...0 .... .... .... .... = IG bit: Individual address (unicast) - * Type: IPv4 (0x0800) - * Internet Protocol Version 4, Src: 192.168.38.63, Dst: 182.61.200.6 - * 0100 .... = Version: 4 - * .... 0101 = Header Length: 20 bytes (5) - * Differentiated Services Field: 0x00 (DSCP: CS0, ECN: Not-ECT) - * 0000 00.. = Differentiated Services Codepoint: Default (0) - * .... ..00 = Explicit Congestion Notification: Not ECN-Capable Transport (0) - * Total Length: 117 - * Identification: 0x0000 (0) - * 010. .... = Flags: 0x2, Don't fragment - * 0... .... = Reserved bit: Not set - * .1.. .... = Don't fragment: Set - * ..0. .... = More fragments: Not set - * ...0 0000 0000 0000 = Fragment Offset: 0 - * Time to Live: 64 - * Protocol: TCP (6) - * Header Checksum: 0xd557 [correct] - * [Header checksum status: Good] - * [Calculated Checksum: 0xd557] - * Source Address: 192.168.38.63 - * Destination Address: 182.61.200.6 - * Transmission Control Protocol, Src Port: 57016, Dst Port: 80, Seq: 1, Ack: 1, Len: 77 - * Source Port: 57016 - * Destination Port: 80 - * [Stream index: 9] - * [Conversation completeness: Complete, WITH_DATA (31)] - * [TCP Segment Len: 77] - * Sequence Number: 1 (relative sequence number) - * Sequence Number (raw): 1965697618 - * [Next Sequence Number: 78 (relative sequence number)] - * Acknowledgment Number: 1 (relative ack number) - * Acknowledgment number (raw): 4259318185 - * 0101 .... = Header Length: 20 bytes (5) - * Flags: 0x018 (PSH, ACK) - * 000. .... .... = Reserved: Not set - * ...0 .... .... = Accurate ECN: Not set - * .... 0... .... = Congestion Window Reduced: Not set - * .... .0.. .... = ECN-Echo: Not set - * .... ..0. .... = Urgent: Not set - * .... ...1 .... = Acknowledgment: Set - * .... .... 1... = Push: Set - * .... .... .0.. = Reset: Not set - * .... .... ..0. = Syn: Not set - * .... .... ...0 = Fin: Not set - * [TCP Flags: ·······AP···] - * Window: 4096 - * [Calculated window size: 262144] - * [Window size scaling factor: 64] - * Checksum: 0x7f51 [correct] - * [Checksum Status: Good] - * [Calculated Checksum: 0x7f51] - * Urgent Pointer: 0 - * [Timestamps] - * [Time since first frame in this TCP stream: 0.010626000 seconds] - * [Time since previous frame in this TCP stream: 0.000042000 seconds] - * [SEQ/ACK analysis] - * [iRTT: 0.010584000 seconds] - * [Bytes in flight: 77] - * [Bytes sent since last PSH flag: 77] - * TCP payload (77 bytes) - * Hypertext Transfer Protocol - * GET / HTTP/1.1\r\n - * [Expert Info (Chat/Sequence): GET / HTTP/1.1\r\n] - * [GET / HTTP/1.1\r\n] - * [Severity level: Chat] - * [Group: Sequence] - * Request Method: GET - * Request URI: / - * Request Version: HTTP/1.1 - * Host: www.baidu.com\r\n - * User-Agent: curl/7.64.1\r\n - */ - // Accept: */*\r\n - /* \r\n - * [Full request URI: http://www.baidu.com/] - * [HTTP request 1/1] - * [Response in frame: 220] - */ - let bytes = [ - 0x48, 0x73, 0x97, 0x96, 0x38, 0x0e, 0x3c, 0xa6, 0xf6, 0x0a, 0xc5, 0xea, 0x08, 0x00, - 0x45, 0x00, 0x00, 0x75, 0x00, 0x00, 0x40, 0x00, 0x40, 0x06, 0xd5, 0x57, 0xc0, 0xa8, - 0x26, 0x3f, 0xb6, 0x3d, 0xc8, 0x06, 0xde, 0xb8, 0x00, 0x50, 0x75, 0x2a, 0x2a, 0x52, - 0xfd, 0xe0, 0x09, 0xa9, 0x50, 0x18, 0x10, 0x00, 0x7f, 0x51, 0x00, 0x00, 0x47, 0x45, - 0x54, 0x20, 0x2f, 0x20, 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, 0x2e, 0x31, 0x0d, 0x0a, - 0x48, 0x6f, 0x73, 0x74, 0x3a, 0x20, 0x77, 0x77, 0x77, 0x2e, 0x62, 0x61, 0x69, 0x64, - 0x75, 0x2e, 0x63, 0x6f, 0x6d, 0x0d, 0x0a, 0x55, 0x73, 0x65, 0x72, 0x2d, 0x41, 0x67, - 0x65, 0x6e, 0x74, 0x3a, 0x20, 0x63, 0x75, 0x72, 0x6c, 0x2f, 0x37, 0x2e, 0x36, 0x34, - 0x2e, 0x31, 0x0d, 0x0a, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x3a, 0x20, 0x2a, 0x2f, - 0x2a, 0x0d, 0x0a, 0x0d, 0x0a, /* HTTP END */ - ]; - - let mut packet = Packet::new(&bytes, bytes.len() as u32); - let result = packet.handle(); - - match result { - Ok(v) => { - println!("SUCCESS: {:?}, {:?}", packet, v); - // println!("SUCCESS: {:#?}, {:?}", packet, v); - // dbg!(packet); - } - Err(e) => { - println!("ERROR Data: {:?}", packet); - println!("ERROR Code: {:?}", e); - println!("ERROR Desc: {}", e); - assert_eq!(0, 1); - } - } - } - - #[test] - fn test_packet_handle3() { + fn test_packet_api() { let mut packet = Packet::new(b"0", 1 as u32); let ipv4_hdr = IPv4Header { version: 4, @@ -857,4 +622,206 @@ mod tests { assert_eq!(packet.get_flow_id(), Some("192.168.0.101->121.14.154.93;TCP->TCP;50081->443;2409:8034:4025::50:a31->2409:8034:4040:5301::204;UDP->UDP;9993->9994;".to_string())); } + + #[test] + fn test_packet_handle_eth_vlan_vlan_ipv4_tcp() { + /* + * Frame 1: 122 bytes on wire (976 bits), 122 bytes captured (976 bits) + * Encapsulation type: Ethernet (1) + * Arrival Time: Jun 30, 2010 03:41:35.140128000 CST + * [Time shift for this packet: 0.000000000 seconds] + * Epoch Time: 1277840495.140128000 seconds + * [Time delta from previous captured frame: 0.000000000 seconds] + * [Time delta from previous displayed frame: 0.000000000 seconds] + * [Time since reference or first frame: 0.000000000 seconds] + * Frame Number: 1 + * Frame Length: 122 bytes (976 bits) + * Capture Length: 122 bytes (976 bits) + * [Frame is marked: False] + * [Frame is ignored: False] + * [Protocols in frame: eth:ethertype:vlan:ethertype:vlan:ethertype:ip:tcp:data] + * [Coloring Rule Name: TCP] + * [Coloring Rule String: tcp] + * Ethernet II, Src: Cisco_df:ae:18 (00:13:c3:df:ae:18), Dst: Cisco_1b:a4:d8 (00:1b:d4:1b:a4:d8) + * Destination: Cisco_1b:a4:d8 (00:1b:d4:1b:a4:d8) + * Address: Cisco_1b:a4:d8 (00:1b:d4:1b:a4:d8) + * .... ..0. .... .... .... .... = LG bit: Globally unique address (factory default) + * .... ...0 .... .... .... .... = IG bit: Individual address (unicast) + * Source: Cisco_df:ae:18 (00:13:c3:df:ae:18) + * Address: Cisco_df:ae:18 (00:13:c3:df:ae:18) + * .... ..0. .... .... .... .... = LG bit: Globally unique address (factory default) + * .... ...0 .... .... .... .... = IG bit: Individual address (unicast) + * Type: 802.1Q Virtual LAN (0x8100) + * 802.1Q Virtual LAN, PRI: 0, DEI: 0, ID: 118 + * 000. .... .... .... = Priority: Best Effort (default) (0) + * ...0 .... .... .... = DEI: Ineligible + * .... 0000 0111 0110 = ID: 118 + * Type: 802.1Q Virtual LAN (0x8100) + * 802.1Q Virtual LAN, PRI: 0, DEI: 0, ID: 10 + * 000. .... .... .... = Priority: Best Effort (default) (0) + * ...0 .... .... .... = DEI: Ineligible + * .... 0000 0000 1010 = ID: 10 + * Type: IPv4 (0x0800) + * Internet Protocol Version 4, Src: 10.118.10.1, Dst: 10.118.10.2 + * 0100 .... = Version: 4 + * .... 0101 = Header Length: 20 bytes (5) + * Differentiated Services Field: 0x00 (DSCP: CS0, ECN: Not-ECT) + * 0000 00.. = Differentiated Services Codepoint: Default (0) + * .... ..00 = Explicit Congestion Notification: Not ECN-Capable Transport (0) + * Total Length: 100 + * Identification: 0x0012 (18) + * 000. .... = Flags: 0x0 + * 0... .... = Reserved bit: Not set + * .0.. .... = Don't fragment: Not set + * ..0. .... = More fragments: Not set + * ...0 0000 0000 0000 = Fragment Offset: 0 + * Time to Live: 255 + * Protocol: TCP (6) + * Header Checksum: 0x9293 [correct] + * [Header checksum status: Good] + * [Calculated Checksum: 0x9293] + * Source Address: 10.118.10.1 + * Destination Address: 10.118.10.2 + * Transmission Control Protocol, Src Port: 2048, Dst Port: 52912, Seq: 1, Ack: 1, Len: 60 + * Source Port: 2048 + * Destination Port: 52912 + * [Stream index: 0] + * [Conversation completeness: Incomplete (8)] + * [TCP Segment Len: 60] + * Sequence Number: 1 (relative sequence number) + * Sequence Number (raw): 196611 + * [Next Sequence Number: 61 (relative sequence number)] + * Acknowledgment Number: 1 (relative ack number) + * Acknowledgment number (raw): 0 + * 0101 .... = Header Length: 20 bytes (5) + * Flags: 0x010 (ACK) + * 000. .... .... = Reserved: Not set + * ...0 .... .... = Accurate ECN: Not set + * .... 0... .... = Congestion Window Reduced: Not set + * .... .0.. .... = ECN-Echo: Not set + * .... ..0. .... = Urgent: Not set + * .... ...1 .... = Acknowledgment: Set + * .... .... 0... = Push: Not set + * .... .... .0.. = Reset: Not set + * .... .... ..0. = Syn: Not set + * .... .... ...0 = Fin: Not set + * [TCP Flags: ·······A····] + * Window: 44916 + * [Calculated window size: 44916] + * [Window size scaling factor: -1 (unknown)] + * Checksum: 0x8965 [correct] + * [Checksum Status: Good] + * [Calculated Checksum: 0x8965] + * Urgent Pointer: 0 + * [Timestamps] + * [Time since first frame in this TCP stream: 0.000000000 seconds] + * [Time since previous frame in this TCP stream: 0.000000000 seconds] + * [SEQ/ACK analysis] + * [Bytes in flight: 60] + * [Bytes sent since last PSH flag: 60] + * TCP payload (60 bytes) + * Data (60 bytes) + * Data: 00cdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd… + * [Length: 60] + */ + + let bytes = [ + 0x00, 0x1b, 0xd4, 0x1b, 0xa4, 0xd8, 0x00, 0x13, 0xc3, 0xdf, 0xae, 0x18, 0x81, 0x00, + 0x00, 0x76, 0x81, 0x00, 0x00, 0x0a, 0x08, 0x00, 0x45, 0x00, 0x00, 0x64, 0x00, 0x12, + 0x00, 0x00, 0xff, 0x06, 0x92, 0x93, 0x0a, 0x76, 0x0a, 0x01, 0x0a, 0x76, 0x0a, 0x02, + 0x08, 0x00, 0xce, 0xb0, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x50, 0x10, + 0xaf, 0x74, 0x89, 0x65, 0x00, 0x00, 0x00, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, + 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, + 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, + 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, + 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, + ]; + + let mut packet = Packet::new(&bytes, bytes.len() as u32); + let result = packet.handle(); + assert_eq!(result.is_ok(), true); + + assert_eq!(packet.encapsulation.len(), 5); + assert_eq!( + packet.encapsulation[0], + Encapsulation::L2_ETH( + EthernetFrame { + source_mac: MacAddress([0x00, 0x13, 0xc3, 0xdf, 0xae, 0x18]), + dest_mac: MacAddress([0x00, 0x1b, 0xd4, 0x1b, 0xa4, 0xd8]), + ether_type: EtherType::VLAN, + }, + &bytes[14..] + ) + ); + assert_eq!( + packet.encapsulation[1], + Encapsulation::L3_VLAN( + VlanHeader { + priority_code_point: 0, + drop_eligible_indicator: false, + vlan_identifier: 118, + ether_type: EtherType::VLAN, + }, + &bytes[18..] + ) + ); + assert_eq!( + packet.encapsulation[2], + Encapsulation::L3_VLAN( + VlanHeader { + priority_code_point: 0, + drop_eligible_indicator: false, + vlan_identifier: 10, + ether_type: EtherType::IPv4, + }, + &bytes[22..] + ) + ); + assert_eq!( + packet.encapsulation[3], + Encapsulation::L3_IPV4( + IPv4Header { + version: 4, + ihl: 20, + tos: 0, + length: 100, + id: 0x12, + flags: 0x0, + frag_offset: 0, + ttl: 255, + protocol: IPProtocol::TCP, + checksum: 0x9293, + source_address: Ipv4Addr::new(10, 118, 10, 1), + dest_address: Ipv4Addr::new(10, 118, 10, 2), + }, + &bytes[42..] + ) + ); + + assert_eq!( + packet.encapsulation[4], + Encapsulation::L4_TCP( + TcpHeader { + source_port: 2048, + dest_port: 52912, + seq_num: 196611, + ack_num: 0, + data_offset: 20, + reserved: 0, + flag_urg: false, + flag_ack: true, + flag_psh: false, + flag_rst: false, + flag_syn: false, + flag_fin: false, + window: 44916, + checksum: 0x8965, + urgent_ptr: 0, + options: None, + }, + &bytes[62..] + ) + ); + // assert_eq!(1, 0); + } } diff --git a/src/protocol/ethernet.rs b/src/protocol/ethernet.rs index 4cfc9da..6e25696 100644 --- a/src/protocol/ethernet.rs +++ b/src/protocol/ethernet.rs @@ -126,7 +126,7 @@ impl From<u16> for EtherType { } impl EtherType { - fn decode(input: &[u8]) -> IResult<&[u8], EtherType> { + pub fn decode(input: &[u8]) -> IResult<&[u8], EtherType> { let (input, ether_type) = number::streaming::be_u16(input)?; Ok((input, ether_type.into())) diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs index 7d191ce..044481a 100644 --- a/src/protocol/mod.rs +++ b/src/protocol/mod.rs @@ -6,4 +6,5 @@ pub mod ipv6; pub mod udp; pub mod tcp; pub mod dns; -pub mod http;
\ No newline at end of file +pub mod http; +pub mod vlan;
\ No newline at end of file diff --git a/src/protocol/vlan.rs b/src/protocol/vlan.rs new file mode 100644 index 0000000..a6b7202 --- /dev/null +++ b/src/protocol/vlan.rs @@ -0,0 +1,93 @@ +use crate::protocol::codec::Decode; +use crate::protocol::ethernet::EtherType; +use nom::bits; +use nom::error::Error; +use nom::sequence; +use nom::IResult; + +/****************************************************************************** + * Struct + ******************************************************************************/ + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct VlanHeader { + // A 3 bit number which refers to the IEEE 802.1p class of service and maps to the frame priority level. + pub priority_code_point: u8, + // Indicate that the frame may be dropped under the presence of congestion. + pub drop_eligible_indicator: bool, + // 12 bits vland identifier. + pub vlan_identifier: u16, + // "Tag protocol identifier": Type id of content after this header. Refer to the "EtherType" for a list of possible supported values. + pub ether_type: EtherType, +} + +/****************************************************************************** + * API + ******************************************************************************/ + +fn bit_decode(input: &[u8]) -> IResult<&[u8], (u8, u8, u16)> { + bits::bits::<_, _, Error<_>, _, _>(sequence::tuple(( + bits::streaming::take(3u8), + bits::streaming::take(1u8), + bits::streaming::take(12u8), + )))(input) +} + +impl Decode for VlanHeader { + type Iterm = VlanHeader; + fn decode(input: &[u8]) -> IResult<&[u8], VlanHeader> { + let (input, (priority_code_point, drop_eligible_indicator, vlan_identifier)) = + bit_decode(input)?; + let (input, ether_type) = EtherType::decode(input)?; + Ok(( + input, + VlanHeader { + priority_code_point, + drop_eligible_indicator: drop_eligible_indicator == 1, + vlan_identifier, + ether_type, + }, + )) + } +} + +/****************************************************************************** + * TEST + ******************************************************************************/ + +#[cfg(test)] +mod tests { + use super::VlanHeader; + use crate::protocol::codec::Decode; + use crate::protocol::ethernet::EtherType; + const LAST_SLICE: &'static [u8] = &[0xff]; + + #[test] + fn vlan_header_decode() { + /* + * 802.1Q Virtual LAN, PRI: 0, DEI: 0, ID: 32 + * 000. .... .... .... = Priority: Best Effort (default) (0) + * ...0 .... .... .... = DEI: Ineligible + * .... 0000 0010 0000 = ID: 32 + * Type: IPv4 (0x0800) + */ + let bytes = [0x00, 0x20, 0x08, 0x00, 0xff /* Payload */]; + + let expectation = VlanHeader { + priority_code_point: 0, + drop_eligible_indicator: false, + vlan_identifier: 32, + ether_type: EtherType::IPv4, + }; + + assert_eq!(VlanHeader::decode(&bytes), Ok((LAST_SLICE, expectation))); + + // example + let vlan = VlanHeader::decode(&bytes); + if let Ok((payload, header)) = vlan { + println!("return: {:?}, payload: {}", header, payload.len()); + } else { + println!("return: Incomplete data"); + } + } +} |
