Skip to main content

fire_scope/
ipv4_utils.rs

1use crate::error::AppError;
2use ipnet::{IpNet, Ipv4Net};
3use std::net::Ipv4Addr;
4
5pub trait ILog2Sub1 {
6    fn ilog2_sub1(&self) -> u32;
7}
8
9impl ILog2Sub1 for u32 {
10    fn ilog2_sub1(&self) -> u32 {
11        if *self == 0 {
12            0
13        } else {
14            31 - self.leading_zeros()
15        }
16    }
17}
18
19pub trait ILog2Sub1U64 {
20    fn ilog2_sub1_u64(&self) -> u32;
21}
22
23impl ILog2Sub1U64 for u64 {
24    fn ilog2_sub1_u64(&self) -> u32 {
25        if *self == 0 {
26            0
27        } else {
28            63 - self.leading_zeros()
29        }
30    }
31}
32
33/// currentから始まりendを超えない最大のIPv4 CIDRプレフィックス長(≤ 32)を返す。
34pub fn largest_ipv4_block(current: u64, end: u64) -> u8 {
35    debug_assert!(current <= end, "current must be <= end");
36
37    // current(32ビット空)の末尾ゼロビットの数
38    let tz: u32 = (current as u32).trailing_zeros();
39    // 残りのアドレス範囲に収まるビット数
40    let span: u32 = (end - current + 1).ilog2_sub1_u64();
41
42    // ホスト部で使用可能なビット
43    let max_block = tz.min(span);
44    // CIDRプレフィックス長(0-32)
45    (32 - max_block) as u8
46}
47
48/// IPv4の範囲[`start`, `end`]をCIDRの最小セットにまとめる。
49pub fn ipv4_summarize_range(start: u64, end: u64) -> Vec<IpNet> {
50    let mut cidrs = Vec::<IpNet>::new();
51    let mut current = start;
52
53    while current <= end {
54        let max_size = largest_ipv4_block(current, end);
55
56        // IPv4Netは32ビットアドレスのみをサポートする
57        if current > u32::MAX as u64 {
58            // 範囲外のセクションを無視する
59            break;
60        }
61
62        if let Ok(net) = Ipv4Net::new(Ipv4Addr::from(current as u32), max_size) {
63            cidrs.push(IpNet::V4(net));
64            let block_size: u64 = 1u64 << (32 - max_size);
65            current = current.saturating_add(block_size);
66        } else {
67            // フェイルセーフ
68            break;
69        }
70    }
71
72    cidrs
73}
74
75/// RIR拡張フォーマットのIPv4行(start, value)をCIDR列へ展開
76pub fn parse_ipv4_range_to_cidrs(start_str: &str, value_str: &str) -> Result<Vec<IpNet>, AppError> {
77    let start_addr = start_str.parse::<Ipv4Addr>()?;
78    let width_u64 = value_str.parse::<u64>()?;
79
80    if width_u64 == 0 {
81        return Err(AppError::ParseError("IPv4 width must be > 0".into()));
82    }
83
84    let start_num = u32::from(start_addr) as u64;
85    let end_num_u64 = start_num
86        .checked_add(width_u64)
87        .and_then(|v| v.checked_sub(1))
88        .ok_or_else(|| AppError::ParseError("IPv4 range is too large".into()))?;
89
90    if end_num_u64 > u32::MAX as u64 {
91        return Err(AppError::ParseError(
92            "IPv4 range exceeds 32-bit boundary".into(),
93        ));
94    }
95
96    Ok(ipv4_summarize_range(start_num, end_num_u64))
97}