Skip to content

ACP: Extended logic for IP networks #235

Closed

Description

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:

  • BitAnd and BitOr impls for Ipv4Addr and Ipv6Addr (not for IpAddr, since the operands must be the same address type)
  • Not for Ipv4Addr, Ipv6Addr, and maybe IpAddr (we can implement this for generic IP addresses, but I'm not sure if it'd be useful)
  • leading_zeros, leading_ones, trailing_zeros, and trailing_ones methods for Ipv4Addr, Ipv6Addr, and potentially IpAddr (there isn't the same restriction as for binary operations, although it's likely you'll still need to narrow down the address type anyway)
  • BITS associated constant for Ipv4Addr and Ipv6Addr set to 32 and 128 respectively
  • Step for Ipv4Addr and Ipv6Addr (again not for IpAddr, since ranges of IPs must have the same type on both ends)

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.

Links and related work

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):

  • We think this problem seems worth solving, and the standard library might be the right place to solve it.
  • We think that this probably doesn't belong in the standard library.

Second, if there's a concrete solution:

  • We think this specific solution looks roughly right, approved, you or someone else should implement this. (Further review will still happen on the subsequent implementation PR.)
  • We're not sure this is the right solution, and the alternatives or other materials don't give us enough information to be sure about that. Here are some questions we have that aren't answered, or rough ideas about alternatives we'd want to see discussed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Assignees

No one assigned

    Labels

    ACP-acceptedAPI Change Proposal is accepted (seconded with no objections)T-libs-apiapi-change-proposalA proposal to add or alter unstable APIs in the standard libraries

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions