diff options
| -rw-r--r-- | src/main.rs | 3 | ||||
| -rw-r--r-- | src/packet/error.rs | 9 | ||||
| -rw-r--r-- | src/packet/packet.rs | 37 | ||||
| -rw-r--r-- | src/protocol/l2tp.rs | 784 | ||||
| -rw-r--r-- | src/protocol/mod.rs | 3 |
5 files changed, 832 insertions, 4 deletions
diff --git a/src/main.rs b/src/main.rs index 4aed3c7..49a0728 100644 --- a/src/main.rs +++ b/src/main.rs @@ -53,6 +53,9 @@ fn trigger_event_by_packet( Encapsulation::LTUN_GTPV1_C(_, _) => { // TODO } + Encapsulation::LTUN_L2TP(_, _) => { + // TODO + } Encapsulation::UNSUPPORTED(_) => { // TODO } diff --git a/src/packet/error.rs b/src/packet/error.rs index f5dbf0a..b6b054b 100644 --- a/src/packet/error.rs +++ b/src/packet/error.rs @@ -26,7 +26,10 @@ pub enum PacketError { // L TUNNEL IncompleteGtpv1Header, - UnsupportGtpProtocol, + UnsupportGtpVersion, + + IncompleteL2tpHeader, + UnsupportL2tpVersion, } impl core::fmt::Display for PacketError { @@ -52,7 +55,9 @@ impl core::fmt::Display for PacketError { PacketError::IncompleteIcmpv6Header => write!(f, "Incomplete ICMPv6 Header"), // L TUNNEL PacketError::IncompleteGtpv1Header => write!(f, "Incomplete GTPv1 Header"), - PacketError::UnsupportGtpProtocol => write!(f, "Unsupport GTP Protocol"), + PacketError::UnsupportGtpVersion => write!(f, "Unsupport GTP Version"), + PacketError::IncompleteL2tpHeader => write!(f, "Incomplete L2TP Header"), + PacketError::UnsupportL2tpVersion => write!(f, "Unsupport L2TP Version"), } } } diff --git a/src/packet/packet.rs b/src/packet/packet.rs index e44968f..781d89b 100644 --- a/src/packet/packet.rs +++ b/src/packet/packet.rs @@ -8,6 +8,8 @@ use crate::protocol::icmpv6::Icmpv6Header; use crate::protocol::ip::IPProtocol; use crate::protocol::ipv4::Ipv4Header; use crate::protocol::ipv6::Ipv6Header; +use crate::protocol::l2tp::L2tpHeader; +use crate::protocol::l2tp::L2tpType; use crate::protocol::mpls::MplsHeader; use crate::protocol::mpls::PwEthHeader; use crate::protocol::tcp::TcpHeader; @@ -32,6 +34,7 @@ pub enum Encapsulation<'a> { L4_ICMPV6(Icmpv6Header, &'a [u8]), LTUN_GTPV1_C(Gtpv1Header, &'a [u8]), + LTUN_L2TP(L2tpHeader, &'a [u8]), UNSUPPORTED(&'a [u8]), } @@ -499,6 +502,8 @@ fn handle_udp<'a>(packet: &mut Packet<'a>, input: &'a [u8]) -> Result<(), Packet match dest_port { // GTP-U 2152 => handle_gtpv1_c(packet, payload), + // L2TPv2 + 1701 => handle_l2tp(packet, payload), _ => Ok(()), } } else { @@ -561,7 +566,37 @@ fn handle_gtpv1_c<'a>(packet: &mut Packet<'a>, input: &'a [u8]) -> Result<(), Pa return Err(PacketError::IncompleteGtpv1Header); } _ => { - return Err(PacketError::UnsupportGtpProtocol); + return Err(PacketError::UnsupportGtpVersion); + } + } +} + +fn handle_l2tp<'a>(packet: &mut Packet<'a>, input: &'a [u8]) -> Result<(), PacketError> { + let result = L2tpHeader::decode(input); + match result { + Ok((payload, header)) => { + dbg!(&header); + + let l2tp_type = header.flag_type; + packet + .encapsulation + .push(Encapsulation::LTUN_L2TP(header, payload)); + + match l2tp_type { + L2tpType::Control => { + return Ok(()); + } + L2tpType::Data => { + // TODO handle PPP + return Ok(()); + } + } + } + Err(Incomplete(_)) => { + return Err(PacketError::IncompleteL2tpHeader); + } + _ => { + return Err(PacketError::UnsupportL2tpVersion); } } } diff --git a/src/protocol/l2tp.rs b/src/protocol/l2tp.rs new file mode 100644 index 0000000..fdf2b69 --- /dev/null +++ b/src/protocol/l2tp.rs @@ -0,0 +1,784 @@ +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 + ******************************************************************************/ + +/* + * L2TP Message Header + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |T|L|x|x|S|x|O|P|x|x|x|x| Ver | Length (opt) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Tunnel ID | Session ID | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Ns (opt) | Nr (opt) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Offset Size (opt) | Offset pad... (opt) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * https://datatracker.ietf.org/doc/html/rfc2661 + */ + +/* + * L2TP AVP Header + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |M|H| rsvd | Length | Vendor ID | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Attribute Type | Attribute Value... + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * [until Length is reached]... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum AvpType { + Message, + Result, + ProtocolVersion, + FramingCapabilites, + BearerCapabilites, + TieBreaker, + FirmwareRevision, + HostName, + VendorName, + AssignedTunnelId, + ReceiveWindowSize, + Challenge, + CauseCode, + ChallengeResponse, + AssignedSessionId, + CallSerialNumber, + MinimumBps, + MaximumBps, + BearerType, + FramingType, + CalledNumber, + CallingNumber, + SubAddress, + TxConnectSpeed, + PhysicalChannelId, + InitialReceivedLcpConfReq, + LastSentLcpConfReq, + LastReceivedLcpConfReq, + ProxyAuthenType, + ProxyAuthenName, + ProxyAuthenChallenge, + ProxyAuthenID, + ProxyAuthenResponse, + CallErrors, + AcctAuthenType, + AcctAuthenName, + AcctAuthenChallenge, + AcctAuthenID, + AcctAuthenResponse, + ChallengeControl, + AcctEapType, + AcctEapId, + AcctEapResponse, + CalledStationId, + CallingStationId, + NasIdentifier, + ProxyState, + LoginLatService, + LoginLatNode, + LoginLatGroup, + FramedAppleTalkLink, + FramedAppleTalkNetwork, + FramedAppleTalkZone, + AcctStatusType, + AcctDelayTime, + AcctInputOctets, + AcctOutputOctets, + AcctSessionId, + AcctAuthentic, + AcctSessionTime, + AcctInputPackets, + AcctOutputPackets, + AcctTerminateCause, + AcctMultiSessionId, + AcctLinkCount, + AcctEventTimeStamp, + AcctTunnelConnection, + AcctTunnelPacketsLost, + NasPortId, + FramedInterfaceId, + FramedIpv6Prefix, + LoginIpv6Host, + FramedIpv6Route, + FramedIpv6Pool, + ErrorCause, + EapMessage, + MessageAuthenticator, + TunnelPassword, + ArapPassword, + ArapFeatures, + ArapZoneAccess, + ArapSecurity, + ArapSecurityData, + PasswordRetry, + Prompt, + ConnectInfo, + ConfigurationToken, + EapMessage2, + MessageAuthenticator2, + ArapChallenge, + Other(u16), +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum L2tpType { + Data, + Control, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct AvpHeader { + pub mandatory: bool, + pub hidden: bool, + pub reserved: u8, + pub length: u16, + pub vendor_id: u16, + pub attribute_type: AvpType, + pub attribute_value: Vec<u8>, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct L2tpHeader { + pub flags: u16, + pub flag_type: L2tpType, + pub flag_length: bool, + pub flag_sequence: bool, + pub flag_offset: bool, + pub flag_priority: bool, + + pub ver: u8, + pub length: Option<u16>, + pub tunnel_id: u16, + pub session_id: u16, + pub ns: Option<u16>, + pub nr: Option<u16>, + pub offset_size: Option<u16>, + pub offset_pad: Option<Vec<u8>>, + pub avps: Option<Vec<AvpHeader>>, +} + +/****************************************************************************** + * API + ******************************************************************************/ + +impl From<bool> for L2tpType { + fn from(raw: bool) -> Self { + match raw { + false => L2tpType::Data, + true => L2tpType::Control, + } + } +} + +impl From<u16> for AvpType { + fn from(raw: u16) -> Self { + match raw { + 0 => AvpType::Message, + 1 => AvpType::Result, + 2 => AvpType::ProtocolVersion, + 3 => AvpType::FramingCapabilites, + 4 => AvpType::BearerCapabilites, + 5 => AvpType::TieBreaker, + 6 => AvpType::FirmwareRevision, + 7 => AvpType::HostName, + 8 => AvpType::VendorName, + 9 => AvpType::AssignedTunnelId, + 10 => AvpType::ReceiveWindowSize, + 11 => AvpType::Challenge, + 12 => AvpType::CauseCode, + 13 => AvpType::ChallengeResponse, + 14 => AvpType::AssignedSessionId, + 15 => AvpType::CallSerialNumber, + 16 => AvpType::MinimumBps, + 17 => AvpType::MaximumBps, + 18 => AvpType::BearerType, + 19 => AvpType::FramingType, + 20 => AvpType::CalledNumber, + 21 => AvpType::CallingNumber, + 22 => AvpType::SubAddress, + 23 => AvpType::TxConnectSpeed, + 24 => AvpType::PhysicalChannelId, + 25 => AvpType::InitialReceivedLcpConfReq, + 26 => AvpType::LastSentLcpConfReq, + 27 => AvpType::LastReceivedLcpConfReq, + 28 => AvpType::ProxyAuthenType, + 29 => AvpType::ProxyAuthenName, + 30 => AvpType::ProxyAuthenChallenge, + 31 => AvpType::ProxyAuthenID, + 32 => AvpType::ProxyAuthenResponse, + 33 => AvpType::CallErrors, + 34 => AvpType::AcctAuthenType, + 35 => AvpType::AcctAuthenName, + 36 => AvpType::AcctAuthenChallenge, + 37 => AvpType::AcctAuthenID, + 38 => AvpType::AcctAuthenResponse, + 39 => AvpType::ChallengeControl, + 40 => AvpType::AcctEapType, + 41 => AvpType::AcctEapId, + 42 => AvpType::AcctEapResponse, + 44 => AvpType::CalledStationId, + 45 => AvpType::CallingStationId, + 46 => AvpType::NasIdentifier, + 47 => AvpType::ProxyState, + 48 => AvpType::LoginLatService, + 49 => AvpType::LoginLatNode, + 50 => AvpType::LoginLatGroup, + 51 => AvpType::FramedAppleTalkLink, + 52 => AvpType::FramedAppleTalkNetwork, + 53 => AvpType::FramedAppleTalkZone, + 61 => AvpType::AcctStatusType, + 62 => AvpType::AcctDelayTime, + 63 => AvpType::AcctInputOctets, + 64 => AvpType::AcctOutputOctets, + 65 => AvpType::AcctSessionId, + 66 => AvpType::AcctAuthentic, + 67 => AvpType::AcctSessionTime, + 68 => AvpType::AcctInputPackets, + 69 => AvpType::AcctOutputPackets, + 70 => AvpType::AcctTerminateCause, + 71 => AvpType::AcctMultiSessionId, + 72 => AvpType::AcctLinkCount, + 73 => AvpType::AcctEventTimeStamp, + 74 => AvpType::AcctTunnelConnection, + 75 => AvpType::AcctTunnelPacketsLost, + 87 => AvpType::NasPortId, + 88 => AvpType::FramedInterfaceId, + 97 => AvpType::FramedIpv6Prefix, + 98 => AvpType::LoginIpv6Host, + 99 => AvpType::FramedIpv6Route, + 100 => AvpType::FramedIpv6Pool, + 101 => AvpType::ErrorCause, + 102 => AvpType::EapMessage, + 103 => AvpType::MessageAuthenticator, + 104 => AvpType::TunnelPassword, + 105 => AvpType::ArapPassword, + 106 => AvpType::ArapFeatures, + 107 => AvpType::ArapZoneAccess, + 108 => AvpType::ArapSecurity, + 109 => AvpType::ArapSecurityData, + 110 => AvpType::PasswordRetry, + 111 => AvpType::Prompt, + 112 => AvpType::ConnectInfo, + 113 => AvpType::ConfigurationToken, + 114 => AvpType::EapMessage2, + 115 => AvpType::MessageAuthenticator2, + 116 => AvpType::ArapChallenge, + other => AvpType::Other(other), + } + } +} + +fn avp_decode(input: &[u8]) -> IResult<&[u8], AvpHeader> { + let (input, (mandatory, hidden, reserved, length)): (&[u8], (u8, u8, u8, u16)) = + bits::bits::<_, _, Error<_>, _, _>(sequence::tuple(( + bits::streaming::take(1u8), + bits::streaming::take(1u8), + bits::streaming::take(4u8), + bits::streaming::take(10u8), + )))(input)?; + let (input, vendor_id) = number::streaming::be_u16(input)?; + let (input, attribute_type) = number::streaming::be_u16(input)?; + /* + * Length: Encodes the number of octets (including the Overall Length + * and bitmask fields) contained in this AVP. The Length may be + * calculated as 6 + the length of the Attribute Value field in octets. + * The field itself is 10 bits, permitting a maximum of 1023 octets of + * data in a single AVP. The minimum Length of an AVP is 6. If the + * length is 6, then the Attribute Value field is absent. + */ + let (input, attribute_value) = bytes::streaming::take(length - 6)(input)?; + + Ok(( + input, + AvpHeader { + mandatory: mandatory != 0, + hidden: hidden != 0, + reserved, + length, + vendor_id, + attribute_type: attribute_type.into(), + attribute_value: attribute_value.to_vec(), + }, + )) +} + +impl Decode for L2tpHeader { + type Iterm = L2tpHeader; + fn decode(input: &[u8]) -> IResult<&[u8], L2tpHeader> { + let (input, (flags, ver)) = bits::bits::<_, _, Error<_>, _, _>(sequence::tuple(( + bits::streaming::take(12u8), + bits::streaming::take(4u8), + )))(input)?; + match ver { + 2 => (), + _ => { + return Err(nom::Err::Error(Error::new( + input, + nom::error::ErrorKind::TagBits, + ))) + } + } + let flag_type = ((flags & 0x800) != 0).into(); + let flag_length = (flags & 0x400) != 0; + let flag_sequence = (flags & 0x80) != 0; + let flag_offset = (flags & 0x20) != 0; + let flag_priority = (flags & 0x10) != 0; + let (input, length) = match flag_length { + true => number::streaming::be_u16(input).map(|(i, l)| (i, Some(l)))?, + false => (input, None), + }; + let (input, tunnel_id) = number::streaming::be_u16(input)?; + let (input, session_id) = number::streaming::be_u16(input)?; + let (input, ns) = match flag_sequence { + true => number::streaming::be_u16(input).map(|(i, l)| (i, Some(l)))?, + false => (input, None), + }; + let (input, nr) = match flag_sequence { + true => number::streaming::be_u16(input).map(|(i, l)| (i, Some(l)))?, + false => (input, None), + }; + let (input, offset_size) = match flag_offset { + true => number::streaming::be_u16(input).map(|(i, l)| (i, Some(l)))?, + false => (input, None), + }; + let (input, offset_pad) = match flag_offset { + true => bytes::streaming::take(offset_size.unwrap())(input) + .map(|(i, l)| (i, Some(l.to_vec())))?, + false => (input, None), + }; + let (input, avps) = match flag_type { + L2tpType::Data => (input, None), + L2tpType::Control => { + let mut avps = Vec::new(); + let mut input = input; + loop { + let (i, avp) = avp_decode(input)?; + avps.push(avp); + input = i; + if input.len() == 0 { + break; + } + } + (input, Some(avps)) + } + }; + + Ok(( + input, + L2tpHeader { + flags, + flag_type, + flag_length, + flag_sequence, + flag_offset, + flag_priority, + ver, + length, + tunnel_id, + session_id, + ns, + nr, + offset_size, + offset_pad, + avps, + }, + )) + } +} + +/****************************************************************************** + * TEST + ******************************************************************************/ + +#[cfg(test)] +mod tests { + use super::AvpHeader; + use super::AvpType; + use super::L2tpHeader; + use super::L2tpType; + use crate::protocol::codec::Decode; + use std::vec; + const EMPTY_SLICE: &'static [u8] = &[]; + const LAST_SLICE: &'static [u8] = &[0xff]; + + #[test] + // Data Message with Length Bit + fn l2tp_header_decode1() { + /* + * Layer 2 Tunneling Protocol + * Flags: 0x4002, Type: Data Message, Length Bit + * 0... .... .... .... = Type: Data Message (0) + * .1.. .... .... .... = Length Bit: Length field is present + * .... 0... .... .... = Sequence Bit: Ns and Nr fields are not present + * .... ..0. .... .... = Offset bit: Offset size field is not present + * .... ...0 .... .... = Priority: No priority + * .... .... .... 0010 = Version: 2 + * Length: 78 + * Tunnel ID: 28998 + * Session ID: 2 + */ + + let bytes = [ + 0x40, 0x02, 0x00, 0x4e, 0x71, 0x46, 0x00, 0x02, 0xff, /* Payload */ + ]; + + let expectation = L2tpHeader { + flags: 0x4002 >> 4, + flag_type: L2tpType::Data, + flag_length: true, + flag_sequence: false, + flag_offset: false, + flag_priority: false, + ver: 2, + length: Some(78), + tunnel_id: 28998, + session_id: 2, + ns: None, + nr: None, + offset_size: None, + offset_pad: None, + avps: None, + }; + + assert_eq!(L2tpHeader::decode(&bytes), Ok((LAST_SLICE, expectation))); + + // example + let result = L2tpHeader::decode(&bytes); + if let Ok((payload, header)) = result { + println!("return: {:?}, payload: {}", header, payload.len()); + } else { + println!("return: Incomplete data"); + } + + // assert_eq!(1, 0); + } + + #[test] + // Data Message without Length Bit + fn l2tp_header_decode2() { + /* + * Layer 2 Tunneling Protocol + * Flags: 0x0002, Type: Data Message + * 0... .... .... .... = Type: Data Message (0) + * .0.. .... .... .... = Length Bit: Length field is not present + * .... 0... .... .... = Sequence Bit: Ns and Nr fields are not present + * .... ..0. .... .... = Offset bit: Offset size field is not present + * .... ...0 .... .... = Priority: No priority + * .... .... .... 0010 = Version: 2 + * Tunnel ID: 13 + * Session ID: 1 + */ + + let bytes = [0x00, 0x02, 0x00, 0x0d, 0x00, 0x01, 0xff /* Payload */]; + + let expectation = L2tpHeader { + flags: 0x0002 >> 4, + flag_type: L2tpType::Data, + flag_length: false, + flag_sequence: false, + flag_offset: false, + flag_priority: false, + ver: 2, + length: None, + tunnel_id: 13, + session_id: 1, + ns: None, + nr: None, + offset_size: None, + offset_pad: None, + avps: None, + }; + + assert_eq!(L2tpHeader::decode(&bytes), Ok((LAST_SLICE, expectation))); + + // example + let result = L2tpHeader::decode(&bytes); + if let Ok((payload, header)) = result { + println!("return: {:?}, payload: {}", header, payload.len()); + } else { + println!("return: Incomplete data"); + } + + // assert_eq!(1, 0); + } + + #[test] + // Data Message with Offset Bit + fn l2tp_header_decode3() { + /* + * Layer 2 Tunneling Protocol + * Flags: 0x0202, Type: Data Message, Offset bit + * 0... .... .... .... = Type: Data Message (0) + * .0.. .... .... .... = Length Bit: Length field is not present + * .... 0... .... .... = Sequence Bit: Ns and Nr fields are not present + * .... ..1. .... .... = Offset bit: Offset Size field is present + * .... ...0 .... .... = Priority: No priority + * .... .... .... 0010 = Version: 2 + * Tunnel ID: 1 + * Session ID: 1 + * Offset: 0 + */ + + let bytes = [ + 0x02, 0x02, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0xff, /* Payload */ + ]; + + let expectation = L2tpHeader { + flags: 0x0202 >> 4, + flag_type: L2tpType::Data, + flag_length: false, + flag_sequence: false, + flag_offset: true, + flag_priority: false, + ver: 2, + length: None, + tunnel_id: 1, + session_id: 1, + ns: None, + nr: None, + offset_size: Some(0), + offset_pad: Some(vec![]), + avps: None, + }; + + assert_eq!(L2tpHeader::decode(&bytes), Ok((LAST_SLICE, expectation))); + + // example + let result = L2tpHeader::decode(&bytes); + if let Ok((payload, header)) = result { + println!("return: {:?}, payload: {}", header, payload.len()); + } else { + println!("return: Incomplete data"); + } + + // assert_eq!(1, 0); + } + + #[test] + // Control Message + fn l2tp_header_decode4() { + /* + * Layer 2 Tunneling Protocol + * Flags: 0xc802, Type: Control Message, Length Bit, Sequence Bit + * 1... .... .... .... = Type: Control Message (1) + * .1.. .... .... .... = Length Bit: Length field is present + * .... 1... .... .... = Sequence Bit: Ns and Nr fields are present + * .... ..0. .... .... = Offset bit: Offset size field is not present + * .... ...0 .... .... = Priority: No priority + * .... .... .... 0010 = Version: 2 + * Length: 105 + * Tunnel ID: 0 + * Session ID: 0 + * Ns: 0 + * Nr: 0 + * Control Message AVP + * 1... .... .... .... = Mandatory: True + * .0.. .... .... .... = Hidden: False + * .... ..00 0000 1000 = Length: 8 + * Vendor ID: Reserved (0) + * AVP Type: Control Message (0) + * Message Type: Start_Control_Request (1) + * Protocol Version AVP + * 1... .... .... .... = Mandatory: True + * .0.. .... .... .... = Hidden: False + * .... ..00 0000 1000 = Length: 8 + * Vendor ID: Reserved (0) + * AVP Type: Protocol Version (2) + * Version: 1 + * Revision: 0 + * Framing Capabilities AVP + * 1... .... .... .... = Mandatory: True + * .0.. .... .... .... = Hidden: False + * .... ..00 0000 1010 = Length: 10 + * Vendor ID: Reserved (0) + * AVP Type: Framing Capabilities (3) + * .... .... .... .... .... .... .... ..0. = Async Framing Supported: False + * .... .... .... .... .... .... .... ...1 = Sync Framing Supported: True + * Bearer Capabilities AVP + * 1... .... .... .... = Mandatory: True + * .0.. .... .... .... = Hidden: False + * .... ..00 0000 1010 = Length: 10 + * Vendor ID: Reserved (0) + * AVP Type: Bearer Capabilities (4) + * .... .... .... .... .... .... .... ..0. = Analog Access Supported: False + * .... .... .... .... .... .... .... ...0 = Digital Access Supported: False + * Firmware Revision AVP + * 0... .... .... .... = Mandatory: False + * .0.. .... .... .... = Hidden: False + * .... ..00 0000 1000 = Length: 8 + * Vendor ID: Reserved (0) + * AVP Type: Firmware Revision (6) + * Firmware Revision: 1537 (0x0601) + * Host Name AVP + * 1... .... .... .... = Mandatory: True + * .0.. .... .... .... = Hidden: False + * .... ..00 0001 0010 = Length: 18 + * Vendor ID: Reserved (0) + * AVP Type: Host Name (7) + * Host Name: IIE-SM-THINK + * Vendor Name AVP + * 0... .... .... .... = Mandatory: False + * .0.. .... .... .... = Hidden: False + * .... ..00 0000 1111 = Length: 15 + * Vendor ID: Reserved (0) + * AVP Type: Vendor Name (8) + * Vendor Name: Microsoft + * Assigned Tunnel ID AVP + * 1... .... .... .... = Mandatory: True + * .0.. .... .... .... = Hidden: False + * .... ..00 0000 1000 = Length: 8 + * Vendor ID: Reserved (0) + * AVP Type: Assigned Tunnel ID (9) + * Assigned Tunnel ID: 1 + * Receive Window Size AVP + * 1... .... .... .... = Mandatory: True + * .0.. .... .... .... = Hidden: False + * .... ..00 0000 1000 = Length: 8 + * Vendor ID: Reserved (0) + * AVP Type: Receive Window Size (10) + * Receive Window Size: 8 + */ + + let bytes = [ + 0xc8, 0x02, 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x08, 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, + 0x80, 0x0a, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x80, 0x0a, 0x00, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x06, 0x06, 0x01, + 0x80, 0x12, 0x00, 0x00, 0x00, 0x07, 0x49, 0x49, 0x45, 0x2d, 0x53, 0x4d, 0x2d, 0x54, + 0x48, 0x49, 0x4e, 0x4b, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x08, 0x4d, 0x69, 0x63, 0x72, + 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x80, 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x01, 0x80, + 0x08, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x08, + ]; + + let expectation = L2tpHeader { + flags: 0xc802 >> 4, + flag_type: L2tpType::Control, + flag_length: true, + flag_sequence: true, + flag_offset: false, + flag_priority: false, + ver: 2, + length: Some(105), + tunnel_id: 0, + session_id: 0, + ns: Some(0), + nr: Some(0), + offset_size: None, + offset_pad: None, + avps: Some(vec![ + AvpHeader { + mandatory: true, + hidden: false, + reserved: 0, + length: 8, + vendor_id: 0, + attribute_type: AvpType::Message, + attribute_value: vec![0x00, 0x01], + }, + AvpHeader { + mandatory: true, + hidden: false, + reserved: 0, + length: 8, + vendor_id: 0, + attribute_type: AvpType::ProtocolVersion, + attribute_value: vec![0x01, 0x00], + }, + AvpHeader { + mandatory: true, + hidden: false, + reserved: 0, + length: 10, + vendor_id: 0, + attribute_type: AvpType::FramingCapabilites, + attribute_value: vec![0x00, 0x00, 0x00, 0x01], + }, + AvpHeader { + mandatory: true, + hidden: false, + reserved: 0, + length: 10, + vendor_id: 0, + attribute_type: AvpType::BearerCapabilites, + attribute_value: vec![0x00, 0x00, 0x00, 0x00], + }, + AvpHeader { + mandatory: false, + hidden: false, + reserved: 0, + length: 8, + vendor_id: 0, + attribute_type: AvpType::FirmwareRevision, + attribute_value: vec![0x06, 0x01], + }, + AvpHeader { + mandatory: true, + hidden: false, + reserved: 0, + length: 18, + vendor_id: 0, + attribute_type: AvpType::HostName, + attribute_value: vec![ + 0x49, 0x49, 0x45, 0x2d, 0x53, 0x4d, 0x2d, 0x54, 0x48, 0x49, 0x4e, 0x4b, + ], + }, + AvpHeader { + mandatory: false, + hidden: false, + reserved: 0, + length: 15, + vendor_id: 0, + attribute_type: AvpType::VendorName, + attribute_value: vec![0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74], + }, + AvpHeader { + mandatory: true, + hidden: false, + reserved: 0, + length: 8, + vendor_id: 0, + attribute_type: AvpType::AssignedTunnelId, + attribute_value: vec![0x00, 0x01], + }, + AvpHeader { + mandatory: true, + hidden: false, + reserved: 0, + length: 8, + vendor_id: 0, + attribute_type: AvpType::ReceiveWindowSize, + attribute_value: vec![0x00, 0x08], + }, + ]), + }; + + assert_eq!(L2tpHeader::decode(&bytes), Ok((EMPTY_SLICE, expectation))); + + // example + let result = L2tpHeader::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/mod.rs b/src/protocol/mod.rs index 82a35da..7c8ade2 100644 --- a/src/protocol/mod.rs +++ b/src/protocol/mod.rs @@ -11,4 +11,5 @@ pub mod vlan; pub mod icmp; pub mod icmpv6; pub mod mpls; -pub mod gtpv1;
\ No newline at end of file +pub mod gtpv1; +pub mod l2tp;
\ No newline at end of file |
