summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorluwenpeng <[email protected]>2023-09-07 18:05:25 +0800
committerluwenpeng <[email protected]>2023-09-08 11:34:48 +0800
commit884a3d2bee16ddd96501a7d669077870bbd237e1 (patch)
treee57d27d085e307b70672b0504af01478a9a0c16f
parente16b028be675ee35f4d08521accbc52ee6e6b182 (diff)
[feature] Support VLAN Decode
-rw-r--r--src/packet/packet.rs479
-rw-r--r--src/protocol/ethernet.rs2
-rw-r--r--src/protocol/mod.rs3
-rw-r--r--src/protocol/vlan.rs93
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");
+ }
+ }
+}