ACP: Extended logic for IP networks · Issue #235 · rust-lang/libs-team (original) (raw)

Proposal

Problem statement

While most networked applications only need to deal with individual IP addresses, it is sometimes desirable to work with entire ranges of IP addresses, especially under IPv6.

Motivating examples or use cases

The primary use case for most workflows will be filtering (allow-listing or deny-listing) ranges of IPs for connections, although more dedicated networking code that routes traffic among several hosts could also benefit from this.

Additionally, configuring IP addresses for certain applications can benefit from logic surrounding IP networks. For example, under IPv6, it's very common for a host to have access to an entire 64-bit address space and to have free reign over which specific addresses they actually listen on. In these cases, ad-hoc connections can be made on a randomized IP address within this space, where a randomly chosen IP is then checked for collision with an existing address rather than iterating over all possible addresses.

Solution sketch

A good minimal proposal is the following:

Note that this proposal does not currently include separate IP network types, although that could be a future extension. The main benefit here is to provide operations which can only be provided by the standard library since it owns the IP address types, while leaving the usage of those operations to implement dedicated types up to the end user and various crate authors.

Examples

Checking a network mask with BitAnd:

let addr = Ipv6Addr::new(0x2001, 0xdb8, 0x692e, 0x6e2e, 0x672e, 0x672e, 0x792e, 0x752e); let mask = Ipv6Addr::new(0xffff, 0xffff, 0, 0, 0, 0, 0, 0); let net = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0); assert_eq!(addr & mask, net);

Creating an address in a network with BitOr:

let rand_token = Ipv6Addr::new(0, 0, 0, 0, 0, 4); let net = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0); let addr = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 4); assert_eq!(net | rand, addr);

Checking validity of a network mask:

let valid = Ipv4Addr::new(255, 255, 255, 192) let invalid = Ipv4Addr::new(192, 255, 255, 255); assert_eq!(valid.leading_ones() + valid.trailing_zeros(), Ipv4Addr::BITS); assert_ne!(invalid.leading_ones() + invalid.trailing_zeros(), Ipv6Addr::BITS);

Getting the first and last IPs in a network:

let net = Ipv4Addr::new(192, 168, 0, 0); let mask = Ipv4Addr::new(255, 255, 255, 0); let mut range = net..=(net | !mask); let first = range.skip(1).next().unwrap(); let last = range.next_back().unwrap(); assert_eq!(first, Ipv4Addr::new(192, 168, 0, 1)); assert_eq!(last, Ipv4Addr::new(192, 168, 0, 255));

Alternatives

All of these operations could be done through the conversions to u32 and u128, and this is indeed what existing crates do. The primary alternative would be to have users rely on these conversions for any of the proposed operations that are not implemented.

Some existing IP network crates:

What happens now?

This issue is part of the libs-api team API change proposal process. Once this issue is filed the libs-api team will review open proposals as capability becomes available. Current response times do not have a clear estimate, but may be up to several months.

Possible responses

The libs team may respond in various different ways. First, the team will consider the problem (this doesn't require any concrete solution or alternatives to have been proposed):

Second, if there's a concrete solution: