1use std::net::Ipv4Addr;
2
3#[derive(Debug, Clone)]
5pub struct Cidr {
6 pub addr: Ipv4Addr,
7 pub prefix: u8,
8}
9
10#[allow(unused)]
11fn ip_to_u32(ip: &str) -> u32 {
12 let addr: Ipv4Addr = ip.parse().unwrap();
13 u32::from(addr)
14}
15
16#[allow(unused)]
17fn u32_to_ip(n: u32) -> Ipv4Addr {
18 Ipv4Addr::from(n)
19}
20
21#[allow(unused)]
22fn is_continuous(block: &[u32]) -> bool {
23 block.windows(2).all(|w| w[1] == w[0] + 1)
24}
25
26#[allow(unused)]
27fn is_aligned(start: u32, size: usize) -> bool {
28 start.is_multiple_of(size as u32)
29}
30
31#[allow(unused)]
32pub fn cidr_to_acl(cidr: &Cidr) -> String {
36 if cidr.prefix == 32 {
37 return format!("host {}", cidr.addr);
38 }
39
40 let mask = u32::MAX << (32 - cidr.prefix);
42 let octets = mask.to_be_bytes();
43
44 format!(
45 "{} {}.{}.{}.{}",
46 cidr.addr, octets[0], octets[1], octets[2], octets[3]
47 )
48}
49
50#[allow(unused)]
51fn is_valid_segment(i: usize, next_size: usize, ints: &[u32]) -> bool {
52 i + next_size <= ints.len()
53 && is_aligned(ints[i], next_size)
54 && is_continuous(&ints[i..i + next_size])
55}
56
57fn build_cidr(start_ip: &u32, prefix: u8) -> Cidr {
58 let mask: u32 = if prefix == 0 {
59 0
60 } else {
61 (!0u32) << (32 - prefix)
62 };
63 let network = start_ip & mask;
64
65 Cidr {
66 addr: Ipv4Addr::from(network),
67 prefix,
68 }
69}
70
71pub fn aggregate_ips(ip_list: &[Ipv4Addr]) -> Vec<Cidr> {
111 let mut ints: Vec<u32> = ip_list.iter().map(|ip| u32::from(*ip)).collect();
112 ints.sort();
113
114 let mut result = Vec::new();
115 let mut i = 0;
116
117 while i < ints.len() {
118 let mut size = 1;
119
120 loop {
121 let next_size = size * 2;
122 if !is_valid_segment(i, next_size, &ints) {
123 break;
124 }
125 size = next_size;
126 }
127
128 let prefix = (32 - size.trailing_zeros()) as u8;
129 result.push(build_cidr(&ints[i], prefix));
130 i += size;
131 }
132 result
133}
134
135#[cfg(test)]
136mod tests {
137 use super::*;
138 use std::net::Ipv4Addr;
139
140 #[test]
141 fn test_ip_to_u32_and_back() {
142 let ip_str = "192.168.1.1";
143 let ip_num = ip_to_u32(ip_str);
144 let ip_back = u32_to_ip(ip_num);
145 assert_eq!(ip_back, Ipv4Addr::new(192, 168, 1, 1));
146 }
147
148 #[test]
149 fn test_is_continuous() {
150 let block = vec![1, 2, 3, 4, 5];
151 assert!(is_continuous(&block));
152 let block2 = vec![1, 2, 4, 5];
153 assert!(!is_continuous(&block2));
154 }
155
156 #[test]
157 fn test_is_aligned() {
158 assert!(is_aligned(8, 4)); assert!(!is_aligned(7, 4)); }
161
162 #[test]
163 fn test_aggregate_ips_basic() {
164 let mut ips: Vec<Ipv4Addr> = Vec::new();
166 for i in 1..=4 {
167 ips.push(Ipv4Addr::new(10, 0, 0, i));
168 }
169
170 let cidrs = aggregate_ips(&ips);
171
172 let expected = vec![
173 Cidr {
174 addr: Ipv4Addr::new(10, 0, 0, 1),
175 prefix: 32,
176 },
177 Cidr {
178 addr: Ipv4Addr::new(10, 0, 0, 2),
179 prefix: 31,
180 },
181 Cidr {
182 addr: Ipv4Addr::new(10, 0, 0, 4),
183 prefix: 32,
184 },
185 ];
186
187 assert_eq!(cidrs.len(), expected.len());
188 assert_eq!(cidrs[0].addr, expected[0].addr);
189 assert_eq!(cidrs[0].prefix, expected[0].prefix);
190 }
191
192 #[test]
193 fn test_aggregate_ips_full_range() {
194 let mut ips: Vec<Ipv4Addr> = Vec::new();
196 for i in 1..=8 {
197 ips.push(Ipv4Addr::new(10, 0, 0, i));
198 }
199
200 let cidrs = aggregate_ips(&ips);
201
202 assert_eq!(cidrs.len(), 4);
203 assert_eq!(cidrs[0].addr, Ipv4Addr::new(10, 0, 0, 1));
204 assert_eq!(cidrs[0].prefix, 32);
205
206 assert_eq!(cidrs[1].addr, Ipv4Addr::new(10, 0, 0, 2));
207 assert_eq!(cidrs[1].prefix, 31);
208
209 assert_eq!(cidrs[2].addr, Ipv4Addr::new(10, 0, 0, 4));
210 assert_eq!(cidrs[2].prefix, 30);
211
212 assert_eq!(cidrs[3].addr, Ipv4Addr::new(10, 0, 0, 8));
213 assert_eq!(cidrs[3].prefix, 32);
214 }
215}