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?