无法接收超时消息

cannot receive time exceeded message

我是根据pwnat的思路做一些测试的,它介绍了一种无需第三方穿越NAT的方法:服务器向固定地址发送ICMP回显请求包(例如,3.3.3.3) 在没有回显回复的地方,客户端假装是互联网上的一跳,向服务器发送一个 ICMP 超时数据包,期望服务器前面的 NAT 转发 ICMP向服务器发送超时消息。
在我 ping 到 3.3.3.3 之后,我 运行 192.168.1.100 下面的代码在 Go 中监听 ICMP 消息:

package main

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

func main() {
    c, err := icmp.ListenPacket("ip4:icmp", "0.0.0.0")
    if err != nil {
        fmt.Println("listen error", err)
    }
    rb := make([]byte, 1500)

    for {
        n, _, err := c.ReadFrom(rb)
        if err != nil {
            fmt.Printf("read err: %s\n", err)
        }
        reply, err := icmp.ParseMessage(1, rb[:n])
        if err != nil {
            fmt.Println("parse icmp err:", err)
            return
        }

        switch reply.Type {
        case ipv4.ICMPTypeTimeExceeded:
            if _, ok := reply.Body.(*icmp.TimeExceeded); ok {
                // internet header(20 bytes) plus the first 64 bits of the original datagram's data
                //fmt.Println("recv id ", binary.BigEndian.Uint16(timeExceed.Data[22:24]))
                fmt.Printf("ttl exceeded\n")
            }
        default:
        }
    }
}

和 运行 在 192.168.2.100 中发送伪造超时消息到 192.168.1.100 的程序:

package main

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

func sendTtle(host string) error {
    conn, err := net.Dial("ip4:icmp", host)

    if err != nil {
        return err
    }

    // original IP header
    h := ipv4.Header{
        Version:  4,
        Len:      20,
        TotalLen: 20 + 8,
        TTL:      64,
        Protocol: 1,
    }
    h.Src = net.ParseIP(host)
    h.Dst = net.ParseIP("3.3.3.3")
    iph, err := h.Marshal()
    if err != nil {
        fmt.Println("ip header error", err)
        return err
    }

    // 8 bytes of original datagram's data
    echo := icmp.Message{
        Type: ipv4.ICMPTypeEcho, Code: 0,
        Body: &icmp.Echo{
            ID: 3456, Seq: 1,
        }}

    oriReq, err := echo.Marshal(nil)
    if err != nil {
        return errors.New("Marshal error")
    }
    data := append(iph, oriReq...)

    te := icmp.Message{
        Type: ipv4.ICMPTypeTimeExceeded,
        Code: 0,
        Body: &icmp.TimeExceeded{
            Data: data,
        }}

    if buf, err := te.Marshal(nil); err == nil {
        fmt.Println("sent")
        if _, err := conn.Write(buf); err != nil {
            return errors.New("write error")
        }
    } else {
        return errors.New("Marshal error")
    }

    return nil
}

func main() {
    argc := len(os.Args)
    if argc < 2 {
        fmt.Println("usage: prpgram + host")
        return
    }
    if err := sendTtle(os.Args[1]); err != nil {
        fmt.Println("failed to send TTL exceeded message: ", err)
    }
}

问题是192.168.1.100收不到消息。可能的原因是什么?

你的代码没有问题。如果您 运行 您的代码在同一网络中(我的意思是没有 NAT/router 参与),程序将按预期收到超时消息。原因是 pwnat 使用的理论现在已经行不通了。

  • 首先,你没有获取到发送的echo请求的标识 192.168.2.1003.3.3.3,标识符将是唯一的 通过 NAPT(如果有的话)映射到外部查询 ID,以便它可以路由 未来的 ICMP Echo 以相同的查询 ID 回复发件人。根据rfc 3022 ICMP error packet modifications section,

    In a NAPT setup, if the IP message embedded within ICMP happens to be a TCP, UDP or ICMP Query packet, you will also need to modify the appropriate TU port number within the TCP/UDP header or the Query Identifier field in the ICMP Query header.

  • 其次,根据 rfc 5508:

    If a NAT device receives an ICMP Error packet from the private realm, and the NAT does not have an active mapping for the embedded payload, the NAT SHOULD silently drop the ICMP Error packet.

所以伪造的超时消息无法通过。 Here 是关于此的更多详细信息。