IPv6数据包标头操作

时间:2016-07-16 16:19:01

标签: sockets networking go ipv6

我需要检查和修改IPv6扩展标头。所以我设置了一个原始套接字来监听本地地址上的所有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*()方法,但似乎只返回没有标头的有效负载。 所以我的问题是:如何访问数据包的IPv6标头?

1 个答案:

答案 0 :(得分:3)

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

  

与IPv4原始套接字的另一个区别是无法使用IPv6原始套接字API发送或接收完整数据包(即带有扩展标头的IPv6数据包)。而是使用辅助数据对象传输扩展头和hoplimit信息,如第6节所述。如果应用程序需要访问完整的IPv6数据包,则必须使用其他一些技术,例如数据链路接口BPF或DLPI。 / p>

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

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

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

当我尝试使用IPv6(即您的原始代码)时,我每次都会得到不同的东西:

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

在这种情况下,每次内核返回直接从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 但不用于IPv6:对于IPv6,没有什么可以删除。

注意,有一些机制会返回额外的信息(但绝不是整个数据包)。例如,IPV6_RECVPKTINFO套接字选项将允许您访问:

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

路由头选项,跳限制等存在类似选项。