mirror of
https://github.com/maciejpedzich/subnet-solver.git
synced 2025-01-18 11:14: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