当前位置: 首页 > >

闲谈IPv6-从ICMP的视角形而上地分析IPv6

发布时间:

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



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
此外,ICMPv4还具有超级多的其它功能,比如前缀查询,翻阅早期的RFC,我们发现看起来是IPv6特有的特性,其实在IPv4早期早就可以被支持,但是最终这些都被废弃了:
CAPEC-294: ICMP Address Mask Request: https://capec.mitre.org/data/definitions/294.html
Formally Deprecating Some ICMPv4 Message Types: 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),路由器发现报文的回应报文,即路由器通告报文的源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



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



友情链接: