From 3b4a5ce285b4e95d5bac4cb60d02a70b127c63fe Mon Sep 17 00:00:00 2001 From: Luke Else Date: Thu, 20 Apr 2023 21:42:13 +0100 Subject: [PATCH] Added to_arr and from_arr methods to IpAddr --- src/networking/ip.rs | 61 +++++++++++++++++++++++++++++++++-- src/networking/mod.rs | 75 +++++++++++++++++++++++++++---------------- 2 files changed, 106 insertions(+), 30 deletions(-) diff --git a/src/networking/ip.rs b/src/networking/ip.rs index 3999585..af6bf88 100644 --- a/src/networking/ip.rs +++ b/src/networking/ip.rs @@ -19,6 +19,45 @@ pub enum IpAddr { V6(String) } +impl IpAddr { + /// Function that generates ar array of 4 octets / error + /// from an IpAddr + /// + /// # Limitation + /// Currently only works for IPs of type IPv4 + /// # Example + /// ``` + /// let ip: IpAddr = IpAddr::V4(127, 0, 0, 1); + /// + /// let ip_add: [u8; 4] = ip.to_arr(); + /// /// ip_add = [127, 0, 0, 1] + /// ``` + pub fn to_arr(&self) -> Result<[u8; 4], NetworkingErr> { + match self { + IpAddr::V4(o1, o2, o3, o4) => { + Ok([*o1, *o2, *o3, *o4]) + } + _ => Err(NetworkingErr::InvalidIPErr) + } + } + + /// Function that generates a IpAddr / Err from an array of + /// a octests + /// + /// # Limitation + /// Currently only works for IPs of type IPv4 + /// # Example + /// ``` + /// let ip_add: [u8; 4] = [127, 0, 0, 1]; + /// + /// let ip: IpAddr = IpAddr::V4(127, 0, 0, 1); + /// /// ip_add = [127, 0, 0, 1] + /// ``` + pub fn from_arr(arr: &[u8; 4]) -> Result { + Ok(IpAddr::V4(arr[0], arr[1], arr[2], arr[3])) + } +} + impl ToString for IpAddr { /// Function that returns an IP address in string form fn to_string(&self) -> String { @@ -29,7 +68,6 @@ impl ToString for IpAddr { } } - impl FromStr for IpAddr { type Err = NetworkingErr; /// Function that generates a IpAddr / Err from a string @@ -85,7 +123,6 @@ mod tests { /// correctly using the ToString trait fn ip_to_string() { use super::*; - let ip = IpAddr::V4(192, 168, 0, 1); assert_eq!(ip.to_string(), "192.168.0.1") } @@ -109,4 +146,24 @@ mod tests { IpAddr::from_str(ip).unwrap(); assert_eq!(IpAddr::from_str(ip), Err(NetworkingErr::InvalidIPErr)) } + + #[test] + /// Tests the conversion of an IPv4 Address to + /// an array + fn ipaddr_to_arr() { + use super::*; + let mut ip_addr = IpAddr::V4(127, 0, 0, 1); + assert_eq!(ip_addr.to_arr().unwrap(), [127, 0, 0, 1]); + ip_addr = IpAddr::V6("::".to_string()); + assert_eq!(ip_addr.to_arr().unwrap_err(), NetworkingErr::InvalidIPErr); + } + + #[test] + /// Tests the conversion of an array to + // an IPv4 Address + fn arr_to_ipaddr() { + use super::*; + let mut ip_addr: [u8; 4] = [127, 0, 0, 1]; + assert_eq!(IpAddr::from_arr(&ip_addr).unwrap(), IpAddr::V4(127, 0, 0, 1)); + } } \ No newline at end of file diff --git a/src/networking/mod.rs b/src/networking/mod.rs index a355ed3..e2df5e6 100644 --- a/src/networking/mod.rs +++ b/src/networking/mod.rs @@ -1,4 +1,4 @@ -use std::{str::FromStr}; +use std::str::FromStr; use ip::IpAddr; pub mod ip; @@ -12,7 +12,6 @@ pub enum NetworkingErr { InvalidIPErr } - #[allow(unused)] pub struct Network { network_address: IpAddr, @@ -45,28 +44,58 @@ impl Network { /// ``` pub fn create_subnet(network_address: &IpAddr, cidr: u8) -> Result, NetworkingErr> { //Get number of host and network bits. - let subnet_mask = match Network::gen_subnet_mask(cidr) { - Ok(ip) => ip, - Err(_) => return Err(NetworkingErr::InvalidCIDRErr) - }; + let subnet_mask = Network::gen_subnet_mask(cidr)?.to_arr()?; + let network_address = network_address.to_arr()?; - let network_bits = match subnet_mask { - IpAddr::V4(oct1, oct2, oct3, oct4) => { - oct1.count_zeros() + oct2.count_zeros() + oct3.count_zeros() + oct4.count_zeros() - }, - IpAddr::V6(_) => return Err(NetworkingErr::InvalidIPErr) - }; + let mut network_bits: u8 = Default::default(); + let mut host_bits: u8 = Default::default(); - //Determine the number of networks needed for the subnet. - let num_networks = get_num_networks(network_bits); - - //Get first address of each network //Need to get the most significant octet //Get number of bits available for host in specific octet - //Determine Spacing + let mut most_sig_octet: usize = Default::default(); + for (i, oct) in subnet_mask.iter().enumerate(){ + if *oct < 255 { + most_sig_octet = i; + network_bits = oct.count_ones() as u8; + break; + } + //There is no significant octet + if i >= 4 { + return Err(NetworkingErr::InvalidSubnetMask); + } + } + + //Determine the number of networks needed for the subnet. + host_bits = subnet_mask.map(|o| o.count_zeros() as u8).iter().sum(); + host_bits -= network_bits; + + //Determine Spacing + let network_spacing = 2 ^ subnet_mask[most_sig_octet].count_ones() as u8; + + //Determine number of networks in the subnet + let mut num_networks: u8 = 0; + for i in 1..network_bits { + num_networks += 2^i; + } + + //Generate base address + let mut base_address = network_address; + base_address[most_sig_octet] = 255 - num_networks; + for i in most_sig_octet+1..=4 { + base_address[i] = 0; + } + //Use Class constructor to generate each network. - Ok(vec![Network::new(&IpAddr::V4(0, 0, 0, 0), &IpAddr::V4(0, 0, 0, 0))]) + let mut networks = vec![]; + for i in 0..num_networks { + let mut new_ip = base_address; + new_ip[most_sig_octet] += i * network_spacing; + + let new_network = Network::new(&IpAddr::from_arr(&new_ip)?, &IpAddr::from_arr(&subnet_mask)?); + networks.push(new_network); + } + Ok(networks) } /// Function that takes in a u8 CIDR and converts it to an IP address. @@ -104,16 +133,6 @@ impl Network { //pub fn generate_subnets(self) -> Vec {} } -fn get_num_networks(mut network_bits: u8) -> u8 { - for _ in 0..4 { - if (network_bits / 8) > 1 { - network_bits -= 8; - continue; - } - }; - network_bits -} - /// Function that takes in a string reference and returns the result of splitting a string into /// both its Address and CIDR ///