@home:~$

IP/Network Address Validation Using Bitwise Operations

Always validate input. You’re sure to get burned at some point if you don’t.

Let’s say you need to validate given a network that the IP provided is the network boundary address, or if a single IP is given that the IP address falls within the netmask provided. There are plenty of ways to go about this, I’m sure there are external libraries you can use to do just this thing, but if you’re using a platform like ServiceNow and you’re utilizing Catalog Client Scripts or Script Includes to create this functionality, this isn’t quite an option for you. ServiceNow does offer an IP Address variable type that maps to a VARCHAR column with some validation but does not account for a netmask. But doing this is quite easy if you understand how to use bitwise operations in Javascript. Given an IP address and a netmask, we can use bitwise operations to determine the network boundary and network broadcast as integers, and if the provided IP address as an integer falls within the range of the network/broadcast range. Let’s look at some examples:

192.168.1.1/24 - Valid 192.168.1.4/30 - Not Valid, sits on a network boundary

Converting IPs to Integers

To determine this, we must convert a valid IP address into an integer. To do this, we do a bitwise zero fill left shift on each octect by an amount of 32 - (Current Octect * 8), so 24 bits for the first octect, 16 bits for the second octet, etc. This gives us the integer value of each octet which we can add together to get the integer value of the IP address in question.

ipasintsmall

Great. Now we need to determine what the lower and upper network boundaries are: network address and broadcast address. In this case that would be 192.168.1.0 and 192.168.1.255, neither of which should be considered a valid value. In order to determine what those boundaries are, we’ll need to look at 3 things: IP address represented as bits, network mask represented as bits, and wildcard mask represented as bits. By doing a bitwise AND on the IP address and netmask and then a bitwise OR on the ip address and wildcard mask, we determine both boundaries as integers.

bitwiseand bitwiseor

Our integer representation of 192.168.1.1 falls within the bounds but is not equal to our calculated boundaries so it is valid.

The Code

Let’s look at what this looks like in code. We can use a regex expression to identify if the passed input is even a valid IP address and if it is then group each part of the IP address into a match result. From there we can bitwise shift each octect to determine the integer representation of our IP address.

var examples = [
    '192.168.1.1/24',
    '192.168.1.4/30'
]

examples.forEach(function(ipAddr) {
    var validIP = ipAddr.match(/^(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\/(8|[1-2][1-9]|30)$/);

    if (validIP) {
        // Capture each octet and bitwise shift
        var o1 = (+validIP[1] << 24) >>> 0;
        var o2 = (+validIP[2] << 16) >>> 0;
        var o3 = (+validIP[3] << 8) >>> 0;
        var o4 = (+validIP[4]) >>> 0;
        var ipAsInt = (o1 + o2 + o3 + o4) >>> 0;
        var mask = -1 << (32 - validIP[5]);

        var start = (ipAsInt & mask) >>> 0;
        var end = (ipAsInt | ~mask) >>> 0;
        var result = start < ipAsInt && ipAsInt < end;
        console.log('---');
        console.log('Checking IP: ' + ipAddr);
        console.log('start: ' + start);
        console.log('end : ' + end);
        console.log('ip : ' + ipAsInt);
        console.log('result: ' + result);
    } else {
        alert(ipAddr + ' is not a valid IPv4 address.');
    }
});
 

And if we run our test cases….

drew@Drews-MacBook-Pro[~/Desktop]  ❯ node test.js
---
Checking IP: 192.168.1.1/24
start: 3232235776
end : 3232236031
ip : 3232235777
result: true
---
Checking IP: 192.168.1.4/30
start: 3232235780
end : 3232235783
ip : 3232235780
result: false

Checking Network Boundary Instead

In some cases the user input we want will actually be a network itself and not an IP. We can use the same the same exact logic but testing for our result case will be slightly modified. Before, we were checking to see if (lower < IP < upper) but to check to see if the provided input is a valid network we simply need to check that the integer value of the provided IP is the same as the result of the bitwise AND done on the IP and it’s netmask.

var result = ipAsInt == start;

Now, if we run it again, the first value is no longer valid but the second example in our array is a valid network boundary.

drew@Drews-MacBook-Pro[~/Desktop]  ❯ node test.js
---
Checking IP: 192.168.1.1/24
start: 3232235776
end : 3232236031
ip : 3232235777
result: false
---
Checking IP: 192.168.1.4/30
start: 3232235780
end : 3232235783
ip : 3232235780
result: true

And indeed our result set shows such is the case.

Some Notes on Regex and Integers

The regex match string is rather long in our example case, but it is needed for both accuracy and grouping. To read more about result grouping check out THIS link. We need our regex expression to capture the octet and netmask in a group so we can extract their values as elements in an array. We could have simply looked for a grouping of numbers seperated by a dot or slash, something like ^(\d+).(\d+).(\d+).(\d+)\/(\d+)$, and this would capture all 4 octets and our netmask. But this would also match 652.0.1.0/35 and that’s clearly not a valid CIDR address.

Also, you may have noticed the »>0 in our example. This zero fill right shift will give us an unsigned integer so we aren’t dealing with negative values.