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(Debug, PartialEq)] pub enum L2TPAVPType { 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)] pub enum L2TPType { Data, Control, } #[derive(Debug, PartialEq)] pub struct L2TPAVPHeader { pub mandatory: bool, pub hidden: bool, pub reserved: u8, pub length: u16, pub vendor_id: u16, pub attribute_type: L2TPAVPType, pub attribute_value: Vec, } #[derive(Debug, PartialEq)] 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, pub tunnel_id: u16, pub session_id: u16, pub ns: Option, pub nr: Option, pub offset_size: Option, pub offset_pad: Option>, pub avps: Option>, } /****************************************************************************** * API ******************************************************************************/ impl From for L2TPType { fn from(raw: bool) -> Self { match raw { false => L2TPType::Data, true => L2TPType::Control, } } } impl From for L2TPAVPType { fn from(raw: u16) -> Self { match raw { 0 => L2TPAVPType::Message, 1 => L2TPAVPType::Result, 2 => L2TPAVPType::ProtocolVersion, 3 => L2TPAVPType::FramingCapabilites, 4 => L2TPAVPType::BearerCapabilites, 5 => L2TPAVPType::TieBreaker, 6 => L2TPAVPType::FirmwareRevision, 7 => L2TPAVPType::HostName, 8 => L2TPAVPType::VendorName, 9 => L2TPAVPType::AssignedTunnelId, 10 => L2TPAVPType::ReceiveWindowSize, 11 => L2TPAVPType::Challenge, 12 => L2TPAVPType::CauseCode, 13 => L2TPAVPType::ChallengeResponse, 14 => L2TPAVPType::AssignedSessionId, 15 => L2TPAVPType::CallSerialNumber, 16 => L2TPAVPType::MinimumBps, 17 => L2TPAVPType::MaximumBps, 18 => L2TPAVPType::BearerType, 19 => L2TPAVPType::FramingType, 20 => L2TPAVPType::CalledNumber, 21 => L2TPAVPType::CallingNumber, 22 => L2TPAVPType::SubAddress, 23 => L2TPAVPType::TxConnectSpeed, 24 => L2TPAVPType::PhysicalChannelId, 25 => L2TPAVPType::InitialReceivedLcpConfReq, 26 => L2TPAVPType::LastSentLcpConfReq, 27 => L2TPAVPType::LastReceivedLcpConfReq, 28 => L2TPAVPType::ProxyAuthenType, 29 => L2TPAVPType::ProxyAuthenName, 30 => L2TPAVPType::ProxyAuthenChallenge, 31 => L2TPAVPType::ProxyAuthenID, 32 => L2TPAVPType::ProxyAuthenResponse, 33 => L2TPAVPType::CallErrors, 34 => L2TPAVPType::AcctAuthenType, 35 => L2TPAVPType::AcctAuthenName, 36 => L2TPAVPType::AcctAuthenChallenge, 37 => L2TPAVPType::AcctAuthenID, 38 => L2TPAVPType::AcctAuthenResponse, 39 => L2TPAVPType::ChallengeControl, 40 => L2TPAVPType::AcctEapType, 41 => L2TPAVPType::AcctEapId, 42 => L2TPAVPType::AcctEapResponse, 44 => L2TPAVPType::CalledStationId, 45 => L2TPAVPType::CallingStationId, 46 => L2TPAVPType::NasIdentifier, 47 => L2TPAVPType::ProxyState, 48 => L2TPAVPType::LoginLatService, 49 => L2TPAVPType::LoginLatNode, 50 => L2TPAVPType::LoginLatGroup, 51 => L2TPAVPType::FramedAppleTalkLink, 52 => L2TPAVPType::FramedAppleTalkNetwork, 53 => L2TPAVPType::FramedAppleTalkZone, 61 => L2TPAVPType::AcctStatusType, 62 => L2TPAVPType::AcctDelayTime, 63 => L2TPAVPType::AcctInputOctets, 64 => L2TPAVPType::AcctOutputOctets, 65 => L2TPAVPType::AcctSessionId, 66 => L2TPAVPType::AcctAuthentic, 67 => L2TPAVPType::AcctSessionTime, 68 => L2TPAVPType::AcctInputPackets, 69 => L2TPAVPType::AcctOutputPackets, 70 => L2TPAVPType::AcctTerminateCause, 71 => L2TPAVPType::AcctMultiSessionId, 72 => L2TPAVPType::AcctLinkCount, 73 => L2TPAVPType::AcctEventTimeStamp, 74 => L2TPAVPType::AcctTunnelConnection, 75 => L2TPAVPType::AcctTunnelPacketsLost, 87 => L2TPAVPType::NasPortId, 88 => L2TPAVPType::FramedInterfaceId, 97 => L2TPAVPType::FramedIpv6Prefix, 98 => L2TPAVPType::LoginIpv6Host, 99 => L2TPAVPType::FramedIpv6Route, 100 => L2TPAVPType::FramedIpv6Pool, 101 => L2TPAVPType::ErrorCause, 102 => L2TPAVPType::EapMessage, 103 => L2TPAVPType::MessageAuthenticator, 104 => L2TPAVPType::TunnelPassword, 105 => L2TPAVPType::ArapPassword, 106 => L2TPAVPType::ArapFeatures, 107 => L2TPAVPType::ArapZoneAccess, 108 => L2TPAVPType::ArapSecurity, 109 => L2TPAVPType::ArapSecurityData, 110 => L2TPAVPType::PasswordRetry, 111 => L2TPAVPType::Prompt, 112 => L2TPAVPType::ConnectInfo, 113 => L2TPAVPType::ConfigurationToken, 114 => L2TPAVPType::EapMessage2, 115 => L2TPAVPType::MessageAuthenticator2, 116 => L2TPAVPType::ArapChallenge, other => L2TPAVPType::Other(other), } } } fn avp_decode(input: &[u8]) -> IResult<&[u8], L2TPAVPHeader> { 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).map(|(i, l)| (i, l.into()))?; /* * 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. */ if length < 6 { return Err(nom::Err::Incomplete(nom::Needed::new((6 - length).into()))); } let (input, attribute_value) = bytes::streaming::take(length - 6)(input).map(|(i, l)| (i, l.to_vec()))?; Ok(( input, L2TPAVPHeader { mandatory: mandatory != 0, hidden: hidden != 0, reserved, length, vendor_id, attribute_type, attribute_value, }, )) } 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::L2TPAVPHeader; use super::L2TPAVPType; 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); match result { Ok((payload, header)) => { println!("OK: {:?}, payload: {}", header, payload.len()); } Err(e) => { println!("ERR: {:?}", e); } } // 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); match result { Ok((payload, header)) => { println!("OK: {:?}, payload: {}", header, payload.len()); } Err(e) => { println!("ERR: {:?}", e); } } // 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); match result { Ok((payload, header)) => { println!("OK: {:?}, payload: {}", header, payload.len()); } Err(e) => { println!("ERR: {:?}", e); } } // 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![ L2TPAVPHeader { mandatory: true, hidden: false, reserved: 0, length: 8, vendor_id: 0, attribute_type: L2TPAVPType::Message, attribute_value: vec![0x00, 0x01], }, L2TPAVPHeader { mandatory: true, hidden: false, reserved: 0, length: 8, vendor_id: 0, attribute_type: L2TPAVPType::ProtocolVersion, attribute_value: vec![0x01, 0x00], }, L2TPAVPHeader { mandatory: true, hidden: false, reserved: 0, length: 10, vendor_id: 0, attribute_type: L2TPAVPType::FramingCapabilites, attribute_value: vec![0x00, 0x00, 0x00, 0x01], }, L2TPAVPHeader { mandatory: true, hidden: false, reserved: 0, length: 10, vendor_id: 0, attribute_type: L2TPAVPType::BearerCapabilites, attribute_value: vec![0x00, 0x00, 0x00, 0x00], }, L2TPAVPHeader { mandatory: false, hidden: false, reserved: 0, length: 8, vendor_id: 0, attribute_type: L2TPAVPType::FirmwareRevision, attribute_value: vec![0x06, 0x01], }, L2TPAVPHeader { mandatory: true, hidden: false, reserved: 0, length: 18, vendor_id: 0, attribute_type: L2TPAVPType::HostName, attribute_value: vec![ 0x49, 0x49, 0x45, 0x2d, 0x53, 0x4d, 0x2d, 0x54, 0x48, 0x49, 0x4e, 0x4b, ], }, L2TPAVPHeader { mandatory: false, hidden: false, reserved: 0, length: 15, vendor_id: 0, attribute_type: L2TPAVPType::VendorName, attribute_value: vec![0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74], }, L2TPAVPHeader { mandatory: true, hidden: false, reserved: 0, length: 8, vendor_id: 0, attribute_type: L2TPAVPType::AssignedTunnelId, attribute_value: vec![0x00, 0x01], }, L2TPAVPHeader { mandatory: true, hidden: false, reserved: 0, length: 8, vendor_id: 0, attribute_type: L2TPAVPType::ReceiveWindowSize, attribute_value: vec![0x00, 0x08], }, ]), }; assert_eq!(L2TPHeader::decode(&bytes), Ok((EMPTY_SLICE, expectation))); // example let result = L2TPHeader::decode(&bytes); match result { Ok((payload, header)) => { println!("OK: {:?}, payload: {}", header, payload.len()); } Err(e) => { println!("ERR: {:?}", e); } } // assert_eq!(1, 0); } }