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

3
 /*
 * 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个回答

2

按位运算可能一开始看起来不太直观,但一旦理解了,你会发现它们非常容易理解。我将尝试解释这段代码在一个以172.16.0.1/23为例的netspec字符串中的作用。

第一部分 - 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

最简单的方法是使用一个32位二进制数,所有位都是1,并在末尾9位处“填充”0。这就是第二行的作用:

您可以忽略bits == 32检查,因为它不是那么相关,很可能只是一种优化。

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

0xFFFFFFFF会给你一个32位的二进制数,所有位都是11左移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

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

注意:这可能不是最直观的计算二进制值的方法。我喜欢从一个全为1的二进制数开始,该数字在有符号整数中具有十进制值-1。然后我只需将其向左移动主机位数即可。(另外,如果您处理的整数大于32位,则可以使用0xFFFFFFFF进行掩码操作):

(-1 << 9) & 0xFFFFFFFF

第二部分 - 二进制转点分十进制

剩下的代码将二进制值转换为点分十进制表示法 —— 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

返回结果如下:

返回语句由多个字符串组成,以IP地址开头,后跟每个八位字节的十进制表示。二进制掩码向右移动(4-n)*8位(其中n是八位字节的编号),并使用二进制AND运算符与0xFF进行运算,仅获取最后8位,然后通过Integer.toString进行解析。

结果为172.16.0.1/255.255.254.0


-1

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

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

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接