IPv6

1. IPv6地址

1.1 IPv6地址表示

IPv6地址使用冒分16进制表示,即用冒号分隔的16进制表示。
128位可以用32个16进制字符表示,每4个字符之间使用冒号进行分隔,这样的话32个16进制被分成8组。

就像这样: fe80:0000:0000:0000:9100:e409:60df:02f1

首先,遇到000a:0000:00bc:…,可以简化成a:0:bc:…,相当于省去前面的零。

把上面例子简化得到: fe80:0:0:0:9100:e409:60df:2f1

其次,这样连续的三组都是零,因此可以将连续三组的零写成双冒号“::”以简化表示: fe80::9100:e409:60df:2f1。

需要注意的是,这样的省略只可以进行一次,比如把0:0:0:0:0:a:0:0:0写成::a::,这样分不清楚前面有多少组零,后面有多少组零。
因此简写最多连续的零::a:0:0:0,这样就清楚前面是5组零,后面是3组零了(如果相同多连续的零,那么优先简写左边的零)

1.2 IPv6地址结构

alt text

1.2.1 全球单播地址(Global Unicast Address,GUA)

  • 前缀001:3 bit,20到3f,占据IPv6的1/8的地址空间。也就是说2和3开头的地址是全球单播地址。
  • 顶级聚合(Top Level Aggregation,TLA):13bit, IPv6的管理机构根据TLA分配不同的地址给某些骨干网的ISP,最大可以得到8192个顶级路由。
  • 保留(Reserve,RES):8 bit,保留使用,为未来扩充TLA或者NLA预留。
  • 次级聚合(Next Level Aggregation,NLA):24bit,骨干网ISP根据NLA为各个中小 ISP分配不同的地址段,中小ISP也可以针对NLA进一步分割不同地址段,分配给不同用户。
  • 站点级聚合(Site Level Aggregation,SLA):16bit,公司或企业内部根据SLA把同一大块地址分成不同的网段,分配给各站点使用,一般用作公司内部网络规划,最大可以有65536个子网。
  • 接口地址(Interface ID):64位,主机使用的地址范围,几乎是远远超出现在的设备数量。

1.2.2 本地单播地址(Unique Local Address,ULA)

fc或者fd开头,占据IPv6的1/128的地址空间。类似IPv4中的私有地址。

1.2.3 链路本地地址

fe80到febf,占据IPv6的1/1024的地址空间。作用就像MAC地址一样,用于本地链路(局域网)之间的通信。(设备上的链路本地地址基本都是以fe80开头)

1.2.4 两个特殊地址

  • 未指定地址: ::/128。全零地址,类似于IPv4中的0.0.0.0。一个设备在没有获取IP地址之前用于向DHCP服务器申请IP地址时,原地址就是设置为0。
  • 本地回环地址: ::1/128。前面全零,只有最后一位为一的地址,类似于IPv4中的127.0.0.1。

1.2.5 内嵌IPv4地址

  • 兼容IPv4的IPv6地址: ::x.x.x.x或者0:0:0:0:0:0:x.x.x.x。就是将前面96位置为零,后面32位设置为IPv4的地址。
  • 映射IPv4的IPv6地址: ::ffff:x.x.x.x。
  • 6to4地址: 以2002为前缀。
    (没搞懂)

1.2.6 组播地址

指定组播地址: ff00到ffff,占据IPv6的1/256的地址空间。

下面是一些本地链路范围的组播地址

  • FF02:0:0:0:0:0:0:1 所有节点的组播地址。
  • FF02:0:0:0:0:0:0:2 所有路由器的组播地址。
  • FF02:0:0:0:0:1:FFXX:XXXX 被请求节点(Solicited-Node)组播地址。
  • FF02:0:0:0:0:0:0:5 所有OSPF路由器组播地址。
  • FF02:0:0:0:0:0:0:6 所有OSPF的DR路由器组播地址。
  • FF02:0:0:0:0:0:0:D 所有PIM路由器组播地址。
    其中最重要的是被请求节点组播地址,它由固定前缀FF02::1:FF00:0/104和单播IPv6地址的最后24bit组成。
    同时设备将组播IP地址的后32位添加到33开头的MAC地址,这样得到一个虚拟的组播MAC地址。
    你可以看到的是只有IPv6地址后24位相同的设备才会加入一个组。
    然后这台设备就会监听这个组播MAC地址和组播IP地址,如果得到指向这个组的地址,才会进行报文处理。
1.2.7 组播地址如何工作?

首先,很明确的一点是IPv6使用组播机制代替广播机制是明智的。
因为广播会让报文充斥整个局域网,并且每台设备都将处理报文并检查与自己是否相关。
然而,广播的范围是可以利用一些信息一定程度缩小的,组播正是利用了这一特性。
比如你希望局域网中的所有Windows设备回复你的消息,你只能广播出去;但是如果每个Windows设备监听一个组播地址,这件事情就变得简单了。
下面是两个例子。
如何使用组播地址完成无状态配置IPv6地址,并做碰撞检测?
在IPv6的环境下,路由器会定期向链路中的主机发送路由通告(Router Advertisement)报文,告知本地链路中的设备网关的存在以及网路信息。

但是当一个设备进入网络时,它立即需要网络相关信息,而不是等待一个时间间隔。

因此它立即向路由器的组播地址发送一个路由请求(Router Solicitation,RS)报文,路由器也立即发送一个路由通告报文,而不用等待时间间隔。

这个报文的目的组播IP地址:FF02::2,目的组播MAC地址:33-33-00-00-00-02

路由器监听这个组播MAC地址,发现这个报文。

于是立即向所有节点的组播地址(ff02::1)发送一个路由通告(Router Advertisement,RA)报文,这个报文携带了网络信息,包括网络前缀。

这样,设备利用网络前缀加上自己随机生成的接口地址拼成一个单播地址,但是它需要证明这个地址没有被其他用户使用。

于是该设备向被请求节点组播地址发出一个邻居请求(Neighbor solicition,NS)报文,这个组播地址最后的24位复制了IP地址的后24位。

这个报文目的被请求组播地址:ff02::01:ffxx:xxxx,目的组播MAC地址:33-33-ff-xx-xx-xx

也就是说只有后24位IP相同的设备才会收到邻居请求,它们被划分为了一个组,只有这个组才可能存在和它IP地址相同的设备。

但是在设备数量比较少的时候(设设备数量为k),这种后24位比特都相同的概率是很小的大概是k/2^24(这里假设前面的设备都没有在一个组中)。

所以,实际上设备的数量相比于2^24比较少的情况下,IPv6的组播等价与单播,这个做法极大的避免了广播的开销。

如果没有收到邻居通告(Neighbor Advertisement,NA)报文,那么重复地址检测 (DAD)完成,说明这个生成的地址没有被占据。

如果收到邻居通告(Neighbor Advertisement,NA)报文,那么说明链路中有使用这个地址的设备,需要重新生成一个不同的IP地址。
如何使用组播完成ARP地址解析?
节点获取单播地址后,会根据单播地址生成一个组播MAC地址。33-33是专门为IPv6组播预留的MAC地址前缀,MAC地址的后32bit从对应的组播IPv6地址的后32bit拷贝而来。
上面的过程将IP地址和MAC地址联系起来,当链路内一个节点发送一个组播的ARP请求,它已经知道目标的IP地址,因此生成一个组播地址作为目标MAC地址。
显然和目标IP地址后24位相同的设备都将监听这个组播MAC。如果IPv6地址的分配后24位尽量没有重复的情况下,IPv6的组播就接近于单播,付出的代价只有设备的网卡多监听一个组播MAC地址。

1.3 IPv6地址分配/生成

1.3.1 SLAAC生成接口地址(无状态配置)

略,上面讲述的就是SLACC地址生成的方式。

1.3.2 EUI-64生成接口地址

将48bits的MAC对半劈开,插入“FFFE”,然后对从左数起的第7位,也就是U/L位取反,该比特位确定48bits的MAC地址的唯一性与否。
一个以太网地址可以有两种含义,地址可被全球管理或本地管理。
全球管理指全球唯一的例如厂商MAC,本地管理指使用自己的值临时写的MAC地址,在这种情况下,这个特殊的位=1表示本地管理;为0表示全球管理。
但是在EUI-64格式中,U/L的含义正好相反,0表示本地管理,1表示全球管理,所以这就是为什么U/L位要取反的原因。

这种地址生成方式有一个显著的特点,中间是fffe,并且第七位为零,因此利用这个特性可以快速判断一个地址是否是EUI-64生成。

相应的,地址扫描工作也将变得简单,难度从64位下降至48位。也可以通过IPv4的ARP扫描获取MAC地址后,直接获取链路内设备的接口地址。

另外,这种地址生成方式将会把MAC地址暴露到外部网络中,而且MAC地址包含了网卡厂商的信息,有一些隐私的风险。
(比如黑客通过MAC地址知道了这个设备是某个厂商的某个时间点推出的,恰好有一个针对性的漏洞,这样就被轻易攻击了。)

更危险的是,这种方式的接口地址将会保持不变,连接到任意的网络之中却使用相同的接口地址,这可以造成用户被跟踪的风险。

(但不得不说这个方法都不需要ARP解析了)

1.3.3 动态主机配置(Dynamic Host Configuration,DHCP)

DHCPv6的IP组播地址是:ff02::1:2,MAC组播地址则是:33-33-00-01-00-02,client使用546端口,sever使用547端口
加入局域网的设备向这个组播地址发送报文,希望得到DHCP服务器回应即可。

2. IPv6报文

2.1 链路层标识

链路层报文:

前同步码(8字节)|目的MAC地址(6字节)|源MAC地址(6字节)|类型(2字节)|数据|循坏冗余检测CRC(4字节)

链路层类型字段标识了链路层的负载是什么:

  • ARP Ethernet Type=0x0806
  • IPv4 Ethernet Type=0x0800
  • IPv6 Ethernet Type=0x86dd

2.2 首部

IPv6的网络层长度为64比特的选项字段和256比特的IP地址信息,共320比特,40字节。MTU是1500字节,因此数据报的负载剩下1460字节。

相比于IPv4,IPv6的首部长度变长了,从20字节变为了40字节,但是选项却精简了不少。而且IPv6固定了选项字段,相比于IPv4引入作用不大的可选选项这精简了路由器处理数据报的速度。

你可以想到,320比特刚好是64比特的5倍,因此在64位机器上运行IPv6是好的,因为64位的寄存器取5次就行。但是在128位的机器上,寄存器变成128位,那么取三次为384比特,浪费一点。

alt text

2.3 版本号(Version)

4个比特,用于标识IP协议的版本,IPv6的值为6,IPv4为4。

这个字段和IPv4版本是公共的,它们字段值的设置遵循相同的标准。

这个字段的长度是足够的,4个比特一共可以表示16个版本,在有用的IP版本不超过16个的情况下,那么废弃没用的版本,这个字段就足够。

2.4 通信类别(Traffic class)

8比特,提供下面两种服务,分别是通信优先级方面和拥塞控制方面。

差分服务字段(Differentiated Services Code Point)字段,DSCP字段,利用前六个比特标识服务优先级等。

  • 具体参考RFC文档
  • 另外一个点,这个字段是不受信任的,因为用户都可以自己修改这个值,如果改为高优先级相当于白嫖了运营商的服务。所以正确的方案应该是在路由器的位置重新设置这个值,但是要防止用户端接近这个路由器,因为用户同样可以修改路由器配置来修改这个值,所以在运营商管理的路由器修改这个值才有意义。

显示拥塞通知(Explicit Congestion Notification)字段,也就是ECN字段,这在IPv4协议中也是存在的。

  • 00: 数据包未使用ECN
  • 01/10: 发送端和接收端开启了ECN功能
  • 11: 路由器发出拥塞指示

2.5 流标签(Flow Label)

20个比特。
对于不支持流标签字段功能的主机和路由器来说(目前的大多数应用都不需要修改以使用流标签,或者根本就不需要QoS处理),在发送数据包时需要将该字段设置为全0,在转发数据包时需要保持该字段不变,在收到数据包后直接忽略该字段的内容。

需要注意的是,流标签和通信类别字段保留了一些未定义比特,如果路由器不对此置为零,那么可以利用这个字段传输秘密信息,这是一个信息隐藏的方法。

2.6 载荷长度

16个比特,载荷长度去除IPv6首部的长度,包括扩展头和载荷(传输层或者ICMP包)的长度。

IPv4中有一个字段是数据报长度,表示数据报的总长度。相比于IPv6,它计入了首部的长度。

这个字段单位为字节byte,可以标识0-65535字节的范围,MTU只有1500字节,减去IPv6报头只有1460字节。因此这个字段的长度也是足够的。

2.7 下一个首部

8个比特,用于指示下一个首部的类型。

这个字段的设置和IPv4的上层协议字段相同,共用一个标准,比如值6标识TCP,值17标识UDP。当然也可以标识一些网络层协议比如ICMP协议,或者标识认证或者加密的协议AH,ESP,又或者是分片头(IPv6将分片的工作交给终端而不是路由器,并且使用协议约定线路的MTU,相比于IPv4将分片交给路由器,这简化了路由器的操作加快了转发速度)。

具体参考IANA的Protocol Numbers

这个字段的长度也是足够的,目前只用了一半多一点。

2.8 跳限制(Hop limit)

8个比特,表示网络层报文在路由器之间的传输次数限制,每到达一次路由器,这个数字减少一,减到零路由器将丢弃这个包。这样避免一个数据包在路由器之间无限循环,浪费资源。

这和IPv4中的的TTL字段一样,只不过换了一个更贴切的名字,因为TTL的意思是生存时间(Time To Live)。

这个字段的设置各个设备有些不同,包括Windows,IOS和Linux,它们默认TTL的值各有不同,也许能够这个值来判断设备的操作系统的信息。

随着IPv6的推进,肯定有越来越多的路由器加入网络,所以TTL的也有变大的可能,当然这个可能需要计算。访问baidu.com消耗了20跳,所以目前这个字段是远远够的。