dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
15.07k stars 4.69k forks source link

[API Proposal]: `IpNetwork` does not allow to check if another network is subnet of current network #104591

Open plachor opened 2 months ago

plachor commented 2 months ago

Background and motivation

Hi, in .NET8 you have added IpNetwork struct. It allows to check if given IpAddress is within given network. Until .NET8 we had to relay on external library that provided such type. This great you provided your own implementation since it is over 34 times more efficient than its opensource competitor additionally almost zero allocations are made when constructing this instance or checking if it contains given IpAddress :+1: .

However it seems you type is very constrained for now we miss additional features like: 1) possibility to calculate Network for given IpAddress and PrefixLength 2) possibility to calculate Broadcast for given IpNetwork 3) possibility to check if one IpNetwork contains another IpNetwork

Do you plan such functionalities in any form in upcoming releases also does my proposals make sens when it comes to logic of such contains and broadcast address calculations ?

API Proposal

1) Possibility to calculate Network for given IpAddress and PrefixLength:

You already have that logic in code I believe you just do not expose it, you could of course extract common part to private method for both (https://github.com/dotnet/runtime/blob/main/src/libraries/System.Net.Primitives/src/System/Net/IPNetwork.cs#L205-L236).

public static IPAddress CalculateNetwork(IPAddress baseAddress, int prefixLength)
{
    if (baseAddress.AddressFamily == AddressFamily.InterNetwork)
    {
        // The cast to long ensures that the mask becomes 0 for the case where 'prefixLength == 0'.
        uint mask = (uint)((long)uint.MaxValue << (32 - prefixLength));
        if (BitConverter.IsLittleEndian)
        {
            mask = BinaryPrimitives.ReverseEndianness(mask);

        return new IPAddress(baseAddress.PrivateAddress & mask);
    }
    else
    {
        UInt128 value = default;
        baseAddress.TryWriteBytes(MemoryMarshal.AsBytes(new Span<UInt128>(ref value)), out int bytesWritten);
        Debug.Assert(bytesWritten == IPAddressParserStatics.IPv6AddressBytes);
        if (prefixLength == 0)
        {
            return new IPAddress(UInt128.Zero);

        UInt128 mask = UInt128.MaxValue << (128 - prefixLength);
        if (BitConverter.IsLittleEndian)
        {
            mask = BinaryPrimitives.ReverseEndianness(mask);

        return new IPAddress(value & mask);
    }
}

2) Possibility to calculate Broadcast for given IpNetwork:

Since BaseAddress is Network as I understand it than you could calculate BroadcastAddress on demand using such approach. If you consider it better it could be calculated upfront in ctor.

public IPAddress BroadcastAddress
{
    get
    {
        if (BaseAddress.AddressFamily == AddressFamily.InterNetwork)
        {
            // The cast to long ensures that the mask becomes 0 for the case where 'prefixLength == 0'.
            uint mask = (uint)((long)uint.MaxValue << (32 - PrefixLength));
            if (BitConverter.IsLittleEndian)
            {
                mask = BinaryPrimitives.ReverseEndianness(mask);

            return new IPAddress(BaseAddress.PrivateAddress + ~mask);
        }
        else
        {
            UInt128 value = default;
            BaseAddress.TryWriteBytes(MemoryMarshal.AsBytes(new Span<UInt128>(ref value)), out int bytesWritten);
            Debug.Assert(bytesWritten == IPAddressParserStatics.IPv6AddressBytes);
            if (PrefixLength == 0)
            {
                return new IPAddress(UInt128.MaxValue);

            UInt128 mask = UInt128.MaxValue << (128 - PrefixLength);
            if (BitConverter.IsLittleEndian)
            {
                mask = BinaryPrimitives.ReverseEndianness(mask);

            return IPAddress(value + ~mask);
        }
    }
}

3) Possibility to check if one IpNetwork contains another IpNetwork:

Given we have Network address and Broadcast address we can check if network is subset of current network with such approach.

public bool Contains(IPNetwork network) => Contains(network.BaseAddress) && Contains(network.BroadcastAddress);

API Usage

public void UsageSample(IPNetwork subnet)
    {
        // finds our network address is "10.0.0.0"
        var networkAddress = IPNetwork.CalculateNetwork(IPAddress.Parse("10.0.0.2"), 8);

        var network = new IPNetwork(networkAddress, 8);
        if (network.Contains(subnet))
        {
            // this is subnet of our network - do some work
        }
    }

Alternative Designs

No response

Risks

No response

dotnet-policy-service[bot] commented 2 months ago

Tagging subscribers to this area: @dotnet/ncl See info in area-owners.md if you want to be subscribed.