Open jcgruenhage opened 4 years ago
Hi @jcgruenhage -- out of curiosity how are you using that?
I'm happy to include it if you want to keep implementing it. 👍
I'm not using that myself, but my flatmate is. The usecase is a list of CIDRs, where some have a flag set and some don't, and the goal is to find the number of IPs that have (or don't have? not sure right now) that bit set.
So, for counting, you check the bigger nets first, but if a smaller net is included in a bigger net, you need to change the count. For that to work, you need to look up whether a supernet is included in that list, so you need to iterate over the supernets. Right now, their code is doing while let
for that, but an iterator would be a lot nicer.
For the actual counting of IPs in an IpNet
, #24 is needed, so to reduce their code further, that'd also be needed.
They also likely need an IpRange
to Iterator<Item = IpNet>
method, which they've also written, so that'd be nice to upstream too once it's gotten a few more tests. The python stdlib has such a method included (which is quite impressive, the python stdlib really has everything).
+1! My use case is to find a set of "parent" CIDR networks for a given network (v4 / v6) up to a desired minimum prefix length and return an iterator. Here's a minimum working example that will supernet 10.224.195.200/29
up to /4
.
Happy to fork + send a PR, it would be my first upstream commit for a Rust project!
use ipnet::IpNet;
use std::cmp::Ordering::{Equal, Greater};
struct Supernets {
network: IpNet,
min_prefix_len: u8,
}
impl Supernets {
fn new(network: IpNet, min_prefix_len: u8) -> Self {
Supernets {
network: network,
min_prefix_len: min_prefix_len,
}
}
}
impl Iterator for Supernets {
type Item = IpNet;
fn next(&mut self) -> Option<Self::Item> {
match self.network.prefix_len().partial_cmp(&self.min_prefix_len) {
Some(Greater) => {
// calc next, return current
let current = self.network;
self.network = current.supernet().unwrap();
Some(current)
}
Some(Equal) => {
// calc next, return current
let current = self.network;
self.network = current.supernet().unwrap();
Some(current)
}
_ => {
// handle end condition
None
}
}
}
}
fn main() {
let start_net = "10.224.195.200/29".parse::<IpNet>().unwrap();
let supernets = Supernets::new(start_net, 4);
for net in supernets {
println!("{:?}", net);
}
}
Produces the following:
10.224.195.200/29
10.224.195.192/28
10.224.195.192/27
10.224.195.192/26
10.224.195.128/25
10.224.195.0/24
10.224.194.0/23
10.224.192.0/22
10.224.192.0/21
10.224.192.0/20
10.224.192.0/19
10.224.192.0/18
10.224.128.0/17
10.224.0.0/16
10.224.0.0/15
10.224.0.0/14
10.224.0.0/13
10.224.0.0/12
10.224.0.0/11
10.192.0.0/10
10.128.0.0/9
10.0.0.0/8
10.0.0.0/7
8.0.0.0/6
8.0.0.0/5
0.0.0.0/4
For ipv6:
fn main() {
let start_net = "2406:3600:25b:c500::/56".parse::<IpNet>().unwrap();
let supernets = Supernets::new(start_net, 48);
for net in supernets {
println!("{:?}", net);
}
}
Output:
2406:3600:25b:c500::/56
2406:3600:25b:c400::/55
2406:3600:25b:c400::/54
2406:3600:25b:c000::/53
2406:3600:25b:c000::/52
2406:3600:25b:c000::/51
2406:3600:25b:c000::/50
2406:3600:25b:8000::/49
2406:3600:25b::/48
That code looks good to me, except for the one match where you have branches with the same content. Maybe you should put Some(Less)
first, handle the end case there, and then handle the other two as one in the _
case.
That code looks good to me, except for the one match where you have branches with the same content. Maybe you should put
Some(Less)
first, handle the end case there, and then handle the other two as one in the_
case.
Hi Jan,
Nice catch. I've sent a PR with your recommendation in it.
It'd be useful if it was possible to iterate over the supernets of a net. This can already be done with something like
Doing this here instead would obv be a lot cleaner:
I've started implementing this, but wanted to post an issue before I sink more than a few minutes into it, to make sure this is something that would get merged if implemented.