Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Iterator for supernets. #23

Open
jcgruenhage opened this issue Jun 19, 2020 · 5 comments
Open

Iterator for supernets. #23

jcgruenhage opened this issue Jun 19, 2020 · 5 comments

Comments

@jcgruenhage
Copy link

It'd be useful if it was possible to iterate over the supernets of a net. This can already be done with something like

let net = "10.0.0.0/8".parse().unwrap();
let mut tmp_net = net.clone();
while let Some(supernet) = tmp_net.supernet() {
  tmp_net = supernet;
  // some code here
}

Doing this here instead would obv be a lot cleaner:

let net = "10.0.0.0/8".parse().unwrap();
for supernet in net.supernets() {
  // some code here
}

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.

@krisprice
Copy link
Owner

Hi @jcgruenhage -- out of curiosity how are you using that?

I'm happy to include it if you want to keep implementing it. 👍

@jcgruenhage
Copy link
Author

jcgruenhage commented Jul 1, 2020

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).

@carneeki
Copy link

carneeki commented Oct 4, 2020

+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

@jcgruenhage
Copy link
Author

jcgruenhage commented Oct 5, 2020

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.

carneeki added a commit to carneeki/ipnet that referenced this issue Oct 19, 2020
carneeki added a commit to carneeki/ipnet that referenced this issue Oct 19, 2020
@carneeki
Copy link

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants