How to see if an IP address belongs inside of a range of IPs using CIDR notation?
Asked Answered
N

8

34

Here I have a static reference the ranges I need to check:

private static List<string> Ip_Range = new List<string>()
{
    "12.144.86.0/23",
    "31.201.1.176/30",
    "46.36.198.101/32",
    "46.36.198.102/31",
    "46.36.198.104/31",
    "46.136.172.0/24",
    "63.65.11.0/24",
    "63.65.12.0/25",
    "63.65.12.128/26",
    "63.65.12.192/27",
    "63.65.12.224/28",
    "63.65.12.240/29",
    "63.65.12.248/30",
    "63.65.12.252/31",
    "63.65.12.254/32",
    "65.173.56.0/21",
    "67.23.241.179/32",
    "67.23.241.180/30",
    "67.23.241.184/29",
    "67.23.241.192/30",
    "67.23.241.196/31",
    "67.23.241.198/32",
    "72.32.164.56/29",
    "72.46.244.32/28",
    "74.91.16.48/29",
    "74.91.16.208/29",
    "74.91.20.48/28",
    "74.91.20.64/29",
    "74.112.134.120/29",
    "74.112.135.104/29",
    "74.205.37.16/29",
    "78.24.205.32/28",
    "98.129.27.88/29",
    "98.129.91.40/29",
    "166.114.0.0/16",
    "167.157.0.0/16",
    "174.143.165.80/29",
    "186.0.156.0/22",
    "186.2.0.0/17",
    "186.27.0.0/17",
    "190.0.248.0/21",
    "190.3.184.0/21"
};

Here's some pseudo code on how I see it working:

public static bool IpIsWithinRange(string ip) //Something like 127.0.0.1 or 184.56.26.35
{
    IPAddress incomingIp = IPAddress.Parse(ip);
    foreach (var subnet in Ip_Range)
    {
        IPAddress sub = IPAddress.Parse(subnet); ?????
        if (incomingIp "is in" sub) ?
            return true;            
    }
    return false;
}

Any suggestions on how to code up this functionality?

Nye answered 8/3, 2012 at 18:39 Comment(8)
Are you essentially asking for us to do the work to make this work?Quartile
The IPNetwork project should be able to handle the majority of what you're looking to do.Gallia
@Tony: A nudge in the right direction is equally good. That's the point of this site. :) I've done this in Ruby, but I'm not really familiar with how this could be done in C#.Nye
So, how did you solve this problem in Ruby? Which part of that are you having trouble translating to C#?Kilgore
@M.Babcock: Pretty easy with that library! Thanks, do you want to post an answer, or should I post the code I used to make this work?Nye
@SergioTapia - I posted an answer, but it probably wouldn't hurt for you to document your result with an answer as well.Gallia
@Gallia The IPNetwork project has moved to github.com/lduchosal/ipnetwork. I think I've changed all the references I could in the answers here, but if you recall linking to it elsewhere you might want to update those references too if you feel like it.Scarabaeoid
Does this answer your question? How to check a input IP fall in a specific IP rangeWisteria
N
26

Decided to answer my own question so people can benefit. If it can be improved, please do!

I used the IPNetwork library and it worked out fantastically!

nuget install IPNetwork2

Below is the code I used:

using System.Net;

public static class RedirectHelpers
{
    public static bool IpIsWithinBoliviaRange(string ip)
    {
        IPAddress incomingIp = IPAddress.Parse(ip);
        foreach (var subnet in Bolivia_Ip_Range)
        {
            IPNetwork network = IPNetwork.Parse(subnet);

            if (IPNetwork.Contains(network, incomingIp))
                return true;
        }
        return false;
    }

    private static List<string> Bolivia_Ip_Range = new List<string>()
    {
        "12.144.86.0/23",
        "31.201.1.176/30",
        "46.36.198.101/32",
        "46.36.198.102/31",
        "46.36.198.104/31",
        "46.136.172.0/24",
        "63.65.11.0/24",
        "63.65.12.0/25",
        "63.65.12.128/26",
        "63.65.12.192/27",
        "63.65.12.224/28",
        "63.65.12.240/29",
        "63.65.12.248/30",
        "63.65.12.252/31",
        "63.65.12.254/32",
        "65.173.56.0/21",
        "67.23.241.179/32",
        "67.23.241.180/30",
        "67.23.241.184/29",
        "67.23.241.192/30",
        "67.23.241.196/31",
        "67.23.241.198/32",
        "72.32.164.56/29",
        "72.46.244.32/28",
        "74.91.16.48/29",
        "74.91.16.208/29",
        "74.91.20.48/28",
        "74.91.20.64/29",
        "74.112.134.120/29",
        "74.112.135.104/29",
        "74.205.37.16/29",
        "78.24.205.32/28",
        "98.129.27.88/29",
        "98.129.91.40/29",
        "166.114.0.0/16",
        "167.157.0.0/16",
        "174.143.165.80/29",
        "186.0.156.0/22",
        "186.2.0.0/17",
        "186.27.0.0/17",
        "190.0.248.0/21",
        "190.3.184.0/21",
        "166.114.0.0/16",
        "167.157.0.0/16",
        "186.2.0.0/18",
        "190.11.64.0/20",
        "190.11.80.0/20",
        "190.103.64.0/20",
        "190.104.0.0/19",
        "190.107.32.0/20",
        "190.129.0.0/17",
        "190.181.0.0/18",
        "190.186.0.0/18",
        "190.186.64.0/18",
        "190.186.128.0/18",
        "200.7.160.0/20",
        "200.58.64.0/20",
        "200.58.80.0/20",
        "200.58.160.0/20",
        "200.58.176.0/20",
        "200.75.160.0/20",
        "200.85.128.0/20",
        "200.87.0.0/17",
        "200.87.128.0/17",
        "200.105.128.0/19",
        "200.105.160.0/19",
        "200.105.192.0/19",
        "200.112.192.0/20",
        "200.119.192.0/20",
        "200.119.208.0/20",
        "201.222.64.0/19",
        "201.222.96.0/19"
    };
}
Nye answered 9/3, 2012 at 0:0 Comment(2)
The bulk (pretty much the foreach) of your IpIsWithinBoliviaRange method could be replaced with a simple LINQ query: return Bolivia_Ip_Range.Any(subnet => IPNetwork.Contains(IPNetwork.Parse(subnet), incomingIp));Gallia
First impresions, this solution looks very inefficient. you are performing IPNetwork network = IPNetwork.Parse(subnet) on every subnet in the list for every call of the method !!!! You know that it will always parse to the same result so why not memoize that? Likewise, you could also (depending on how many repeat IP visitors you'd likely receive) memoize the final result against the incoming IP address to save lookup - although this might not actually be a good idea if large numbers of different ip addresses expected with low repeats.Staceestacey
S
44

If you don't want/can't add another library (as the IPnetwork one) to your project and just need to deal with IPv4 CIDR ranges, here's a quick solution to your problem

// true if ipAddress falls inside the CIDR range, example
// bool result = IsInRange("10.50.30.7", "10.0.0.0/8");
private bool IsInRange(string ipAddress, string CIDRmask)
{
    string[] parts = CIDRmask.Split('/');

    int IP_addr = BitConverter.ToInt32(IPAddress.Parse(ipAddress).GetAddressBytes(), 0);
    int CIDR_addr = BitConverter.ToInt32(IPAddress.Parse(parts[0]).GetAddressBytes(), 0);
    int CIDR_mask = IPAddress.HostToNetworkOrder(-1 << (32 - int.Parse(parts[1])));

    return ((IP_addr & CIDR_mask) == (CIDR_addr & CIDR_mask));
}

the above will allow you to quickly check if a given IPv4 address falls inside a given CIDR range; notice that the above code is barebone, it's up to you to check if a given IP (string) and CIDR range are correct before feeding them to the function (you may just use the tryparse or whatever...)

School answered 20/6, 2013 at 9:25 Comment(5)
It doesn't really effect the outcome, but you should probably swap the value being parsed for IP_addr and CIDR_addr.Misteach
This does not pass the CIDR range of 0.0.0.0/0 in other words all. Other than that it's great!Literatim
In case of CIDR mask being zero I have simply added an if to return true as anything valid would match.Literatim
The IP_addr and CIDR_addr variables were swapped as Jeffrey suggested.Acrid
Could someone explain this line: IPAddress.HostToNetworkOrder(-1 << (32 - int.Parse(parts[1]))); ? I don't understand why -1 and why host to network order function... :')Savina
N
26

Decided to answer my own question so people can benefit. If it can be improved, please do!

I used the IPNetwork library and it worked out fantastically!

nuget install IPNetwork2

Below is the code I used:

using System.Net;

public static class RedirectHelpers
{
    public static bool IpIsWithinBoliviaRange(string ip)
    {
        IPAddress incomingIp = IPAddress.Parse(ip);
        foreach (var subnet in Bolivia_Ip_Range)
        {
            IPNetwork network = IPNetwork.Parse(subnet);

            if (IPNetwork.Contains(network, incomingIp))
                return true;
        }
        return false;
    }

    private static List<string> Bolivia_Ip_Range = new List<string>()
    {
        "12.144.86.0/23",
        "31.201.1.176/30",
        "46.36.198.101/32",
        "46.36.198.102/31",
        "46.36.198.104/31",
        "46.136.172.0/24",
        "63.65.11.0/24",
        "63.65.12.0/25",
        "63.65.12.128/26",
        "63.65.12.192/27",
        "63.65.12.224/28",
        "63.65.12.240/29",
        "63.65.12.248/30",
        "63.65.12.252/31",
        "63.65.12.254/32",
        "65.173.56.0/21",
        "67.23.241.179/32",
        "67.23.241.180/30",
        "67.23.241.184/29",
        "67.23.241.192/30",
        "67.23.241.196/31",
        "67.23.241.198/32",
        "72.32.164.56/29",
        "72.46.244.32/28",
        "74.91.16.48/29",
        "74.91.16.208/29",
        "74.91.20.48/28",
        "74.91.20.64/29",
        "74.112.134.120/29",
        "74.112.135.104/29",
        "74.205.37.16/29",
        "78.24.205.32/28",
        "98.129.27.88/29",
        "98.129.91.40/29",
        "166.114.0.0/16",
        "167.157.0.0/16",
        "174.143.165.80/29",
        "186.0.156.0/22",
        "186.2.0.0/17",
        "186.27.0.0/17",
        "190.0.248.0/21",
        "190.3.184.0/21",
        "166.114.0.0/16",
        "167.157.0.0/16",
        "186.2.0.0/18",
        "190.11.64.0/20",
        "190.11.80.0/20",
        "190.103.64.0/20",
        "190.104.0.0/19",
        "190.107.32.0/20",
        "190.129.0.0/17",
        "190.181.0.0/18",
        "190.186.0.0/18",
        "190.186.64.0/18",
        "190.186.128.0/18",
        "200.7.160.0/20",
        "200.58.64.0/20",
        "200.58.80.0/20",
        "200.58.160.0/20",
        "200.58.176.0/20",
        "200.75.160.0/20",
        "200.85.128.0/20",
        "200.87.0.0/17",
        "200.87.128.0/17",
        "200.105.128.0/19",
        "200.105.160.0/19",
        "200.105.192.0/19",
        "200.112.192.0/20",
        "200.119.192.0/20",
        "200.119.208.0/20",
        "201.222.64.0/19",
        "201.222.96.0/19"
    };
}
Nye answered 9/3, 2012 at 0:0 Comment(2)
The bulk (pretty much the foreach) of your IpIsWithinBoliviaRange method could be replaced with a simple LINQ query: return Bolivia_Ip_Range.Any(subnet => IPNetwork.Contains(IPNetwork.Parse(subnet), incomingIp));Gallia
First impresions, this solution looks very inefficient. you are performing IPNetwork network = IPNetwork.Parse(subnet) on every subnet in the list for every call of the method !!!! You know that it will always parse to the same result so why not memoize that? Likewise, you could also (depending on how many repeat IP visitors you'd likely receive) memoize the final result against the incoming IP address to save lookup - although this might not actually be a good idea if large numbers of different ip addresses expected with low repeats.Staceestacey
G
11

Luckily most of the work has already been done for you (so we don't have to). Check out the IPNetwork project. You'll parse all of your CIDR addresses with IPNetwork.Parse. Then to see if a specific IPAddress is in range just use IPNetwork.Contains method.


I got bored so here's a method you can use to test whether an IP address is in range or not:

private Dictionary<string, IPNetwork> netCache = null;
public bool IsInRange(string ipAddress)
{
    if (netCache == null)
        netCache = Ip_Range.ToDictionary((keyItem) => keyItem, (valueItem) => IPNetwork.Parse(valueItem));

    return netCache.Values.Any(net => IPNetwork.Contains(net, IPAddress.Parse(ipAddress)));
}

This is dependent on the Ip_Range list from your question but translates them to IPNetwork instances (missing sanity checks for brevity).

Usage:

List<string> addrList = new List<string> { "12.144.86.1", "10.254.6.172" };
addrList.ForEach(addr => Console.WriteLine("{0}: {1}", addr, IsInRange(addr)));

Test Output:

12.144.86.1: True
10.254.6.172: False

Of course there is still a lot that could (and probably should) be done with it, but this proves the concept.

Gallia answered 8/3, 2012 at 18:52 Comment(5)
Can't get IPNetwork to run in a VB.Net project... Intellisense likes it, but it barfs on run, wanting to find IPNetwork.cs! :( Might be restricted to C#?Assentor
@PatTrainor - Please open a new question so your situation can be reviewed by the appropriate members of the community. I can assure you, it's not possible to restrict the languages which are able to use an assembly.Gallia
I'm with you on that! Dangest thing I ever saw... Will create new thread... [No way to ask the author, either...]Assentor
@M.Babcock. Please could you explain why are you using a Dictionary instead of List?Styrene
@Styrene - It just happened to be what came to my mind while writing the answer 3 years ago. There are arguably several performance improvements that could be done to the presented code for which a Dictionary would be warranted.Gallia
A
4
    public static bool IpIsInRange(string subnet, string ip)
    {
        var splitSubnet = subnet.Split('/');
        var maskBits = 32 - int.Parse(splitSubnet[1]);
        if (maskBits == 32)
        {
            return true;
        }
        var subnetIp = BitConverter.ToInt32(IPAddress.Parse(splitSubnet[0]).GetAddressBytes().Reverse().ToArray(), 0) >> maskBits << maskBits;
        var clientIp = BitConverter.ToInt32(IPAddress.Parse(ip).GetAddressBytes().Reverse().ToArray(), 0) >> maskBits << maskBits;
        return subnetIp == clientIp;
    }
Airfield answered 3/8, 2017 at 10:16 Comment(0)
Q
2

If you understand the CIDR notation, you can easily do the math in your parse method.

You basically know that an IPv4 address is 32bits long and that the CIDR notation means that the number of bits behind the "/" are the network address bits (ie the masked out bits), therefore the leftover bits are represent the number of hosts in the subnet.

From wikipedia article:

The number of addresses of a subnet defined by the mask or prefix can be calculated as 2address size - prefix size, in which the address size for IPv6 is 128 and 32 for IPv4. For example, in IPv4, a prefix size of /29 gives: 232-29 = 23 = 8 addresses.

So you could (no I'm not going to work out the details for you) convert your addresses into binary and do the binary AND with the given mask (also in binary form), then you have the network address part of the IP address left, which should match with whatever address you're checking against to see if it's in a particular subnet.

Quartile answered 8/3, 2012 at 18:51 Comment(1)
The fact that you state "no I'm not going to work out the details for you" suggests this is likely reinventing the wheel (time consuming tasks for every day problems like this get automated v.quickly).Staceestacey
A
1

For start, you should use that:

IPNetwork ipnetwork = IPNetwork.Parse("192.168.168.100/29");
Console.WriteLine("CIDR: {0}", ipnetwork.Cidr);

Output

CIDR: 29
Arrears answered 8/3, 2012 at 18:58 Comment(1)
... ipnetwork.codeplex.com has moved to github.com/lduchosal/ipnetwork.Scarabaeoid
C
0

Here is a modernized version of @ObiWan's answer with improved error handling.

public static bool IsInRange(IPAddress ipAddress, string cidrRange)
{
    var parts = cidrRange.Split('/');

    if (parts.Length != 2 
          || !int.TryParse(parts[1], out var maskSize)
          || maskSize > 32
          || maskSize < 0
          || !IPAddress.TryParse(parts[0], out var cidrIpAddress))
    {
        throw new ArgumentException($"invalid CIDR string '{cidrRange}'");
    }

    int cidrMask = IPAddress.HostToNetworkOrder(-1 << (32 - maskSize));

    int ipBits = BitConverter.ToInt32(ipAddress.GetAddressBytes(), 0);
    int cidrBits = BitConverter.ToInt32(cidrIpAddress.GetAddressBytes(), 0);

    return (ipBits & cidrMask) == (cidrBits & cidrMask);
}
Circumstantiate answered 15/12, 2023 at 15:54 Comment(0)
T
-1

I use the below method to determine whether a given IP Address is a public one or private/internal:

private bool IsInternalIpAddress(string ipAddress)
    {
        // notes: http://whatismyipaddress.com/private-ip

        var internalIpRanges = Enumerable
            .Range(16, 31)
            .Select(i => "172.{0}.".FormatWith(i))
            .Concat(new[] {"192.168.", "10.", "127."})
            .ToArray();

        return ipAddress.StartsWith(internalIpRanges);
    }
Tactile answered 14/7, 2016 at 9:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.