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 /src/protocol | |
| parent | 8755e95f068da56c099e9c51dc4a89b55960e48d (diff) | |
[feature] Support GTPv1-U Decode
Diffstat (limited to 'src/protocol')
| -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 |
5 files changed, 338 insertions, 60 deletions
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 |
