Compare commits

...

38 Commits

Author SHA1 Message Date
b2ca7f35f3 Changed function name and removed unused code. 2023-09-19 10:23:53 +01:00
53837d7b33 Used cargo fmt 2023-08-05 13:24:09 +01:00
aeae3af5e6 Updated version number in cargo toml 2023-08-05 13:15:13 +01:00
aee8974e2b Made netwrok struct parameters public 2023-08-05 13:11:13 +01:00
f224cf4c29 Added ability to create full networks, including the number of hosts on each as well as the broadcast address for each given network 2023-08-04 11:30:03 +01:00
108f8f2ab4 #5 Update cargo.toml and license to allow for publish to crates.io 2023-05-26 14:20:19 +01:00
00735d5691 #4 Adapated code to contain a lib.rs so can be used as library. Have also updated doctests as they were poorly formatted and did not actually test any of the functionality 2023-05-26 14:04:21 +01:00
30344b0605 Cleaned up some minor issues in main.rs making the final output easier for the end user to read and comprehend 2023-05-15 22:27:54 +01:00
625233ab86 #2 Completed the implementation of create_subnet function, all subnet network addresses are now ALL correctly appended to the vector. Would like to add some more resilient unit testing at some point down the line to verify the accuracy / robustness of the function 2023-05-15 20:26:49 +01:00
8c88d7945a #2 Added IpAddr from u32 function. Finally ready to integrate it into subnet calculation 2023-05-15 19:51:26 +01:00
6a4573450c #2 Made IpAddr to u32 function more efficient by using big endian functions built into rust 2023-05-15 19:35:07 +01:00
4ace257932 #2 Added new function to convert an IPv4 Address to an Unsigned 32-bit interger. Also did some small bits of code cleanup 2023-05-15 19:22:02 +01:00
55ccf05902 Adapted the create_subnet function to itterate through each octet to stop the overflows. There is however still an issue where the actual IP addresses aren't added correctly into the networks list. 2023-05-12 14:17:53 +01:00
924142e96c Bugfix: u8 no longer overflows when there are no borrowed bits. 2023-05-02 07:29:32 +01:00
b8e4ff21b0 Completed first draft of create_subnet() and added additional tests. NOTE: One test is still failing in order to outline the fact that there is still a slight bug in the code 2023-05-02 07:25:05 +01:00
aad67836df create_subnet function now works when the CIDR borrows 8 or less bits, need to figure out a way to have it so that we can modify the other octest sequentially 2023-04-30 14:16:24 +01:00
2dcbd405e4 First draft of subnet generation function. Still not complete, does overflow but I believe it is only small logic changes 2023-04-27 23:28:34 +01:00
cf35765cb4 Further itterations on create_subnet function and relevant tests 2023-04-26 21:44:13 +01:00
6fafa97d61 Made alterations to functions using IP to make it use new functions 2023-04-26 21:19:35 +01:00
d0da5f587f Removed create_subnet function because of incorrect implementation, required network class to be take into account. Added network class and relevant tests 2023-04-25 21:45:43 +01:00
44bafbd0e6 Fnal upload of subnet_calculator before re-write 2023-04-25 20:16:28 +01:00
7f8f2a41fe Corrected use of bitwise or in place of exponent operation 2023-04-24 17:45:59 +01:00
ee116523d8 Started testing create_subnet function. Still WIP!!! 2023-04-20 22:30:33 +01:00
45c5227a19 removed mutability of test variable that is not needed. 2023-04-20 21:57:50 +01:00
3b4a5ce285 Added to_arr and from_arr methods to IpAddr 2023-04-20 21:42:13 +01:00
7d61265f08 Added useful comments to outline create_subnet function 2023-04-18 20:40:53 +01:00
e1a978cb1f Added new functionality to Network and changed error handling to be a single enum. create_subnet() is still WIP 2023-04-17 17:24:35 +01:00
a067a3e349 Created new errors for CIDR / Subnet mask generation 2023-04-10 11:13:36 +01:00
d1d4bc02ca Added function to convert CIDR to subnet mask 2023-04-09 10:07:15 +01:00
edc125d5fc Removed redundant variables 2023-03-06 21:51:47 +00:00
a9bb5c1d52 Moved varialble out of loop to extend scope 2023-03-06 21:42:31 +00:00
41fc1a3f6d Added documentation to ip_and_cidr_from_string() 2023-02-20 21:25:37 +00:00
6567553f8a Created new function to get IP and CIDR from a string independently + test 2023-02-20 18:06:57 +00:00
d792ea8c30 Added CIDR parser -> Really Janky code so will look to review / refactor the method at some point in the very near future 2023-02-13 22:05:33 +00:00
4f8412f3d0 Fixed getting IP address from CLI 2023-02-11 21:22:24 +00:00
f2ad0de89b Tried but can't seem to get Console input (Includes newline character) 2023-02-11 17:41:33 +00:00
43cd0f5bc5 Changed test for IpAddr::from_str() to test more points of failure 2023-02-11 16:49:23 +00:00
d5ec71a000 Added documentation to FromString trait on IpAddr 2023-02-11 16:35:30 +00:00
7 changed files with 669 additions and 115 deletions

View File

@ -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

View File

@ -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
View 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
View 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)
);
}
}

View File

@ -1,5 +0,0 @@
mod networking;
fn main() {
println!("Hello, World!")
}

View File

@ -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();
}
}

View File

@ -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 {}
}