#ip #prefix #ipv4-address #subnet #cidr #networking #generics

no-std generic-ip

IP地址类型,用于泛型编程

9个版本

0.1.1 2024年3月6日
0.1.0 2024年2月2日
0.1.0-rc.42023年8月7日
0.1.0-rc.22023年4月27日
0.1.0-alpha.22022年4月13日

#963网络编程

37 每月下载量
4 crates 中使用

MIT 许可证

10MB
6.5K SLoC

generic-ip-rs

Crates.io CI/CD codecov docs.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(())
}

方向

AddressInterfacePrefixAfi之类的名称在crate的不同模块中重复使用。例如,Address用于命名

如果不了解crate的布局,这将使得理解给定名称所引用的项目变得困难。

地址族

IP地址族 ipv4ipv6 在类型系统中通过零大小类型 concrete::Ipv4concrete::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 通过定义从“地址族类”到与其关联的类型(例如 AddressPrefix 等)的类型级映射来进一步泛化,以避免在 [any] 或 concrete 之间进行选择。

AfiClass 为每个 Ipv4Ipv6 实现。在这种情况下,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
);

依赖项