diff options
author | HimbeerserverDE <himbeerserverde@gmail.com> | 2023-07-26 19:16:07 +0200 |
---|---|---|
committer | HimbeerserverDE <himbeerserverde@gmail.com> | 2023-07-26 19:16:07 +0200 |
commit | 35155e0f63e1a4269a1c2355c94f8d255f07220c (patch) | |
tree | bb8b9a2cbd9718d8e81828748825d74af0caeb96 /src | |
parent | 3bd9f255b44cc142286920ba4550310cf2a786fe (diff) |
implement IPCP
Diffstat (limited to 'src')
-rw-r--r-- | src/error.rs | 8 | ||||
-rw-r--r-- | src/ipcp.rs | 422 | ||||
-rw-r--r-- | src/lib.rs | 3 | ||||
-rw-r--r-- | src/ppp.rs | 110 | ||||
-rw-r--r-- | src/types.rs | 25 |
5 files changed, 567 insertions, 1 deletions
diff --git a/src/error.rs b/src/error.rs index 034a4bc..6858d3d 100644 --- a/src/error.rs +++ b/src/error.rs @@ -33,6 +33,14 @@ pub enum Error { #[error("invalid chap algorithm: {0}")] InvalidChapAlgorithm(u8), + #[error("invalid ipcp code: {0}")] + InvalidIpcpCode(u8), + #[error("invalid ipcp option type: {0}")] + InvalidIpcpOptionType(u8), + + #[error("invalid ip compression protocol: {0}")] + InvalidIpCompressionProtocol(u16), + #[error("conversion from utf8: {0}")] FromUtf8(#[from] string::FromUtf8Error), #[error("io: {0}")] diff --git a/src/ipcp.rs b/src/ipcp.rs new file mode 100644 index 0000000..4d00c5c --- /dev/null +++ b/src/ipcp.rs @@ -0,0 +1,422 @@ +use crate::{Deserialize, Error, IpCompressionProtocol, Ipv4Addr, Result, Serialize}; + +use std::io::{Read, Write}; + +use ppproperly_macros::{Deserialize, Serialize}; + +const IPCP_CONFIGURE_REQUEST: u8 = 1; +const IPCP_CONFIGURE_ACK: u8 = 2; +const IPCP_CONFIGURE_NAK: u8 = 3; +const IPCP_CONFIGURE_REJECT: u8 = 4; +const IPCP_TERMINATE_REQUEST: u8 = 5; +const IPCP_TERMINATE_ACK: u8 = 6; +const IPCP_CODE_REJECT: u8 = 7; + +// The IP-Addresses option is deprecated and won't be implemented by me. +// Contributions are welcome. +const OPT_IP_COMPRESSION_PROTOCOL: u8 = 2; +const OPT_IP_ADDRESS: u8 = 3; + +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum IpcpOpt { + IpCompressionProtocol(IpCompressionProtocol), + IpAddr(Ipv4Addr), +} + +impl Serialize for IpcpOpt { + fn serialize<W: Write>(&self, w: &mut W) -> Result<()> { + match self { + Self::IpCompressionProtocol(payload) => payload.serialize(w), + Self::IpAddr(payload) => payload.serialize(w), + } + } +} + +impl IpcpOpt { + fn discriminant(&self) -> u8 { + match self { + Self::IpCompressionProtocol(_) => OPT_IP_COMPRESSION_PROTOCOL, + Self::IpAddr(_) => OPT_IP_ADDRESS, + } + } + + fn len(&self) -> u8 { + match self { + Self::IpCompressionProtocol(payload) => payload.len(), + Self::IpAddr(_) => 4, + } + } + + fn deserialize_with_discriminant<R: Read>( + &mut self, + r: &mut R, + discriminant: &u8, + ) -> Result<()> { + match *discriminant { + OPT_IP_COMPRESSION_PROTOCOL => { + let mut tmp = IpCompressionProtocol::default(); + + tmp.deserialize(r)?; + *self = Self::IpCompressionProtocol(tmp); + } + OPT_IP_ADDRESS => { + let mut tmp = Ipv4Addr::default(); + + tmp.deserialize(r)?; + *self = Self::IpAddr(tmp); + } + _ => return Err(Error::InvalidIpcpOptionType(*discriminant)), + } + + Ok(()) + } +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] +pub struct IpcpOption { + #[ppproperly(discriminant_for(field = "value", data_type = "u8"))] + #[ppproperly(len_for(field = "value", offset = 2, data_type = "u8"))] + value: IpcpOpt, +} + +impl IpcpOption { + pub fn len(&self) -> u8 { + 2 + self.value.len() + } + + pub fn is_empty(&self) -> bool { + self.len() == 2 + } +} + +impl From<IpcpOpt> for IpcpOption { + fn from(value: IpcpOpt) -> Self { + Self { value } + } +} + +impl Serialize for [IpcpOption] { + fn serialize<W: Write>(&self, w: &mut W) -> Result<()> { + for option in self { + option.serialize(w)?; + } + + Ok(()) + } +} + +impl Deserialize for Vec<IpcpOption> { + fn deserialize<R: Read>(&mut self, r: &mut R) -> Result<()> { + while r.bytes().size_hint().0 > 0 { + let mut tmp = IpcpOption::from(IpcpOpt::IpAddr(Ipv4Addr::default())); + + tmp.deserialize(r)?; + self.push(tmp); + } + + Ok(()) + } +} + +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum IpcpData { + ConfigureRequest(IpcpConfigureRequest), + ConfigureAck(IpcpConfigureAck), + ConfigureNak(IpcpConfigureNak), + ConfigureReject(IpcpConfigureReject), + TerminateRequest(IpcpTerminateRequest), + TerminateAck(IpcpTerminateAck), + CodeReject(IpcpCodeReject), +} + +impl Default for IpcpData { + fn default() -> Self { + Self::ConfigureRequest(IpcpConfigureRequest::default()) + } +} + +impl Serialize for IpcpData { + fn serialize<W: Write>(&self, w: &mut W) -> Result<()> { + match self { + Self::ConfigureRequest(payload) => payload.serialize(w), + Self::ConfigureAck(payload) => payload.serialize(w), + Self::ConfigureNak(payload) => payload.serialize(w), + Self::ConfigureReject(payload) => payload.serialize(w), + Self::TerminateRequest(payload) => payload.serialize(w), + Self::TerminateAck(payload) => payload.serialize(w), + Self::CodeReject(payload) => payload.serialize(w), + } + } +} + +impl IpcpData { + fn discriminant(&self) -> u8 { + match self { + Self::ConfigureRequest(_) => IPCP_CONFIGURE_REQUEST, + Self::ConfigureAck(_) => IPCP_CONFIGURE_ACK, + Self::ConfigureNak(_) => IPCP_CONFIGURE_NAK, + Self::ConfigureReject(_) => IPCP_CONFIGURE_REJECT, + Self::TerminateRequest(_) => IPCP_TERMINATE_REQUEST, + Self::TerminateAck(_) => IPCP_TERMINATE_ACK, + Self::CodeReject(_) => IPCP_CODE_REJECT, + } + } + + fn len(&self) -> u16 { + match self { + Self::ConfigureRequest(payload) => payload.len(), + Self::ConfigureAck(payload) => payload.len(), + Self::ConfigureNak(payload) => payload.len(), + Self::ConfigureReject(payload) => payload.len(), + Self::TerminateRequest(payload) => payload.len(), + Self::TerminateAck(payload) => payload.len(), + Self::CodeReject(payload) => payload.len(), + } + } + + fn deserialize_with_discriminant<R: Read>( + &mut self, + r: &mut R, + discriminant: &u8, + ) -> Result<()> { + match *discriminant { + IPCP_CONFIGURE_REQUEST => { + let mut tmp = IpcpConfigureRequest::default(); + + tmp.deserialize(r)?; + *self = Self::ConfigureRequest(tmp); + } + IPCP_CONFIGURE_ACK => { + let mut tmp = IpcpConfigureAck::default(); + + tmp.deserialize(r)?; + *self = Self::ConfigureAck(tmp); + } + IPCP_CONFIGURE_NAK => { + let mut tmp = IpcpConfigureNak::default(); + + tmp.deserialize(r)?; + *self = Self::ConfigureNak(tmp); + } + IPCP_CONFIGURE_REJECT => { + let mut tmp = IpcpConfigureReject::default(); + + tmp.deserialize(r)?; + *self = Self::ConfigureReject(tmp); + } + IPCP_TERMINATE_REQUEST => { + let mut tmp = IpcpTerminateRequest::default(); + + tmp.deserialize(r)?; + *self = Self::TerminateRequest(tmp); + } + IPCP_TERMINATE_ACK => { + let mut tmp = IpcpTerminateAck::default(); + + tmp.deserialize(r)?; + *self = Self::TerminateAck(tmp); + } + IPCP_CODE_REJECT => { + let mut tmp = IpcpCodeReject::default(); + + tmp.deserialize(r)?; + *self = Self::CodeReject(tmp); + } + _ => return Err(Error::InvalidIpcpCode(*discriminant)), + } + + Ok(()) + } +} + +#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] +pub struct IpcpPkt { + #[ppproperly(discriminant_for(field = "data", data_type = "u8"))] + identifier: u8, + #[ppproperly(len_for(field = "data", offset = 4, data_type = "u16"))] + data: IpcpData, +} + +impl IpcpPkt { + pub fn new_configure_request(identifier: u8, options: Vec<IpcpOption>) -> Self { + Self { + identifier, + data: IpcpData::ConfigureRequest(IpcpConfigureRequest { options }), + } + } + + pub fn new_configure_ack(identifier: u8, options: Vec<IpcpOption>) -> Self { + Self { + identifier, + data: IpcpData::ConfigureAck(IpcpConfigureAck { options }), + } + } + + pub fn new_configure_nak(identifier: u8, options: Vec<IpcpOption>) -> Self { + Self { + identifier, + data: IpcpData::ConfigureNak(IpcpConfigureNak { options }), + } + } + + pub fn new_configure_reject(identifier: u8, options: Vec<IpcpOption>) -> Self { + Self { + identifier, + data: IpcpData::ConfigureReject(IpcpConfigureReject { options }), + } + } + + pub fn new_terminate_request(identifier: u8, data: Vec<u8>) -> Self { + Self { + identifier, + data: IpcpData::TerminateRequest(IpcpTerminateRequest { data }), + } + } + + pub fn new_terminate_ack(identifier: u8, data: Vec<u8>) -> Self { + Self { + identifier, + data: IpcpData::TerminateAck(IpcpTerminateAck { data }), + } + } + + pub fn new_code_reject(identifier: u8, pkt: Vec<u8>) -> Self { + Self { + identifier, + data: IpcpData::CodeReject(IpcpCodeReject { pkt }), + } + } + + pub fn len(&self) -> u16 { + 4 + self.data.len() + } + + pub fn is_empty(&self) -> bool { + self.len() == 4 + } +} + +#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] +pub struct IpcpConfigureRequest { + options: Vec<IpcpOption>, +} + +impl IpcpConfigureRequest { + pub fn len(&self) -> u16 { + self.options + .iter() + .map(|option| option.len()) + .reduce(|acc, n| acc + n) + .unwrap_or(0) + .into() + } + + pub fn is_empty(&self) -> bool { + self.options.is_empty() + } +} + +#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] +pub struct IpcpConfigureAck { + options: Vec<IpcpOption>, +} + +impl IpcpConfigureAck { + pub fn len(&self) -> u16 { + self.options + .iter() + .map(|option| option.len()) + .reduce(|acc, n| acc + n) + .unwrap_or(0) + .into() + } + + pub fn is_empty(&self) -> bool { + self.options.is_empty() + } +} + +#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] +pub struct IpcpConfigureNak { + options: Vec<IpcpOption>, +} + +impl IpcpConfigureNak { + pub fn len(&self) -> u16 { + self.options + .iter() + .map(|option| option.len()) + .reduce(|acc, n| acc + n) + .unwrap_or(0) + .into() + } + + pub fn is_empty(&self) -> bool { + self.options.is_empty() + } +} + +#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] +pub struct IpcpConfigureReject { + options: Vec<IpcpOption>, +} + +impl IpcpConfigureReject { + pub fn len(&self) -> u16 { + self.options + .iter() + .map(|option| option.len()) + .reduce(|acc, n| acc + n) + .unwrap_or(0) + .into() + } + + pub fn is_empty(&self) -> bool { + self.options.is_empty() + } +} + +#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] +pub struct IpcpTerminateRequest { + data: Vec<u8>, +} + +impl IpcpTerminateRequest { + pub fn len(&self) -> u16 { + self.data.len().try_into().unwrap() + } + + pub fn is_empty(&self) -> bool { + self.data.is_empty() + } +} + +#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] +pub struct IpcpTerminateAck { + data: Vec<u8>, +} + +impl IpcpTerminateAck { + pub fn len(&self) -> u16 { + self.data.len().try_into().unwrap() + } + + pub fn is_empty(&self) -> bool { + self.data.is_empty() + } +} + +#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] +pub struct IpcpCodeReject { + pkt: Vec<u8>, // Vec makes MRU truncating easier without overwriting (de)ser impls. +} + +impl IpcpCodeReject { + pub fn len(&self) -> u16 { + self.pkt.len().try_into().unwrap() + } + + pub fn is_empty(&self) -> bool { + self.pkt.is_empty() + } +} @@ -7,6 +7,9 @@ pub use de::*; pub mod error; pub use error::*; +pub mod ipcp; +pub use ipcp::*; + pub mod lcp; pub use lcp::*; @@ -1,4 +1,4 @@ -use crate::{ChapPkt, Deserialize, Error, LcpPkt, PapPkt, Result, Serialize}; +use crate::{ChapPkt, Deserialize, Error, IpcpPkt, LcpPkt, PapPkt, Result, Serialize}; use std::io::{Read, Write}; @@ -7,7 +7,10 @@ use ppproperly_macros::{Deserialize, Serialize}; const LCP: u16 = 0xc021; const PAP: u16 = 0xc023; const CHAP: u16 = 0xc223; +const IPCP: u16 = 0x8021; + const LQR: u16 = 0xc025; +const VAN_JACOBSON: u16 = 0x002d; const CHAP_MD5: u8 = 5; @@ -204,10 +207,100 @@ impl From<QualityProto> for QualityProtocol { } #[derive(Clone, Debug, Eq, PartialEq)] +pub enum IpCompressionProto { + VanJacobsonTcpIp(VanJacobsonConfig), +} + +impl Default for IpCompressionProto { + fn default() -> Self { + Self::VanJacobsonTcpIp(VanJacobsonConfig::default()) + } +} + +impl Serialize for IpCompressionProto { + fn serialize<W: Write>(&self, w: &mut W) -> Result<()> { + match self { + Self::VanJacobsonTcpIp(payload) => payload.serialize(w), + } + } +} + +impl IpCompressionProto { + fn discriminant(&self) -> u16 { + match self { + Self::VanJacobsonTcpIp(_) => VAN_JACOBSON, + } + } + + fn len(&self) -> u8 { + match self { + Self::VanJacobsonTcpIp(payload) => payload.len(), + } + } + + fn deserialize_with_discriminant<R: Read>( + &mut self, + r: &mut R, + discriminant: &u16, + ) -> Result<()> { + match *discriminant { + VAN_JACOBSON => { + let mut tmp = VanJacobsonConfig::default(); + + tmp.deserialize(r)?; + *self = Self::VanJacobsonTcpIp(tmp); + } + _ => return Err(Error::InvalidIpCompressionProtocol(*discriminant)), + } + + Ok(()) + } +} + +#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] +pub struct IpCompressionProtocol { + #[ppproperly(discriminant_for(field = "protocol", data_type = "u16"))] + protocol: IpCompressionProto, +} + +impl IpCompressionProtocol { + pub fn len(&self) -> u8 { + 2 + self.protocol.len() + } + + pub fn is_empty(&self) -> bool { + self.len() == 2 + } +} + +impl From<IpCompressionProto> for IpCompressionProtocol { + fn from(protocol: IpCompressionProto) -> Self { + Self { protocol } + } +} + +#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] +pub struct VanJacobsonConfig { + max_slot_id: u8, + comp_slot_id: u8, +} + +impl VanJacobsonConfig { + pub fn len(&self) -> u8 { + 2 + } + + pub fn is_empty(&self) -> bool { + true + } +} + +#[derive(Clone, Debug, Eq, PartialEq)] pub enum PppData { Lcp(LcpPkt), Pap(PapPkt), Chap(ChapPkt), + Ipcp(IpcpPkt), } impl Default for PppData { @@ -222,6 +315,7 @@ impl Serialize for PppData { Self::Lcp(payload) => payload.serialize(w), Self::Pap(payload) => payload.serialize(w), Self::Chap(payload) => payload.serialize(w), + Self::Ipcp(payload) => payload.serialize(w), } } } @@ -232,6 +326,7 @@ impl PppData { Self::Lcp(_) => LCP, Self::Pap(_) => PAP, Self::Chap(_) => CHAP, + Self::Ipcp(_) => IPCP, } } @@ -240,6 +335,7 @@ impl PppData { Self::Lcp(payload) => payload.len(), Self::Pap(payload) => payload.len(), Self::Chap(payload) => payload.len(), + Self::Ipcp(payload) => payload.len(), } } @@ -267,6 +363,12 @@ impl PppData { tmp.deserialize(r)?; *self = Self::Chap(tmp); } + IPCP => { + let mut tmp = IpcpPkt::default(); + + tmp.deserialize(r)?; + *self = Self::Ipcp(tmp); + } _ => return Err(Error::InvalidPppProtocol(*discriminant)), } @@ -299,6 +401,12 @@ impl PppPkt { } } + pub fn new_ipcp(ipcp: IpcpPkt) -> Self { + Self { + data: PppData::Ipcp(ipcp), + } + } + pub fn len(&self) -> u16 { 2 + self.data.len() } diff --git a/src/types.rs b/src/types.rs index 7d191d7..b0cf1ca 100644 --- a/src/types.rs +++ b/src/types.rs @@ -33,3 +33,28 @@ impl Deserialize for VerType { self.0.deserialize(r) } } + +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct Ipv4Addr(pub std::net::Ipv4Addr); + +impl Default for Ipv4Addr { + fn default() -> Self { + Self(std::net::Ipv4Addr::UNSPECIFIED) + } +} + +impl Serialize for Ipv4Addr { + fn serialize<W: Write>(&self, w: &mut W) -> Result<()> { + u32::from(self.0).serialize(w) + } +} + +impl Deserialize for Ipv4Addr { + fn deserialize<R: Read>(&mut self, r: &mut R) -> Result<()> { + let mut tmp = u32::default(); + tmp.deserialize(r)?; + + self.0 = tmp.into(); + Ok(()) + } +} |