IPv6 数据包 header 操作

IPv6 packet header manipulation

我需要检查和修改 IPv6 扩展 headers。所以我设置了一个原始套接字来监听本地地址上的所有 IP 数据包。

package main

import (
    "log"
    "net"
)

func main() {
    c, err := net.ListenIP("ip6:tcp", &net.IPAddr{
        IP:   net.IPv6loopback,
        Zone: "",
    })
    if err != nil {
        panic(err)
    }

    buf := make([]byte, 1024)
    for {
        numRead, ipaddr, err := c.ReadFromIP(buf)
        log.Print(numRead, ipaddr, err)
        log.Printf("% X\n", buf[:numRead])
    }
}

我在连接上尝试了所有 Read*() 方法,但似乎它们只是 return 没有 header 的有效载荷。 所以我的问题是:如何访问数据包的 IPv6 header?

IPv6 的原始套接字与 IPv4 不同。与您的案例相关的是 RFC 3542。注:

Another difference from IPv4 raw sockets is that complete packets (that is, IPv6 packets with extension headers) cannot be sent or received using the IPv6 raw sockets API. Instead, ancillary data objects are used to transfer the extension headers and hoplimit information, as described in Section 6. Should an application need access to the complete IPv6 packet, some other technique, such as the datalink interfaces BPF or DLPI, must be used.


这个你可以自己找。通过 运行(和 strace'ing)我收到的每个数据包在我的盒子上的程序:

recvfrom(3, "\x45\x00\x00\x3c\x6a\x7d...
               ^^

这意味着 IP 版本 = 4,IHL = 5(20 字节)。

当我用 IPv6 尝试同样的事情时(即你的原始代码),我每次都得到不同的东西:

recvfrom(3, "\xc6\x22\x00\x50\x4d

在这种情况下,每次内核 returning 直接从 TCP 开始的东西(在这种情况下,端口是 50722,即 0xC622)。

sources 中还应注意另一个有趣的部分:

switch sa := sa.(type) {
case *syscall.SockaddrInet4:
    addr = &IPAddr{IP: sa.Addr[0:]}
    n = stripIPv4Header(n, b)
case *syscall.SockaddrInet6:
    addr = &IPAddr{IP: sa.Addr[0:], Zone: zoneToString(int(sa.ZoneId))}
}

对于 IPv4,header 是手动删除的 ,但对于 IPv6 则没有:对于 IPv6,没有什么可以删除的。


请注意,有些机制会 return 额外信息(但绝不是整个数据包)。例如 IPV6_RECVPKTINFO 套接字选项将使您可以访问:

struct in6_pktinfo {
    struct in6_addr ipi6_addr;    /* src/dst IPv6 address */
    unsigned int    ipi6_ifindex; /* send/recv interface index */
};

路由 header 选项、跳数限制等存在类似选项