summaryrefslogtreecommitdiff
path: root/src/protocol/l2tp.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/protocol/l2tp.rs')
-rw-r--r--src/protocol/l2tp.rs784
1 files changed, 784 insertions, 0 deletions
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);
+ }
+}