从CIDR表示法转换为IP地址/子网掩码(点分十进制)

时间:2014-04-30 10:32:40

标签: java ip subnet james cidr

 /*
 * 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);
}

这是apache james中的一个函数,用于将ip转换为指定的格式。你能解释一下这个功能里面发生了什么吗?对这种位移和转换感到困惑。 在此先感谢。

2 个答案:

答案 0 :(得分:2)

乍一看,按位操作可能不是最直观的,但是一旦你得到它,你就会发现它们很容易理解。我将尝试解释此代码对172.16.0.1/23作为netspec字符串的示例所做的事情。

第1部分 - CIDR到二进制

目标是根据给定的CIDR前缀长度制作子网掩码的二进制表示。 CIDR前缀长度只是子网掩码中的1位数。第一行

final int bits = 32 - Integer.parseInt(netspec.substring(netspec.indexOf('/')+1));

通过获取/的索引并解析成功的整数(在我的示例中为23)来查找CIDR前缀长度。从32中减去此数字以获得子网掩码中的0个数 - 这些位也称为主机位。

在此示例中,我们知道我们正在处理/23前缀,并且它的子网掩码应如下所示:

  

n表示网络(B类网络为16位),s表示子网,h表示主机。   对于我们来说,网络和子网位在功能上是相同的,但我只是为了精确区分。我们的兴趣只在于主机位(数量)。

nnnnnnnn nnnnnnnn sssssssh hhhhhhhh
11111111 11111111 11111110 00000000

最简单的方法是获得所有1的32位二进制数和&#39;填充&#39; 0的最后9位。这是第二行的来源:

  

您可以忽略bits == 32检查,因为它不相关,可能就像优化一样。

//final int mask = (bits == 32) ? 0 : 0xFFFFFFFF - ((1 << bits)-1); 
final int mask = 0xFFFFFFFF - ((1 << 9)-1); 

0xFFFFFFFF将为您提供所有1的32位二进制数。 1左移9位(1 << bits)将为您提供512,而512 - 1二进制为111111111

  1 << 9                               10 00000000
-      1                                         1
--------------------------------------------------
                                        1 11111111

当您减去这些值时,您将获得二进制的子网掩码:

  0xFFFFFFFF = 11111111 11111111 11111111 11111111
- (1 << 9)-1 =                          1 11111111
--------------------------------------------------
               11111111 11111111 11111110 00000000

这正是我们想要的网络掩码。

注意:这可能不是计算二进制值的最直观方式。我喜欢从所有的二进制数字开始,并且signed int中的数字的小数值为-1。然后我只是将主机位的数量向左移动即可。 (另外,如果你正在处理大于32位的整数,你可以用0xFFFFFFFF掩盖它):

(-1 << 9) & 0xFFFFFFFF

第2部分 - 二进制到点分十进制

其余代码将二进制值转换为点分十进制表示形式 - 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

return语句由几个连接的字符串组成,从IP地址开始,后跟每个八位字节的十进制表示。二进制掩码向右移位(4-n)*8位(其中n是八位字节数),使用二进制AND和0xFF,只得到最后的8位,然后由Integer.toString解析。

结果为172.16.0.1/255.255.254.0

答案 1 :(得分:0)

将cidr表示法转换为二进制的另一种方法:

input = '1.2.3.4/5'
cidr = input.split('/') 

bin_mask = '1' * cidr + '0' * (32 - cidr)