mirror of
https://github.com/maciejpedzich/subnet-solver.git
synced 2025-01-18 19:24:46 +01:00
Initial commit
This commit is contained in:
commit
ed9402b947
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
/target
|
||||||
|
.idea
|
54
Cargo.lock
generated
Normal file
54
Cargo.lock
generated
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aho-corasick"
|
||||||
|
version = "1.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.7.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "podsieci-rust"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"regex",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex"
|
||||||
|
version = "1.11.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-automata",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-automata"
|
||||||
|
version = "0.4.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-syntax"
|
||||||
|
version = "0.8.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
7
Cargo.toml
Normal file
7
Cargo.toml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
[package]
|
||||||
|
name = "podsieci-rust"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
regex = "1.11.1"
|
6
rustfmt.toml
Normal file
6
rustfmt.toml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
max_width = 80
|
||||||
|
format_code_in_doc_comments = true
|
||||||
|
imports_granularity = "Crate"
|
||||||
|
imports_layout = "Vertical"
|
||||||
|
use_small_heuristics = "Max"
|
||||||
|
wrap_comments = true
|
139
src/main.rs
Normal file
139
src/main.rs
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
use regex::Regex;
|
||||||
|
use std::cmp::Ordering;
|
||||||
|
use std::collections::BTreeSet;
|
||||||
|
use std::env::args;
|
||||||
|
use std::fmt::{Display, Formatter};
|
||||||
|
use std::net::Ipv4Addr;
|
||||||
|
use std::process::exit;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
println!("(c) 2024 Maciej Pędzich. Released under the GNU General Public License v3.0");
|
||||||
|
println!();
|
||||||
|
|
||||||
|
let arguments = args().collect::<Vec<String>>();
|
||||||
|
|
||||||
|
if arguments.len() != 4 {
|
||||||
|
eprintln!("Please enter 3 arguments:");
|
||||||
|
eprintln!();
|
||||||
|
eprintln!("1. Main subnet CIDR, eg. 12.34.56.78/9");
|
||||||
|
eprintln!(
|
||||||
|
"2. Comma-separated list of subnets with their names and minimum number of hosts, \
|
||||||
|
eg. \"(A,12), (B,34), (C,56)\""
|
||||||
|
);
|
||||||
|
eprintln!(
|
||||||
|
"3. A-Z to order subnets with the same sizes alphabetically, \
|
||||||
|
or Z-A to use reverse alphabetical order"
|
||||||
|
);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let input_cidr = arguments[1].split_once("/").unwrap();
|
||||||
|
let input_ip = Ipv4Addr::from_str(input_cidr.0).unwrap();
|
||||||
|
let input_num_subnet_bits = input_cidr.1.parse::<u32>().unwrap();
|
||||||
|
let input_num_host_bits = Ipv4Addr::BITS - input_num_subnet_bits;
|
||||||
|
|
||||||
|
let input_subnet_mask = Ipv4Addr::from(
|
||||||
|
((1 << input_num_subnet_bits) - 1) << input_num_host_bits,
|
||||||
|
);
|
||||||
|
let input_broadcast_mask = Ipv4Addr::from((1 << input_num_host_bits) - 1);
|
||||||
|
let input_subnet_base_ip = input_ip & input_subnet_mask;
|
||||||
|
let input_subnet_broadcast_ip = input_subnet_base_ip | input_broadcast_mask;
|
||||||
|
|
||||||
|
println!("Input subnet's base IP: {}", input_subnet_base_ip);
|
||||||
|
println!("Input subnet's broadcast IP: {}", input_subnet_broadcast_ip);
|
||||||
|
println!();
|
||||||
|
|
||||||
|
let subnet_pattern = Regex::new(r"\((\w),(\d+)\)").unwrap();
|
||||||
|
let ordered_subnets = subnet_pattern
|
||||||
|
.captures_iter(&arguments[2])
|
||||||
|
.map(|capture| {
|
||||||
|
let [name, min_num_hosts] = capture.extract::<2>().1;
|
||||||
|
let min_num_ips = min_num_hosts.parse::<u32>().unwrap() + 2;
|
||||||
|
|
||||||
|
Subnet::new(name, min_num_ips)
|
||||||
|
})
|
||||||
|
.collect::<BTreeSet<Subnet>>();
|
||||||
|
|
||||||
|
let mut current_subnet_base_ip = input_subnet_base_ip;
|
||||||
|
|
||||||
|
for subnet in ordered_subnets {
|
||||||
|
print!("{}, ", subnet);
|
||||||
|
print!("Base IP: {}, ", current_subnet_base_ip);
|
||||||
|
|
||||||
|
let num_subnet_bits = (subnet.size - 1).leading_zeros();
|
||||||
|
let num_host_bits = Ipv4Addr::BITS - num_subnet_bits;
|
||||||
|
let subnet_mask =
|
||||||
|
Ipv4Addr::from(((1 << num_subnet_bits) - 1) << num_host_bits);
|
||||||
|
|
||||||
|
print!("Subnet mask: {}/{}, ", subnet_mask, num_subnet_bits);
|
||||||
|
|
||||||
|
current_subnet_base_ip =
|
||||||
|
Ipv4Addr::from(current_subnet_base_ip.to_bits() + subnet.size);
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"Broadcast IP: {}",
|
||||||
|
Ipv4Addr::from(current_subnet_base_ip.to_bits() - 1)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let num_input_subnet_ips = 1 << input_num_host_bits;
|
||||||
|
let total_num_subnet_ips =
|
||||||
|
current_subnet_base_ip.to_bits() - input_subnet_base_ip.to_bits();
|
||||||
|
|
||||||
|
println!();
|
||||||
|
println!("Number of available IPs: {}", num_input_subnet_ips);
|
||||||
|
println!("Number of IPs used by all subnets: {}", total_num_subnet_ips);
|
||||||
|
|
||||||
|
if total_num_subnet_ips > num_input_subnet_ips {
|
||||||
|
eprintln!("ERROR: Not enough available IPs");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Subnet {
|
||||||
|
name: String,
|
||||||
|
size: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Subnet {
|
||||||
|
fn new(name: &str, min_num_ips: u32) -> Self {
|
||||||
|
let num_host_bits = Ipv4Addr::BITS - (min_num_ips - 1).leading_zeros();
|
||||||
|
|
||||||
|
Subnet { name: name.to_string(), size: 1 << num_host_bits }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for Subnet {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.name == other.name && self.size == other.size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for Subnet {}
|
||||||
|
|
||||||
|
impl Ord for Subnet {
|
||||||
|
fn cmp(&self, other: &Self) -> Ordering {
|
||||||
|
other.size.cmp(&self.size).then_with(|| {
|
||||||
|
let name_order = args().nth(3).unwrap();
|
||||||
|
|
||||||
|
if name_order == "A-Z" {
|
||||||
|
self.name.cmp(&other.name)
|
||||||
|
} else {
|
||||||
|
other.name.cmp(&self.name)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialOrd<Self> for Subnet {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
|
Some(self.cmp(other))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Subnet {
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
|
||||||
|
write!(f, "{}) Size: {}", self.name, self.size)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user