aboutsummaryrefslogtreecommitdiff
path: root/src/ipcp.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/ipcp.rs')
-rw-r--r--src/ipcp.rs422
1 files changed, 422 insertions, 0 deletions
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()
+ }
+}