写点什么

IPv6 原理、应用与实践(上)

  • 2019-10-28
  • 本文字数:6530 字

    阅读完需:约 21 分钟

IPv6原理、应用与实践(上)

2017 年 11 月 26 日,中共中央办公厅和国务院办公厅印发了《推荐互联网协议第六版(IPv6)规模部署行动计划》,并发出通知,要求各地区各部门结合实际认真贯彻落实。这条新闻传达了一个很重要的信息:这个是推进中国 IPv6 发展的战略总动员令。


本文将会从以下几个方面进一步介绍 IPv6,包括有:


1.IPv6 的基本概念


2.IPv6 在 Linux 操作系统下的实现


3.IPv6 的实验


4.IPv6 的过渡技术介绍


5.IPv6 在 Linux 平台下 socket 编程应该注意的问题


6.实现简易版 TGW 支持 IPv6 雏形 demo


值得说的是,目前我们接触得比较多的主流操作系统内核,已经很好地支持 IPv6 协议栈,例如:


  • Windows: windows 7、windows 8.x、windows 10,默认开启 IPv6

  • Linux: 内核 2.6.x、内核 3.x、内核 4.x 已经支持 IPv6(需要手动开启)

  • IOS:IOS9 开始已经支持 IPv6 Only,2016 年苹果已经强制要求 app 必须支持 IPv6


本文提到的 IPv6 节点,没有特殊说明,一般指的是纯 IPv6 节点(IPv6 Only),也就是只支持 IPv6 协议栈;IPv4 节点,是指纯 IPv4 的节点,也就是只支持 IPv4 协议栈;如果节点支持 IPv6 和 IPv4 双栈,会指明是双栈节点。

IPv6 的基本概念

众所周知,32 位的 IPv4 地址已经耗竭,IPv6 采用 128 位的地址长度拥有更大的地址空间。首先我们先来认识一下 IPv6 到底长成什么样子。

初识 IPv6


图 1 IPv6 数据报文


上图是我们最熟悉的 ping 的 IPv6 版本 ICMPv6。可以看到,IPv6 数据报文和 IPv4 有很大的差别:


  • 数据链路层(L2)的 type 字段标识为 0x86dd,表示承载的上层协议是 IPv6


IPv4 对比:type 字段为 0x0800


  • IPv6 的头部字段,和 IPv4 差别巨大(可以猜测到,IPv6 和 IPv4 无法兼容)


IPv6 的报文头部格式如下:



图 2 IPv6 报文头部(该图片来自互联网)


IPv6 报文头部更精简了,字段更少了,对比起 IPv4,有以下几个地方值得注意:


  • IPv6 报文头部是定长(固定为 40 字节),IPv4 报文头部是变长的。这个意味着,写代码处理 IPv6 数据报文的效率会提高很多:)

  • IPv6 中 Hop Limit 字段含义类似 IPv4 的 TTL。

  • IPv6 中的 Traffic Class 字段含义类似 IPv4 中的 TOS(Type Of Service)。

  • IPv6 的报文头部取消了校验和字段。取消这个字段也是对 IPv4 协议的一个改进。当 IPv4 报文在网路间传输,每经过一个路由器转发就是修改 TTL 字段,就需要重新计算校验和,而由于数据链路层 L2 和传输层 L4 的校验已经足够强壮,因此 IPv6 取消这个字段会提高路由器的转发效率。值得一提的是,在 IPv6 协议下,传输层 L4 协议 UDP、TCP 是强制需要进行校验和的(IPv4 是可选的)。

  • IPv6 报文头部中的 Next Header 字段表示“承载上一层的协议类型”或者“扩展头部类型”。这里的含义与 IPv4 有很大的差别,需要加以解释:


当 IPv6 数据报文承载的是上层协议 ICMPv6、TCP、UDP 等的时候,Next Header 的值分别为 58、6、17,这个时候和 IPv4 报文头部中的 Protocol 字段很类似。


当不是以上 3 种协议类型的时候,IPv6 报文头部紧接的是扩展头部。扩展头部是 IPv6 引入的一个新的概念,每个 IPv6 的数据报文可以承载 0 个或多个扩展头部,扩展头部通过链表的形式组织起来。当 IPv6 数据报文承载着扩展头部的时候,Next Header 的数值为扩展头部的类型值。


为什么要引入扩展头部这个概念,这里也是 IPv6 对 IPv4 改进的一个方面,用扩展头部取代了 IPv4 的可选项信息,精简了 IPv6 的头部,增强了 IPv6 的扩展性。有同学会不会有疑问,IPv6 的分片数据报文怎么处理?其实就是使用了 IPv6 扩展头部。我们来抓一个 UDP 分片报文来看看。



图 3 IPv6 分片报文


当发送一个分片 IPv6 数据报文的时候,IPv6 使用的是扩展头部的形式组织各个分片的信息,如图 IPv6 报文头部 Next Header 字段值为 44 表示存在扩展头部,扩展头部是 IPv6 分片数据信息。


对比 IPv4,分片信息是记录在 IPv4 报文头部的分片字段中。


IPv6 的扩展头部类型有很多种,除了上述的分片头部,还有路由头部、逐跳可选头部等,具体的可以参考 RFC2460。


本章主要介绍了 IPv6 的一些很直观的认识,下面逐渐介绍 IPv6 上的基本知识和概念。

IPv6 的地址语法

一个 IPv6 的地址使用冒号十六进制表示方法:128 位的地址每 16 位分成一段,每个 16 位的段用十六进制表示并用冒号分隔开,例如:


一个普通公网 IPv6 地址:2001:0D12:0000:0000:02AA:0987:FE29:9871


IPv6 地址支持压缩前导零的表示方法,例如上面的地址可以压缩表示为:


2001:D12:0:0:2AA:987:FE29:9871


为了进一步精简 IPv6 地址,当冒号十六进制格式中出现连续几段数值 0 的位段时,这些段可以压缩为双冒号的表示,例如上面的地址还可以进一步精简表示为:


2001:D12::2AA:987:FE29:9871


又例如 IPv6 的地址 FF80:0:0:0:FF:3BA:891:67C2 可以进一步精简表示为:


FE80::FF:3BA:891:67C2


这里值得注意的是,双冒号只能出现一次。


IPv6 地址的号段划分和前缀表示法


IPv6 拥有 128 位巨大的地址空间,对于那么大的空间,也不是随意的划分,而是使用按照 bit 位进行号段划分(与鹅厂内部一些的 64 位 uin 改造放号的 zone 划分算法)。


IPv6 的地址结构如下图:



图 4 IPv6 地址结构


例如 RFC4291 中定义了 n=48, m=16,也就是子网和接口 ID 与各占 64 位


IPv6 支持子网前缀标识方法,类似于 IPv4 的无分类域间路由 CIDR 机制(注意:IPv6 没有子网掩码 mask 的概念)。使用“IPv6 地址/前缀长度”表示方法,例如:


2001:C3:0:2C6A::/64 表示一个子网


而 2001:C3:0:2C6A:C9B4:FF12:48BC:1A22/64 表示该子网下的一个节点地址。


可以看到,一个 IPv6 的地址有子网前缀+接口 ID 构成,子网前缀由地址分配和管理机构定义和分配,而接口 ID 可以由各操作系统实现生成,生成算法后面的章节会介绍。

IPv6 的地址类型

IPv6 地址分三种类型


1、单播,对应于 IPv4 的普通公网和私网地址


2、组播,对应于 IPv4 的组播(多播)地址


3、任播,IPv6 新增的地址概念类型


IPv6 没有广播地址,用组播地址实现广播的功能。实际上我们工作和生活最可能最多接触的就是单播地址,接下来本文重点会讲解单播地址的种类。组播和任播地址有兴趣的同学自行查阅相关 RFC 和文献。

IPv6 单播地址

注意,大家如果在网上搜索 IPv6 的地址,可能都是千篇一律的把所有“出现过”的单播地址介绍出来,其实有一些单播地址类型已经在相关的 RFC 中被废除或者不建议使用,而本节会指出这类地址。同时,在介绍单播地址的时候,尽量与 IPv4 中对应的或者相类似的概念做对比,加深理解。


IPv6 单播地址有以下几种:


1、全球单播地址



图 5 IPv6 全球单播地址结构


前缀 2000::/3,相当于 IPv4 的公网地址(IPv6 的诞生根本上就是为了解决 IPv4 公网地址耗尽的问题)。这种地址在全球的路由器间可以路由。


2、链路本地地址



图 6 链路本地地址结构


前缀 FE80::/10,顾名思义,此类地址用于同一链路上的节点间的通信,主要用于自动配置地址和邻居节点发现过程。Windows 和 Linux 支持或开启 IPv6 后,默认会给网卡接口自动配置一个链路本地地址。也就是说,一个接口一定有一个链路本地地址。如下图:



图 7 Linux 下查看链路本地地址



图 8 Windows 下查看链路本地地址


值得说的是,每个接口必须至少有一个链路本地地址;每个接口可以配置 1 个以上的单播地址,例如一个接口可以配置一个链路本地地址,同时也可以配置一个全球单播地址。


注意,很容易会把链路本地地址和 IPv4 的私网/内网地址对应起来,其实链路本地地址对应于 IPv4 的 APIPA 地址,也就是 169.254 开头的地址(典型场景就是 windows 开启自动获取地址而获取失败后自动分配一个 169.254 的地址)。而 IPv4 私网对应于 IPv6 的什么地址,后面会介绍。


特别地,在 IPv6 socket 编程中,可以使用链路本地地址编程通信,但是需要增加一些额外的参数(这是一个小坑),在后面介绍编程的章节会介绍。


3、唯一本地地址



图 9 唯一本地地址结构


前缀 FC00::/7,相当于 IPv4 的私网地址(10.0.0.0、172.16.0.0、192.168.0.0),在 RFC4193 中新定义的一种解决私网需求的单播地址类型,用来代替废弃使用的站点本地地址。


可能看到这里,有同学会跳出来说:IPv6 不是为了解决 IPv4 地址耗尽的问题吗,既然 IPv6 的地址空间那么大,可以为每一个网络节点分配公网 IPv6 的节点,那为什么 IPv6 还需要支持私网?这里需要谈谈对 IPv6 下私网支持的认识。


在 IPv4 中,利用 NAT 技术私网内的网络节点可以使用统一的公网出口访问互联网资源,大大节省了 IPv4 公网地址的消耗(IPv6 推进缓慢的原因之一)。另一方面,由于默认情况下私网内节点与外界通信的发起是单向的,网络访问仅仅能从私网内发起,外部发起的请求会被统一网关或者防火墙阻隔掉,这样的网络架构很好的保护了私网内的节点安全性和私密性。可以设想一下,如果鹅厂内部每台办公电脑都配置了 IPv6 的公网地址上网,是多么可怕的事情,每台办公电脑都会面临被黑客入侵的威胁(肉鸡真多)。


因此,在安全性和私密性要求下,IPv6 中同样需要支持私网,并且也需要支持 NAT。在 Linux 内核 3.7 版本开始加入对 IPv6 NAT 的支持,实现的方式和 IPv4 下的差别不大(Linux 内核代码中变量和函数的命名几乎就是 ctrl+c 和 ctrl+v 过来的-_-||)。


4、站点本地地址


前缀 FEC9::/48,以前是用来部署私网的,但 RFC3879 中已经不建议使用这类地址,建议使用唯一本地地址。大家知道有这么一回事就可以了。网上还有很多文章还提到这种地址,但是没有说明这种地址已经不再使用。


5、特殊地址:回环地址


0:0:0:0:0:0:0:1 或::1,等同于 IPv4 的 127.0.0.1


6、过渡地址:内嵌 IPv4 地址的 IPv6 地址


就是在 IPv6 的某一些十六进制段内嵌这 IPv4 的地址,例如 IPv6 地址中 64:ff9b::10.10.10.10,此 IPv6 地址最后 4 个字节内嵌一个 IPv4 的地址,这类地址主要用于 IPv6/IPv4 的过渡技术中。

一、IPv4 兼容地址

0:0:0:0:0:0:w.x.y.z 或::w.x.y.z(其中 w.x.y.z 是点分十进制的 IPv4 地址)。但在 RFC4291 中已经不推荐使用这类地址,大家知道有这么一回事就可以了。

二、过渡地址:IPv4 映射地址

0:0:0:0:0:FFFF:w.x.y.z 或::FFFF:w.x.y.z(其中 w.x.y.z 是点分十进制的 IPv4 地址),用于 IPv6 地址表示 IPv4 地址。主要用于某些场景下 IPv6 节点与 IPv4 节点通信,Linux 内核对这类地址很好地支持,在后面编程和内核分析的章节会分析使用过程。

三、过渡地址:特定过渡技术地址

6to4 地址、ISATAP 地址、Teredo 地址主要用于对应的过渡技术的地址,在后面介绍过渡技术的时候会介绍。

IPv6 接口 ID 生成算法

从前面的介绍中可以看出,IPv6 单播地址是由前缀(64 位)+接口 ID(64 位)组成。接口 ID 的生成算法主要有以下几种:


1、根据 RFC4291 定义,接口 ID 可以从 EUI-64 地址生成。


详细算法可以查看 regli 同学的 PPT 第 14 页。


2、为了可以具备某种程度的匿名信,接口 ID 可以使用一个随机分配的,windows 操作系统默认就是使用这种生成算法,Linux 下也是默认开启这个算法。


3、使用状态化的自动配置技术分配,例如 DHCPv6 分配。


4、手工配置。

IPv6 地址配置

前面对 IPv6 的地址、前缀、接口等等做了介绍,接下来就是要介绍一个接口如何配置 IPv6 地址。IPv6 一个比 IPv4 更厉害的方面,就是可以自动配置地址,甚至这个配置过程不需要 DHCPv6(在 IPv4 中是 DHCPv4)这样的地址配置协议。最典型的例子就是,只要开启了 IPv6 协议栈的操作系统,每个接口就能自动配置了链路本地地址,这个是和 IPv4 最重要的区别之一。


IPv6 的地址配置有以下几种:


1、只要开启了 IPv6 协议栈,接口自动分配链路本地地址。


2、无状态自动配置地址(RFC2462),后面会有实验演示。


3、有状态自动配置地址,例如 DHCPv6。


4、手动配置。

IPv6 的域名解析

由于 IPv6 的地址扩展为 128 位,比 IPv4 的更难书写和记忆,因此 IPv6 下的 DNS 变得尤为重要。IPv6 的的 DNS 资源记录类型为 AAAA(又称作 4A),用于解析指向 IPv6 地址的完全有效域名。下面是一个示例:


Hostipv6.example.wechat.com IN AAAA 2001:db8:1::1


IPv6 下的域名解析可以认为是 IPv4 的扩展,详细可以查看 RFC3596.

Linux 内核 IPv6 架构简析

本文后面主要的分析都是基于 Linux,会有涉及关于 Linux 内核对 IPv6 的实现。主要是因为,现在 IPv6 的参考资料不多,除了与 IPv6 相关的 RFC 之外,还有少数可以参阅的 IPv6 国外文献,而 Linux 内核一直都与跟随着 IPv6 的协议更新和变化,Linux 内核 IPv6 的实现是十分重要的参考材料之一。而且从事后台开发工作主要也是在 Linux 平台下,熟悉 Linux 下 IPv6 的实现也是为以后的工作做知识储备。


PS:客户端开发的同学可以参考各自平台的文档…


Linux 在很早之前就已经开始支持 IPv6,目前我们接触最多的 Linux 内核版本都很好地支持 IPv6,同时也是支持 IPv4/IPv6 双栈体系。在 Linux 操作系统中,IPv4 是默认必须开启,IPv6 是可选编译和配置开启。


例如在编译内核的时候,需要选择 IPv6 编译选项才支持 IPv6



图 10 Linux 内核编译支持 IPv6


当开启支持 IPv6 的 Linux 的内核网络双栈的结构,如下图:



图 11 Linux 内核双栈架构


Linux 内核中,IPv6 协议栈与 IPv4 协议栈并行关系。IPv6 和 IPv4 完全是两套不一样的代码实现。IPv6 完整的协议栈逻辑模块包括:


1、网络层 IPv6,核心逻辑:IPv6 路由子系统


2、传输层 TCP/UDP 实现:TCPv6、UDPv6


3、控制报文协议 ICMPv6,这里值得一提的是 ICMPv6 在 IPv6 协议中的地位十分重要。


ICMPv6 不仅提供了与 ICMPv4 相同的服务诊断功能,例如报告数据包的错误和提供简单的 echo 服务,ICMPv6 是 IPv6 中邻居发现协议的重要组成部分,用于管理链路上的点到点的通信。


4、邻居子系统的实现:邻居发现协议 NDP(对应于 IPv4 里面的 ARP 协议)


5、其他高级实现(IPv6 NAT、IPv6 隧道、iPv6 IPSec 等)


由于我们平时的开发工作在应用层,以上 1-4 是将会接触得最多。

IPv6 实验

本章我们通过实验,加深对 IPv6 的认识。这里的实验没有使用真实现网的 IPv6 接入点(目前国内绝大部分接入点都是教育网),而实验的目的主要是观察 IPv6 的数据包结构、IPv6 的路由配置等,所以决定自己通过搭建中间路由器、应用服务器的方式做实验,便于抓包和代码分析。


客户端:windows 7


路由器:中间路由器使用自己编译和搭建的 Linux 系统(内核 2.6.32.27)


应用服务器:Ubuntu16.04LTS 版本。


为什么要使用自己编译的 Linux 作为路由器?因为 IPv6 的实践类能参考的文献比较少,而 Linux 内核的 IPv6 模块是最重要的参考资源之一,在实践中遇到问题可以使用打 LOG 和分析代码的方法解决。

1、无状态自动配置地址实验

IPv6 地址的获取是最重要的环节之一。本实验使用开源的无状态自动配置服务 radvd 进行实验。



图 12 IPv6 无状态自动配置



图 13 IPv6 无状态自动配置报文分析


无状态自动配置过程:


1、由链路上的主机向链路发起“路由请求”报文,这个报文是以组播协议发送,寻找链路上最合适的路由器。


2、路由器收到请求会返回“路由通告”报文,报文里面带着本链路的地址前缀信息主机将接收到的前缀和自身的接口 ID,组成完整的新地址。


3、主机尝试使用新地址发起地址重复检测,检测链路上是否有其他主机也是这个地址,如果有,就停止使用该地址;如果没有,就启用这个新地址。


可以看到无状态自动配置过程十分简易(对比 DHCPv4 和 DHCPv6 来说),实际上,无状态自动配置可以单独组网使用,也可以配合有状态自动配置一般会配合使用,加强网络节点管理。涉及自动配置和地址检测等更多细节,可以查阅 RFC1971、RFC4861。

2、IPv6 静态路由配置实验

本次实验主要是了解 windows 和 linux 的静态路由配置。



图 14 IPv6 典型的网络拓扑


由于各自的网络前缀(网段)不一致,在不使用默认路由的情况下,我们尝试配置路由让客户端可以访问到服务器。


一、Windows 7 配置静态路由:


去往服务器的 2001:db8:5::/64 网段的路由



图 15 Windows 配置 IPv6 路由


二、路由器 1 配置



图 16 Linux 下配置 IPv6 路由


三、路由器 2 配置



图 17 Linux 下配置 IPv6 路由


四、服务器静态路由配置



图 18 服务器配置 IPv6 路由


五、结果



图 19 客户端访问服务器


客户端可以顺利 ping 通服务器。可以看到,IPv6 下的路由配置,无论是 windows 还是 linux,与 IPv4 的配置差别不大,熟悉 IPv4 各个平台路由配置的同学可以很快上手 IPv6 的路由配置。


2019-10-28 10:181896

评论

发布
暂无评论
发现更多内容

Java 发展史

kcodez

Java 后端

2023-02-14:魔物了占领若干据点,这些据点被若干条道路相连接, roads[i] = [x, y] 表示编号 x、y 的两个据点通过一条道路连接。 现在勇者要将按照以下原则将这些据点逐一夺回:

福大大架构师每日一题

算法 rust 福大大

用户卖家平台三方螺旋成长 如何让商品推荐更智能

阿里技术

全球化技术能力

一文盘点,ZBC的应用场景与通缩场景

鳄鱼视界

活动预告|Triton Meetup 2023

AI Infra

AI

OneFlow源码解析:Eager模式下的SBP Signature推导

OneFlow

人工智能 深度学习 框架解析

Three.js 进阶之旅:物理效果-碰撞和声音 💥

dragonir

CSS JavaScript html 前端 three.js

SpringBoot 如何保证接口安全?老鸟们都是这么玩的!

做梦都在改BUG

Java Spring Boot 接口

Svelte框架结合SpreadJS实现表格协同文档

葡萄城技术团队

2022Q4手机银行运营亮点:“新版本迭代潮”叠加“个人养老金账户争夺战”

易观分析

金融 银行 经济

2023年第一季度汽车行业行情预测分析

不脱发的程序猿

汽车电子 2023年第一季汽车行业分析

入门数据分析师的最强秘籍,都在这4本书里!

博文视点Broadview

音乐APP用户争夺战,火山引擎VeDI助力用户体验升级!

字节跳动数据平台

大数据 增长 音乐 企业号 2 月 PK 榜

【Redis 故障排查】「连接失败问题排查和解决」带你总体分析CPU及内存的使用率高问题排查指南及方案

洛神灬殇

redis 性能调优 缓存服务 2月日更

5 如何优雅的告诉老板复制 ChatGPT几乎是“impossible”的?

涛哥 数字产品和业务架构

ChatGPT 业务架构师

中美ChatGPT的商业化分野

脑极体

ChatGPT

ChatGPT时代的打工人众生相

白洞计划

ChatGPT

智能汽车商业化、产业化演进及投资机会分析

不脱发的程序猿

汽车电子 智能汽车商业化 汽车行业投资机会分析

Java Map操作解锁新姿势

派大星

热点面试题: Array中有哪些非破坏性方法?

Immerse

JavaScript array 前端面试题 Javascript框架 超全前端面试题

银行业上云进行时,OLAP 云服务如何解决传统数仓之痛?

Kyligence

OLAP技术 传统数仓

软件测试/测试开发 | 网页 frame 与多窗口处理

测试人

软件测试 自动化测试 测试开发 Web自动化测试

线程私有变量ThreadLocal详解

Java随想录

Java 线程 并发

啊啊啊!小程序小游戏也可以在自己的App上架❗️❗️

没有用户名丶

微信小程序 小程序游戏

宽表为什么横行?

王磊

进击中的 Zebec 生态,Web2 与 Web3 世界的连接器

BlockChain先知

瑞萨RH850 CS+环境下设置堆和栈空间

不脱发的程序猿

嵌入式 汽车电子 MCU RH850 瑞萨IDE

中国工商银行签约易观千帆,夯实数字基石,助力用户价值增长

易观分析

金融 银行

OKR之剑·实战篇06:OKR致胜法宝-氛围&业绩双轮驱动(下)

vivo互联网技术

团队管理 OKR

上架的时候怎么向某个版本添加构建版本

雪奈椰子

apple ios开发

探讨丨传统行业必须数字化转型吗?

优秀

数字化转型

IPv6原理、应用与实践(上)_文化 & 方法_唐永彬_InfoQ精选文章