user123
user123

Reputation: 55

cannot receive time exceeded message

I'm doing some tests based on the idea of pwnat, it introduced a method for NAT traversal without 3rd party: the server sends ICMP echo request packets to the fixed address(for example, 3.3.3.3) where no echo replies won't be returned from, the client, pretending to be a hop on the Internet, sends an ICMP Time Exceeded packet to the server, expect the NAT in the front of the server to forward the ICMP time exceeded message to the server.
After I pinged to 3.3.3.3, then I run the code below in 192.168.1.100 to listen ICMP messages in Go:

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:
        }
    }
}

and a program which runs in 192.168.2.100 to send forged time exceeded message to 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)
    }
}

the problem is 192.168.1.100 cannot receive the message. What're the possible reasons?

Upvotes: 3

Views: 838

Answers (1)

jfly
jfly

Reputation: 7990

Your code has no problem. If you run your code in the same network(I mean no NAT/router involvement), the program will receive time exceeded message as expected. The reason is the theory pwnat uses doesn't work nowadays.

  • First, you didn't get the identifier of the echo request sent by 192.168.2.100 to 3.3.3.3, the identifier will be uniquely mapped to an external query ID by NAPT(if any) so that it can route future ICMP Echo Replies with the same query ID to the sender. According to 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.

  • Second, according to 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.

So the forged time exceeded message wouldn't get through. Here is more details about this.

Upvotes: 2

Related Questions