use ip::IpAddr; use std::str::FromStr; pub mod ip; #[allow(dead_code)] #[derive(Debug, Eq, PartialEq)] pub enum NetworkingErr { InvalidCIDRErr, InvalidSubnetMask, InvalidNetwork, InvalidIPErr, } /// enum to allow the identification of the class of the network #[derive(Debug, Clone, Copy, Eq, PartialEq)] enum NetworkClass { A = 8, B = 16, C = 24, } #[allow(unused)] #[derive(Debug)] pub struct Network { pub network_address: IpAddr, pub broadcast_addr: IpAddr, pub num_hosts: u32, } #[derive(Debug)] #[allow(unused)] pub struct Subnet { pub networks: Vec, pub subnet_mask: IpAddr, } impl Network { #[allow(unused)] /// Function that constucts a network struct. /// /// ``` /// //Awaiting implementation before creating doctest /// //let network = Network::new(&IpAddr::V4(127, 0, 0, 1), 32); /// ``` fn new(given_address: &IpAddr, broadcast_address: &IpAddr, num_hosts: u32) -> Network { Network { network_address: given_address.clone(), broadcast_addr: broadcast_address.clone(), num_hosts, } } /// Function that constucts a subnet, returning a vector of all subnets included /// /// ``` /// use subnet_calculator::ip::IpAddr; /// use subnet_calculator::Network; /// /// let subnets = Network::create_subnet(&IpAddr::V4(127, 0, 0, 1), 16).unwrap(); /// /// assert_eq!(subnets.networks.len(), 256); /// ``` pub fn create_subnet(network_address: &IpAddr, cidr: u8) -> Result { //Get number of host and network bits. let network_class = Network::get_network_class(network_address)?; let network_address = network_address.to_arr()?; // If the CIDR < class network enum associated value, then there is an invalid subnet. if cidr < network_class as u8 { return Err(NetworkingErr::InvalidCIDRErr); } // Determine the starting network let mut base_network = network_address.clone(); let most_sig_octet: u8 = match network_class { NetworkClass::A => 1, NetworkClass::B => 2, NetworkClass::C => 3, }; for i in most_sig_octet..4 { base_network[i as usize] = 0; } // Remove trailing values from the base network let num_borrowed_bits = cidr - network_class as u8; // Get the base network and next added address as u32's let mut base_addr = u32::from(IpAddr::from_arr(&base_network)?); let subnet_mask = u32::from(Network::gen_subnet_mask(cidr)?); let addr_increment = u32::pow(2, subnet_mask.trailing_zeros()); // The number of hosts per network can be determined using the number of remaining // bits raised to the second power. let num_hosts = u32::pow(2, (32 - network_class as u8 - num_borrowed_bits) as u32); // Determine how many networks are available // We know that this value is >0 at this point let num_networks = u32::pow(2, num_borrowed_bits as u32); // The broadcast address will always be 1 less than the next subnet network address let mut broadcast_addr = (base_addr + addr_increment) - 1; //Create the subnets let mut subnet = Subnet::new( &IpAddr::from(base_addr), &IpAddr::from(broadcast_addr), num_hosts, cidr, )?; // Determine the networking gaps //If there is only 1 network, returning the base network will be sufficient if num_networks > 1 { for _ in 0..num_networks - 1 { //Increment address and append to list base_addr += addr_increment; // We can start adding the full subnet range now instead of 1 less broadcast_addr += addr_increment; subnet.networks.push(Network::new( &IpAddr::from(base_addr), &IpAddr::from(broadcast_addr), num_hosts, )); } } Ok(subnet) } /// Function that is used to determine the class of network that an IP is in. /// /// ```ignore /// use subnet_calculator::Network; /// /// let network_class = Network::get_network_class(&[127, 0, 0, 1]).unwrap(); /// assert_eq!(network_class, NetworkClass::A); /// ``` fn get_network_class(network_address: &IpAddr) -> Result { let network_address = network_address.to_arr()?; if network_address[0] > 224 { return Err(NetworkingErr::InvalidIPErr); } else if network_address[0] >= 192 { return Ok(NetworkClass::C); } else if network_address[0] >= 128 { return Ok(NetworkClass::B); } else if network_address[0] >= 1 { return Ok(NetworkClass::A); } return Err(NetworkingErr::InvalidIPErr); } #[allow(unused)] /// Function that takes in a u8 CIDR and converts it to an IP address. /// /// ```ignore /// let cidr: u8 = 22; /// let subnet_mask: [u8; 4] = gen_subnet_mask(cidr).unwrap(); /// /// assert_eq!(subnet_mask, IpAddr::V4(255, 255, 252, 0)); /// ``` fn gen_subnet_mask(mut cidr: u8) -> Result { if cidr > 32 { return Err(NetworkingErr::InvalidCIDRErr); } let mut oct: [u8; 4] = [0; 4]; for octet in oct.iter_mut() { *octet = if usize::from(cidr) >= 8 { cidr -= 8; u8::MAX } else { // Count the number of remaining 1s and convert to binary let mut count: u8 = 0; for i in ((8 - cidr)..8).rev() { count += u8::pow(2, u32::from(i)); } cidr = 0; count } } Ok(IpAddr::from_arr(&oct)?) } } /// Function that takes in a string reference and returns the result of splitting a string into /// both its Address and CIDR /// /// ``` /// use subnet_calculator::ip_and_cidr_from_string; /// use subnet_calculator::ip::IpAddr; /// /// let ip_string = String::from("192.168.0.1/24"); /// let result = match ip_and_cidr_from_string(&ip_string) { /// Err(_) => panic!(), /// Ok(ip_and_cidr) => ip_and_cidr /// }; /// /// assert_eq!(result, (IpAddr::V4(192, 168, 0, 1), 24)); /// ``` pub fn ip_and_cidr_from_string(ip_and_cidr: &String) -> Result<(IpAddr, u8), ip::InvalidIPErr> { let mut cidr: u8 = Default::default(); let mut ip: String = Default::default(); if ip_and_cidr.contains("/") { let split_ip_cidr = ip_and_cidr.split("/"); if split_ip_cidr.clone().count() > 2 { return Err(ip::InvalidIPErr); } ip = split_ip_cidr .clone() .into_iter() .next() .unwrap_or("0.0.0.0") .to_owned(); cidr = match split_ip_cidr.into_iter().last() { None => return Err(ip::InvalidIPErr), Some(cidr) => match cidr.trim().parse::() { Err(_) => return Err(ip::InvalidIPErr), Ok(cidr) => cidr, }, }; } let ip_address: IpAddr = match IpAddr::from_str(&ip.trim()) { Err(_) => return Err(ip::InvalidIPErr), Ok(ip) => ip, }; return Ok((ip_address, cidr)); } impl Subnet { /// Function to return a new subbet struct /// fn new( base_address: &IpAddr, broadcast_addr: &IpAddr, num_hosts: u32, cidr: u8, ) -> Result { Ok(Subnet { networks: vec![Network::new(base_address, broadcast_addr, num_hosts)], subnet_mask: Network::gen_subnet_mask(cidr)?, }) } } mod tests { #[cfg(test)] #[test] fn string_to_ip_cidr_test() { use super::*; let ip_string = String::from("127.0.0.1/8"); let result = match ip_and_cidr_from_string(&ip_string) { Err(_) => panic!(), Ok(ip_and_cidr) => ip_and_cidr, }; assert_eq!(result.0, IpAddr::V4(127, 0, 0, 1)); assert_eq!(result.1, 8); } #[test] fn cidr_to_ip_test() { use super::*; assert_eq!( Network::gen_subnet_mask(22).unwrap(), IpAddr::V4(255, 255, 252, 0) ); assert_eq!( Network::gen_subnet_mask(24).unwrap(), IpAddr::V4(255, 255, 255, 0) ); assert_eq!(Network::gen_subnet_mask(0).unwrap(), IpAddr::V4(0, 0, 0, 0)); assert_eq!( Network::gen_subnet_mask(4).unwrap(), IpAddr::V4(240, 0, 0, 0) ); assert_eq!( Network::gen_subnet_mask(35).unwrap_err(), NetworkingErr::InvalidCIDRErr ); } #[test] fn network_class_test() { use super::*; assert_eq!( Network::get_network_class(&IpAddr::V4(127, 0, 0, 1)).unwrap(), NetworkClass::A ); assert_eq!( Network::get_network_class(&IpAddr::V4(172, 6, 8, 10)).unwrap(), NetworkClass::B ); assert_eq!( Network::get_network_class(&IpAddr::V4(192, 168, 0, 1)).unwrap(), NetworkClass::C ); assert_eq!( Network::get_network_class(&IpAddr::V4(10, 6, 8, 10)).unwrap(), NetworkClass::A ); assert_eq!( Network::get_network_class(&IpAddr::V4(225, 255, 255, 255)).unwrap_err(), NetworkingErr::InvalidIPErr ); assert_eq!( Network::get_network_class(&IpAddr::V4(225, 0, 0, 0)).unwrap_err(), NetworkingErr::InvalidIPErr ); assert_eq!( Network::get_network_class(&IpAddr::V4(0, 0, 0, 0)).unwrap_err(), NetworkingErr::InvalidIPErr ); assert_eq!( Network::get_network_class(&IpAddr::V4(0, 0, 0, 1)).unwrap_err(), NetworkingErr::InvalidIPErr ); } #[test] fn subnet_creation_test() { use super::*; assert_eq!( Network::create_subnet(&IpAddr::V4(192, 168, 0, 1), 23).unwrap_err(), NetworkingErr::InvalidCIDRErr ); let networks = Network::create_subnet(&IpAddr::V4(127, 0, 0, 1), 10).unwrap(); assert_eq!(networks.networks.len(), 4); let networks = Network::create_subnet(&IpAddr::V4(192, 168, 200, 1), 31) .unwrap() .networks; assert_eq!(networks.len(), 128); assert_eq!( networks.last().unwrap().network_address, IpAddr::V4(192, 168, 200, 254) ); assert_eq!( networks.last().unwrap().broadcast_addr, IpAddr::V4(192, 168, 200, 255) ); assert_eq!(networks.last().unwrap().num_hosts, 2); let networks = Network::create_subnet(&IpAddr::V4(127, 0, 0, 0), 8) .unwrap() .networks; assert_eq!(networks.len(), 1); assert_eq!( networks.last().unwrap().network_address, IpAddr::V4(127, 0, 0, 0) ); assert_eq!( networks.last().unwrap().broadcast_addr, IpAddr::V4(127, 255, 255, 255) ); assert_eq!(networks.last().unwrap().num_hosts, 16_777_216); let networks = Network::create_subnet(&IpAddr::V4(127, 0, 0, 0), 9) .unwrap() .networks; assert_eq!(networks.len(), 2); assert_eq!( networks.first().unwrap().network_address, IpAddr::V4(127, 0, 0, 0) ); assert_eq!( networks.first().unwrap().broadcast_addr, IpAddr::V4(127, 127, 255, 255) ); let networks = Network::create_subnet(&IpAddr::V4(168, 20, 0, 0), 17) .unwrap() .networks; assert_eq!(networks.len(), 2); assert_eq!( networks.last().unwrap().network_address, IpAddr::V4(168, 20, 128, 0) ); assert_eq!( networks.last().unwrap().broadcast_addr, IpAddr::V4(168, 20, 255, 255) ); assert_eq!(networks.last().unwrap().num_hosts, 32_768); let networks = Network::create_subnet(&IpAddr::V4(127, 0, 0, 0), 17) .unwrap() .networks; assert_eq!(networks.len(), 512); assert_eq!( networks.last().unwrap().network_address, IpAddr::V4(127, 255, 128, 0) ); } }