9个版本
0.1.1 | 2024年3月6日 |
---|---|
0.1.0 | 2024年2月2日 |
0.1.0-rc.4 | 2023年8月7日 |
0.1.0-rc.2 | 2023年4月27日 |
0.1.0-alpha.2 | 2022年4月13日 |
#963 在 网络编程 中
37 每月下载量
在 4 crates 中使用
10MB
6.5K SLoC
generic-ip-rs
关于
stdlib IP地址类型的替代品,旨在提供对IP地址族的泛型抽象的简化。
lib.rs
:
用于在地址族上泛型处理IP地址和前缀的类型和特性。
std::net
中的IP地址类型不共享任何表示“这是一个IP地址”的公共特性。
这种限制使得以地址族无关的方式处理IP地址的代码编写变得不必要地困难。
此crate提供了一系列类型,旨在与std::net
的地址类型和来自流行crate ipnet
的前缀类型兼容,但这些类型是地址族的泛型。
例如
use ip::{Address, Afi, Error, Ipv4, Ipv6, Prefix};
struct RibEntry<A: Afi> {
prefix: Prefix<A>,
next_hop: Address<A>,
}
impl<A: Afi> RibEntry<A> {
fn get_next_hop(&self, addr: Address<A>) -> Option<Address<A>> {
(self.prefix >= addr).then(|| self.next_hop)
}
}
fn main() -> Result<(), Error> {
let v4: RibEntry<Ipv4> = RibEntry {
prefix: "192.0.2.0/24".parse()?,
next_hop: "198.51.100.1".parse()?,
};
let v6: RibEntry<Ipv6> = RibEntry {
prefix: "2001:db8::/48".parse()?,
next_hop: "2001:db8:f00::1".parse()?,
};
assert_eq!(
v4.get_next_hop("192.0.2.127".parse()?),
Some("198.51.100.1".parse()?)
);
assert_eq!(v6.get_next_hop("2001:db8:ffff::ffff".parse()?), None);
Ok(())
}
方向
如Address
、Interface
、Prefix
或 Afi
之类的名称在crate的不同模块中重复使用。例如,Address
用于命名
如果不了解crate的布局,这将使得理解给定名称所引用的项目变得困难。
地址族
IP地址族 ipv4
和 ipv6
在类型系统中通过零大小类型 concrete::Ipv4
和 concrete::Ipv6
来表示。
这些“具体”地址族实现了 traits::Afi
,这反过来又限制了由 concrete
模块导出的项的泛型参数,例如 concrete::Address<A>
和 concrete::Prefix<A>
。
相反,[any
] 模块导出了一系列对应于两个具体地址族的 enum
,每个变体都包含相应的 concrete::*
项目。
地址族类
通常,一个给定的用例将需要处理单个已知(在编译时)地址族的对象,或者可能属于任一地址族的对象,如下所示
use ip::{any, concrete, Afi, Ipv4, Ipv6};
// `x` and `y` must be the same address-family
fn longer_concrete<A: Afi>(
x: concrete::Prefix<A>,
y: concrete::Prefix<A>,
) -> concrete::Prefix<A> {
if x.length() > y.length() {
x
} else {
y
}
}
// `x` and `y` may be of different address families, so may not be
// comparable
fn longer_any(x: any::Prefix, y: any::Prefix) -> Option<any::Prefix> {
match (x, y) {
(any::Prefix::Ipv4(x), any::Prefix::Ipv4(y)) => Some(longer_concrete(x, y).into()),
(any::Prefix::Ipv6(x), any::Prefix::Ipv6(y)) => Some(longer_concrete(x, y).into()),
_ => None,
}
}
let x4: concrete::Prefix<Ipv4> = "192.0.2.0/24".parse().unwrap();
let y4: concrete::Prefix<Ipv4> = "203.0.113.128/25".parse().unwrap();
let x6: concrete::Prefix<Ipv6> = "2001:db8:f00::/48".parse().unwrap();
let y6: concrete::Prefix<Ipv6> = "2001:db8::/32".parse().unwrap();
assert_eq!(longer_concrete(x4, y4), y4);
assert_eq!(longer_concrete(x6, y6), x6);
assert_eq!(longer_any(x4.into(), y4.into()), Some(y4.into()));
assert_eq!(longer_any(x4.into(), y6.into()), None);
然而,有时可能需要一个数据结构,该结构有时可能包含多种地址族的混合,但在其他时候必须只包含单个地址族。
为了处理这种需求,traits::AfiClass
通过定义从“地址族类”到与其关联的类型(例如 Address
、Prefix
等)的类型级映射来进一步泛化,以避免在 [any
] 或 concrete
之间进行选择。
AfiClass
为每个 Ipv4
和 Ipv6
实现。在这种情况下,Ipv4
/Ipv6
可以从概念上视为地址族 { ipv4 }
和 { ipv6 }
的单例类。
此外,类型 any::Any
实现了 AfiClass
,为 [any
] 模块中的项目提供类型级别的映射。 [Any
] 可以被看作是类 { ipv4, ipv6 }
。
在 crate 根目录中定义了各种类型别名,以便轻松访问此映射。通常,使用 Address<Ipv4>
或 Address<Any>
比使用 concrete::Address<Ipv4>
或 any::Address
更容易、更清晰。
示例
use ip::{Address, Afi, AfiClass, Any, Ipv4};
#[derive(Debug, PartialEq)]
struct Foo<A: AfiClass> {
addr: Address<A>,
}
impl<A: AfiClass> Foo<A> {
fn new(addr: Address<A>) -> Self {
Self { addr }
}
fn into_concrete<C>(self) -> Option<Foo<C>>
where
C: Afi,
Address<C>: TryFrom<Address<A>>,
{
self.addr.try_into().map(Foo::new).ok()
}
}
let anys: Vec<Foo<Any>> = vec![
Foo {
addr: Address::<Any>::Ipv4("192.0.2.1".parse().unwrap()),
},
Foo {
addr: Address::<Any>::Ipv6("2001:db8::1".parse().unwrap()),
},
Foo {
addr: Address::<Any>::Ipv4("198.51.100.1".parse().unwrap()),
},
];
let filtered: Vec<Foo<Ipv4>> = vec![
Foo {
addr: "192.0.2.1".parse().unwrap(),
},
Foo {
addr: "198.51.100.1".parse().unwrap(),
},
];
assert_eq!(
anys.into_iter()
.filter_map(Foo::into_concrete)
.collect::<Vec<Foo<Ipv4>>>(),
filtered
);