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, } #[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, 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 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. */ if length < 6 { return Err(nom::Err::Incomplete(nom::Needed::new((6 - length).into()))); } 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); } }