2016-03-23 23 views
0

TTLが1のicmpメッセージを送信しようとしましたが、時間超過メッセージを受け取ることを期待しています。そのメッセージは来る(私はwiresharkからそれを見る)が、私のプログラムはsyscall.Recvfromをブロックする。なぜ誰も知っていますか?
rawソケットがicmp応答を受信しませんでした

package main 

import (
    "bytes" 
    "encoding/binary" 
    "fmt" 
    "net" 
    "os" 
    "syscall" 
) 

type ICMP struct { 
    Type  uint8 
    Code  uint8 
    Checksum uint16 
    Identifier uint16 
    SeqNo  uint16 
} 

func Checksum(data []byte) uint16 { 
    var (
     sum uint32 
     length int = len(data) 
     index int 
    ) 

    for length > 1 { 
     sum += uint32(data[index])<<8 + uint32(data[index+1]) 
     index += 2 
     length -= 2 
    } 

    if length > 0 { 
     sum += uint32(data[index]) 
    } 

    sum += (sum >> 16) 

    return uint16(^sum) 
} 

func main() { 
    h := Header{ 
     Version: 4, 
     Len:  20, 
     TotalLen: 20 + 8, 
     TTL:  1, 
     Protocol: 1, 
     // Dst: 
    } 

    argc := len(os.Args) 
    if argc < 2 { 
     fmt.Println("usage: program + host") 
     return 
    } 

    ipAddr, _ := net.ResolveIPAddr("ip", os.Args[1]) 
    h.Dst = ipAddr.IP 

    icmpReq := ICMP{ 
     Type:  8, 
     Code:  0, 
     Identifier: 0, 
     SeqNo:  0, 
    } 

    out, err := h.Marshal() 
    if err != nil { 
     fmt.Println("ip header error", err) 
     return 
    } 

    var icmpBuf bytes.Buffer 
    binary.Write(&icmpBuf, binary.BigEndian, icmpReq) 
    icmpReq.Checksum = Checksum(icmpBuf.Bytes()) 

    icmpBuf.Reset() 
    binary.Write(&icmpBuf, binary.BigEndian, icmpReq) 

    fd, _ := syscall.Socket(syscall.AF_INET, syscall.SOCK_RAW, syscall.IPPROTO_RAW) 
    addr := syscall.SockaddrInet4{ 
     Port: 0, 
    } 

    copy(addr.Addr[:], ipAddr.IP[12:16]) 
    pkg := append(out, icmpBuf.Bytes()...) 

    fmt.Println("ip length", len(pkg)) 

    if err := syscall.Sendto(fd, pkg, 0, &addr); err != nil { 
     fmt.Println("Sendto err:", err) 
    } 

    var recvBuf []byte 
    if nBytes, rAddr, err := syscall.Recvfrom(fd, recvBuf, 0); err == nil { 
     fmt.Printf("recv %d bytes from %v\n", nBytes, rAddr) 
    } 
} 

icmp.go加えて、私はあなたがあなたのソケットを作成プロトコルとしてIPPROTO_ICMPを与える必要があると思うhttps://github.com/golang/net/tree/master/ipv4

答えて

0

アンディが指摘したように、raw(7) man pageは言う:

アンIPPROTO_RAWソケットは送信専用です。実際に すべてのIPパケットを受信したい場合は、ETH_P_IPプロトコルでパケット(7)ソケットを使用してください。 パケットソケットは未加工の ソケットとは異なり、IPフラグメントを再構成しません。

は、私はソケットを作成するプロトコルとしてIPPROTO_ICMPを設定した場合、私はICMP応答を受け取ることができます知っているが、私はTTL IPレイヤで行わなければなりません1に設定する必要があります。したがって、IPPROTO_RAWソケットでICMPリクエストを送信した後、net.ListenIPを使用してICMPメッセージを受信します。ここでは、コードは次のとおりです。実際に

package main 

import (
    "bytes" 
    "encoding/binary" 
    "log" 
    "net" 
    "os" 
    "syscall" 
) 

const icmpID uint16 = 43565 // use a magic number for now 

type ICMP struct { 
    Type  uint8 
    Code  uint8 
    Checksum uint16 
    Identifier uint16 
    SeqNo  uint16 
} 

func Checksum(data []byte) uint16 { 
    var (
     sum uint32 
     length int = len(data) 
     index int 
    ) 

    for length > 1 { 
     sum += uint32(data[index])<<8 + uint32(data[index+1]) 
     index += 2 
     length -= 2 
    } 

    if length > 0 { 
     sum += uint32(data[index]) 
    } 

    sum += (sum >> 16) 

    return uint16(^sum) 
} 

func main() { 
    h := Header{ 
     Version: 4, 
     Len:  20, 
     TotalLen: 20 + 8, 
     TTL:  1, 
     Protocol: 1, 
    } 

    argc := len(os.Args) 
    if argc < 2 { 
     log.Println("usage: program + host") 
     return 
    } 

    ipAddr, _ := net.ResolveIPAddr("ip", os.Args[1]) 
    h.Dst = ipAddr.IP 

    icmpReq := ICMP{ 
     Type:  8, 
     Code:  0, 
     Identifier: icmpID, 
     SeqNo:  1, 
    } 

    out, err := h.Marshal() 
    if err != nil { 
     log.Println("ip header error", err) 
     return 
    } 

    var icmpBuf bytes.Buffer 
    binary.Write(&icmpBuf, binary.BigEndian, icmpReq) 
    icmpReq.Checksum = Checksum(icmpBuf.Bytes()) 

    icmpBuf.Reset() 
    binary.Write(&icmpBuf, binary.BigEndian, icmpReq) 

    fd, _ := syscall.Socket(syscall.AF_INET, syscall.SOCK_RAW, syscall.IPPROTO_RAW) 
    addr := syscall.SockaddrInet4{ 
     Port: 0, 
    } 

    copy(addr.Addr[:], ipAddr.IP[12:16]) 
    pkg := append(out, icmpBuf.Bytes()...) 

    if err := syscall.Sendto(fd, pkg, 0, &addr); err != nil { 
     log.Println("Sendto err:", err) 
    } 

    laddr, err := net.ResolveIPAddr("ip4:icmp", "0.0.0.0") 
    if err != nil { 
     log.Fatal(err) 

    } 

    c, err := net.ListenIP("ip4:icmp", laddr) 
    if err != nil { 
     log.Fatal(err) 
    } 

    for { 
     buf := make([]byte, 2048) 
     n, raddr, err := c.ReadFrom(buf) 
     if err != nil { 
      log.Println(err) 
      continue 
     } 
     icmpType := buf[0] 
     if icmpType == 11 { 
      if n == 36 { // Time exceeded messages 
       // A time exceeded message contain IP header(20 bytes) and first 64 bits of the original payload 
       id := binary.BigEndian.Uint16(buf[32:34]) 
       log.Println("recv id", id) 
       if id == icmpID { 
        log.Println("recv Time Exceeded from", raddr) 
       } 
      } 
     } 
    } 
} 

、誰もがそのことについて興味を持っている場合、私は、外出先でのtracerouteを書いて、全体のコードはgithubです。

1

からheader.gohelper.goを使用しています。 raw(7) man pageは、IPPROTO_RAWソケットは送信のみであると言います。また、IPPROTO_ICMPを使用する場合は、IPヘッダーを指定しないでください。 (注:私は実際に行くにはこれを試していない。)

+0

IP層で行う必要がある 'TTL'を1に設定する必要があるので、チップをおかげで、私はソケットを作成するときに' IPPROTO_ICMP'をプロトコルとして設定できません。 – jfly

+0

TTLを設定するソケットオプションはありませんか? –

+0

このようなメソッドは標準ライブラリにありません。 – jfly

関連する問題