8

If I have an IP address range (CIDR notation) and I need to know if some arbitrary IP address is within that range -- both presented as strings -- what is the easiest way to do this with C#?

Examples:

  • IPv4 Range: 192.168.168.100/24, IP to check: 192.168.168.200
  • IPv6 Range: fe80::202:b3ff:fe1e:8329/24, IP to check: 2001:db8::
ahsteele
  • 26,243
  • 28
  • 134
  • 248
ahmd0
  • 16,633
  • 33
  • 137
  • 233
  • Would you be interested in seeing an IPv4 only solution? – Tung May 10 '12 at 02:02
  • 1
    No. ipv4 is pretty easy. What makes it challenging is ipv6, or when the two formats are mixed. Judging by the lack of responses here, it seems like not many people care about it though... – ahmd0 May 10 '12 at 02:31

3 Answers3

13

Here's a simple class:

public class IPSubnet
{
    private readonly byte[] _address;
    private readonly int _prefixLength;

    public IPSubnet(string value)
    {
        if (value == null)
            throw new ArgumentNullException("value");

        string[] parts = value.Split('/');
        if (parts.Length != 2)
            throw new ArgumentException("Invalid CIDR notation.", "value");

        _address = IPAddress.Parse(parts[0]).GetAddressBytes();
        _prefixLength = Convert.ToInt32(parts[1], 10);
    }

    public bool Contains(string address)
    {
        return this.Contains(IPAddress.Parse(address).GetAddressBytes());
    }

    public bool Contains(byte[] address)
    {
        if (address == null)
            throw new ArgumentNullException("address");

        if (address.Length != _address.Length)
            return false; // IPv4/IPv6 mismatch

        int index = 0;
        int bits = _prefixLength;

        for (; bits >= 8; bits -= 8)
        {
            if (address[index] != _address[index])
                return false;
            ++index;
        }

        if (bits > 0)
        {
            int mask = (byte)~(255 >> bits);
            if ((address[index] & mask) != (_address[index] & mask))
                return false;
        }

        return true;
    }
}

Sample usage:

Console.WriteLine(new IPSubnet("192.168.168.100/24").Contains("192.168.168.200")); // True
Console.WriteLine(new IPSubnet("fe80::202:b3ff:fe1e:8329/24").Contains("2001:db8::")); // False

This class treats all IPv4 addresses as distinct from all IPv6 addresses, making no attempt to translate between IPv4 and IPv6.

Michael Liu
  • 52,147
  • 13
  • 117
  • 150
  • Thanks. Although IPv4 should be converted to IPv6, for instance, one can use "::192.168.168.100" that will be treated as IPv6 by your code. – ahmd0 May 10 '12 at 05:06
  • 1
    Are you sure it's correct to convert an IPv4 address to IPv6 by simply prefixing it with "::"? The [IPv6 address](http://en.wikipedia.org/wiki/IPv6_address#Special_addresses) Wikipedia article doesn't list this as one of the IPv4 translation mechanisms. – Michael Liu May 10 '12 at 14:06
  • Sorry for this delay. I found it online, if you search "convert Ipv4 to IPv6." So I don't know... – ahmd0 May 14 '12 at 20:28
3

I'm not going to write code for you but:

should be enough to get you started.

Good luck

Community
  • 1
  • 1
LukeP
  • 10,422
  • 6
  • 29
  • 48
  • 1
    Well, too bad, because it's not very clear how to do this comparison. For instance, what if the range is in ipv6 notation but the address is ipv4 and vice verse? Also just applying the subnet mask to ipv6 addresses isn't clear either. And there're not many examples of it on the web... – ahmd0 May 10 '12 at 01:26
  • I'm not sure what you mean? IPv4 and IPv6 are 2 completely different things. No IPv4 will be in IPv6 notation. I wouldn't treat ::192.168.1.1 the same as other IPv6 addresses. – LukeP May 10 '12 at 07:46
1

I would recommend the use of IPNetwork Library https://github.com/lduchosal/ipnetwork. As of version 2, it supports IPv4 and IPv6 as well.

IPv6

  IPNetwork ipnetwork = IPNetwork.Parse("fe80::202:b3ff:fe1e:8329/24");

  IPAddress ipaddress = IPAddress.Parse("2001:db8::");
  IPAddress ipaddress2 = IPAddress.Parse("fe80::202:b3ff:fe1e:1");

  bool contains1 = IPNetwork.Contains(ipnetwork, ipaddress);
  bool contains2 = IPNetwork.Contains(ipnetwork, ipaddress2);

  Console.WriteLine("{0} contains {1} : {2}", ipnetwork, ipaddress,     contains1);
  Console.WriteLine("{0} contains {1} : {2}", ipnetwork, ipaddress2,     contains2);

Output

   fe80::/24 contains 2001:db8:: : False
   fe80::/24 contains fe80::202:b3ff:fe1e:1 : True

IPv4

  IPNetwork ipnetwork = IPNetwork.Parse("192.168.168.100/24");

  IPAddress ipaddress = IPAddress.Parse("192.168.168.200");
  IPAddress ipaddress2 = IPAddress.Parse("192.168.0.200");

  bool contains1 = IPNetwork.Contains(ipnetwork, ipaddress);
  bool contains2 = IPNetwork.Contains(ipnetwork, ipaddress2);

  Console.WriteLine("{0} contains {1} : {2}", ipnetwork, ipaddress, contains1);
  Console.WriteLine("{0} contains {1} : {2}", ipnetwork, ipaddress2, contains2);

Output

  192.168.168.0/24 contains 192.168.168.200 : True
  192.168.168.0/24 contains 192.168.0.200 : False

Have fun !

LukeSkywalker
  • 341
  • 2
  • 7