Golang中如何访问IP层

Golang中如何访问IP层 大家好,

请问有谁能告诉我IP层是在哪里处理的吗?具体在“go/src/net/”目录下的哪个文件中? 我希望能够访问诸如ip.lengthip.flagsip.ttl这样的属性。

期待您的回复!

8 回复

一个小观察……在网络栈的低层工作可能需要特权权限才能执行(例如,Linux 系统上的 root 权限)。请注意这一点,否则许多操作将无法进行。

更多关于Golang中如何访问IP层的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


TTL等概念确实是IP层面的概念,而HTTP请求是通过TCP传输的。TCP的套接字API不暴露TTL,因为在那里它没有意义——你的HTTP请求可能是由许多具有不同TTL值的IP数据包组装而成的。

IP 是在操作系统中实现的。不过,你可以使用例如 x/net/ipv4 来访问一些较低层级的选项。

是的,我同意。那么,假设我想修改Go原生库的源代码,例如,让每个传入的(TCP)IP请求的TTL属性都能被打印到屏幕上,我可以在哪里进行这样的修改呢?

你无法做到,因为Go自身使用的API(TCP套接字)不暴露此信息。TCP套接字没有TTL信息。这类似于你也无法获取IP包长度或源MAC地址。这完全是来自协议栈错误层级的信息。

// 代码示例:TCP套接字操作
func handleConnection(conn net.Conn) {
    defer conn.Close()
    // 这里无法获取TTL信息
}

非常感谢你的帮助 @calmh

IP 层是无法触及的。 那么,是否有可能访问 TCP 层呢?例如,从 TCP SYN 数据包中提取 tcp.optionstcp.window_size

或者说,在 Go 中(至少在使用 src/net 时),TCP 套接字是原样接收的,唯一可访问的“低层”属性只有 tcp.src_iptcp.src_port

感谢大家的回复。

为了确保我的提问方式是正确的:我有一个HTTP服务器,它导入了(以及其他内容)https://golang.org/src/net/。 我想打印例如传入请求的TTL。

数据必须从IP层传递到TCP层,最终到达应用层,因为请求的源IP地址也是从IP层获取的,并且在不同库的代码中的多个位置都可以访问。所以像(源请求的)TTL这样的值也应该可以访问。

因此我假设它会在某个文件中,比如 ipsock.go,但在那里没有找到。

在Go标准库中,IP层处理主要在go/src/net/ipraw.gogo/src/net/ip.go文件中。要访问IP头部字段,需要使用golang.org/x/net/ipv4golang.org/x/net/ipv6扩展包。

以下是访问IP头部字段的示例:

package main

import (
    "fmt"
    "golang.org/x/net/ipv4"
    "net"
)

func main() {
    // 创建原始套接字
    conn, err := net.ListenPacket("ip4:tcp", "0.0.0.0")
    if err != nil {
        panic(err)
    }
    defer conn.Close()

    // 包装为ipv4.RawConn
    rawConn, err := ipv4.NewRawConn(conn)
    if err != nil {
        panic(err)
    }

    // 读取IP数据包
    buf := make([]byte, 1500)
    hdr, payload, _, err := rawConn.ReadFrom(buf)
    if err != nil {
        panic(err)
    }

    // 访问IP头部字段
    fmt.Printf("Total Length: %d\n", hdr.TotalLen)
    fmt.Printf("TTL: %d\n", hdr.TTL)
    fmt.Printf("Flags: %v\n", hdr.Flags)
    fmt.Printf("Protocol: %d\n", hdr.Protocol)
    fmt.Printf("Src: %s\n", hdr.Src.String())
    fmt.Printf("Dst: %s\n", hdr.Dst.String())

    // 处理payload
    _ = payload
}

对于IPv6,使用golang.org/x/net/ipv6

package main

import (
    "fmt"
    "golang.org/x/net/ipv6"
    "net"
)

func main() {
    conn, err := net.ListenPacket("ip6:tcp", "::")
    if err != nil {
        panic(err)
    }
    defer conn.Close()

    rawConn, err := ipv6.NewPacketConn(conn)
    if err != nil {
        panic(err)
    }

    buf := make([]byte, 1500)
    hdr, payload, _, err := rawConn.ReadFrom(buf)
    if err != nil {
        panic(err)
    }

    fmt.Printf("Payload Length: %d\n", hdr.PayloadLen)
    fmt.Printf("Hop Limit: %d\n", hdr.HopLimit)
    fmt.Printf("Next Header: %d\n", hdr.NextHeader)
    fmt.Printf("Src: %s\n", hdr.Src.String())
    fmt.Printf("Dst: %s\n", hdr.Dst.String())

    _ = payload
}

如果需要手动解析IP数据包,可以直接读取原始数据:

package main

import (
    "encoding/binary"
    "fmt"
    "net"
)

func parseIPv4Header(data []byte) {
    if len(data) < 20 {
        return
    }
    
    // 解析IPv4头部
    version := data[0] >> 4
    ihl := data[0] & 0x0F
    totalLength := binary.BigEndian.Uint16(data[2:4])
    ttl := data[8]
    protocol := data[9]
    
    fmt.Printf("IPv4 Header:\n")
    fmt.Printf("  Version: %d\n", version)
    fmt.Printf("  IHL: %d (header length: %d bytes)\n", ihl, ihl*4)
    fmt.Printf("  Total Length: %d\n", totalLength)
    fmt.Printf("  TTL: %d\n", ttl)
    fmt.Printf("  Protocol: %d\n", protocol)
}

func main() {
    conn, err := net.ListenIP("ip4:tcp", &net.IPAddr{IP: net.IPv4zero})
    if err != nil {
        panic(err)
    }
    defer conn.Close()

    buf := make([]byte, 1500)
    n, _, err := conn.ReadFrom(buf)
    if err != nil {
        panic(err)
    }

    parseIPv4Header(buf[:n])
}

注意:原始套接字操作通常需要管理员/root权限。

回到顶部