又是周六的大清早。意义在时间中流逝,剩下的只有沉淀的污秽,但是也没有办法,这就是生命终将消逝的本质原因。只有感叹,然后做点什么。


ICMP可谓是IP协议的首席助力协议,是IP协议的带内信令协议,如果我们把IP协议按照数据,控制,管理三个平面划分的话,那么IP协议本身自然属于数据面,而ICMP和IGP/BGP则分别属于控制面和管理面。

可是,ICMP在IPv4版本中并没有发挥其应该发挥的作用(ICMPv4中的很多功能被阉割,甚至废除!)。这也许是因为当时的网络协议各方面技术都不完善。


互联网伊始一切靠摸索当然会走很多的弯路,然而现在,大家都只想着短平快解决问题,30年后回望现在,也并不是什么都是对的,后人哀之而不鉴之,亦使后人而复哀后人也,呜呼!


然而我认为,之所以ICMP在IPv4中大部分功能夭折,那安全是因为IPv4固有的缺陷所导致,并不是ICMP协议本身的锅!你看,ICMPv6不是已经大张旗鼓了么。让我们拭目以待!

IPv4的地址空间是平坦的,本身没有严格的scope的概念,一切都是靠路由:

* 数据发送,源地址选择靠路由
* 数据传输,逐跳转发靠路由
* 数据最后一跳,ARP解析靠路由
* 数据到达,本地投递靠路由
也就是说,只要你掌握了一些技巧,一些trick,哪怕再崎岖的路径,也能够配通了,让IPv4报文从这一端到达那一端。

从安全的角度来看,这种灵活性并不能带来便利,反而会带来风险和一系列的问题。

我在《闲谈IPv6》系列文章中提到过很多IPv6的很好玩的技术,比如无状态自动配置,Anycast,源地址选择等等,如果我们从IPv6回望到IPv4,问自己,
这些特性可以在IPv4协议上实现吗?

答案是, 完全可以!

只是地址空间缩小了而已,如果1980年代前后在设计IP协议的时候,人们按照IPv6的思维来设计IPv4,想象一下是不是很酷!

* 强制按照可聚合规则规划地址分配;
* 15.0.0.0/9是本地链路地址,不能跨三层;
* 采用224.0.0.X/24本地组播来进行地址解析(IANA早就拥有00-00-5e这个OUI了);
* 使用ICMPv4进行路由器发现和自动配置;
* …

如果真的这样实现IPv4的话,到了互联网大爆发的纪元,IPv4面临的问题就真的仅仅是地址不够用这么简单了,到时候升级到IPv6简单地把地址空间一放大,保持前向兼容,就平滑过度了,但是事实上,IPv4当初并没有如我们事后所愿那般被设计出来,上述列举的特性一个也没有实现…


不过想想也是,如果IPv4真的当初是按照我们现在的IPv6的思想设计的,只是地址空间小到32位的话,到了互联网爆发的纪元,遇到的问题肯定也不再仅仅是地址不够用这么简单,肯定还会有更多别的问题,在那条假象的时间线上,IPv4的发展轨迹我们可以猜测一下:

* 强依赖自动配置,DHCP或许不会被设计出来,至少弱化很多;
* ARP不会出现,不会有广播,这反过来会影响交换机的设计以及STP协议的设计;
* 聚合路由表项非常少,路由器复杂度会降低很多;
* 路由通告非常简单,BGP/IGP会简单很多;
* 聚合地址分配,天生无环,所有路由协议复杂性降低;
* 思科,华为这种设备公司在设备上的投入会降低;
* 底层网络解放出来的人可能会涌入到应用层,互联网更猛烈地爆发
* …
我们想象一下,当前有多少越来越复杂的网络技术是为了弥补IPv4的设计缺陷的,不胜枚举,太多了,然而却正是这些复杂的技术支撑了整个产业!


另一方面,如果真的如上所诉,在那条另外的时间线上,IPv4的简单导致底层技术的简单,人力资源涌入上层,互联网更猛烈的爆发后,一切被IPv4的精简设计掩盖的问题将还是会暴露出来,因此这些问题还是要被解决,这就是那条时间线上IPv6的任务!

因此,那条时间线上的IPv6自然也不会是我们现在的IPv6这个样子咯,它会把我们的现实时间线的IPv4的发展轨迹重新走过。

这就是历史,历史不容假设!因为你避开的东西早晚会在后面让你再遇到,所谓的不同时间维度,只是遇见的先后顺序变了变而已。这就是我的历史观。

互联网本身就是进化出来的而不是设计出来的,这就是自然的东西,历史即自然!


看看我们自己的身体,或粗糙或细腻的皮肤后面,总是藏着那些绕成一团团乱麻的血管,肠道以及里面包裹着的代谢物,如果捅破这层我们自己看起来还过得去的表皮,里面真实的汁液或会流出,红色,绿色,黄色,人也就容易一命呜呼,我们看起来还华丽的外表竟然由这些看起来很污的汁液支撑,我们并没有那种
其构成本应该如此坚固和简单 的躯体,相反,我们的躯体非常脆弱和复杂,且携带非常多的看起来是缺陷的东西,但这就是进化,这就是历史,杂乱即美。


举一个和互联网IP协议升级相对的例子,那就是通信领域的固定电话号码升位,我靠,完全兼容啊!一夜间之后醒来,你只要在原7位电话号码前面简单加拨一个6或者8,就能打通!电话就是一个
功能纯粹的被设计出来的 的东西,它是被设计的,因为没有促进它进化的动力,没有丰富的应用,没有太多人们可以折腾的内容…

让我们回到ICMP协议继续扯。

其实,在早期,RARP和ICMP路由器发现以及ICMP路由器通告一起合力,便可以实现IPv4的 无状态自动配置 ,详情参见:
ICMP Router Discovery Protocol:
https://en.wikipedia.org/wiki/ICMP_Router_Discovery_Protocol
<https://en.wikipedia.org/wiki/ICMP_Router_Discovery_Protocol>

此外,ICMPv4还具有超级多的其它功能,比如前缀查询,翻阅早期的RFC,我们发现看起来是IPv6特有的特性,其实在IPv4早期早就可以被支持,但是最终这些都被废弃了:
CAPEC-294: ICMP Address Mask Request:
https://capec.mitre.org/data/definitions/294.html
<https://capec.mitre.org/data/definitions/294.html>
Formally Deprecating Some ICMPv4 Message Types:
https://tools.ietf.org/html/rfc6918 <https://tools.ietf.org/html/rfc6918>


这些强大方便的功能特性,在IPv4看来是不合时宜的,或许看来太超前了,IPv4并没有构建出足以支撑这些特性的底层基础设施。最终,DHCP被设计出来,取代了ICMP,ARP成了地址解析的标准。

如今,我们能在Linux内核中看到一份列表,看看哪些ICMP是被废弃的:
/* * This table is the definition of how we handle ICMP. */ static const struct
icmp_control icmp_pointers[NR_ICMP_TYPES + 1] = { [ICMP_ECHOREPLY] = { .handler
= ping_rcv, }, [1] = { .handler = icmp_discard, .error = 1, }, [2] = { .handler
= icmp_discard, .error = 1, }, [ICMP_DEST_UNREACH] = { .handler = icmp_unreach,
.error = 1, }, [ICMP_SOURCE_QUENCH] = { .handler = icmp_unreach, .error = 1, },
[ICMP_REDIRECT] = { .handler = icmp_redirect, .error = 1, }, [6] = { //
Alternate Host Address .handler = icmp_discard, .error = 1, }, [7] = { .handler
= icmp_discard, .error = 1, }, [ICMP_ECHO] = { .handler = icmp_echo, }, [9] = {
// Router Advertisement .handler = icmp_discard, .error = 1, }, [10] = { //
Router discovery/selection/solicitation .handler = icmp_discard, .error = 1, },
[ICMP_TIME_EXCEEDED] = { .handler = icmp_unreach, .error = 1, }, [
ICMP_PARAMETERPROB] = { .handler = icmp_unreach, .error = 1, }, [ICMP_TIMESTAMP]
= { .handler = icmp_timestamp, }, [ICMP_TIMESTAMPREPLY] = { .handler =
icmp_discard, }, [ICMP_INFO_REQUEST] = { // Information Request .handler =
icmp_discard, }, [ICMP_INFO_REPLY] = { // Information Reply .handler =
icmp_discard, }, [ICMP_ADDRESS] = { // Address Mask Request .handler =
icmp_discard, }, [ICMP_ADDRESSREPLY] = { // Address Mask Reply .handler =
icmp_discard, }, };
一大半都被废弃了!原因肯定有,安全原因或者必要性原因都存在。


以自动地址配置为例,DHCP目前竟然成了显然的协议,不断有人问为什么IPv6不用DHCP协议而非要用其自带的无状态自动配置,也许人们早就已经习惯了DHCP协议,但其实,DHCP才是晚到的。(
PS:IPv6也是有DHCP的,也经常被使用,只是在大多数简单的情况,自动配置更简便高效)

说说邻居解析吧。

IPv4的ARP在设计上是独立于IP协议的,你看ARP报文就不是用IP来封包的,这么做考虑以下两点:

* IPv4网段规模和以太网规模使得使用广播比使用组播更加简单便捷;
* IPv4地址没有强制scope,一切靠路由,为了本地链路解析,使用非IP封包可以阻止ARP广播跨越IP路由器。
但是IPv6的邻居地址解析却与此不同,IPv6采用ICMPv6报文封装解析请求,使用 本地组播 作为目标。这意味着:

* IPv6的地址解析报文是一个普通的IPv6封装的报文,就是一个IP包;
* IPv6的本地组播本身就无法跨越路由器。
使能IPv6的网卡是无条件接收组播报文的,而且每一个配置的IPv6地址都会加入一个组播组,这意味着所有接收到的组播都会被 本地处理。

由于邻居解析报文就是一个普通的IPv6报文,所以说它对IPv6协议头和ICMPv6协议体里的被解析对象IP地址没有任何强制的关联要求:

以下是回复报文:



IPv6收到邻居请求报文时(可以是组播,也可以是单播,都可以被接收),就像收到一个普通的IP报文那般处理,它检查接收网卡上配置的IP地址,只要这块网卡上配置被请求的IP地址,那么就会回复其MAC地址,这里和IPv4的ARP处理有大不同。

IPv4的邻居概念上就和IPv6有大不同:

* IPv4的邻居是主机,是设备
* IPv6的邻居只是网卡
以上的大不同造成了实现复杂度上的大不同。


IPv4内网的主机可能会请求同网段路由器的外网地址的MAC,按照规范,路由器也应当回复,因此,在收到IPv4的ARP请求报文后,只要被请求的IP地址是本机的地址,就有可能会回复,实现这个的方法只有查找路由,以Linux为例,即查找Local表的路由,Local表的路由目的地都是本地地址。


在路由查询过程中,有可能还会有rp_filter的校验,这需要你必须有一条到达源IP的路由,在路由查询后,还要有arp_ignore,arp_filter配置的校验,这里能实现超级多的trick…你把IP地址从Local表里删掉,看看还能被解析吗?

IPv6就没有这么麻烦,它的处理逻辑超乎寻常地简单:

* 接收到邻居请求报文;
* 查看接收网卡上配置的IP地址是否有被解析的地址;
* 如果有,则回复MAC,如果没有,什么都不做。
再看路由器发现的ICMPv6报文。

在IPv4年代,一个远程设备可能会发出一个ICMP Address
Request报文去嗅探远程路由器的地址前缀信息,如果你没有规范强大的ACL来防范,这将是一个严重的安全隐患。

换句话说,IPv4的Address Request要靠外部的额外ACL来框定其有效范围,这对网络管理员是一个挑战。

在IPv6时代,这一切自然而然由地址scope来解决。


既然路由器解析报文只是一个普通的IP报文,我们不能规定数据报文的发送者必须做,即便规定了他们也不一定会遵守,特别是那些持有恶意的人,那么IPv6的路由器发现规定(
RFC4861 <https://tools.ietf.org/html/rfc4861#section-4.2>
),路由器发现报文的回应报文,即路由器通告报文的源IP必须是link-local地址:


我不能控制请求,但我可以控制回应!这个link local
scope限制了该报文的范围,它是不能跨越路由器的。IPv4的问题在IPv6中用这种标准化的方式得到了解决。

IPv6很多特性之所以让事情变得简单便捷,其根源在于IPv6地址本身。我总结三点:

* IPv6地址在分配前,规定了地址scope,人们可以用这些scope来做很多的约束;
* IPv6地址在分配时,规定了地址聚合分配,从而实现了路由的简单;
* IPv6地址分配只管到子网,不管主机。

这个需要解释一下。如果你看到类似2402:ff:1000:0010/56这样的表示,不要惊讶。你只要把/64作为地址分配的终点就好了,在IPv4中,我们不是经常见到192.168.188.123/24的表示吗?
还有别的吗?

最后,来个答疑。

问题:如果IPv6报文添加了 路由头 ,那么在某个中间路由器上,该报文将经过哪些Netfilter HOOK点呢?

答:IPv6的路由头将 逐跳转发 变成了 接力转发 ,每一个经过的路由头列表里的中间路由器都将作为目标地址,因此数据报文经过的HOOK为:
PREROUTING–>INPUT–>FORWARD–>POSTROUTING

浙江温州皮鞋湿,下雨进水不会胖。

友情链接
KaDraw流程图
API参考文档
OK工具箱
云服务器优惠
阿里云优惠券
腾讯云优惠券
华为云优惠券
站点信息
问题反馈
邮箱:ixiaoyang8@qq.com
QQ群:637538335
关注微信