Compare commits
38 Commits
247587d99b
...
master
Author | SHA1 | Date | |
---|---|---|---|
b2ca7f35f3 | |||
53837d7b33 | |||
aeae3af5e6 | |||
aee8974e2b | |||
f224cf4c29 | |||
108f8f2ab4 | |||
00735d5691 | |||
30344b0605 | |||
625233ab86 | |||
8c88d7945a | |||
6a4573450c | |||
4ace257932 | |||
55ccf05902 | |||
924142e96c | |||
b8e4ff21b0 | |||
aad67836df | |||
2dcbd405e4 | |||
cf35765cb4 | |||
6fafa97d61 | |||
d0da5f587f | |||
44bafbd0e6 | |||
7f8f2a41fe | |||
ee116523d8 | |||
45c5227a19 | |||
3b4a5ce285 | |||
7d61265f08 | |||
e1a978cb1f | |||
a067a3e349 | |||
d1d4bc02ca | |||
edc125d5fc | |||
a9bb5c1d52 | |||
41fc1a3f6d | |||
6567553f8a | |||
d792ea8c30 | |||
4f8412f3d0 | |||
f2ad0de89b | |||
43cd0f5bc5 | |||
d5ec71a000 |
@ -1,7 +1,11 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "subnet_calculator"
|
name = "subnet_calculator"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
description = "Subnet Calculator written in Rust"
|
||||||
|
license = "MIT"
|
||||||
|
homepage = "https://git.luke-else.co.uk/luke-else/subnet_calculator"
|
||||||
|
repository = "https://git.luke-else.co.uk/luke-else/subnet_calculator"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) <year> <copyright holders>
|
Copyright (c) 2023 Luke Else
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
258
src/ip.rs
Normal file
258
src/ip.rs
Normal file
@ -0,0 +1,258 @@
|
|||||||
|
use super::NetworkingErr;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub struct InvalidIPErr;
|
||||||
|
|
||||||
|
/// Ip address enum that includes associated type
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
/// ```
|
||||||
|
/// //Loopback Addresses:
|
||||||
|
/// use subnet_calculator::ip::IpAddr;
|
||||||
|
/// use std::str::FromStr;
|
||||||
|
///
|
||||||
|
/// IpAddr::V4(127, 0, 0, 1);
|
||||||
|
/// IpAddr::V6(String::from("::1"));
|
||||||
|
/// ```
|
||||||
|
#[allow(unused)]
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||||
|
pub enum IpAddr {
|
||||||
|
V4(u8, u8, u8, u8),
|
||||||
|
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
|
||||||
|
/// ```
|
||||||
|
/// use subnet_calculator::ip::IpAddr;
|
||||||
|
///
|
||||||
|
/// let ip: IpAddr = IpAddr::V4(127, 0, 0, 1);
|
||||||
|
///
|
||||||
|
/// let ip_add: [u8; 4] = ip.to_arr().unwrap();
|
||||||
|
/// assert_eq!(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
|
||||||
|
/// ```
|
||||||
|
/// use subnet_calculator::ip::IpAddr;
|
||||||
|
///
|
||||||
|
/// let ip_add: [u8; 4] = [127, 0, 0, 1];
|
||||||
|
///
|
||||||
|
/// let ip: IpAddr = IpAddr::from_arr(&ip_add).unwrap();
|
||||||
|
/// assert_eq!(ip, IpAddr::V4(127, 0, 0, 1));
|
||||||
|
/// ```
|
||||||
|
pub fn from_arr(arr: &[u8; 4]) -> Result<IpAddr, NetworkingErr> {
|
||||||
|
Ok(IpAddr::V4(arr[0], arr[1], arr[2], arr[3]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<IpAddr> for u32 {
|
||||||
|
/// Function that converts an Ip address
|
||||||
|
/// into an Unsigned 32 bit integer.
|
||||||
|
///
|
||||||
|
/// # Limitation
|
||||||
|
/// Currently only works for IPs of type IPv4
|
||||||
|
/// # Example
|
||||||
|
/// ```
|
||||||
|
/// use subnet_calculator::ip::IpAddr;
|
||||||
|
///
|
||||||
|
/// let ip: IpAddr = IpAddr::V4(127, 0, 0, 1);
|
||||||
|
///
|
||||||
|
/// let ip_u32: u32 = u32::from(ip);
|
||||||
|
/// assert_eq!(ip_u32, 2130706433);
|
||||||
|
/// ```
|
||||||
|
fn from(ip: IpAddr) -> Self {
|
||||||
|
u32::from_be_bytes(ip.to_arr().unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<u32> for IpAddr {
|
||||||
|
/// Function that converts an Unsigned 32-bit Ip address
|
||||||
|
/// into an IpAddr
|
||||||
|
///
|
||||||
|
/// # Limitation
|
||||||
|
/// Currently only works for IPs of type IPv4
|
||||||
|
/// # Example
|
||||||
|
/// ```
|
||||||
|
/// use subnet_calculator::ip::IpAddr;
|
||||||
|
///
|
||||||
|
/// let ip: u32 = 2_130_706_433;
|
||||||
|
///
|
||||||
|
/// let ip_addr: IpAddr = IpAddr::from(ip);
|
||||||
|
/// assert_eq!(ip_addr, IpAddr::V4(127, 0, 0, 1));
|
||||||
|
/// ```
|
||||||
|
fn from(ip: u32) -> Self {
|
||||||
|
IpAddr::from_arr(&(ip.to_be_bytes() as [u8; 4])).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToString for IpAddr {
|
||||||
|
/// Function that returns an IP address in string form
|
||||||
|
fn to_string(&self) -> String {
|
||||||
|
match self {
|
||||||
|
IpAddr::V4(oct1, oct2, oct3, oct4) => format!("{}.{}.{}.{}", oct1, oct2, oct3, oct4),
|
||||||
|
IpAddr::V6(addr) => format!("{}", addr),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for IpAddr {
|
||||||
|
type Err = NetworkingErr;
|
||||||
|
/// Function that generates a IpAddr / Err from a string
|
||||||
|
///
|
||||||
|
/// # Limitation
|
||||||
|
/// Currently only works for IPs of type IPv4
|
||||||
|
/// # Example
|
||||||
|
/// ```
|
||||||
|
/// use subnet_calculator::ip::IpAddr;
|
||||||
|
/// use std::str::FromStr;
|
||||||
|
///
|
||||||
|
/// let ip: &str = "127.0.0.1";
|
||||||
|
///
|
||||||
|
/// let parsed_ip: IpAddr = IpAddr::from_str(ip).unwrap();
|
||||||
|
/// assert_eq!(parsed_ip, IpAddr::V4(127,0,0,1));
|
||||||
|
/// ```
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
let split_ip = s.split('.');
|
||||||
|
if split_ip.clone().count() != 4 {
|
||||||
|
//Invalid IP address entered
|
||||||
|
return Err(super::NetworkingErr::InvalidIPErr);
|
||||||
|
}
|
||||||
|
let mut ip: [u8; 4] = Default::default();
|
||||||
|
//Go through each octet and ensure it can be parsed;
|
||||||
|
for (i, oct) in split_ip.into_iter().enumerate() {
|
||||||
|
if i > ip.len() {
|
||||||
|
//Ip string is out of the range of the 4 octets in an IPv4 Address
|
||||||
|
return Err(NetworkingErr::InvalidIPErr);
|
||||||
|
}
|
||||||
|
match oct.parse::<u8>() {
|
||||||
|
Ok(parsed_oct) => ip[i] = parsed_oct,
|
||||||
|
Err(_) => return Err(NetworkingErr::InvalidIPErr),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(IpAddr::from_arr(&ip)?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Function that takes in an IP address and then prints a formatted string to the CLI
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
/// ```
|
||||||
|
/// use subnet_calculator::ip::{IpAddr, print_ip};
|
||||||
|
///
|
||||||
|
/// let ip = IpAddr::V4(127, 0, 0, 1);
|
||||||
|
///
|
||||||
|
/// print_ip(ip);
|
||||||
|
/// //Output: IP Address: 127.0.0.1
|
||||||
|
/// ```
|
||||||
|
#[allow(unused)]
|
||||||
|
pub fn print_ip(ip_address: IpAddr) {
|
||||||
|
println!("IP Address: {}", ip_address.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
mod tests {
|
||||||
|
#[test]
|
||||||
|
/// Tests if an IP is converted to a string
|
||||||
|
/// 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")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
/// Tests if an IP is converted from a string
|
||||||
|
/// correctly using the FromString trait
|
||||||
|
fn string_to_ip() {
|
||||||
|
use super::*;
|
||||||
|
let ip = "127.0.0.1";
|
||||||
|
assert_eq!(IpAddr::from_str(ip).unwrap(), IpAddr::V4(127, 0, 0, 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic]
|
||||||
|
///Tests if an invalid Ip will cause a panic when
|
||||||
|
/// converting from a string to an IpAddr
|
||||||
|
fn invalid_string_to_ip() {
|
||||||
|
use super::*;
|
||||||
|
let ip = "127.0.0.0.1";
|
||||||
|
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 ip_addr: [u8; 4] = [127, 0, 0, 1];
|
||||||
|
assert_eq!(
|
||||||
|
IpAddr::from_arr(&ip_addr).unwrap(),
|
||||||
|
IpAddr::V4(127, 0, 0, 1)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
/// Tests the conversion of an IPv4 Address to
|
||||||
|
/// an unsigned 32-bit integer
|
||||||
|
fn ipaddr_to_u32() {
|
||||||
|
use super::*;
|
||||||
|
let ip = IpAddr::V4(0, 0, 0, 0);
|
||||||
|
assert_eq!(0, u32::from(ip));
|
||||||
|
|
||||||
|
let ip = IpAddr::V4(127, 0, 0, 1);
|
||||||
|
assert_eq!(2_130_706_433, u32::from(ip));
|
||||||
|
|
||||||
|
let ip = IpAddr::V4(10, 10, 10, 0);
|
||||||
|
assert_eq!(168_430_080, u32::from(ip));
|
||||||
|
|
||||||
|
let ip = IpAddr::V4(255, 255, 255, 255);
|
||||||
|
assert_eq!(u32::MAX, u32::from(ip));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
/// Tests the conversion of an u32 IPv4 Address to
|
||||||
|
/// an IpAddr
|
||||||
|
fn u32_to_ipaddr() {
|
||||||
|
use super::*;
|
||||||
|
let ip = u32::from(IpAddr::V4(0, 0, 0, 0));
|
||||||
|
assert_eq!(IpAddr::V4(0, 0, 0, 0), IpAddr::from(ip));
|
||||||
|
|
||||||
|
let ip = u32::from(IpAddr::V4(127, 0, 0, 1));
|
||||||
|
assert_eq!(IpAddr::V4(127, 0, 0, 1), IpAddr::from(ip));
|
||||||
|
|
||||||
|
let ip = u32::from(IpAddr::V4(10, 10, 10, 0));
|
||||||
|
assert_eq!(IpAddr::V4(10, 10, 10, 0), IpAddr::from(ip));
|
||||||
|
|
||||||
|
let ip = u32::from(IpAddr::V4(255, 255, 255, 255));
|
||||||
|
assert_eq!(IpAddr::V4(255, 255, 255, 255), IpAddr::from(ip));
|
||||||
|
}
|
||||||
|
}
|
405
src/lib.rs
Normal file
405
src/lib.rs
Normal file
@ -0,0 +1,405 @@
|
|||||||
|
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<Network>,
|
||||||
|
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<Subnet, NetworkingErr> {
|
||||||
|
//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<NetworkClass, NetworkingErr> {
|
||||||
|
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<IpAddr, NetworkingErr> {
|
||||||
|
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::<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));
|
||||||
|
}
|
||||||
|
|
||||||
|
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)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +0,0 @@
|
|||||||
mod networking;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
println!("Hello, World!")
|
|
||||||
}
|
|
@ -1,94 +0,0 @@
|
|||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
/// Ip address enum that includes associated type
|
|
||||||
///
|
|
||||||
/// # Example
|
|
||||||
/// ```
|
|
||||||
/// //Loopback Addresses:
|
|
||||||
/// IpAddr::V4(127, 0, 0, 1)
|
|
||||||
/// IpAddr::V6(String::from("::1"))
|
|
||||||
/// ```
|
|
||||||
#[allow(unused)]
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
|
||||||
pub enum IpAddr {
|
|
||||||
V4(u8, u8, u8, u8),
|
|
||||||
V6(String)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct InvalidIPErr;
|
|
||||||
|
|
||||||
impl ToString for IpAddr {
|
|
||||||
/// Function that returns an IP address in string form
|
|
||||||
fn to_string(&self) -> String {
|
|
||||||
match self {
|
|
||||||
IpAddr::V4(oct1, oct2, oct3, oct4) => format!("{}.{}.{}.{}", oct1, oct2, oct3, oct4),
|
|
||||||
IpAddr::V6(addr) => format!("{}", addr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
impl FromStr for IpAddr {
|
|
||||||
type Err = InvalidIPErr;
|
|
||||||
/// Function that generates a IpAddr / Err from a string
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
||||||
let split_ip = s.split('.');
|
|
||||||
if split_ip.clone().count() != 4 {
|
|
||||||
//Invalid IP address entered
|
|
||||||
return Err(InvalidIPErr)
|
|
||||||
}
|
|
||||||
let mut ip: [u8; 4] = Default::default();
|
|
||||||
//Go through each octet and ensure it can be parsed;
|
|
||||||
for (i, oct) in split_ip.into_iter().enumerate() {
|
|
||||||
match oct.parse::<u8>() {
|
|
||||||
Ok(parsed_oct) => ip[i] = parsed_oct,
|
|
||||||
Err(_) => return Err(InvalidIPErr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(IpAddr::V4(ip[0],ip[1],ip[2],ip[3]))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Function that takes in an IP address and then prints a formatted string to the CLI
|
|
||||||
///
|
|
||||||
/// # Example
|
|
||||||
/// ```
|
|
||||||
/// let ip = IpAddr::V4(127, 0, 0, 1);
|
|
||||||
///
|
|
||||||
/// print_ip(ip);
|
|
||||||
/// //Output: IP Address: 127.0.0.1
|
|
||||||
/// ```
|
|
||||||
#[allow(unused)]
|
|
||||||
pub fn print_ip(ip_address: IpAddr) {
|
|
||||||
println!("IP Address: {}", ip_address.to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
mod tests {
|
|
||||||
#[test]
|
|
||||||
/// Tests if an IP is converted to a string
|
|
||||||
/// 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")
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
/// Tests if an IP is converted from a string
|
|
||||||
/// correctly using the FromString trait
|
|
||||||
fn string_to_ip() {
|
|
||||||
use super::*;
|
|
||||||
let ip = "127.0.0.1";
|
|
||||||
assert_eq!(IpAddr::from_str(ip).unwrap(), IpAddr::V4(127, 0, 0, 1))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[should_panic]
|
|
||||||
fn invalid_string_to_ip() {
|
|
||||||
use super::*;
|
|
||||||
let ip = "Hello, world";
|
|
||||||
IpAddr::from_str(ip).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
mod ip;
|
|
||||||
use ip::IpAddr;
|
|
||||||
|
|
||||||
#[allow(unused)]
|
|
||||||
pub struct Network {
|
|
||||||
network_address: IpAddr,
|
|
||||||
broadcast_addr: IpAddr,
|
|
||||||
subnet_mask: Option<IpAddr>
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Network {
|
|
||||||
// pub fn generate_subnets(self) -> Vec<Network> {}
|
|
||||||
// fn get_net_id(self) -> u8 {}
|
|
||||||
}
|
|
Reference in New Issue
Block a user