reconcile/gen_ip.rs
1// Copyright 2023 Developers of the reconcile project.
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9//! Provides utility methods to generate IP addresses from a CIDR.
10
11use ipnet::{IpBitAnd, IpBitOr, IpNet, Ipv4Net, Ipv6Net};
12use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
13
14use rand::Rng;
15
16/// Select a random IP address from the given network
17/// ```
18/// use rand::SeedableRng;
19/// use reconcile::gen_ip::gen_ip;
20///
21/// let mut rng = rand::rngs::StdRng::seed_from_u64(42);
22/// let net = "192.168.1.0/24".parse().unwrap();
23/// assert_eq!(gen_ip(&mut rng, net).to_string(), "192.168.1.162");
24/// ```
25pub fn gen_ip<R: Rng>(rng: &mut R, network: IpNet) -> IpAddr {
26 match network {
27 IpNet::V4(network) => IpAddr::V4(gen_ipv4(rng, network)),
28 IpNet::V6(network) => IpAddr::V6(gen_ipv6(rng, network)),
29 }
30}
31
32/// Select a random IPv4 address from the given network
33/// ```
34/// use rand::SeedableRng;
35/// use reconcile::gen_ip::gen_ipv4;
36///
37/// let mut rng = rand::rngs::StdRng::seed_from_u64(42);
38/// let net = "192.168.42.0/24".parse().unwrap();
39/// assert_eq!(gen_ipv4(&mut rng, net).to_string(), "192.168.42.162");
40/// ```
41pub fn gen_ipv4<R: Rng>(rng: &mut R, network: Ipv4Net) -> Ipv4Addr {
42 let random: Ipv4Addr = rng.gen::<u32>().into();
43 network.network().bitor(random.bitand(network.hostmask()))
44}
45
46/// Select a random IPv6 address from the given network
47/// ```
48/// use rand::SeedableRng;
49/// use reconcile::gen_ip::gen_ipv6;
50///
51/// let mut rng = rand::rngs::StdRng::seed_from_u64(42);
52/// let net = "2001:db8::/32".parse().unwrap();
53/// assert_eq!(gen_ipv6(&mut rng, net).to_string(), "2001:db8:3fad:517d:86cc:7763:2227:24a2");
54/// ```
55pub fn gen_ipv6<R: Rng>(rng: &mut R, network: Ipv6Net) -> Ipv6Addr {
56 let random: Ipv6Addr = rng.gen::<u128>().into();
57 network.network().bitor(random.bitand(network.hostmask()))
58}
59
60#[cfg(test)]
61mod tests {
62 use std::collections::HashSet;
63
64 use rand::SeedableRng;
65
66 use super::gen_ip;
67
68 const N_ADDR: usize = 1000;
69
70 #[test]
71 fn rand_ipv4() {
72 let mut rng = rand::rngs::StdRng::seed_from_u64(42);
73 let net = "127.0.0.0/8".parse().unwrap();
74 let addrs: HashSet<_> = (0..N_ADDR).map(|_| gen_ip(&mut rng, net)).collect();
75 assert_eq!(addrs.len(), N_ADDR);
76 for addr in addrs {
77 assert!(net.contains(&addr), "{net} should contain {addr}");
78 }
79 }
80
81 #[test]
82 fn rand_ipv6() {
83 let mut rng = rand::rngs::StdRng::seed_from_u64(42);
84 let net = "2001:db8::/32".parse().unwrap();
85 let addrs: HashSet<_> = (0..N_ADDR).map(|_| gen_ip(&mut rng, net)).collect();
86 assert_eq!(addrs.len(), N_ADDR);
87 for addr in addrs {
88 assert!(net.contains(&addr), "{net} should contain {addr}");
89 }
90 }
91}