CIDR(Classless Inter-Domain Routing, 無類別域間路由)是用來區分IP網段十分好用的方法,之所以會被稱為「無類別」,是相對於過去將IPv4分為Class A~E這五類的方式,CIDR則是直接以IP位址的前綴來分類網路,要多長有多長、要多短有多短,十分自由。Rust程式語言的標準函式庫雖然有內建IPv4和IPv6的結構體,但充其量也只能拿來表示IP位址與進行類型的判斷罷了,更為複雜的操作,都需要由程式設計師再另外開發。在開發或是設定網路服務的時候經常會需要使用到CIDR,究竟在Rust程式語言中該如何處理它呢?
CIDR Utils
「CIDR Utils」是筆者開發的套件,提供了一些資料結構和函數來處理IPv4與IPv6的CIDR,可以將CIDR的子網路合併大網路,也可以將大網路切割成子網路。
Crates.io
Cargo.toml
使用方法
cidr_utils
這個crate中提供了cidr
模組,提供了能將CIDR給模組化的結構體。命名方式參照Rust標準函式庫的IpAddr
、IPv4Addr
和IPv6Addr
,cidr
模組的結構體名稱為IpCidr
、Ipv4Cidr
、Ipv6Cidr
,並且還提供了能讓其它型別直接用來表示IP的Ipv4Able
和Ipv6Able
特性。預設實作Ipv4Able
特性的型別有u32
、[u8; 4]
、IPv4Addr
;預設實作Ipv6Able
特性的型別有u128
、[u8; 16]
、[u16; 8]
和IPv6Addr
。
至於CIDR則可以由IP前綴加上網路遮罩或是IP前綴加上位元數量,或是直接用如IP/位元數量
這樣的字串格式來表示。詳細的字串格式可以參考以下程式實例:
use cidr_utils::cidr::IpCidr;
assert_eq!(true, IpCidr::is_ip_cidr("192.168.1.0/24"));
assert_eq!(true, IpCidr::is_ip_cidr("192.168.1.1/24"));
assert_eq!(true, IpCidr::is_ip_cidr("192.168.1.0/0"));
assert_eq!(false, IpCidr::is_ip_cidr("192.168.1.0/33"));
assert_eq!(true, IpCidr::is_ip_cidr("192.168.1.0/255.255.255.0"));
assert_eq!(true, IpCidr::is_ip_cidr("192.168.1.0/255.255.254.0"));
assert_eq!(false, IpCidr::is_ip_cidr("192.168.1.0/255.255.255.1"));
assert_eq!(true, IpCidr::is_ip_cidr("192.168.1.0")); // 192.168.1.0/32
assert_eq!(true, IpCidr::is_ip_cidr("192.168.1")); // 192.168.1/24
assert_eq!(true, IpCidr::is_ip_cidr("192.168")); // 192.168/16
assert_eq!(true, IpCidr::is_ip_cidr("192")); // 192/8
assert_eq!(true, IpCidr::is_ip_cidr("192/8"));
assert_eq!(true, IpCidr::is_ip_cidr("192/255.0.0.0"));
assert_eq!(false, IpCidr::is_ip_cidr("192/16"));
assert_eq!(false, IpCidr::is_ip_cidr("192/255.255.0.0"));
assert_eq!(true, IpCidr::is_ip_cidr("2001:4f8:3:ba::/64"));
assert_eq!(true, IpCidr::is_ip_cidr("2001:4f8:3:ba:2e0:81ff:fe22:d1f1/128"));
assert_eq!(true, IpCidr::is_ip_cidr("::ffff:1.2.3.0/120"));
assert_eq!(true, IpCidr::is_ip_cidr("::ffff:1.2.3.1/120"));
assert_eq!(false, IpCidr::is_ip_cidr("::ffff:1.2.3.0/129"));
assert_eq!(true, IpCidr::is_ip_cidr("2001:4f8:3:ba:2e0:81ff:fe22:d1f1")); // 2001:4f8:3:ba:2e0:81ff:fe22:d1f1/128
assert_eq!(true, IpCidr::is_ip_cidr("2001:4f8:3:ba::"));
使用IpCidr
等結構實體的contains
方法,可以判斷一個IP位址是否在該CIDR的範圍內。size
方法則可以用來計算該CIDR的範圍內的IP位址數量。參考以下程式實例:
use std::net::IpAddr;
use std::str::FromStr;
use cidr_utils::cidr::IpCidr;
let cidr = IpCidr::from_str("192.168.51.0/24").unwrap();
assert_eq!(true, cidr.contains(IpAddr::from_str("192.168.51.103").unwrap()));
assert_eq!(false, cidr.contains(IpAddr::from_str("192.168.50.103").unwrap()));
use std::net::IpAddr;
use std::str::FromStr;
use cidr_utils::cidr::IpCidr;
let cidr = IpCidr::from_str("192.168.51.0/24").unwrap();
assert_eq!(true, cidr.contains(IpAddr::from_str("192.168.51.103").unwrap()));
assert_eq!(false, cidr.contains(IpAddr::from_str("192.168.50.103").unwrap()));
use std::net::Ipv4Addr;
use cidr_utils::cidr::Ipv4Cidr;
let cidr = Ipv4Cidr::from_str("192.168.51.0/24").unwrap();
assert_eq!(true, cidr.contains([192, 168, 51, 103]));
assert_eq!(true, cidr.contains(Ipv4Addr::new(192, 168, 51, 103)));
assert_eq!(false, cidr.contains([192, 168, 50, 103]));
assert_eq!(256, cidr.size());
另外在cidr_utils
這個crate中還提供了utils
模組,可以用來對CIDR進行特殊的處理。例如IpCidrCombiner
、Ipv4CidrCombiner
和Ipv6CidrCombiner
結構體,可以用來將多個CIDR網路合併成比較大的CIDR網路。
程式如下:
use cidr_utils::cidr::Ipv4Cidr;
use cidr_utils::utils::Ipv4CidrCombiner;
let mut combiner = Ipv4CidrCombiner::new();
combiner.push(Ipv4Cidr::from_str("192.168.51.100").unwrap());
assert_eq!(1, combiner.len());
assert_eq!("192.168.51.100/32".to_string(), combiner[0].to_string());
combiner.push(Ipv4Cidr::from_str("192.168.51.101").unwrap());
assert_eq!(1, combiner.len());
assert_eq!("192.168.51.100/31".to_string(), combiner[0].to_string());
combiner.push(Ipv4Cidr::from_str("192.168.51.102").unwrap());
assert_eq!(2, combiner.len());
assert_eq!("192.168.51.100/31".to_string(), combiner[0].to_string());
assert_eq!("192.168.51.102/32".to_string(), combiner[1].to_string());
combiner.push(Ipv4Cidr::from_str("192.168.51.103").unwrap());
assert_eq!(1, combiner.len());
assert_eq!("192.168.51.100/30".to_string(), combiner[0].to_string());
assert_eq!(true, combiner.contains([192, 168, 51, 102]));
assert_eq!(false, combiner.contains([192, 168, 51, 105]));
assert_eq!(4, combiner.size());
utils
模組也提供了IpCidrSeparator
、Ipv4CidrSeparator
和Ipv6CidrSeparator
結構體,可以用來將一個CIDR網路分割成多個較小的CIDR網路。
程式如下:
use cidr_utils::cidr::Ipv4Cidr;
use cidr_utils::utils::Ipv4CidrSeparator;
let cidr = Ipv4Cidr::from_str("192.168.56.0/24").unwrap();
let result = Ipv4CidrSeparator::divide_by(&cidr, 4).unwrap();
assert_eq!(4, result.len());
assert_eq!(64, result[0].size());
assert_eq!(64, result[1].size());
assert_eq!(64, result[2].size());
assert_eq!(64, result[3].size());
assert_eq!("[192.168.56.0/26]".to_string(), result[0].to_string());
assert_eq!("[192.168.56.64/26]".to_string(), result[1].to_string());
assert_eq!("[192.168.56.128/26]".to_string(), result[2].to_string());
assert_eq!("[192.168.56.192/26]".to_string(), result[3].to_string());
let result = Ipv4CidrSeparator::divide_by(&cidr, 5).unwrap();
assert_eq!(5, result.len());
assert_eq!(51, result[0].size());
assert_eq!(51, result[1].size());
assert_eq!(51, result[2].size());
assert_eq!(51, result[3].size());
assert_eq!(52, result[4].size());
assert_eq!("[192.168.56.0/27, 192.168.56.32/28, 192.168.56.48/31, 192.168.56.50/32]".to_string(), result[0].to_string());
assert_eq!("[192.168.56.51/32, 192.168.56.52/30, 192.168.56.56/29, 192.168.56.64/27, 192.168.56.96/30, 192.168.56.100/31]".to_string(), result[1].to_string());
assert_eq!("[192.168.56.102/31, 192.168.56.104/29, 192.168.56.112/28, 192.168.56.128/28, 192.168.56.144/29, 192.168.56.152/32]".to_string(), result[2].to_string());
assert_eq!("[192.168.56.153/32, 192.168.56.154/31, 192.168.56.156/30, 192.168.56.160/27, 192.168.56.192/29, 192.168.56.200/30]".to_string(), result[3].to_string());
assert_eq!("[192.168.56.204/30, 192.168.56.208/28, 192.168.56.224/27]".to_string(), result[4].to_string());
let result = Ipv4CidrSeparator::sub_networks(&cidr, 26).unwrap();
assert_eq!(4, result.len());
assert_eq!(64, result[0].size());
assert_eq!(64, result[1].size());
assert_eq!(64, result[2].size());
assert_eq!(64, result[3].size());
assert_eq!("192.168.56.0/26".to_string(), result[0].to_string());
assert_eq!("192.168.56.64/26".to_string(), result[1].to_string());
assert_eq!("192.168.56.128/26".to_string(), result[2].to_string());
assert_eq!("192.168.56.192/26".to_string(), result[3].to_string());
Serde框架支援
啟用serde
特色,來讓這個套件支援Serde框架。
[dependencies.cidr-utils]
version = "*"
features = ["serde"]