summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock17
-rw-r--r--Cargo.toml1
-rw-r--r--src/vrrpv2.rs143
3 files changed, 157 insertions, 4 deletions
diff --git a/Cargo.lock b/Cargo.lock
index e81a06c..5ae3ddc 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -67,6 +67,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]]
+name = "minimal-lexical"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
+
+[[package]]
name = "mio"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -79,6 +85,16 @@ dependencies = [
]
[[package]]
+name = "nom"
+version = "7.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36"
+dependencies = [
+ "memchr",
+ "minimal-lexical",
+]
+
+[[package]]
name = "num_cpus"
version = "1.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -227,6 +243,7 @@ checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
name = "vrrpd"
version = "0.1.0"
dependencies = [
+ "nom",
"tokio",
]
diff --git a/Cargo.toml b/Cargo.toml
index b58a36a..e252638 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -7,3 +7,4 @@ edition = "2021"
[dependencies]
tokio = { version = "1.22.0", features = ["full"] }
+nom = "7"
diff --git a/src/vrrpv2.rs b/src/vrrpv2.rs
index 559b913..9f4ffea 100644
--- a/src/vrrpv2.rs
+++ b/src/vrrpv2.rs
@@ -1,21 +1,45 @@
+use nom::bits::{bits, streaming::take};
+use nom::combinator::map_res;
+use nom::error::{Error, ErrorKind};
+use nom::multi::count;
+use nom::number::complete::{be_u16, be_u32, u8};
+use nom::sequence::tuple;
+use nom::{Err, IResult};
use std::net::Ipv4Addr;
-pub enum VRRPv2Error {}
+const VRRP_REQUIRED_VERSION: u8 = 2;
+const VRRP_REQUIRED_TYPE: u8 = 1; // Advertisement
+#[derive(Debug, Clone, PartialEq)]
+pub enum VRRPv2Error {
+ VRRPv2ParseError,
+}
+
+type NomError<'a> = nom::Err<nom::error::Error<&'a [u8]>>;
+impl From<NomError<'_>> for VRRPv2Error {
+ fn from(_: NomError) -> Self {
+ Self::VRRPv2ParseError
+ }
+}
+
+#[derive(Debug, PartialEq)]
pub enum VRRPVersion {
- V2(u8),
+ V2,
}
+#[derive(Debug, PartialEq)]
pub enum VRRPv2Type {
VRRPv2Advertisement,
}
+#[derive(Debug, PartialEq)]
pub enum VRRPv2AuthType {
VRRPv2AuthNoAuth = 0x00,
VRRPv2AuthReserved1 = 0x01,
VRRPv2AuthReserved2 = 0x02,
}
+#[derive(Debug, PartialEq)]
pub struct VRRPv2 {
pub version: VRRPVersion,
pub type_: VRRPv2Type,
@@ -28,6 +52,117 @@ pub struct VRRPv2 {
pub ip_addrs: Vec<Ipv4Addr>,
}
-pub fn from_bytes(_bytes: &[u8]) -> Result<&VRRPv2, VRRPv2Error> {
- unimplemented!()
+fn two_nibbles(input: &[u8]) -> IResult<&[u8], (u8, u8)> {
+ bits::<_, _, Error<(&[u8], usize)>, _, _>(tuple((take(4usize), take(4usize))))(input)
+}
+
+fn parse_version_type(input: &[u8]) -> IResult<&[u8], (VRRPVersion, VRRPv2Type)> {
+ let (input, pair) = two_nibbles(input)?;
+ match pair {
+ (VRRP_REQUIRED_VERSION, VRRP_REQUIRED_TYPE) => {
+ Ok((input, (VRRPVersion::V2, VRRPv2Type::VRRPv2Advertisement)))
+ }
+ _ => Err(Err::Error(Error::new(input, ErrorKind::Alt))),
+ }
+}
+
+fn parse_auth_type(input: &[u8]) -> IResult<&[u8], VRRPv2AuthType> {
+ map_res(u8, |auth_type| {
+ Ok(match auth_type {
+ 0 => VRRPv2AuthType::VRRPv2AuthNoAuth,
+ 1 => VRRPv2AuthType::VRRPv2AuthReserved1,
+ 2 => VRRPv2AuthType::VRRPv2AuthReserved2,
+ _ => return Err(Err::Error(Error::new(input, ErrorKind::Alt))),
+ })
+ })(input)
+}
+
+fn parse(input: &[u8]) -> IResult<&[u8], VRRPv2> {
+ let (input, (version, type_)) = parse_version_type(input)?;
+ let (input, virtual_router_id) = u8(input)?;
+ let (input, priority) = u8(input)?;
+ let (input, count_ip_addrs) = u8(input)?;
+ let (input, auth_type) = parse_auth_type(input)?;
+ let (input, advertisement_interval) = u8(input)?;
+ let (input, checksum) = be_u16(input)?;
+ // TODO verify checksum
+ let (input, xs) = count(be_u32, usize::from(count_ip_addrs))(input)?;
+ let ip_addrs = xs.into_iter().map(Ipv4Addr::from).collect();
+ Ok((
+ input,
+ VRRPv2 {
+ version,
+ type_,
+ virtual_router_id,
+ priority,
+ count_ip_addrs,
+ auth_type,
+ advertisement_interval,
+ checksum,
+ ip_addrs,
+ },
+ ))
+}
+
+pub fn from_bytes(bytes: &[u8]) -> Result<VRRPv2, VRRPv2Error> {
+ match parse(bytes) {
+ Ok((_, v)) => Ok(v),
+ Err(e) => Err(e.into()),
+ }
+}
+
+#[test]
+fn test_standard_bytes() {
+ let bytes = [
+ 0x21, 0x01, 0x64, 0x01, 0x00, 0x01, 0xba, 0x52, 0xc0, 0xa8, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ ];
+ let got = from_bytes(&bytes).unwrap();
+ let expected = VRRPv2 {
+ version: VRRPVersion::V2,
+ type_: VRRPv2Type::VRRPv2Advertisement,
+ virtual_router_id: 1,
+ priority: 100,
+ count_ip_addrs: 1,
+ auth_type: VRRPv2AuthType::VRRPv2AuthNoAuth,
+ checksum: 47698,
+ advertisement_interval: 1,
+ ip_addrs: vec![Ipv4Addr::from([192, 168, 0, 1])],
+ };
+ assert_eq!(got, expected);
+}
+
+#[test]
+fn test_incomplete_bytes() {
+ let bytes = [0x21, 0x01];
+ let got = from_bytes(&bytes);
+ assert_eq!(got.is_err(), true);
+ assert_eq!(got.err(), Some(VRRPv2Error::VRRPv2ParseError));
+}
+
+#[test]
+fn test_invalid_version_type() {
+ let bytes = [
+ 0x00, 0x01, 0x64, 0x01, 0x00, 0x01, 0xba, 0x52, 0xc0, 0xa8, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ ];
+ let got = from_bytes(&bytes);
+ assert_eq!(got.is_err(), true);
+ assert_eq!(got.err(), Some(VRRPv2Error::VRRPv2ParseError));
+}
+
+#[test]
+fn test_invalid_auth_type() {
+ let bytes = [
+ 0x21, 0x01, 0x64, 0x01, 0x03, 0x01, 0xba, 0x52, 0xc0, 0xa8, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ ];
+ let got = from_bytes(&bytes);
+ assert_eq!(got.is_err(), true);
+ assert_eq!(got.err(), Some(VRRPv2Error::VRRPv2ParseError));
+}
+
+#[test]
+fn test_invalid_checksum() {
+ panic!("fix me");
}