aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorHimbeerserverDE <himbeerserverde@gmail.com>2023-07-26 20:08:38 +0200
committerHimbeerserverDE <himbeerserverde@gmail.com>2023-07-26 20:08:38 +0200
commit83036fd7bdf75c49e94b448828a68d62c62d0d6a (patch)
tree49b3f3d946cccad333f029fe02b03ea0a9a10d00 /src
parent033e07d8878a6d408947a618f32b3f58dd85cc58 (diff)
implement IPv6CP
Diffstat (limited to 'src')
-rw-r--r--src/error.rs5
-rw-r--r--src/ipv6cp.rs409
-rw-r--r--src/lib.rs3
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()
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
index 82406d8..4d55f55 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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::*;