IP地址,子网掩码,子网前缀长度,路由表的的算法

发布于 2017-01-18  220 次阅读


高三时曾用PHP制作一个IP子网计算器,今天尝试使用C实现同样的功能,遂来分享下算法。

本文的算法从IP地址的原理出发,使用IPv4举例说明,如果你掌握了IPv4的算法,那么IPv6的算法也不是问题。

IP地址

首先我们要知道,我们现在使用的二进制计算机,仅认识0与1,坚持此原则的话,那你也能猜到,对于计算机来说,IP地址亦不过是0与1。

IPv4的二进制数长度为32位。

以192.168.1.1这个IPv4地址为例,有四个字段

192

168

1

1

我们来分别把他们转换为二进制,得

11000000

10101000

1

1

这里的数组合起来明显不够32位嘛,既然IPv4分成四个字段,那么很自然是平均分的,不够八位就补零

11000000

10101000

00000001

00000001

最后按顺序组合起来,得到二进制形式的IPv4地址:11000000101010000000000100000001,转换为十进制,得到3232235777:

IP地址,子网掩码,子网前缀长度,路由表的的算法

IP地址,子网掩码,子网前缀长度,路由表的的算法

纯数字IP地址,有趣吧!

到这里,你应该知道每个IP地址都对应一个十进制整数,还有为什么每一个IPv4字段的最大值是255。

子网掩码

子网掩码,如果你看过,那你肯定知道他看起来是一个IP地址。

妇孺皆知的子网掩码,莫过于255.255.255.0。

按IPv4转二进制的算法,得:

11111111111111111111111100000000

还有十进制:

4294967040

这和子网大小有何关系呢?

IPv4地址是32位,那么二进制的最大值是三十二个"1",转为十进制是2^32-1=4294967295,对应IPv4 255.255.255.255。

注意:虽然十进制数是4294967295,但勿忘0也是一个数,0是第一个数,遂IPv4有4294967296个IP,255.255.255.255乃第4294967296个IP。

4294967296(IPv4地址总数量) - 4294967040(子网掩码的十进制数值) = 256

256,就是子网大小,意味着,此子网共有256个IPv4地址。

如IP 192.168.1.0,子网掩码 255.255.255.0,则表示本IP段的始IP为192.168.1.0,末IP为192.168.1.255,共256个IP,本网段理论上,不通过路由器或网关,是无法访问192.168.1.0-192.168.1.255之外的IP地址的。

既然是理论上,那就意味着要访问也是可以的,根本问题就在于路由表,路由表规定了系统发出的数据包所走的路径。

使用192.168.1.0/255.255.255.0的系统一般不会带有针对192.168.0.0-192.168.0.255的路由表规则。

当访问192.168.0.0-192.168.0.255时,数据包会匹配系统的默认路由规则,一旦默认路由规则走的并非192.168.0.0-192.168.0.255所在的网络接口,那数据包就无法发送给192.168.0.0-192.168.0.255。

IP地址,子网掩码,子网前缀长度,路由表的的算法

如图所示,第一个ip route show,显示出默认路由规则是把数据包发送到网卡apcli0,并没有针对192.168.0.0-192.168.0.255的路由规则,到192.168.0.0-192.168.0.255的数据包将会走apcli0(然而正确的是br-lan),因此ping 192.168.0.15显示100%丢包。

IP地址,子网掩码,子网前缀长度,路由表的的算法

后来执行ip route add添加了一条针对192.168.0.0-192.168.1.255的路由规则(192.168.0.0/23和192.168.0.0/255.255.254.0是等价的,下面将会介绍),告诉内核发送到192.168.0.0-192.168.1.255的数据包走网卡br-lan,因此后面执行ping,显示0%丢包。

子网前缀长度

子网掩码部分提到:“192.168.0.0/23和192.168.0.0/255.255.254.0是等价的”,那23与255.255.254.0之间有何微妙关系?

255.255.254.0的二进制值是11111111111111111111111100000000,你可以数一下,有23个“1”,23就是这么来的。

合法的子网掩码的1仅能全部向左排列,因此嘛,不用怀疑你的想法,合法的子网掩码真的只有32个,子网前缀长度的数值范围是0-32。

IP地址,子网掩码,子网前缀长度,路由表的的算法

通过子网前缀长度,可以计算出子网范围,公式为:IP数目=2^(32-子网前缀长度)。

子网前缀长度为23,那就有2^9=512个IP。

子网内随机IP转子网首IP

我之所以将此看作为重点,是因为构思子网计算器时根本没意识到本问题,几乎在弄好后才发现,印象比较深刻。

可以看到这里,相信你已经掌握了IP子网计算的算法。

那么请你计算一下10.20.30.40/27的范围。

10.20.30.40-10.20.30.71?如果你的答案是这个,恭喜你,错了,正确答案是10.20.30.32-10.20.30.63。

10.20.30.40是包含在10.20.30.40/27这个子网内的一个IP,并非首IP,若直接把10.20.30.40当作起始IP加上IP数量的话,后面的一部分IP就并非本网段的了。

相信你也懂了,这里的重点就在于,如何找出子网首IP。

极限思维法:

0.0.0.0/0,只要掌握了本文的算法,谁都能得到0.0.0.0-255.255.255.255

0.0.0.10/0,如果直接用本文的算法,会得到0.0.0.10-255.255.255.255,很明显,这样的话前面有十个IP漏下了。

资源可贵,浪费是绝对不允许的,为了充分利用IP资源,当然务必使所有IP都能按合法的子网掩码划分出去。

IP地址,子网掩码,子网前缀长度,路由表的的算法IP地址,子网掩码,子网前缀长度,路由表的的算法

10.20.30.40的十进制值是169090600,也就是,10.20.30.40是第169090600个IP,子网前缀长度27,有32个IP,我们看看169090600个IP能按32 IP/子网划分出多少个子网,然后把169090600减去剩余的IP数量,不就得到首IP了吗?

通过169090600对32求余得8,169090600 - 8 = 169090592,169090592的意义在于,这个数小于且最接近169090600,又能按每个子网32个IP,毫不保留地把IP划分出去。

169090592就是你要找的网段首IP的数值地址。

2^(32-27) = 32,169090592 + 32 -1 = 169090623,算得末IP是169090623,之所以要减一,是因为169090592是首IP,是第一个IP。

阅读完本文的你,相信也有能力写一个子网计算器了。