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: &Vec<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
57pub fn aggregate_ips(ip_list: &[Ipv4Addr]) -> Vec<Cidr> {
97 let mut ints: Vec<u32> = ip_list.iter().map(|ip| u32::from(*ip)).collect();
98 ints.sort();
99
100 let mut result = Vec::new();
101 let mut i = 0;
102
103 while i < ints.len() {
104 let mut size = 1;
105
106 loop {
107 let next_size = size * 2;
108 if !is_valid_segment(i, next_size, &ints) {
109 break;
110 }
111 size = next_size;
112 }
113
114 let prefix = (32 - size.trailing_zeros()) as u8;
115 let mask: u32 = if prefix == 0 {
116 0
117 } else {
118 (!0u32) << (32 - prefix)
119 };
120 let network = ints[i] & mask;
121
122 result.push(Cidr {
123 addr: Ipv4Addr::from(network),
124 prefix,
125 });
126
127 i += size;
128 }
129
130 result
131}
132
133#[cfg(test)]
134mod tests {
135 use super::*;
136 use std::net::Ipv4Addr;
137
138 #[test]
139 fn test_ip_to_u32_and_back() {
140 let ip_str = "192.168.1.1";
141 let ip_num = ip_to_u32(ip_str);
142 let ip_back = u32_to_ip(ip_num);
143 assert_eq!(ip_back, Ipv4Addr::new(192, 168, 1, 1));
144 }
145
146 #[test]
147 fn test_is_continuous() {
148 let block = vec![1, 2, 3, 4, 5];
149 assert!(is_continuous(&block));
150 let block2 = vec![1, 2, 4, 5];
151 assert!(!is_continuous(&block2));
152 }
153
154 #[test]
155 fn test_is_aligned() {
156 assert!(is_aligned(8, 4)); assert!(!is_aligned(7, 4)); }
159
160 #[test]
161 fn test_aggregate_ips_basic() {
162 let mut ips: Vec<Ipv4Addr> = Vec::new();
164 for i in 1..=4 {
165 ips.push(Ipv4Addr::new(10, 0, 0, i));
166 }
167
168 let cidrs = aggregate_ips(&ips);
169
170 let expected = vec![
171 Cidr {
172 addr: Ipv4Addr::new(10, 0, 0, 1),
173 prefix: 32,
174 },
175 Cidr {
176 addr: Ipv4Addr::new(10, 0, 0, 2),
177 prefix: 31,
178 },
179 Cidr {
180 addr: Ipv4Addr::new(10, 0, 0, 4),
181 prefix: 32,
182 },
183 ];
184
185 assert_eq!(cidrs.len(), expected.len());
186 assert_eq!(cidrs[0].addr, expected[0].addr);
187 assert_eq!(cidrs[0].prefix, expected[0].prefix);
188 }
189
190 #[test]
191 fn test_aggregate_ips_full_range() {
192 let mut ips: Vec<Ipv4Addr> = Vec::new();
194 for i in 1..=8 {
195 ips.push(Ipv4Addr::new(10, 0, 0, i));
196 }
197
198 let cidrs = aggregate_ips(&ips);
199
200 assert_eq!(cidrs.len(), 4);
201 assert_eq!(cidrs[0].addr, Ipv4Addr::new(10, 0, 0, 1));
202 assert_eq!(cidrs[0].prefix, 32);
203
204 assert_eq!(cidrs[1].addr, Ipv4Addr::new(10, 0, 0, 2));
205 assert_eq!(cidrs[1].prefix, 31);
206
207 assert_eq!(cidrs[2].addr, Ipv4Addr::new(10, 0, 0, 4));
208 assert_eq!(cidrs[2].prefix, 30);
209
210 assert_eq!(cidrs[3].addr, Ipv4Addr::new(10, 0, 0, 8));
211 assert_eq!(cidrs[3].prefix, 32);
212 }
213}