Reputation: 1361
/*
* RFC 1518, 1519 - Classless Inter-Domain Routing (CIDR)
* This converts from "prefix + prefix-length" format to
* "address + mask" format, e.g. from xxx.xxx.xxx.xxx/yy
* to xxx.xxx.xxx.xxx/yyy.yyy.yyy.yyy.
*/
static private String normalizeFromCIDR(final String netspec)
{
final int bits = 32 - Integer.parseInt(netspec.substring(netspec.indexOf('/')+1));
final int mask = (bits == 32) ? 0 : 0xFFFFFFFF - ((1 << bits)-1);
return netspec.substring(0, netspec.indexOf('/') + 1) +
Integer.toString(mask >> 24 & 0xFF, 10) + "." +
Integer.toString(mask >> 16 & 0xFF, 10) + "." +
Integer.toString(mask >> 8 & 0xFF, 10) + "." +
Integer.toString(mask >> 0 & 0xFF, 10);
}
This is a function in apache james to convert the ip to the specified format. Can you please explain what's happening inside the function. Confused with this bit shifting and conversion. Thanks in Advance.
Upvotes: 3
Views: 4470
Reputation: 1
another way to get cidr notation to binary:
input = '1.2.3.4/5'
cidr = input.split('/')
bin_mask = '1' * cidr + '0' * (32 - cidr)
Upvotes: -1
Reputation: 2212
Bit-wise operations maybe are not most intuitive at first glance but once you got it you'll see that they're quite easy to understand. I'll try to explain what this code does on a example of 172.16.0.1/23
as a netspec
string.
The goal is to make a binary representation of a subnet mask from a given CIDR prefix length. CIDR prefix length is just a number of 1
bits in a subnet mask. The first line
final int bits = 32 - Integer.parseInt(netspec.substring(netspec.indexOf('/')+1));
finds CIDR prefix length by getting the index of /
and parsing the integer that succeeds it (23
in my example). This number is subtracted from 32 to get a number of 0
in a subnet mask — those bits are also called host bits.
In this example we know that we're dealing with /23
prefix and that it's subnet mask should look like:
n
represents network (16 bits for class B network),s
represents subnet,h
represents host. For us network and subnet bits are functionally the same, but I made a distinction just to be precise. Our interest is just in host bits (number of it).
nnnnnnnn nnnnnnnn sssssssh hhhhhhhh
11111111 11111111 11111110 00000000
The easiest way to make this is to have a 32bit binary number of all 1
s and 'fill' the last 9 bits with 0
. This is where the second line comes in:
You can ignore the
bits == 32
check since it is not that relevant and probably is there just as a optimization.
//final int mask = (bits == 32) ? 0 : 0xFFFFFFFF - ((1 << bits)-1);
final int mask = 0xFFFFFFFF - ((1 << 9)-1);
0xFFFFFFFF
will give you 32bit binary number of all 1
s. 1
shifted left 9 bits (1 << bits
) will give you 512 and 512 - 1
in binary is 111111111
:
1 << 9 10 00000000
- 1 1
--------------------------------------------------
1 11111111
When you subtract those values you will get the subnet mask in binary:
0xFFFFFFFF = 11111111 11111111 11111111 11111111
- (1 << 9)-1 = 1 11111111
--------------------------------------------------
11111111 11111111 11111110 00000000
Which is exactly the network mask we wanted.
Note: This is maybe not the most intuitive way of calculating the binary value. I like to start with a binary number of all ones and that number in signed int has a decimal value of -1
. Then i just shift it the number of host bits to the left and that's it. (Additionally if you're dealing with integers that are larger than 32bits you can mask it with 0xFFFFFFFF):
(-1 << 9) & 0xFFFFFFFF
The rest of the code converts the binary value to a dotted-decimal representation — 255.255.254.0.
return netspec.substring(0, netspec.indexOf('/') + 1) + // part of the netspec string before '/' -> IP address
Integer.toString(mask >> 24 & 0xFF, 10) + "." + // 11111111 & 0xFF = 0xFF
Integer.toString(mask >> 16 & 0xFF, 10) + "." + // 1111111111111111 & 0xFF = 0xFF
Integer.toString(mask >> 8 & 0xFF, 10) + "." + // 111111111111111111111110 & 0xFF = 0xFE
Integer.toString(mask >> 0 & 0xFF, 10); // 11111111111111111111111000000000 & 0xFF = 0x00
The return statement is composed of several concatenated strings, starting with IP address and following by decimal representation of each octet. Binary mask is shifted right for (4-n)*8
bits (where n
is octet number) and using binary AND with 0xFF you get only the last 8bits which are then parsed by Integer.toString
.
The result is 172.16.0.1/255.255.254.0
.
Upvotes: 2