摘要

  • RFC 2133 保留了核心 socket 函数的调用形式,因为它们本来就传递不透明地址指针和长度;IPv6 仍需要 PF_INET6、sockaddr_in6 以及新的名称和地址转换接口。
  • 旧 PF_INET 程序继续与 IPv4 节点通信,和新 PF_INET6 程序把 IPv4 对端表示为映射地址,是两项不同的兼容承诺;两者都不能代替实际连通性证据。

最能说明问题的不是创建 socket,而是交接。一个进程把仍然打开的 socket 留给下一个进程;接手者照旧调用 getpeername(),却按错误的结构解释返回地址。1997 年的 RFC 2133 专门讨论了这种情况。它是一份 Informational 文件,后来被 RFC 2553 废止,因此适合分析当年的设计取舍,不宜当成今天的编程规范。

当时的基本矛盾很具体:IPv4 地址为 32 位,IPv6 地址为 128 位。核心 socket 函数把地址作为指针并附带长度传递,所以 bind()、connect()、sendto()、accept() 的语法可以沿用;但是 IPv4 的 sockaddr_in 即使有闲置字节,也装不下完整的 IPv6 地址、地址族和端口。规范于是增设 AF_INET6 与 PF_INET6,定义 sockaddr_in6,并补上地址文本转换、名称解析等接口。4.3BSD 与 4.4BSD 对新结构的长度及族字段还有不同布局。调用名不变,缓冲区大小、强制类型转换和二进制布局仍可能出错。

规范先保护原有程序。实现新接口的系统仍须接受 PF_INET 与 sockaddr_in,使旧应用的源码和二进制继续按原来的方式同 IPv4 节点通信。这不是让未改动的程序自动理解 IPv6。另一个承诺给新程序:使用 PF_INET6 时,可以把 IPv4 对端编码进 sockaddr_in6,形如 ::FFFF:<IPv4-address>。这只是 API 中表示 IPv4 对端的方式。看到 128 位外形,不能推断对端说的是 IPv6,更不能把它计作原生 IPv6 业务成功。

通配和回环地址也有自己的范围。IPv6 的未指定地址可用于让系统选择本地地址;回环地址指向本机。调用用了这些常量,不能推出所有网络族都在监听,也不能推出远端用户能访问。规范描述接口允许什么,没有提供任何特定网络的部署测量。

跨进程交接揭示了更深的状态限制。RFC 2133 提出 IPV6_ADDRFORM,让接手的程序改变打开的 socket 对外呈现的 PF_INET 或 PF_INET6 地址视图。若要降为 PF_INET,已关联的非通配地址必须全部是 IPv4 映射地址。这个条件说明,转换不只是换一个标签:已有的内核地址状态限制了哪些视图合理。它是历史提案中的选项,不能据此假定现代系统都以同样方式实现。

接口索引又把判断带回本机。规范用 if_nametoindex 等函数对应接口名与内核分配的索引;多播选项可选发送接口或本地入组接口。索引和入组设置证明的是本地选择,不是数据已到达别的主机。名称查询返回地址也只是准备好一次调用的输入,不是连接完成的回执。

RFC 2133 的历史贡献,是把旧程序兼容、新程序的跨族表示和真实网络结果拆开陈述。恒路谈运行代码优先时强调观察运行系统;这里借用的是证据视角,而非把他的评论当作 RFC 的技术条款。

来源:RFC 2133、RFC Editor 状态记录、RFC 2553。