aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorHimbeerserverDE <himbeerserverde@gmail.com>2023-07-26 19:16:07 +0200
committerHimbeerserverDE <himbeerserverde@gmail.com>2023-07-26 19:16:07 +0200
commit35155e0f63e1a4269a1c2355c94f8d255f07220c (patch)
treebb8b9a2cbd9718d8e81828748825d74af0caeb96 /src
parent3bd9f255b44cc142286920ba4550310cf2a786fe (diff)
implement IPCP
Diffstat (limited to 'src')
-rw-r--r--src/error.rs8
-rw-r--r--src/ipcp.rs422
-rw-r--r--src/lib.rs3
-rw-r--r--src/ppp.rs110
-rw-r--r--src/types.rs25
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()
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
index 89c3d13..216bc12 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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::*;
diff --git a/src/ppp.rs b/src/ppp.rs
index 956ef25..68494ed 100644
--- a/src/ppp.rs
+++ b/src/ppp.rs
@@ -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(())
+ }
+}