Check Whether or Not a Cidr Subnet Contains an Ip Address

Check whether or not a CIDR subnet contains an IP address

If only using IPv4:

  • use ip2long() to convert the IPs and the subnet range into long integers
  • convert the /xx into a subnet mask
  • do a bitwise 'and' (i.e. ip & mask)' and check that that 'result = subnet'

something like this should work:

function cidr_match($ip, $range)
{
list ($subnet, $bits) = explode('/', $range);
if ($bits === null) {
$bits = 32;
}
$ip = ip2long($ip);
$subnet = ip2long($subnet);
$mask = -1 << (32 - $bits);
$subnet &= $mask; # nb: in case the supplied subnet wasn't correctly aligned
return ($ip & $mask) == $subnet;
}

Find whether a given IP exists in CIDR or not

OK, try following this 5 simple steps...

1. Store your CIDRs into array (read 'em from database; guess you know how to get this)

$cidrs = array(
'192.168.1.20/27',
'192.168.0.10/32'
);

2. Get user's IP (remote address)

$user_ip = $_SERVER['REMOTE_ADDR'];

3. Add this function

function IPvsCIDR($user_ip, $cidr) {
$parts = explode('/', $cidr);
$ipc = explode('.', $parts[0]);
foreach ($ipc as &$v)
$v = str_pad(decbin($v), 8, '0', STR_PAD_LEFT);
$ipc = substr(join('', $ipc), 0, $parts[1]);
$ipu = explode('.', $user_ip);
foreach ($ipu as &$v)
$v = str_pad(decbin($v), 8, '0', STR_PAD_LEFT);
$ipu = substr(join('', $ipu), 0, $parts[1]);
return $ipu == $ipc;
}

4. Compare user's IP address against $cidrs

$validaddr = false;
foreach ($cidrs as $addr)
if (IPvsCIDR($user_ip, $addr)) {
$validaddr = true;
break;
}

5. Decide what to do with user

if ($validaddr) {
echo "CORRECT IP ADDRESS";
}
else {
echo "INCORRECT IP ADDRESS";
}

That's it!

how this function works. It converts CIDR address-part (x.x.x.x) into binary string and takes first N digits. Then it does same job with user's IP and checks do values match.

Example 2 (complete job from function)


function testUserIP($user_ip, $cidrs) {
$ipu = explode('.', $user_ip);
foreach ($ipu as &$v)
$v = str_pad(decbin($v), 8, '0', STR_PAD_LEFT);
$ipu = join('', $ipu);
$res = false;
foreach ($cidrs as $cidr) {
$parts = explode('/', $cidr);
$ipc = explode('.', $parts[0]);
foreach ($ipc as &$v) $v = str_pad(decbin($v), 8, '0', STR_PAD_LEFT);
$ipc = substr(join('', $ipc), 0, $parts[1]);
$ipux = substr($ipu, 0, $parts[1]);
$res = ($ipc === $ipux);
if ($res) break;
}
return $res;
}

Usage:


$user_ip = $_SERVER['REMOTE_ADDR'];
$cidrs = array('192.168.1.20/27', '192.168.0.10/32');
if (testUserIP($user_ip, $cidrs)) {
// user ip is ok
}
else {
// access denied
}

check if an IP is within a range of CIDR in Python

You can't really do string comparisons on a dot separated list of numbers because your test will simply fail on input say 1.1.99.99 as '9' is simply greater than '2'

>>> '1.1.99.99' < '1.1.255.255'
False

So instead you can convert the input into tuples of integers through comprehension expression

def convert_ipv4(ip):
return tuple(int(n) for n in ip.split('.'))

Note the lack of type checking, but if your input is a proper IP address it will be fine. Since you have a 2-tuple of IP addresses, you can create a function that takes both start and end as argument, pass that tuple in through argument list, and return that with just one statement (as Python allows chaining of comparisons). Perhaps like:

def check_ipv4_in(addr, start, end):
return convert_ipv4(start) < convert_ipv4(addr) < convert_ipv4(end)

Test it out.

>>> ip_range = ('1.1.0.0', '1.1.255.255')
>>> check_ipv4_in('1.1.99.99', *ip_range)
True

With this method you can lazily expand it to IPv6, though the conversion to and from hex (instead of int) will be needed instead.

How can I check if an ip is in a network in Python?

This article shows you can do it with socket and struct modules without too much extra effort. I added a little to the article as follows:

import socket,struct

def makeMask(n):
"return a mask of n bits as a long integer"
return (2L<<n-1) - 1

def dottedQuadToNum(ip):
"convert decimal dotted quad string to long integer"
return struct.unpack('L',socket.inet_aton(ip))[0]

def networkMask(ip,bits):
"Convert a network address to a long integer"
return dottedQuadToNum(ip) & makeMask(bits)

def addressInNetwork(ip,net):
"Is an address in a network"
return ip & net == net

address = dottedQuadToNum("192.168.1.1")
networka = networkMask("10.0.0.0",24)
networkb = networkMask("192.168.0.0",24)
print (address,networka,networkb)
print addressInNetwork(address,networka)
print addressInNetwork(address,networkb)

This outputs:

False
True

If you just want a single function that takes strings it would look like this:

import socket,struct

def addressInNetwork(ip,net):
"Is an address in a network"
ipaddr = struct.unpack('L',socket.inet_aton(ip))[0]
netaddr,bits = net.split('/')
netmask = struct.unpack('L',socket.inet_aton(netaddr))[0] & ((2L<<int(bits)-1) - 1)
return ipaddr & netmask == netmask

Check if an IP exists in multiple CIDR ranges

Unless you've deeply internalized the format, you can seldom look at the dotted quad notation and have any idea what the subnetting is for anything other than a multiple of 8.

The binary representation of those networks is:

1.2.220.142 31 00000001 00000010 11011100 10001110
1.2.220.144 29 00000001 00000010 11011100 10010000
1.2.220.152 30 00000001 00000010 11011100 10011000

1.2.220.142/31 is wholly different from the other two, and none overlap.

How to check if an IP address is within a particular subnet

Using the answers from Thomas and Chris together with Ciscos Subnetting Examples I finally got something to work for IPv4 and IPv6 if you use the CIDR notation (IPAddress/PrefixLength). My IPv6-Implementation might be a bit too straight forward but as there is no UInt128-datatype I couldn't adapt Thomas's solution. Here is the code that seems to work well:

public static bool IsInSubnet(this IPAddress address, string subnetMask)
{
var slashIdx = subnetMask.IndexOf("/");
if (slashIdx == -1)
{ // We only handle netmasks in format "IP/PrefixLength".
throw new NotSupportedException("Only SubNetMasks with a given prefix length are supported.");
}

// First parse the address of the netmask before the prefix length.
var maskAddress = IPAddress.Parse(subnetMask.Substring(0, slashIdx));

if (maskAddress.AddressFamily != address.AddressFamily)
{ // We got something like an IPV4-Address for an IPv6-Mask. This is not valid.
return false;
}

// Now find out how long the prefix is.
int maskLength = int.Parse(subnetMask.Substring(slashIdx + 1));

if (maskLength == 0)
{
return true;
}

if (maskLength < 0)
{
throw new NotSupportedException("A Subnetmask should not be less than 0.");
}

if (maskAddress.AddressFamily == AddressFamily.InterNetwork)
{
// Convert the mask address to an unsigned integer.
var maskAddressBits = BitConverter.ToUInt32(maskAddress.GetAddressBytes().Reverse().ToArray(), 0);

// And convert the IpAddress to an unsigned integer.
var ipAddressBits = BitConverter.ToUInt32(address.GetAddressBytes().Reverse().ToArray(), 0);

// Get the mask/network address as unsigned integer.
uint mask = uint.MaxValue << (32 - maskLength);

// https://stackoverflow.com/a/1499284/3085985
// Bitwise AND mask and MaskAddress, this should be the same as mask and IpAddress
// as the end of the mask is 0000 which leads to both addresses to end with 0000
// and to start with the prefix.
return (maskAddressBits & mask) == (ipAddressBits & mask);
}

if (maskAddress.AddressFamily == AddressFamily.InterNetworkV6)
{
// Convert the mask address to a BitArray. Reverse the BitArray to compare the bits of each byte in the right order.
var maskAddressBits = new BitArray(maskAddress.GetAddressBytes().Reverse().ToArray());

// And convert the IpAddress to a BitArray. Reverse the BitArray to compare the bits of each byte in the right order.
var ipAddressBits = new BitArray(address.GetAddressBytes().Reverse().ToArray());
var ipAddressLength = ipAddressBits.Length;

if (maskAddressBits.Length != ipAddressBits.Length)
{
throw new ArgumentException("Length of IP Address and Subnet Mask do not match.");
}

// Compare the prefix bits.
for (var i = ipAddressLength - 1; i >= ipAddressLength - maskLength; i--)
{
if (ipAddressBits[i] != maskAddressBits[i])
{
return false;
}
}

return true;
}

throw new NotSupportedException("Only InterNetworkV6 or InterNetwork address families are supported.");
}

And this are the XUnit tests I tested it with:

public class IpAddressExtensionsTests
{
[Theory]
[InlineData("192.168.5.85/24", "192.168.5.1")]
[InlineData("192.168.5.85/24", "192.168.5.254")]
[InlineData("10.128.240.50/30", "10.128.240.48")]
[InlineData("10.128.240.50/30", "10.128.240.49")]
[InlineData("10.128.240.50/30", "10.128.240.50")]
[InlineData("10.128.240.50/30", "10.128.240.51")]
[InlineData("192.168.5.85/0", "0.0.0.0")]
[InlineData("192.168.5.85/0", "255.255.255.255")]
public void IpV4SubnetMaskMatchesValidIpAddress(string netMask, string ipAddress)
{
var ipAddressObj = IPAddress.Parse(ipAddress);
Assert.True(ipAddressObj.IsInSubnet(netMask));
}

[Theory]
[InlineData("192.168.5.85/24", "192.168.4.254")]
[InlineData("192.168.5.85/24", "191.168.5.254")]
[InlineData("10.128.240.50/30", "10.128.240.47")]
[InlineData("10.128.240.50/30", "10.128.240.52")]
[InlineData("10.128.240.50/30", "10.128.239.50")]
[InlineData("10.128.240.50/30", "10.127.240.51")]
public void IpV4SubnetMaskDoesNotMatchInvalidIpAddress(string netMask, string ipAddress)
{
var ipAddressObj = IPAddress.Parse(ipAddress);
Assert.False(ipAddressObj.IsInSubnet(netMask));
}

[Theory]
[InlineData("2001:db8:abcd:0012::0/64", "2001:0DB8:ABCD:0012:0000:0000:0000:0000")]
[InlineData("2001:db8:abcd:0012::0/64", "2001:0DB8:ABCD:0012:FFFF:FFFF:FFFF:FFFF")]
[InlineData("2001:db8:abcd:0012::0/64", "2001:0DB8:ABCD:0012:0001:0000:0000:0000")]
[InlineData("2001:db8:abcd:0012::0/64", "2001:0DB8:ABCD:0012:FFFF:FFFF:FFFF:FFF0")]
[InlineData("2001:db8:abcd:0012::0/128", "2001:0DB8:ABCD:0012:0000:0000:0000:0000")]
[InlineData("2001:db8:abcd:5678::0/53", "2001:0db8:abcd:5000:0000:0000:0000:0000")]
[InlineData("2001:db8:abcd:5678::0/53", "2001:0db8:abcd:57ff:ffff:ffff:ffff:ffff")]
[InlineData("2001:db8:abcd:0012::0/0", "::")]
[InlineData("2001:db8:abcd:0012::0/0", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")]
public void IpV6SubnetMaskMatchesValidIpAddress(string netMask, string ipAddress)
{
var ipAddressObj = IPAddress.Parse(ipAddress);
Assert.True(ipAddressObj.IsInSubnet(netMask));
}

[Theory]
[InlineData("2001:db8:abcd:0012::0/64", "2001:0DB8:ABCD:0011:FFFF:FFFF:FFFF:FFFF")]
[InlineData("2001:db8:abcd:0012::0/64", "2001:0DB8:ABCD:0013:0000:0000:0000:0000")]
[InlineData("2001:db8:abcd:0012::0/64", "2001:0DB8:ABCD:0013:0001:0000:0000:0000")]
[InlineData("2001:db8:abcd:0012::0/64", "2001:0DB8:ABCD:0011:FFFF:FFFF:FFFF:FFF0")]
[InlineData("2001:db8:abcd:0012::0/128", "2001:0DB8:ABCD:0012:0000:0000:0000:0001")]
[InlineData("2001:db8:abcd:5678::0/53", "2001:0db8:abcd:4999:0000:0000:0000:0000")]
[InlineData("2001:db8:abcd:5678::0/53", "2001:0db8:abcd:5800:0000:0000:0000:0000")]
public void IpV6SubnetMaskDoesNotMatchInvalidIpAddress(string netMask, string ipAddress)
{
var ipAddressObj = IPAddress.Parse(ipAddress);
Assert.False(ipAddressObj.IsInSubnet(netMask));
}
}

As base for the tests I used Ciscos Subnetting Examples and IBMs IPV6 address examples.

I hope someone finds this helpful ;)

Go - check if IP address is in a network

The Go net package includes the following functions:

  • ParseCIDR: takes a string representing an IP/mask and returns an
    IP and an IPNet
  • IPNet.Contains: checks whether an IP is in a
    network

This should cover your needs.

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

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"
};
}


Related Topics



Leave a reply



Submit