猿问

原始套接字未收到 icmp 响应

我正在尝试发送 TTL 仅为 1 的 icmp 消息,并希望收到超时消息。该消息确实来了(我从wireshark 看到它),但是我的程序在syscall.Recvfrom. 有谁知道为什么?

icmp.go


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)

    }


另外,我使用header.go和helper.go来自https://github.com/golang/net/tree/master/ipv4


繁华开满天机
浏览 209回答 2
2回答

萧十郎

正如安迪指出的那样,raw(7) 手册页说:仅发送 IPPROTO_RAW 套接字。如果您真的想接收所有 IP 数据包,请使用具有 ETH_P_IP 协议的 packet(7) 套接字。请注意,与原始套接字不同,数据包套接字不会重新组装 IP 片段。我知道如果我IPPROTO_ICMP在创建套接字时设置为协议,我可以收到 ICMP 回复,但我需要设置TTL为 1,这必须在 IP 层完成。因此IPPROTO_RAW,我使用套接字发送 ICMP 请求,然后net.ListenIP用于接收 ICMP 消息。这是代码:package mainimport (&nbsp; &nbsp; "bytes"&nbsp; &nbsp; "encoding/binary"&nbsp; &nbsp; "log"&nbsp; &nbsp; "net"&nbsp; &nbsp; "os"&nbsp; &nbsp; "syscall")const icmpID uint16 = 43565 // use a magic number for nowtype ICMP struct {&nbsp; &nbsp; Type&nbsp; &nbsp; &nbsp; &nbsp;uint8&nbsp; &nbsp; Code&nbsp; &nbsp; &nbsp; &nbsp;uint8&nbsp; &nbsp; Checksum&nbsp; &nbsp;uint16&nbsp; &nbsp; Identifier uint16&nbsp; &nbsp; SeqNo&nbsp; &nbsp; &nbsp; uint16}func Checksum(data []byte) uint16 {&nbsp; &nbsp; var (&nbsp; &nbsp; &nbsp; &nbsp; sum&nbsp; &nbsp; uint32&nbsp; &nbsp; &nbsp; &nbsp; length int = len(data)&nbsp; &nbsp; &nbsp; &nbsp; index&nbsp; int&nbsp; &nbsp; )&nbsp; &nbsp; for length > 1 {&nbsp; &nbsp; &nbsp; &nbsp; sum += uint32(data[index])<<8 + uint32(data[index+1])&nbsp; &nbsp; &nbsp; &nbsp; index += 2&nbsp; &nbsp; &nbsp; &nbsp; length -= 2&nbsp; &nbsp; }&nbsp; &nbsp; if length > 0 {&nbsp; &nbsp; &nbsp; &nbsp; sum += uint32(data[index])&nbsp; &nbsp; }&nbsp; &nbsp; sum += (sum >> 16)&nbsp; &nbsp; return uint16(^sum)}func main() {&nbsp; &nbsp; h := Header{&nbsp; &nbsp; &nbsp; &nbsp; Version:&nbsp; 4,&nbsp; &nbsp; &nbsp; &nbsp; Len:&nbsp; &nbsp; &nbsp; 20,&nbsp; &nbsp; &nbsp; &nbsp; TotalLen: 20 + 8,&nbsp; &nbsp; &nbsp; &nbsp; TTL:&nbsp; &nbsp; &nbsp; 1,&nbsp; &nbsp; &nbsp; &nbsp; Protocol: 1,&nbsp; &nbsp; }&nbsp; &nbsp; argc := len(os.Args)&nbsp; &nbsp; if argc < 2 {&nbsp; &nbsp; &nbsp; &nbsp; log.Println("usage: program + host")&nbsp; &nbsp; &nbsp; &nbsp; return&nbsp; &nbsp; }&nbsp; &nbsp; ipAddr, _ := net.ResolveIPAddr("ip", os.Args[1])&nbsp; &nbsp; h.Dst = ipAddr.IP&nbsp; &nbsp; icmpReq := ICMP{&nbsp; &nbsp; &nbsp; &nbsp; Type:&nbsp; &nbsp; &nbsp; &nbsp;8,&nbsp; &nbsp; &nbsp; &nbsp; Code:&nbsp; &nbsp; &nbsp; &nbsp;0,&nbsp; &nbsp; &nbsp; &nbsp; Identifier: icmpID,&nbsp; &nbsp; &nbsp; &nbsp; SeqNo:&nbsp; &nbsp; &nbsp; 1,&nbsp; &nbsp; }&nbsp; &nbsp; out, err := h.Marshal()&nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; &nbsp; log.Println("ip header error", err)&nbsp; &nbsp; &nbsp; &nbsp; return&nbsp; &nbsp; }&nbsp; &nbsp; var icmpBuf bytes.Buffer&nbsp; &nbsp; binary.Write(&icmpBuf, binary.BigEndian, icmpReq)&nbsp; &nbsp; icmpReq.Checksum = Checksum(icmpBuf.Bytes())&nbsp; &nbsp; icmpBuf.Reset()&nbsp; &nbsp; binary.Write(&icmpBuf, binary.BigEndian, icmpReq)&nbsp; &nbsp; fd, _ := syscall.Socket(syscall.AF_INET, syscall.SOCK_RAW, syscall.IPPROTO_RAW)&nbsp; &nbsp; addr := syscall.SockaddrInet4{&nbsp; &nbsp; &nbsp; &nbsp; Port: 0,&nbsp; &nbsp; }&nbsp; &nbsp; copy(addr.Addr[:], ipAddr.IP[12:16])&nbsp; &nbsp; pkg := append(out, icmpBuf.Bytes()...)&nbsp; &nbsp; if err := syscall.Sendto(fd, pkg, 0, &addr); err != nil {&nbsp; &nbsp; &nbsp; &nbsp; log.Println("Sendto err:", err)&nbsp; &nbsp; }&nbsp; &nbsp; laddr, err := net.ResolveIPAddr("ip4:icmp", "0.0.0.0")&nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; &nbsp; log.Fatal(err)&nbsp; &nbsp; }&nbsp; &nbsp; c, err := net.ListenIP("ip4:icmp", laddr)&nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; &nbsp; log.Fatal(err)&nbsp; &nbsp; }&nbsp; &nbsp; for {&nbsp; &nbsp; &nbsp; &nbsp; buf := make([]byte, 2048)&nbsp; &nbsp; &nbsp; &nbsp; n, raddr, err := c.ReadFrom(buf)&nbsp; &nbsp; &nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; log.Println(err)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; continue&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; icmpType := buf[0]&nbsp; &nbsp; &nbsp; &nbsp; if icmpType == 11 {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if n == 36 { // Time exceeded messages&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // A time exceeded message contain IP header(20 bytes) and first 64 bits of the original payload&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; id := binary.BigEndian.Uint16(buf[32:34])&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; log.Println("recv id", id)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if id == icmpID {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; log.Println("recv Time Exceeded from", raddr)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }}实际上,我正在用 go 编写 traceroute,如果有人对此感兴趣,整个代码都在github 中。

倚天杖

我认为您需要IPPROTO_ICMP在创建套接字时提供协议。该原料(7)手册页说,一个IPPROTO_RAW插座只发送。此外,如果您使用IPPROTO_ICMP,则不会提供 IP 标头。(注意:我实际上并没有在 Go 中尝试过这个。)
随时随地看视频慕课网APP

相关分类

Go
我要回答