diff options
| author | luwenpeng <[email protected]> | 2023-09-12 16:11:32 +0800 |
|---|---|---|
| committer | luwenpeng <[email protected]> | 2023-09-13 19:20:06 +0800 |
| commit | 8f9e0f719055487e4a352f5da74cae063bbcdbcc (patch) | |
| tree | a7d44661b4b2d79202abd2a79f3eeda1f791b98b | |
| parent | 8755e95f068da56c099e9c51dc4a89b55960e48d (diff) | |
[feature] Support GTPv1-U Decode
| -rw-r--r-- | src/main.rs | 3 | ||||
| -rw-r--r-- | src/packet/error.rs | 7 | ||||
| -rw-r--r-- | src/packet/packet.rs | 369 | ||||
| -rw-r--r-- | src/plugin/example.rs | 18 | ||||
| -rw-r--r-- | src/protocol/gtpv1.rs | 278 | ||||
| -rw-r--r-- | src/protocol/ip.rs | 29 | ||||
| -rw-r--r-- | src/protocol/ipv4.rs | 18 | ||||
| -rw-r--r-- | src/protocol/ipv6.rs | 70 | ||||
| -rw-r--r-- | src/protocol/mod.rs | 3 |
9 files changed, 702 insertions, 93 deletions
diff --git a/src/main.rs b/src/main.rs index 24e907c..4aed3c7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -50,6 +50,9 @@ fn trigger_event_by_packet( Encapsulation::L4_ICMPV6(_, _) => { // TODO } + Encapsulation::LTUN_GTPV1_C(_, _) => { + // TODO + } Encapsulation::UNSUPPORTED(_) => { // TODO } diff --git a/src/packet/error.rs b/src/packet/error.rs index 744c6e8..f5dbf0a 100644 --- a/src/packet/error.rs +++ b/src/packet/error.rs @@ -23,6 +23,10 @@ pub enum PacketError { IncompleteTcpHeader, IncompleteIcmpHeader, IncompleteIcmpv6Header, + + // L TUNNEL + IncompleteGtpv1Header, + UnsupportGtpProtocol, } impl core::fmt::Display for PacketError { @@ -46,6 +50,9 @@ impl core::fmt::Display for PacketError { PacketError::IncompleteTcpHeader => write!(f, "Incomplete TCP Header"), PacketError::IncompleteIcmpHeader => write!(f, "Incomplete ICMP Header"), PacketError::IncompleteIcmpv6Header => write!(f, "Incomplete ICMPv6 Header"), + // L TUNNEL + PacketError::IncompleteGtpv1Header => write!(f, "Incomplete GTPv1 Header"), + PacketError::UnsupportGtpProtocol => write!(f, "Unsupport GTP Protocol"), } } } diff --git a/src/packet/packet.rs b/src/packet/packet.rs index 59dfce1..e44968f 100644 --- a/src/packet/packet.rs +++ b/src/packet/packet.rs @@ -2,16 +2,18 @@ use crate::packet::error::PacketError; use crate::protocol::codec::Decode; use crate::protocol::ethernet::EtherType; use crate::protocol::ethernet::EthernetFrame; +use crate::protocol::gtpv1::Gtpv1Header; use crate::protocol::icmp::IcmpHeader; use crate::protocol::icmpv6::Icmpv6Header; use crate::protocol::ip::IPProtocol; -use crate::protocol::ipv4::IPv4Header; -use crate::protocol::ipv6::IPv6Header; +use crate::protocol::ipv4::Ipv4Header; +use crate::protocol::ipv6::Ipv6Header; use crate::protocol::mpls::MplsHeader; use crate::protocol::mpls::PwEthHeader; use crate::protocol::tcp::TcpHeader; use crate::protocol::udp::UdpHeader; use crate::protocol::vlan::VlanHeader; +use nom::Err::Incomplete; #[allow(non_camel_case_types)] #[derive(Clone, Debug, PartialEq)] @@ -21,14 +23,16 @@ pub enum Encapsulation<'a> { L2_MPLS(MplsHeader, &'a [u8]), L2_PWETH(PwEthHeader, &'a [u8]), - L3_IPV4(IPv4Header, &'a [u8]), - L3_IPV6(IPv6Header, &'a [u8]), + L3_IPV4(Ipv4Header, &'a [u8]), + L3_IPV6(Ipv6Header, &'a [u8]), L4_TCP(TcpHeader, &'a [u8]), L4_UDP(UdpHeader, &'a [u8]), L4_ICMP(IcmpHeader, &'a [u8]), L4_ICMPV6(Icmpv6Header, &'a [u8]), + LTUN_GTPV1_C(Gtpv1Header, &'a [u8]), + UNSUPPORTED(&'a [u8]), } @@ -424,7 +428,7 @@ fn handle_pw_eth<'a>(packet: &mut Packet<'a>, input: &'a [u8]) -> Result<(), Pac } fn handle_ipv4<'a>(packet: &mut Packet<'a>, input: &'a [u8]) -> Result<(), PacketError> { - let result = IPv4Header::decode(input); + let result = Ipv4Header::decode(input); if let Ok((payload, header)) = result { dbg!(&header); @@ -442,7 +446,7 @@ fn handle_ipv4<'a>(packet: &mut Packet<'a>, input: &'a [u8]) -> Result<(), Packe } fn handle_ipv6<'a>(packet: &mut Packet<'a>, input: &'a [u8]) -> Result<(), PacketError> { - let result = IPv6Header::decode(input); + let result = Ipv6Header::decode(input); if let Ok((payload, header)) = result { dbg!(&header); @@ -450,7 +454,7 @@ fn handle_ipv6<'a>(packet: &mut Packet<'a>, input: &'a [u8]) -> Result<(), Packe for extension in header.extensions.iter() { next_proto = extension.next_header; - if next_proto == IPProtocol::IPV6_FRAGMENT_HDR { + if next_proto == IPProtocol::IPV6FRAGMENT { // TODO IPv6 Fragment } } @@ -487,11 +491,16 @@ fn handle_udp<'a>(packet: &mut Packet<'a>, input: &'a [u8]) -> Result<(), Packet if let Ok((payload, header)) = result { dbg!(&header); + let dest_port = header.dest_port; packet .encapsulation .push(Encapsulation::L4_UDP(header, payload)); - return Ok(()); + match dest_port { + // GTP-U + 2152 => handle_gtpv1_c(packet, payload), + _ => Ok(()), + } } else { return Err(PacketError::IncompleteUdpHeader); } @@ -527,6 +536,36 @@ fn handle_icmpv6<'a>(packet: &mut Packet<'a>, input: &'a [u8]) -> Result<(), Pac } } +fn handle_gtpv1_c<'a>(packet: &mut Packet<'a>, input: &'a [u8]) -> Result<(), PacketError> { + let result = Gtpv1Header::decode(input); + match result { + Ok((payload, header)) => { + dbg!(&header); + + packet + .encapsulation + .push(Encapsulation::LTUN_GTPV1_C(header, payload)); + + if payload.len() < 1 { + return Ok(()); + } + + let next_proto = payload[0] >> 4; + match next_proto { + 0x4 => handle_ipv4(packet, payload), + 0x6 => handle_ipv6(packet, payload), + _ => Ok(()), + } + } + Err(Incomplete(_)) => { + return Err(PacketError::IncompleteGtpv1Header); + } + _ => { + return Err(PacketError::UnsupportGtpProtocol); + } + } +} + fn handle_l3<'a>( packet: &mut Packet<'a>, input: &'a [u8], @@ -573,11 +612,14 @@ mod tests { use crate::protocol::ethernet::EtherType; use crate::protocol::ethernet::EthernetFrame; use crate::protocol::ethernet::MacAddress; + use crate::protocol::gtpv1::Gtpv1ExtensionHeader; + use crate::protocol::gtpv1::Gtpv1Header; + use crate::protocol::gtpv1::Gtpv1Option; use crate::protocol::icmp::IcmpHeader; use crate::protocol::icmp::IcmpType; use crate::protocol::ip::IPProtocol; - use crate::protocol::ipv4::IPv4Header; - use crate::protocol::ipv6::IPv6Header; + use crate::protocol::ipv4::Ipv4Header; + use crate::protocol::ipv6::Ipv6Header; use crate::protocol::mpls::MplsHeader; use crate::protocol::mpls::PwEthHeader; use crate::protocol::tcp::TcpHeader; @@ -590,7 +632,7 @@ mod tests { #[test] fn test_packet_api() { let mut packet = Packet::new(b"0", 1 as u32); - let ipv4_hdr = IPv4Header { + let ipv4_hdr = Ipv4Header { version: 4, ihl: 20, tos: 0, @@ -604,7 +646,7 @@ mod tests { source_address: Ipv4Addr::new(192, 168, 0, 101), dest_address: Ipv4Addr::new(121, 14, 154, 93), }; - let ipv6_hdr = IPv6Header { + let ipv6_hdr = Ipv6Header { version: 6, dsc: 0, ecn: 0, @@ -869,7 +911,7 @@ mod tests { assert_eq!( packet.encapsulation[3], Encapsulation::L3_IPV4( - IPv4Header { + Ipv4Header { version: 4, ihl: 20, tos: 0, @@ -1068,7 +1110,7 @@ mod tests { assert_eq!( packet.encapsulation[3], Encapsulation::L3_IPV4( - IPv4Header { + Ipv4Header { version: 4, ihl: 20, tos: 0, @@ -1297,7 +1339,7 @@ mod tests { assert_eq!( packet.encapsulation[1], Encapsulation::L3_IPV6( - IPv6Header { + Ipv6Header { version: 6, dsc: 0, ecn: 0, @@ -1315,7 +1357,7 @@ mod tests { assert_eq!( packet.encapsulation[2], Encapsulation::L3_IPV4( - IPv4Header { + Ipv4Header { version: 4, ihl: 20, tos: 0, @@ -1500,7 +1542,7 @@ mod tests { assert_eq!( packet.encapsulation[1], Encapsulation::L3_IPV4( - IPv4Header { + Ipv4Header { version: 4, ihl: 20, tos: 0, @@ -1520,7 +1562,7 @@ mod tests { assert_eq!( packet.encapsulation[2], Encapsulation::L3_IPV6( - IPv6Header { + Ipv6Header { version: 6, dsc: 0, ecn: 0, @@ -1667,7 +1709,7 @@ mod tests { assert_eq!( packet.encapsulation[1], Encapsulation::L3_IPV6( - IPv6Header { + Ipv6Header { version: 6, dsc: 0, ecn: 0, @@ -1689,7 +1731,7 @@ mod tests { assert_eq!( packet.encapsulation[2], Encapsulation::L3_IPV6( - IPv6Header { + Ipv6Header { version: 6, dsc: 0, ecn: 0, @@ -1879,7 +1921,7 @@ mod tests { assert_eq!( packet.encapsulation[3], Encapsulation::L3_IPV4( - IPv4Header { + Ipv4Header { version: 4, ihl: 20, tos: 0xb8, @@ -1899,7 +1941,7 @@ mod tests { assert_eq!( packet.encapsulation[4], Encapsulation::L3_IPV4( - IPv4Header { + Ipv4Header { version: 4, ihl: 20, tos: 0xb8, @@ -2086,7 +2128,7 @@ mod tests { assert_eq!( packet.encapsulation[3], Encapsulation::L3_IPV4( - IPv4Header { + Ipv4Header { version: 4, ihl: 20, tos: 0xb0, @@ -2293,7 +2335,7 @@ mod tests { assert_eq!( packet.encapsulation[5], Encapsulation::L3_IPV4( - IPv4Header { + Ipv4Header { version: 4, ihl: 20, tos: 0x00, @@ -2325,4 +2367,283 @@ mod tests { // assert_eq!(1, 0); } + + #[test] + fn test_packet_handle_eth_vlan_ipv6_udp_gtp_ipv4_tcp() { + /* + * Frame 1: 122 bytes on wire (976 bits), 122 bytes captured (976 bits) + * Encapsulation type: Ethernet (1) + * Arrival Time: Sep 8, 2021 13:03:49.146532000 CST + * [Time shift for this packet: 0.000000000 seconds] + * Epoch Time: 1631077429.146532000 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:ipv6:udp:gtp:ip:tcp] + * [Coloring Rule Name: HTTP] + * [Coloring Rule String: http || tcp.port == 80 || http2] + * Ethernet II, Src: HuaweiTe_40:e9:c3 (ac:b3:b5:40:e9:c3), Dst: zte_0e:f5:14 (74:4a:a4:0e:f5:14) + * Destination: zte_0e:f5:14 (74:4a:a4:0e:f5:14) + * Address: zte_0e:f5:14 (74:4a:a4:0e:f5:14) + * .... ..0. .... .... .... .... = LG bit: Globally unique address (factory default) + * .... ...0 .... .... .... .... = IG bit: Individual address (unicast) + * Source: HuaweiTe_40:e9:c3 (ac:b3:b5:40:e9:c3) + * Address: HuaweiTe_40:e9:c3 (ac:b3:b5:40:e9:c3) + * .... ..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: 2, DEI: 0, ID: 504 + * 010. .... .... .... = Priority: Excellent Effort (2) + * ...0 .... .... .... = DEI: Ineligible + * .... 0001 1111 1000 = ID: 504 + * Type: IPv6 (0x86dd) + * Internet Protocol Version 6, Src: 2409:8034:4025::1:941, Dst: 2409:8034:4040:5300::205 + * 0110 .... = Version: 6 + * .... 0100 1000 .... .... .... .... .... = Traffic Class: 0x48 (DSCP: AF21, ECN: Not-ECT) + * .... 0100 10.. .... .... .... .... .... = Differentiated Services Codepoint: Assured Forwarding 21 (18) + * .... .... ..00 .... .... .... .... .... = Explicit Congestion Notification: Not ECN-Capable Transport (0) + * .... 0000 0000 0000 0000 0000 = Flow Label: 0x00000 + * Payload Length: 64 + * Next Header: UDP (17) + * Hop Limit: 252 + * Source Address: 2409:8034:4025::1:941 + * Destination Address: 2409:8034:4040:5300::205 + * User Datagram Protocol, Src Port: 2152, Dst Port: 2152 + * Source Port: 2152 + * Destination Port: 2152 + * Length: 64 + * Checksum: 0x8c07 [correct] + * [Calculated Checksum: 0x8c07] + * [Checksum Status: Good] + * [Stream index: 0] + * [Timestamps] + * [Time since first frame: 0.000000000 seconds] + * [Time since previous frame: 0.000000000 seconds] + * UDP payload (56 bytes) + * GPRS Tunneling Protocol + * Flags: 0x36 + * 001. .... = Version: GTP release 99 version (1) + * ...1 .... = Protocol type: GTP (1) + * .... 0... = Reserved: 0 + * .... .1.. = Is Next Extension Header present?: Yes + * .... ..1. = Is Sequence Number present?: Yes + * .... ...0 = Is N-PDU number present?: No + * Message Type: T-PDU (0xff) + * Length: 48 + * TEID: 0x1c6596fc (476419836) + * Sequence number: 0x6739 (26425) + * Next extension header type: PDU Session container (0x85) + * Extension header (PDU Session container) + * Extension Header Length: 1 + * PDU Session Container + * 0001 .... = PDU Type: UL PDU SESSION INFORMATION (1) + * .... 0000 = Spare: 0x0 + * 00.. .... = Spare: 0x0 + * ..00 0001 = QoS Flow Identifier (QFI): 1 + * Next extension header type: No more extension headers (0x00) + * Internet Protocol Version 4, Src: 10.16.131.196, Dst: 112.49.26.208 + * 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: 40 + * Identification: 0x55b2 (21938) + * 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: 0xcc48 [correct] + * [Header checksum status: Good] + * [Calculated Checksum: 0xcc48] + * Source Address: 10.16.131.196 + * Destination Address: 112.49.26.208 + * Transmission Control Protocol, Src Port: 47892, Dst Port: 80, Seq: 1, Ack: 1, Len: 0 + * Source Port: 47892 + * Destination Port: 80 + * [Stream index: 0] + * [Conversation completeness: Incomplete (12)] + * [TCP Segment Len: 0] + * Sequence Number: 1 (relative sequence number) + * Sequence Number (raw): 2480465049 + * [Next Sequence Number: 1 (relative sequence number)] + * Acknowledgment Number: 1 (relative ack number) + * Acknowledgment number (raw): 3580561259 + * 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: 1634 + * [Calculated window size: 1634] + * [Window size scaling factor: -1 (unknown)] + * Checksum: 0x7fef [correct] + * [Checksum Status: Good] + * [Calculated Checksum: 0x7fef] + * 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] + */ + + let bytes = [ + 0x74, 0x4a, 0xa4, 0x0e, 0xf5, 0x14, 0xac, 0xb3, 0xb5, 0x40, 0xe9, 0xc3, 0x81, 0x00, + 0x41, 0xf8, 0x86, 0xdd, 0x64, 0x80, 0x00, 0x00, 0x00, 0x40, 0x11, 0xfc, 0x24, 0x09, + 0x80, 0x34, 0x40, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x09, 0x41, + 0x24, 0x09, 0x80, 0x34, 0x40, 0x40, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x05, 0x08, 0x68, 0x08, 0x68, 0x00, 0x40, 0x8c, 0x07, 0x36, 0xff, 0x00, 0x30, + 0x1c, 0x65, 0x96, 0xfc, 0x67, 0x39, 0x00, 0x85, 0x01, 0x10, 0x01, 0x00, 0x45, 0x00, + 0x00, 0x28, 0x55, 0xb2, 0x40, 0x00, 0x40, 0x06, 0xcc, 0x48, 0x0a, 0x10, 0x83, 0xc4, + 0x70, 0x31, 0x1a, 0xd0, 0xbb, 0x14, 0x00, 0x50, 0x93, 0xd8, 0xe4, 0x99, 0xd5, 0x6b, + 0x07, 0x6b, 0x50, 0x10, 0x06, 0x62, 0x7f, 0xef, 0x00, 0x00, + ]; + + 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(), 7); + assert_eq!( + packet.encapsulation[0], + Encapsulation::L2_ETH( + EthernetFrame { + source_mac: MacAddress([0xac, 0xb3, 0xb5, 0x40, 0xe9, 0xc3]), + dest_mac: MacAddress([0x74, 0x4a, 0xa4, 0x0e, 0xf5, 0x14]), + ether_type: EtherType::VLAN, + }, + &bytes[14..] + ) + ); + assert_eq!( + packet.encapsulation[1], + Encapsulation::L2_VLAN( + VlanHeader { + priority_code_point: 2, + drop_eligible_indicator: false, + vlan_identifier: 504, + ether_type: EtherType::IPv6, + }, + &bytes[18..] + ) + ); + assert_eq!( + packet.encapsulation[2], + Encapsulation::L3_IPV6( + Ipv6Header { + version: 6, + dsc: 18, + ecn: 0, + flow_label: 0, + length: 64, + next_header: IPProtocol::UDP, + hop_limit: 252, + source_address: Ipv6Addr::new(0x2409, 0x8034, 0x4025, 0, 0, 0, 0x1, 0x941), + dest_address: Ipv6Addr::new(0x2409, 0x8034, 0x4040, 0x5300, 0, 0, 0, 0x205), + extensions: vec![], + }, + &bytes[58..] + ) + ); + assert_eq!( + packet.encapsulation[3], + Encapsulation::L4_UDP( + UdpHeader { + source_port: 2152, + dest_port: 2152, + length: 64, + checksum: 0x8c07, + }, + &bytes[66..] + ) + ); + assert_eq!( + packet.encapsulation[4], + Encapsulation::LTUN_GTPV1_C( + Gtpv1Header { + version: 1, + protocol_type: 1, + reserved: 0, + extension_header_flag: 1, + sequence_number_flag: 1, + npdu_number_flag: 0, + message_type: 0xff, + message_length: 48, + teid: 476419836, + options: Some(Gtpv1Option { + sequence_number: 26425, + npdu_number: 0, + next_header_type: 0x85, + }), + extensions: vec![Gtpv1ExtensionHeader { + length: 1, + contents: vec![0x10, 0x01], + next_header_type: 0x00, + }], + }, + &bytes[82..] + ) + ); + assert_eq!( + packet.encapsulation[5], + Encapsulation::L3_IPV4( + Ipv4Header { + version: 4, + ihl: 20, + tos: 0x00, + length: 40, + id: 0x55b2, + flags: 0x2, + frag_offset: 0, + ttl: 64, + protocol: IPProtocol::TCP, + checksum: 0xcc48, + source_address: Ipv4Addr::new(10, 16, 131, 196), + dest_address: Ipv4Addr::new(112, 49, 26, 208), + }, + &bytes[102..] + ) + ); + assert_eq!( + packet.encapsulation[6], + Encapsulation::L4_TCP( + TcpHeader { + source_port: 47892, + dest_port: 80, + seq_num: 2480465049, + ack_num: 3580561259, + data_offset: 20, + reserved: 0, + flag_urg: false, + flag_ack: true, + flag_psh: false, + flag_rst: false, + flag_syn: false, + flag_fin: false, + window: 1634, + checksum: 0x7fef, + urgent_ptr: 0, + options: None, + }, + &bytes[122..] + ) + ); + + // assert_eq!(1, 0); + } } diff --git a/src/plugin/example.rs b/src/plugin/example.rs index 6819d75..bce1724 100644 --- a/src/plugin/example.rs +++ b/src/plugin/example.rs @@ -78,19 +78,19 @@ impl EventHandle for ExamplePulgin { packet: Option<&Packet>, session: Option<Rc<RefCell<Session>>>, ) { + self.plugin_ctx.borrow_mut().clear(); + self.plugin_ctx.borrow_mut().push_str("1"); + if session.is_none() { return; } - let length = packet.unwrap().orig_len; - let flow_id = packet.unwrap().get_flow_id(); - println!( - "{} handle Packet: {:?} {:?}", - self.plugin_name, length, flow_id - ); - - self.plugin_ctx.borrow_mut().clear(); - self.plugin_ctx.borrow_mut().push_str("1"); + if packet.is_some() { + let flow_id = packet.unwrap().get_flow_id(); + println!("{} handle Packet: {:?}", self.plugin_name, flow_id); + } else { + println!("{} handle Packet: None", self.plugin_name); + } let session = session.unwrap(); if index == self.tcp_opening_event { diff --git a/src/protocol/gtpv1.rs b/src/protocol/gtpv1.rs new file mode 100644 index 0000000..3791a58 --- /dev/null +++ b/src/protocol/gtpv1.rs @@ -0,0 +1,278 @@ +use crate::protocol::codec::Decode; +use nom::bits; +use nom::bytes; +use nom::error::Error; +use nom::number; +use nom::sequence; +use nom::IResult; + +/****************************************************************************** + * Struct + ******************************************************************************/ + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Gtpv1Option { + pub sequence_number: u16, // 16bit + pub npdu_number: u8, // 8bit + pub next_header_type: u8, // 8bit +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Gtpv1ExtensionHeader { + pub length: u8, // 8bit (单位4字节,包括长度/内容/下一扩展消息头字段) + pub contents: Vec<u8>, + pub next_header_type: u8, // 8bit +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Gtpv1Header { + pub version: u8, // 3bit 1: GTPv1 + pub protocol_type: u8, // 1bit + pub reserved: u8, // 1bit + pub extension_header_flag: u8, // 1bit + pub sequence_number_flag: u8, // 1bit + pub npdu_number_flag: u8, // 1bit + + pub message_type: u8, // 8bit + pub message_length: u16, // 16bit (单位为字节,不包括GTP头前8字节的必选字段) + pub teid: u32, // 32bit + + // extension_header_flag/sequence_number_flag/npdu_number_flag任意一个取值为1时options字段才存在 + pub options: Option<Gtpv1Option>, + pub extensions: Vec<Gtpv1ExtensionHeader>, +} + +/****************************************************************************** + * API + ******************************************************************************/ + +fn bit_decode(input: &[u8]) -> IResult<&[u8], (u8, u8, u8, u8, u8, u8)> { + bits::bits::<_, _, Error<_>, _, _>(sequence::tuple(( + bits::streaming::take(3u8), + bits::streaming::take(1u8), + bits::streaming::take(1u8), + bits::streaming::take(1u8), + bits::streaming::take(1u8), + bits::streaming::take(1u8), + )))(input) +} + +fn option_decode(input: &[u8]) -> IResult<&[u8], Gtpv1Option> { + let (input, sequence_number) = number::streaming::be_u16(input)?; + let (input, npdu_number) = number::streaming::be_u8(input)?; + let (input, next_header_type) = number::streaming::be_u8(input)?; + + Ok(( + input, + Gtpv1Option { + sequence_number, + npdu_number, + next_header_type, + }, + )) +} + +fn extension_decode(input: &[u8]) -> IResult<&[u8], Gtpv1ExtensionHeader> { + let (input, length) = number::streaming::be_u8(input)?; + let (input, contents) = bytes::streaming::take(length * 4 - 2)(input)?; + let (input, next_header_type) = number::streaming::be_u8(input)?; + Ok(( + input, + Gtpv1ExtensionHeader { + length, + contents: contents.to_vec(), + next_header_type, + }, + )) +} + +impl Decode for Gtpv1Header { + type Iterm = Gtpv1Header; + fn decode(input: &[u8]) -> IResult<&[u8], Gtpv1Header> { + let ( + input, + ( + version, + protocol_type, + reserved, + extension_header_flag, + sequence_number_flag, + npdu_number_flag, + ), + ) = bit_decode(input)?; + match (version, protocol_type) { + (1, 1) => (), + (_, _) => { + return Err(nom::Err::Error(Error::new( + input, + nom::error::ErrorKind::TagBits, + ))) + } + } + let (input, message_type) = number::streaming::be_u8(input)?; + let (input, message_length) = number::streaming::be_u16(input)?; + let (input, teid) = number::streaming::be_u32(input)?; + + // Optional word of GTP header, present if any of extension_header_flag, sequence_number_flag, npdu_number_flag is set + let mut remain = input; + let mut options = None; + if extension_header_flag == 1 || sequence_number_flag == 1 || npdu_number_flag == 1 { + let (left, _options) = option_decode(remain)?; + remain = left; + options = Some(_options); + } + + let mut extensions = Vec::new(); + if options.is_some() { + let mut next_header = options.clone().unwrap().next_header_type; + while next_header != 0 { + let (left, extension) = extension_decode(remain)?; + remain = left; + next_header = extension.next_header_type; + extensions.push(extension); + } + } + + Ok(( + remain, + Gtpv1Header { + version, + protocol_type, + reserved, + extension_header_flag, + sequence_number_flag, + npdu_number_flag, + message_type, + message_length, + teid, + options, + extensions, + }, + )) + } +} + +/****************************************************************************** + * TEST + ******************************************************************************/ + +#[cfg(test)] +mod tests { + use super::Gtpv1ExtensionHeader; + use super::Gtpv1Header; + use crate::protocol::codec::Decode; + const LAST_SLICE: &'static [u8] = &[0xff]; + + #[test] + fn gtpv1_header_decode() { + /* + * GPRS Tunneling Protocol + * Flags: 0x30 + * 001. .... = Version: GTP release 99 version (1) + * ...1 .... = Protocol type: GTP (1) + * .... 0... = Reserved: 0 + * .... .0.. = Is Next Extension Header present?: No + * .... ..0. = Is Sequence Number present?: No + * .... ...0 = Is N-PDU number present?: No + * Message Type: T-PDU (0xff) + * Length: 64 + * TEID: 0x1f54d4b5 (525653173) + */ + + let bytes = [ + 0x30, 0xff, 0x00, 0x40, 0x1f, 0x54, 0xd4, 0xb5, 0xff, /* Payload */ + ]; + + let expectation = Gtpv1Header { + version: 1, + protocol_type: 1, + reserved: 0, + extension_header_flag: 0, + sequence_number_flag: 0, + npdu_number_flag: 0, + message_type: 0xff, + message_length: 64, + teid: 525653173, + options: None, + extensions: vec![], + }; + + assert_eq!(Gtpv1Header::decode(&bytes), Ok((LAST_SLICE, expectation))); + + // example + let result = Gtpv1Header::decode(&bytes); + if let Ok((payload, header)) = result { + println!("return: {:?}, payload: {}", header, payload.len()); + } else { + println!("return: Incomplete data"); + } + + // assert_eq!(1, 0); + } + + #[test] + fn gtpv1_extension_decode() { + /* + * GPRS Tunneling Protocol + * Flags: 0x36 + * 001. .... = Version: GTP release 99 version (1) + * ...1 .... = Protocol type: GTP (1) + * .... 0... = Reserved: 0 + * .... .1.. = Is Next Extension Header present?: Yes + * .... ..1. = Is Sequence Number present?: Yes + * .... ...0 = Is N-PDU number present?: No + * Message Type: T-PDU (0xff) + * Length: 48 + * TEID: 0x1c6596fc (476419836) + * Sequence number: 0x6739 (26425) + * Next extension header type: PDU Session container (0x85) + * Extension header (PDU Session container) + * Extension Header Length: 1 + * PDU Session Container + * 0001 .... = PDU Type: UL PDU SESSION INFORMATION (1) + * .... 0000 = Spare: 0x0 + * 00.. .... = Spare: 0x0 + * ..00 0001 = QoS Flow Identifier (QFI): 1 + * Next extension header type: No more extension headers (0x00) + */ + + let bytes = [ + 0x36, 0xff, 0x00, 0x30, 0x1c, 0x65, 0x96, 0xfc, 0x67, 0x39, 0x00, 0x85, 0x01, 0x10, + 0x01, 0x00, 0xff, /* Payload */ + ]; + + let expectation = Gtpv1Header { + version: 1, + protocol_type: 1, + reserved: 0, + extension_header_flag: 1, + sequence_number_flag: 1, + npdu_number_flag: 0, + message_type: 0xff, + message_length: 48, + teid: 476419836, + options: Some(super::Gtpv1Option { + sequence_number: 26425, + npdu_number: 0, + next_header_type: 0x85, + }), + extensions: vec![Gtpv1ExtensionHeader { + length: 1, + contents: vec![0x10, 0x01], + next_header_type: 0x00, + }], + }; + + assert_eq!(Gtpv1Header::decode(&bytes), Ok((LAST_SLICE, expectation))); + + // example + let result = Gtpv1Header::decode(&bytes); + if let Ok((payload, header)) = result { + println!("return: {:?}, payload: {}", header, payload.len()); + } else { + println!("return: Incomplete data"); + } + + // assert_eq!(1, 0); + } +} diff --git a/src/protocol/ip.rs b/src/protocol/ip.rs index 8d22e15..c9b4f53 100644 --- a/src/protocol/ip.rs +++ b/src/protocol/ip.rs @@ -6,10 +6,9 @@ use nom::IResult; * Struct ******************************************************************************/ -#[allow(non_camel_case_types)] #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum IPProtocol { - IPV6_HOP_HDR, + IPV6HOP, ICMP, IGMP, GGP, @@ -28,12 +27,12 @@ pub enum IPProtocol { CHAOS, UDP, IPV6, - IPV6_ROUTING_HDR, - IPV6_FRAGMENT_HDR, + IPV6ROUTING, + IPV6FRAGMENT, ESP, AUTH, ICMP6, - IPV6_DEST_HDR, + IPV6DEST, Other(u8), } @@ -44,7 +43,7 @@ pub enum IPProtocol { impl From<u8> for IPProtocol { fn from(raw: u8) -> Self { match raw { - 0 => IPProtocol::IPV6_HOP_HDR, // IPv6 Hop-by-Hop Options + 0 => IPProtocol::IPV6HOP, // IPv6 Hop-by-Hop Options 1 => IPProtocol::ICMP, 2 => IPProtocol::IGMP, 3 => IPProtocol::GGP, @@ -63,12 +62,12 @@ impl From<u8> for IPProtocol { 16 => IPProtocol::CHAOS, 17 => IPProtocol::UDP, 41 => IPProtocol::IPV6, - 43 => IPProtocol::IPV6_ROUTING_HDR, // IPv6 Routing Header - 44 => IPProtocol::IPV6_FRAGMENT_HDR, // IPv6 Fragment Header - 50 => IPProtocol::ESP, // Encap Security Payload [RFC4303] - 51 => IPProtocol::AUTH, // Authentication Header [RFC4302] + 43 => IPProtocol::IPV6ROUTING, // IPv6 Routing Header + 44 => IPProtocol::IPV6FRAGMENT, // IPv6 Fragment Header + 50 => IPProtocol::ESP, // Encap Security Payload [RFC4303] + 51 => IPProtocol::AUTH, // Authentication Header [RFC4302] 58 => IPProtocol::ICMP6, - 60 => IPProtocol::IPV6_DEST_HDR, // IPv6 Destination Options + 60 => IPProtocol::IPV6DEST, // IPv6 Destination Options other => IPProtocol::Other(other), } } @@ -77,12 +76,12 @@ impl From<u8> for IPProtocol { impl IPProtocol { pub fn is_ipv6_ext_header(next_header: IPProtocol) -> bool { match next_header { - IPProtocol::IPV6_HOP_HDR => true, - IPProtocol::IPV6_ROUTING_HDR => true, - IPProtocol::IPV6_FRAGMENT_HDR => true, + IPProtocol::IPV6HOP => true, + IPProtocol::IPV6ROUTING => true, + IPProtocol::IPV6FRAGMENT => true, // IPProtocol::ESP => true, IPProtocol::AUTH => true, - IPProtocol::IPV6_DEST_HDR => true, + IPProtocol::IPV6DEST => true, _ => false, } } diff --git a/src/protocol/ipv4.rs b/src/protocol/ipv4.rs index 61dce29..a869031 100644 --- a/src/protocol/ipv4.rs +++ b/src/protocol/ipv4.rs @@ -32,7 +32,7 @@ use std::net::Ipv4Addr; */ #[derive(Clone, Debug, PartialEq, Eq)] -pub struct IPv4Header { +pub struct Ipv4Header { pub version: u8, // 4 bit pub ihl: u8, // 4 bit pub tos: u8, @@ -71,9 +71,9 @@ fn address_v4_decode(input: &[u8]) -> IResult<&[u8], Ipv4Addr> { Ok((input, Ipv4Addr::from(<[u8; 4]>::try_from(ipv4).unwrap()))) } -impl Decode for IPv4Header { - type Iterm = IPv4Header; - fn decode(input: &[u8]) -> IResult<&[u8], IPv4Header> { +impl Decode for Ipv4Header { + type Iterm = Ipv4Header; + fn decode(input: &[u8]) -> IResult<&[u8], Ipv4Header> { let (input, verihl) = version_hlen_decode(input)?; let (input, tos) = number::streaming::be_u8(input)?; let (input, length) = number::streaming::be_u16(input)?; @@ -89,7 +89,7 @@ impl Decode for IPv4Header { Ok(( input, - IPv4Header { + Ipv4Header { version: verihl.0, ihl: verihl.1 * 4, // verihl.1 * 32 / 8 tos, @@ -113,7 +113,7 @@ impl Decode for IPv4Header { #[cfg(test)] mod tests { - use super::IPv4Header; + use super::Ipv4Header; use crate::protocol::codec::Decode; use crate::protocol::ip::IPProtocol; use std::net::Ipv4Addr; @@ -160,7 +160,7 @@ mod tests { 0xff, /* Payload */ ]; - let expectation = IPv4Header { + let expectation = Ipv4Header { version: 4, ihl: 20, tos: 0, @@ -175,10 +175,10 @@ mod tests { dest_address: Ipv4Addr::new(121, 14, 154, 93), }; - assert_eq!(IPv4Header::decode(&bytes), Ok((LAST_SLICE, expectation))); + assert_eq!(Ipv4Header::decode(&bytes), Ok((LAST_SLICE, expectation))); // example - let result = IPv4Header::decode(&bytes); + let result = Ipv4Header::decode(&bytes); if let Ok((payload, header)) = result { println!("return: {:?}, payload: {}", header, payload.len()); } else { diff --git a/src/protocol/ipv6.rs b/src/protocol/ipv6.rs index 5a30847..c32d21b 100644 --- a/src/protocol/ipv6.rs +++ b/src/protocol/ipv6.rs @@ -60,14 +60,14 @@ use std::net::Ipv6Addr; */ #[derive(Clone, Debug, PartialEq, Eq)] -pub struct IPv6Extension { +pub struct Ipv6Extension { pub next_header: IPProtocol, pub ext_length: u8, // Extension total Length pub data: Vec<u8>, // Extension data length (ext_length - 2) } #[derive(Clone, Debug, PartialEq, Eq)] -pub struct IPv6Header { +pub struct Ipv6Header { pub version: u8, // 4 bit pub dsc: u8, // Differentiated Services Codepoint: 6 bit pub ecn: u8, // Explicit Congestion Notification: 2 bit @@ -77,7 +77,7 @@ pub struct IPv6Header { pub hop_limit: u8, pub source_address: Ipv6Addr, pub dest_address: Ipv6Addr, - pub extensions: Vec<IPv6Extension>, + pub extensions: Vec<Ipv6Extension>, } /****************************************************************************** @@ -97,7 +97,7 @@ fn address_v6_decode(input: &[u8]) -> IResult<&[u8], Ipv6Addr> { Ok((input, Ipv6Addr::from(<[u8; 16]>::try_from(ipv6).unwrap()))) } -fn extension_decode(input: &[u8], curr_proto: IPProtocol) -> IResult<&[u8], IPv6Extension> { +fn extension_decode(input: &[u8], curr_proto: IPProtocol) -> IResult<&[u8], Ipv6Extension> { let (input, next_header) = IPProtocol::decode(input)?; let (input, mut ext_length) = number::streaming::be_u8(input)?; @@ -117,7 +117,7 @@ fn extension_decode(input: &[u8], curr_proto: IPProtocol) -> IResult<&[u8], IPv6 Ok(( input, - IPv6Extension { + Ipv6Extension { next_header, ext_length, data: data.to_vec(), @@ -125,9 +125,9 @@ fn extension_decode(input: &[u8], curr_proto: IPProtocol) -> IResult<&[u8], IPv6 )) } -impl Decode for IPv6Header { - type Iterm = IPv6Header; - fn decode(input: &[u8]) -> IResult<&[u8], IPv6Header> { +impl Decode for Ipv6Header { + type Iterm = Ipv6Header; + fn decode(input: &[u8]) -> IResult<&[u8], Ipv6Header> { let (input, ver_tc) = half_byte_decode(input)?; let (input, tc_fl) = half_byte_decode(input)?; let (input, fl): (_, u32) = @@ -150,7 +150,7 @@ impl Decode for IPv6Header { Ok(( remain, - IPv6Header { + Ipv6Header { version: ver_tc.0, dsc: (ver_tc.1 << 2) + ((tc_fl.0 & 0b1100) >> 2), ecn: tc_fl.0 & 0b11, @@ -172,8 +172,8 @@ impl Decode for IPv6Header { #[cfg(test)] mod tests { - use super::IPv6Extension; - use super::IPv6Header; + use super::Ipv6Extension; + use super::Ipv6Header; use crate::protocol::codec::Decode; use crate::protocol::ip::IPProtocol; use std::net::Ipv6Addr; @@ -210,7 +210,7 @@ mod tests { 0xff, /* Payload */ ]; - let expectation = IPv6Header { + let expectation = Ipv6Header { version: 6, dsc: 0, ecn: 0, @@ -227,10 +227,10 @@ mod tests { extensions: Vec::new(), }; - assert_eq!(IPv6Header::decode(&bytes), Ok((LAST_SLICE, expectation))); + assert_eq!(Ipv6Header::decode(&bytes), Ok((LAST_SLICE, expectation))); // example - let result = IPv6Header::decode(&bytes); + let result = Ipv6Header::decode(&bytes); if let Ok((payload, header)) = result { println!("return: {:?}, payload: {}", header, payload.len()); } else { @@ -280,13 +280,13 @@ mod tests { 0xff, /* Payload */ ]; - let expectation = IPv6Header { + let expectation = Ipv6Header { version: 6, dsc: 0, ecn: 0, flow_label: 0, length: 36, - next_header: IPProtocol::IPV6_HOP_HDR, + next_header: IPProtocol::IPV6HOP, hop_limit: 1, source_address: Ipv6Addr::new( 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, @@ -294,17 +294,17 @@ mod tests { dest_address: Ipv6Addr::new( 0xff02, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0016, ), - extensions: vec![IPv6Extension { + extensions: vec![Ipv6Extension { next_header: IPProtocol::ICMP6, ext_length: 8, data: vec![0x05, 0x02, 0x00, 0x00, 0x01, 0x00], }], }; - assert_eq!(IPv6Header::decode(&bytes), Ok((LAST_SLICE, expectation))); + assert_eq!(Ipv6Header::decode(&bytes), Ok((LAST_SLICE, expectation))); // example - let result = IPv6Header::decode(&bytes); + let result = Ipv6Header::decode(&bytes); if let Ok((payload, header)) = result { println!("return: {:?}, payload: {}", header, payload.len()); } else { @@ -350,13 +350,13 @@ mod tests { 0xff, /* Payload */ ]; - let expectation = IPv6Header { + let expectation = Ipv6Header { version: 6, dsc: 0, ecn: 0, flow_label: 0, length: 32, - next_header: IPProtocol::IPV6_ROUTING_HDR, + next_header: IPProtocol::IPV6ROUTING, hop_limit: 4, source_address: Ipv6Addr::new( 0x2200, 0x0000, 0x0000, 0x0244, 0x0212, 0x3fff, 0xfeae, 0x22f7, @@ -364,7 +364,7 @@ mod tests { dest_address: Ipv6Addr::new( 0x2200, 0x0000, 0x0000, 0x0240, 0x0002, 0x0000, 0x0000, 0x0004, ), - extensions: vec![IPv6Extension { + extensions: vec![Ipv6Extension { next_header: IPProtocol::ICMP6, ext_length: 24, data: vec![ @@ -374,10 +374,10 @@ mod tests { }], }; - assert_eq!(IPv6Header::decode(&bytes), Ok((LAST_SLICE, expectation))); + assert_eq!(Ipv6Header::decode(&bytes), Ok((LAST_SLICE, expectation))); // example - let result = IPv6Header::decode(&bytes); + let result = Ipv6Header::decode(&bytes); if let Ok((payload, header)) = result { println!("return: {:?}, payload: {}", header, payload.len()); } else { @@ -458,13 +458,13 @@ mod tests { 0xff, /* Payload */ ]; - let expectation = IPv6Header { + let expectation = Ipv6Header { version: 6, dsc: 0, ecn: 0, flow_label: 0, length: 32, - next_header: IPProtocol::IPV6_HOP_HDR, + next_header: IPProtocol::IPV6HOP, hop_limit: 64, source_address: Ipv6Addr::new( 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, @@ -473,22 +473,22 @@ mod tests { 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0003, ), extensions: vec![ - IPv6Extension { - next_header: IPProtocol::IPV6_DEST_HDR, + Ipv6Extension { + next_header: IPProtocol::IPV6DEST, ext_length: 8, data: vec![0x01, 0x04, 0x00, 0x00, 0x00, 0x00], }, - IPv6Extension { - next_header: IPProtocol::IPV6_ROUTING_HDR, + Ipv6Extension { + next_header: IPProtocol::IPV6ROUTING, ext_length: 8, data: vec![0x01, 0x04, 0x00, 0x00, 0x00, 0x00], }, - IPv6Extension { - next_header: IPProtocol::IPV6_FRAGMENT_HDR, + Ipv6Extension { + next_header: IPProtocol::IPV6FRAGMENT, ext_length: 8, data: vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00], }, - IPv6Extension { + Ipv6Extension { next_header: IPProtocol::Other(59), ext_length: 8, data: vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00], @@ -496,10 +496,10 @@ mod tests { ], }; - assert_eq!(IPv6Header::decode(&bytes), Ok((LAST_SLICE, expectation))); + assert_eq!(Ipv6Header::decode(&bytes), Ok((LAST_SLICE, expectation))); // example - let result = IPv6Header::decode(&bytes); + let result = Ipv6Header::decode(&bytes); if let Ok((payload, header)) = result { println!("return: {:?}, payload: {}", header, payload.len()); } else { diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs index 36f0ae6..82a35da 100644 --- a/src/protocol/mod.rs +++ b/src/protocol/mod.rs @@ -10,4 +10,5 @@ pub mod http; pub mod vlan; pub mod icmp; pub mod icmpv6; -pub mod mpls;
\ No newline at end of file +pub mod mpls; +pub mod gtpv1;
\ No newline at end of file |
