diff options
author | HimbeerserverDE <himbeerserverde@gmail.com> | 2023-07-26 20:08:38 +0200 |
---|---|---|
committer | HimbeerserverDE <himbeerserverde@gmail.com> | 2023-07-26 20:08:38 +0200 |
commit | 83036fd7bdf75c49e94b448828a68d62c62d0d6a (patch) | |
tree | 49b3f3d946cccad333f029fe02b03ea0a9a10d00 /src | |
parent | 033e07d8878a6d408947a618f32b3f58dd85cc58 (diff) |
implement IPv6CP
Diffstat (limited to 'src')
-rw-r--r-- | src/error.rs | 5 | ||||
-rw-r--r-- | src/ipv6cp.rs | 409 | ||||
-rw-r--r-- | src/lib.rs | 3 |
3 files changed, 417 insertions, 0 deletions
diff --git a/src/error.rs b/src/error.rs index 6858d3d..9400e96 100644 --- a/src/error.rs +++ b/src/error.rs @@ -41,6 +41,11 @@ pub enum Error { #[error("invalid ip compression protocol: {0}")] InvalidIpCompressionProtocol(u16), + #[error("invalid ipv6cp code: {0}")] + InvalidIpv6cpCode(u8), + #[error("invalid ipv6cp option type: {0}")] + InvalidIpv6cpOptionType(u8), + #[error("conversion from utf8: {0}")] FromUtf8(#[from] string::FromUtf8Error), #[error("io: {0}")] diff --git a/src/ipv6cp.rs b/src/ipv6cp.rs new file mode 100644 index 0000000..69548a0 --- /dev/null +++ b/src/ipv6cp.rs @@ -0,0 +1,409 @@ +use crate::{Deserialize, Error, Result, Serialize}; + +use std::io::{Read, Write}; + +use ppproperly_macros::{Deserialize, Serialize}; + +const IPV6CP_CONFIGURE_REQUEST: u8 = 1; +const IPV6CP_CONFIGURE_ACK: u8 = 2; +const IPV6CP_CONFIGURE_NAK: u8 = 3; +const IPV6CP_CONFIGURE_REJECT: u8 = 4; +const IPV6CP_TERMINATE_REQUEST: u8 = 5; +const IPV6CP_TERMINATE_ACK: u8 = 6; +const IPV6CP_CODE_REJECT: u8 = 7; + +const OPT_INTERFACE_IDENTIFIER: u8 = 1; + +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum Ipv6cpOpt { + InterfaceId(u64), +} + +impl Serialize for Ipv6cpOpt { + fn serialize<W: Write>(&self, w: &mut W) -> Result<()> { + match self { + Self::InterfaceId(payload) => payload.serialize(w), + } + } +} + +impl Ipv6cpOpt { + fn discriminant(&self) -> u8 { + match self { + Self::InterfaceId(_) => OPT_INTERFACE_IDENTIFIER, + } + } + + fn len(&self) -> u8 { + match self { + Self::InterfaceId(_) => 8, + } + } + + fn deserialize_with_discriminant<R: Read>( + &mut self, + r: &mut R, + discriminant: &u8, + ) -> Result<()> { + match *discriminant { + OPT_INTERFACE_IDENTIFIER => { + let mut tmp = u64::default(); + + tmp.deserialize(r)?; + *self = Self::InterfaceId(tmp); + } + _ => return Err(Error::InvalidIpv6cpOptionType(*discriminant)), + } + + Ok(()) + } +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] +pub struct Ipv6cpOption { + #[ppproperly(discriminant_for(field = "value", data_type = "u8"))] + #[ppproperly(len_for(field = "value", offset = 2, data_type = "u8"))] + value: Ipv6cpOpt, +} + +impl Ipv6cpOption { + pub fn len(&self) -> u8 { + 2 + self.value.len() + } + + pub fn is_empty(&self) -> bool { + self.len() == 2 + } +} + +impl From<Ipv6cpOpt> for Ipv6cpOption { + fn from(value: Ipv6cpOpt) -> Self { + Self { value } + } +} + +impl Serialize for [Ipv6cpOption] { + fn serialize<W: Write>(&self, w: &mut W) -> Result<()> { + for option in self { + option.serialize(w)?; + } + + Ok(()) + } +} + +impl Deserialize for Vec<Ipv6cpOption> { + fn deserialize<R: Read>(&mut self, r: &mut R) -> Result<()> { + while r.bytes().size_hint().0 > 0 { + let mut tmp = Ipv6cpOption::from(Ipv6cpOpt::InterfaceId(u64::default())); + + tmp.deserialize(r)?; + self.push(tmp); + } + + Ok(()) + } +} + +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum Ipv6cpData { + ConfigureRequest(Ipv6cpConfigureRequest), + ConfigureAck(Ipv6cpConfigureAck), + ConfigureNak(Ipv6cpConfigureNak), + ConfigureReject(Ipv6cpConfigureReject), + TerminateRequest(Ipv6cpTerminateRequest), + TerminateAck(Ipv6cpTerminateAck), + CodeReject(Ipv6cpCodeReject), +} + +impl Default for Ipv6cpData { + fn default() -> Self { + Self::ConfigureRequest(Ipv6cpConfigureRequest::default()) + } +} + +impl Serialize for Ipv6cpData { + 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 Ipv6cpData { + fn discriminant(&self) -> u8 { + match self { + Self::ConfigureRequest(_) => IPV6CP_CONFIGURE_REQUEST, + Self::ConfigureAck(_) => IPV6CP_CONFIGURE_ACK, + Self::ConfigureNak(_) => IPV6CP_CONFIGURE_NAK, + Self::ConfigureReject(_) => IPV6CP_CONFIGURE_REJECT, + Self::TerminateRequest(_) => IPV6CP_TERMINATE_REQUEST, + Self::TerminateAck(_) => IPV6CP_TERMINATE_ACK, + Self::CodeReject(_) => IPV6CP_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 { + IPV6CP_CONFIGURE_REQUEST => { + let mut tmp = Ipv6cpConfigureRequest::default(); + + tmp.deserialize(r)?; + *self = Self::ConfigureRequest(tmp); + } + IPV6CP_CONFIGURE_ACK => { + let mut tmp = Ipv6cpConfigureAck::default(); + + tmp.deserialize(r)?; + *self = Self::ConfigureAck(tmp); + } + IPV6CP_CONFIGURE_NAK => { + let mut tmp = Ipv6cpConfigureNak::default(); + + tmp.deserialize(r)?; + *self = Self::ConfigureNak(tmp); + } + IPV6CP_CONFIGURE_REJECT => { + let mut tmp = Ipv6cpConfigureReject::default(); + + tmp.deserialize(r)?; + *self = Self::ConfigureReject(tmp); + } + IPV6CP_TERMINATE_REQUEST => { + let mut tmp = Ipv6cpTerminateRequest::default(); + + tmp.deserialize(r)?; + *self = Self::TerminateRequest(tmp); + } + IPV6CP_TERMINATE_ACK => { + let mut tmp = Ipv6cpTerminateAck::default(); + + tmp.deserialize(r)?; + *self = Self::TerminateAck(tmp); + } + IPV6CP_CODE_REJECT => { + let mut tmp = Ipv6cpCodeReject::default(); + + tmp.deserialize(r)?; + *self = Self::CodeReject(tmp); + } + _ => return Err(Error::InvalidIpv6cpCode(*discriminant)), + } + + Ok(()) + } +} + +#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] +pub struct Ipv6cpPkt { + #[ppproperly(discriminant_for(field = "data", data_type = "u8"))] + identifier: u8, + #[ppproperly(len_for(field = "data", offset = 4, data_type = "u16"))] + data: Ipv6cpData, +} + +impl Ipv6cpPkt { + pub fn new_configure_request(identifier: u8, options: Vec<Ipv6cpOption>) -> Self { + Self { + identifier, + data: Ipv6cpData::ConfigureRequest(Ipv6cpConfigureRequest { options }), + } + } + + pub fn new_configure_ack(identifier: u8, options: Vec<Ipv6cpOption>) -> Self { + Self { + identifier, + data: Ipv6cpData::ConfigureAck(Ipv6cpConfigureAck { options }), + } + } + + pub fn new_configure_nak(identifier: u8, options: Vec<Ipv6cpOption>) -> Self { + Self { + identifier, + data: Ipv6cpData::ConfigureNak(Ipv6cpConfigureNak { options }), + } + } + + pub fn new_configure_reject(identifier: u8, options: Vec<Ipv6cpOption>) -> Self { + Self { + identifier, + data: Ipv6cpData::ConfigureReject(Ipv6cpConfigureReject { options }), + } + } + + pub fn new_terminate_request(identifier: u8, data: Vec<u8>) -> Self { + Self { + identifier, + data: Ipv6cpData::TerminateRequest(Ipv6cpTerminateRequest { data }), + } + } + + pub fn new_terminate_ack(identifier: u8, data: Vec<u8>) -> Self { + Self { + identifier, + data: Ipv6cpData::TerminateAck(Ipv6cpTerminateAck { data }), + } + } + + pub fn new_code_reject(identifier: u8, pkt: Vec<u8>) -> Self { + Self { + identifier, + data: Ipv6cpData::CodeReject(Ipv6cpCodeReject { 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 Ipv6cpConfigureRequest { + options: Vec<Ipv6cpOption>, +} + +impl Ipv6cpConfigureRequest { + 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 Ipv6cpConfigureAck { + options: Vec<Ipv6cpOption>, +} + +impl Ipv6cpConfigureAck { + 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 Ipv6cpConfigureNak { + options: Vec<Ipv6cpOption>, +} + +impl Ipv6cpConfigureNak { + 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 Ipv6cpConfigureReject { + options: Vec<Ipv6cpOption>, +} + +impl Ipv6cpConfigureReject { + 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 Ipv6cpTerminateRequest { + data: Vec<u8>, +} + +impl Ipv6cpTerminateRequest { + 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 Ipv6cpTerminateAck { + data: Vec<u8>, +} + +impl Ipv6cpTerminateAck { + 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 Ipv6cpCodeReject { + pkt: Vec<u8>, // Vec makes MRU truncating easier without overwriting (de)ser impls. +} + +impl Ipv6cpCodeReject { + pub fn len(&self) -> u16 { + self.pkt.len().try_into().unwrap() + } + + pub fn is_empty(&self) -> bool { + self.pkt.is_empty() + } +} @@ -10,6 +10,9 @@ pub use error::*; pub mod ipcp; pub use ipcp::*; +pub mod ipv6cp; +pub use ipv6cp::*; + pub mod lcp; pub use lcp::*; |