IPv6学习笔记

Posted by 云谷计算 on September 29, 2019

IPv6协议

  • 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字段表示“承载上一层的协议类型”或者“扩展头部类型”。

IPv6的地址语法

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

一个普通公网IPv6地址:

2001:0D12:0000:0000:02AA:0987:FE29:9871

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

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

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

2001:12::2AA:987:FE29:9871

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

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

IPv6地址前缀表示法

IPv6支持子网前缀标识方法,类似于IPv4的无分类域间路由CIDR机制(注意:IPv6没有子网掩码mask的概念)。

使用IPv6地址/前缀长度表示方法,例如:

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

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

IPv6寻址模式

  • 单播 : 跟ipv4单播一致,在单播寻址模式下,IPv6接口(host)在网段中唯一标识。 IPv6数据包包含源IP地址和目标IP地址。 主机接口配备有在该网络段中唯一的IP地址。
  • 多播 : IPv6组播模式与IPv4相同。 目的地为多个主机的数据包在特殊的多播地址上发送。 所有对该组播信息感兴趣的主机需要首先加入该组播组。 加入组的所有接口接收组播数据包并对其进行处理,而对组播数据不感兴趣的其他主机则忽略组播信息。
  • 任播 : IPv6引入了一种新型的寻址,称为Anycast寻址。 在此寻址模式下,多个接口(host)被分配相同的任播IP地址。 当主机希望与配备有任播IP地址的主机通信时,它发送单播消息。 在复杂的路由机制的帮助下,在路由成本方面,该单播消息被递送到最接近发送方的主机。

IPv6没有广播地址,用组播地址实现广播的功能。

IPv6单播地址

类型 地址 说明
全球单播地址 前缀2000::/3 相当于IPv4的公网地址。这种地址在全球的路由器间可以路由。
链路本地地址 前缀FE80::/10 Windows和Linux支持或开启IPv6后,默认会给网卡接口自动配置一个链路本地地址。也就是说,一个接口一定有一个链路本地地址。
唯一本地地址 前缀FC00::/7 前缀FC00::和 FD00::的IPV6地址,相当于IPv4的私网地址10.0.0.0、172.16.0.0、192.168.0.0。
回环地址 ::1 等同于IPv4的127.0.0.1。

namespace测试ipv6

创建两个namespacefe, ns1和ns2, 分别用veth连接ovs网桥br1上, 并在ns1和ns2的veth接口上设置ipv4和ipv6地址:

# ns1 & ns2
ip netns add ns1
ip netns add ns2

# tap1 & tap2
ip link add tap1 type veth peer name ovs-tap1
ip link add tap2 type veth peer name ovs-tap2

# veth01=>ns1, veth02=>ns2
ip link set tap1 netns ns1
ip link set tap2 netns ns2

# ovs
ovs-vsctl add-br br1
ovs-vsctl add-port br1 ovs-tap1
ovs-vsctl add-port br1 ovs-tap2
ip link set dev ovs-tap1 up
ip link set dev ovs-tap2 up


# 设置ns1 veth接口ipv4&ipv6地址
ip netns exec ns1 ip link set dev tap1 up
ip netns exec ns1 ifconfig tap1 192.168.88.2 netmask 255.255.255.0
ip netns exec ns1 ip -6 addr add fd00::1/64 dev tap1

# 设置ns2 veth接口ipv4&ipv6地址
ip netns exec ns2 ip link set dev tap2 up
ip netns exec ns2 ifconfig tap2 192.168.88.3 netmask 255.255.255.0
ip netns exec ns2 ip -6 addr add fd00::2/64 dev tap2

ipv6 ping验证:

ip netns exec ns1 ping6 fd00::2
PING fd00::2(fd00::2) 56 data bytes
64 bytes from fd00::2: icmp_seq=1 ttl=64 time=0.786 ms
64 bytes from fd00::2: icmp_seq=2 ttl=64 time=0.086 ms
...

清理环境:

# clean up
ovs-vsctl del-br br1
ip netns del ns1
ip netns del ns2

面向IPv6的应用开发

Golang服务端

package main

import (
	"fmt"
	"net"
	"net/http"
)

func main() {
	var err error
	http.Handle("/", &helloHandler{})

	// 监听本地IPv4地址的8083端口
	// err = http.ListenAndServe(":8083", nil)
	// 监听指定IPv6地址的8083端口
	// err = http.ListenAndServe("[2604:180:3:dd3::276e]:8083", nil)
	// 同时监听本地IPv4和IPv6地址的8083端口
	err = ListenAndServe(":8083", nil)
	if err != nil {
		fmt.Println(err)
	}

}

Curl客户端

curl "http://[2604:180:3:dd3::276e]:8083"
curl -g -6 'http://[2604:180:3:dd3::276e]:8083/'

Python UDP服务端:

import socket

UDP_IP = "::" # = IPv4 0.0.0.0
UDP_PORT = 5005

sock = socket.socket(socket.AF_INET6, # Internet
						socket.SOCK_DGRAM) # UDP
sock.bind((UDP_IP, UDP_PORT))

while True:
	data, addr = sock.recvfrom(1024) # buffer size is 1024 bytes
	print "received message:", data

Python UDP客户端:


import socket

UDP_IP = "::1"  # localhost
UDP_PORT = 5005
MESSAGE = "Hello, World!"

print "UDP target IP:", UDP_IP
print "UDP target port:", UDP_PORT
print "message:", MESSAGE

sock = socket.socket(socket.AF_INET6, # Internet
					socket.SOCK_DGRAM) # UDP
sock.sendto(MESSAGE, (UDP_IP, UDP_PORT))

参考