2023-02-11 16:15:28 +00:00
|
|
|
use ip::IpAddr;
|
2023-05-26 13:04:21 +00:00
|
|
|
use std::str::FromStr;
|
2023-02-11 16:15:28 +00:00
|
|
|
|
2023-02-20 18:06:57 +00:00
|
|
|
pub mod ip;
|
|
|
|
|
2023-04-17 16:24:35 +00:00
|
|
|
#[allow(dead_code)]
|
|
|
|
#[derive(Debug, Eq, PartialEq)]
|
|
|
|
pub enum NetworkingErr {
|
|
|
|
InvalidCIDRErr,
|
|
|
|
InvalidSubnetMask,
|
|
|
|
InvalidNetwork,
|
|
|
|
InvalidIPErr
|
|
|
|
}
|
|
|
|
|
2023-04-25 19:16:28 +00:00
|
|
|
/// enum to allow the identification of the class of the network
|
2023-04-26 20:44:13 +00:00
|
|
|
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
2023-04-25 19:16:28 +00:00
|
|
|
enum NetworkClass {
|
|
|
|
A = 8,
|
|
|
|
B = 16,
|
|
|
|
C = 24
|
|
|
|
}
|
|
|
|
|
2023-02-11 16:15:28 +00:00
|
|
|
#[allow(unused)]
|
2023-04-26 20:44:13 +00:00
|
|
|
#[derive(Debug)]
|
2023-02-11 16:15:28 +00:00
|
|
|
pub struct Network {
|
|
|
|
network_address: IpAddr,
|
|
|
|
broadcast_addr: IpAddr,
|
2023-04-17 16:24:35 +00:00
|
|
|
num_hosts: u32,
|
2023-08-04 10:30:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
#[allow(unused)]
|
|
|
|
pub struct Subnet {
|
|
|
|
pub networks: Vec<Network>,
|
|
|
|
pub subnet_mask: IpAddr,
|
2023-02-11 16:15:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Network {
|
2023-04-17 16:24:35 +00:00
|
|
|
|
|
|
|
#[allow(unused)]
|
|
|
|
/// Function that constucts a network struct.
|
|
|
|
///
|
|
|
|
/// ```
|
2023-05-26 13:04:21 +00:00
|
|
|
/// //Awaiting implementation before creating doctest
|
|
|
|
/// //let network = Network::new(&IpAddr::V4(127, 0, 0, 1), 32);
|
2023-04-17 16:24:35 +00:00
|
|
|
/// ```
|
2023-08-04 10:30:03 +00:00
|
|
|
fn new(given_address: &IpAddr, broadcast_address: &IpAddr, num_hosts: u32) -> Network {
|
2023-04-17 16:24:35 +00:00
|
|
|
Network {
|
|
|
|
network_address: given_address.clone(),
|
2023-08-04 10:30:03 +00:00
|
|
|
broadcast_addr: broadcast_address.clone(),
|
|
|
|
num_hosts,
|
2023-04-17 16:24:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Function that constucts a subnet, returning a vector of all subnets included
|
|
|
|
///
|
|
|
|
/// ```
|
2023-05-26 13:04:21 +00:00
|
|
|
/// use subnet_calculator::ip::IpAddr;
|
|
|
|
/// use subnet_calculator::Network;
|
|
|
|
///
|
2023-08-04 10:30:03 +00:00
|
|
|
/// let subnets = Network::create_subnet(&IpAddr::V4(127, 0, 0, 1), 16).unwrap();
|
2023-05-26 13:04:21 +00:00
|
|
|
///
|
2023-08-04 10:30:03 +00:00
|
|
|
/// assert_eq!(subnets.networks.len(), 256);
|
2023-04-17 16:24:35 +00:00
|
|
|
/// ```
|
2023-08-04 10:30:03 +00:00
|
|
|
pub fn create_subnet(network_address: &IpAddr, cidr: u8) -> Result<Subnet, NetworkingErr> {
|
2023-04-17 16:24:35 +00:00
|
|
|
//Get number of host and network bits.
|
2023-04-26 20:44:13 +00:00
|
|
|
let network_class = Network::get_network_class(network_address)?;
|
2023-04-20 20:42:13 +00:00
|
|
|
let network_address = network_address.to_arr()?;
|
|
|
|
|
2023-04-26 20:44:13 +00:00
|
|
|
// If the CIDR < class network enum associated value, then there is an invalid subnet.
|
2023-05-02 06:25:05 +00:00
|
|
|
if cidr < network_class as u8 {
|
2023-04-26 20:44:13 +00:00
|
|
|
return Err(NetworkingErr::InvalidCIDRErr)
|
2023-08-04 10:30:03 +00:00
|
|
|
}
|
2023-04-20 20:42:13 +00:00
|
|
|
|
2023-04-26 20:44:13 +00:00
|
|
|
// Determine the starting network
|
2023-04-27 22:28:34 +00:00
|
|
|
let mut base_network = network_address.clone();
|
|
|
|
let most_sig_octet: u8 = match network_class {
|
|
|
|
NetworkClass::A => 1,
|
|
|
|
NetworkClass::B => 2,
|
|
|
|
NetworkClass::C => 3
|
2023-08-04 10:30:03 +00:00
|
|
|
};
|
|
|
|
|
2023-04-27 22:28:34 +00:00
|
|
|
for i in most_sig_octet..4 {
|
|
|
|
base_network[i as usize] = 0;
|
|
|
|
}
|
|
|
|
|
2023-08-04 10:30:03 +00:00
|
|
|
// 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);
|
|
|
|
|
2023-05-15 18:22:02 +00:00
|
|
|
// Determine how many networks are available
|
2023-04-27 22:28:34 +00:00
|
|
|
// We know that this value is >0 at this point
|
2023-05-12 13:17:53 +00:00
|
|
|
let num_networks = u32::pow(2, num_borrowed_bits as u32);
|
2023-04-26 20:44:13 +00:00
|
|
|
|
2023-08-04 10:30:03 +00:00
|
|
|
// The broadcast address will always be 1 less than the next subnet network address
|
|
|
|
let mut broadcast_addr = (base_addr + addr_increment) - 1;
|
|
|
|
|
2023-04-27 22:28:34 +00:00
|
|
|
//Create the subnets
|
2023-08-04 10:30:03 +00:00
|
|
|
let mut subnet = Subnet::new(&IpAddr::from(base_addr), &IpAddr::from(broadcast_addr), num_hosts, cidr)?;
|
2023-04-30 13:16:24 +00:00
|
|
|
|
2023-05-02 06:29:32 +00:00
|
|
|
// Determine the networking gaps
|
|
|
|
//If there is only 1 network, returning the base network will be sufficient
|
|
|
|
if num_networks > 1 {
|
2023-05-15 19:26:49 +00:00
|
|
|
|
|
|
|
for _ in 0..num_networks-1 {
|
|
|
|
//Increment address and append to list
|
2023-08-04 10:30:03 +00:00
|
|
|
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));
|
2023-05-02 06:29:32 +00:00
|
|
|
}
|
2023-04-27 22:28:34 +00:00
|
|
|
}
|
2023-05-02 06:29:32 +00:00
|
|
|
|
2023-08-04 10:30:03 +00:00
|
|
|
Ok(subnet)
|
2023-04-25 20:45:43 +00:00
|
|
|
}
|
2023-04-20 20:42:13 +00:00
|
|
|
|
2023-04-25 20:45:43 +00:00
|
|
|
/// Function that is used to determine the class of network that an IP is in.
|
|
|
|
///
|
2023-05-26 13:04:21 +00:00
|
|
|
/// ```ignore
|
|
|
|
/// use subnet_calculator::Network;
|
|
|
|
///
|
|
|
|
/// let network_class = Network::get_network_class(&[127, 0, 0, 1]).unwrap();
|
|
|
|
/// assert_eq!(network_class, NetworkClass::A);
|
2023-04-25 20:45:43 +00:00
|
|
|
/// ```
|
2023-04-26 20:19:35 +00:00
|
|
|
fn get_network_class(network_address: &IpAddr) -> Result<NetworkClass, NetworkingErr> {
|
|
|
|
let network_address = network_address.to_arr()?;
|
2023-04-25 20:45:43 +00:00
|
|
|
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);
|
2023-04-20 20:42:13 +00:00
|
|
|
}
|
2023-04-25 20:45:43 +00:00
|
|
|
return Err(NetworkingErr::InvalidIPErr)
|
2023-04-17 16:24:35 +00:00
|
|
|
}
|
2023-04-09 09:07:15 +00:00
|
|
|
|
2023-05-12 13:17:53 +00:00
|
|
|
#[allow(unused)]
|
2023-04-09 09:07:15 +00:00
|
|
|
/// Function that takes in a u8 CIDR and converts it to an IP address.
|
|
|
|
///
|
2023-05-26 13:04:21 +00:00
|
|
|
/// ```ignore
|
|
|
|
/// let cidr: u8 = 22;
|
|
|
|
/// let subnet_mask: [u8; 4] = gen_subnet_mask(cidr).unwrap();
|
2023-04-09 09:07:15 +00:00
|
|
|
///
|
2023-05-26 13:04:21 +00:00
|
|
|
/// assert_eq!(subnet_mask, IpAddr::V4(255, 255, 252, 0));
|
2023-04-09 09:07:15 +00:00
|
|
|
/// ```
|
2023-04-17 16:24:35 +00:00
|
|
|
fn gen_subnet_mask(mut cidr: u8) -> Result<IpAddr, NetworkingErr> {
|
|
|
|
if cidr > 32 {
|
|
|
|
return Err(NetworkingErr::InvalidCIDRErr)
|
2023-04-09 09:07:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
let mut oct: [u8; 4] = [0; 4];
|
|
|
|
|
|
|
|
for octet in oct.iter_mut() {
|
2023-04-17 16:24:35 +00:00
|
|
|
*octet = if usize::from(cidr) >= 8 {
|
|
|
|
cidr -= 8;
|
2023-04-09 09:07:15 +00:00
|
|
|
u8::MAX
|
|
|
|
}else{
|
|
|
|
// Count the number of remaining 1s and convert to binary
|
|
|
|
let mut count: u8 = 0;
|
2023-04-17 16:24:35 +00:00
|
|
|
for i in ((8-cidr)..8).rev() {
|
2023-04-09 09:07:15 +00:00
|
|
|
count += u8::pow(2, u32::from(i));
|
|
|
|
}
|
2023-04-17 16:24:35 +00:00
|
|
|
cidr = 0;
|
2023-04-09 09:07:15 +00:00
|
|
|
count
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-26 20:19:35 +00:00
|
|
|
Ok(IpAddr::from_arr(&oct)?)
|
2023-04-09 09:07:15 +00:00
|
|
|
}
|
|
|
|
//pub fn generate_subnets(self) -> Vec<Network> {}
|
|
|
|
}
|
|
|
|
|
2023-02-20 21:25:37 +00:00
|
|
|
/// Function that takes in a string reference and returns the result of splitting a string into
|
|
|
|
/// both its Address and CIDR
|
|
|
|
///
|
|
|
|
/// ```
|
2023-05-26 13:04:21 +00:00
|
|
|
/// use subnet_calculator::ip_and_cidr_from_string;
|
|
|
|
/// use subnet_calculator::ip::IpAddr;
|
|
|
|
///
|
2023-02-20 21:25:37 +00:00
|
|
|
/// 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
|
|
|
|
/// };
|
|
|
|
///
|
2023-05-26 13:04:21 +00:00
|
|
|
/// assert_eq!(result, (IpAddr::V4(192, 168, 0, 1), 24));
|
2023-02-20 21:25:37 +00:00
|
|
|
/// ```
|
2023-02-20 18:06:57 +00:00
|
|
|
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::<u8>() {
|
|
|
|
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))
|
|
|
|
}
|
|
|
|
|
2023-08-04 10:30:03 +00:00
|
|
|
impl Subnet {
|
|
|
|
/// Function to return a new subbet struct
|
|
|
|
///
|
|
|
|
fn new(base_address: &IpAddr, broadcast_addr: &IpAddr, num_hosts: u32, cidr: u8) -> Result<Subnet, NetworkingErr> {
|
|
|
|
Ok(
|
|
|
|
Subnet {
|
|
|
|
networks: vec![Network::new(base_address, broadcast_addr, num_hosts)],
|
|
|
|
subnet_mask: Network::gen_subnet_mask(cidr)?,
|
|
|
|
}
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-20 18:06:57 +00:00
|
|
|
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);
|
|
|
|
}
|
2023-04-09 09:07:15 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn cidr_to_ip_test() {
|
|
|
|
use super::*;
|
2023-04-10 10:13:36 +00:00
|
|
|
|
2023-04-09 09:07:15 +00:00
|
|
|
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));
|
2023-04-17 16:24:35 +00:00
|
|
|
assert_eq!(Network::gen_subnet_mask(35).unwrap_err(), NetworkingErr::InvalidCIDRErr);
|
2023-04-09 09:07:15 +00:00
|
|
|
}
|
2023-04-25 20:45:43 +00:00
|
|
|
|
|
|
|
#[test]
|
2023-04-26 20:44:13 +00:00
|
|
|
fn network_class_test() {
|
2023-04-25 20:45:43 +00:00
|
|
|
use super::*;
|
2023-04-26 20:44:13 +00:00
|
|
|
|
2023-04-26 20:19:35 +00:00
|
|
|
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);
|
2023-04-25 20:45:43 +00:00
|
|
|
}
|
2023-04-26 20:44:13 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn subnet_creation_test() {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
assert_eq!(Network::create_subnet(&IpAddr::V4(192, 168, 0, 1), 23).unwrap_err(), NetworkingErr::InvalidCIDRErr);
|
2023-05-02 06:25:05 +00:00
|
|
|
|
|
|
|
let networks = Network::create_subnet(&IpAddr::V4(127, 0, 0, 1), 10).unwrap();
|
2023-08-04 10:30:03 +00:00
|
|
|
assert_eq!(networks.networks.len(), 4);
|
2023-05-02 06:25:05 +00:00
|
|
|
|
2023-08-04 10:30:03 +00:00
|
|
|
let networks = Network::create_subnet(&IpAddr::V4(192, 168, 200, 1), 31).unwrap().networks;
|
2023-05-02 06:25:05 +00:00
|
|
|
assert_eq!(networks.len(), 128);
|
2023-08-04 10:30:03 +00:00
|
|
|
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;
|
2023-05-02 06:25:05 +00:00
|
|
|
assert_eq!(networks.len(), 1);
|
2023-08-04 10:30:03 +00:00
|
|
|
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);
|
2023-05-12 13:17:53 +00:00
|
|
|
|
2023-08-04 10:30:03 +00:00
|
|
|
let networks = Network::create_subnet(&IpAddr::V4(127, 0, 0, 0), 9).unwrap().networks;
|
2023-05-12 13:17:53 +00:00
|
|
|
assert_eq!(networks.len(), 2);
|
2023-08-04 10:30:03 +00:00
|
|
|
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));
|
2023-05-12 13:17:53 +00:00
|
|
|
|
2023-08-04 10:30:03 +00:00
|
|
|
let networks = Network::create_subnet(&IpAddr::V4(168, 20, 0, 0), 17).unwrap().networks;
|
2023-05-15 19:26:49 +00:00
|
|
|
assert_eq!(networks.len(), 2);
|
2023-08-04 10:30:03 +00:00
|
|
|
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);
|
2023-05-15 19:26:49 +00:00
|
|
|
|
2023-08-04 10:30:03 +00:00
|
|
|
let networks = Network::create_subnet(&IpAddr::V4(127, 0, 0, 0), 17).unwrap().networks;
|
2023-05-15 19:26:49 +00:00
|
|
|
assert_eq!(networks.len(), 512);
|
2023-08-04 10:30:03 +00:00
|
|
|
assert_eq!(networks.last().unwrap().network_address, IpAddr::V4(127, 255, 128, 0));
|
2023-04-26 20:44:13 +00:00
|
|
|
}
|
2023-05-12 13:17:53 +00:00
|
|
|
}
|