原始套接字未收到icmp响应

fly

我正在尝试发送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)
    }

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

另外,我使用header.gohelper.gohttps://github.com/golang/net/tree/master/ipv4

fly

正如Andy指出的那样,raw(7)手册页指出:

仅发送IPPROTO_RAW套接字。如果您确实想接收所有IP数据包,请使用具有ETH_P_IP协议的packet(7)套接字。请注意,与原始套接字不同,数据包套接字不会重组IP片段。

我知道如果IPPROTO_ICMP在创建套接字时将其设置为协议,则可以收到ICMP答复,但是我需要将其设置TTL为1,这必须在IP层中完成。因此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)
                }
            }
        }
    }
}

实际上,我在go中编写了一个traceroute,如果有人对此感兴趣,则整个代码都在github中

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

Java套接字未收到来自Print Writer的响应

来自分类Dev

套接字未收到完整的流

来自分类Dev

套接字未收到完整的流

来自分类Dev

C#-套接字未收到所有字节

来自分类Dev

无法接收到原始套接字的数据包

来自分类Dev

为什么会收到“错误套接字挂断”响应?

来自分类Dev

收到握手响应之前,套接字连接已关闭

来自分类Dev

Java套接字将发送但未接收到响应

来自分类Dev

数据包套接字未收到自定义协议ID的数据

来自分类Dev

使用RAW套接字发送SYN后未收到SYN / ACK

来自分类Dev

C 套接字在 HTTP 站点开始挂起之前未收到所有数据

来自分类Dev

PostAsync后未收到响应

来自分类Dev

AJAX 未收到 PHP 响应

来自分类Dev

OpenBSD 上的原始套接字 icmp 协议族不支持 sendto 地址族

来自分类Dev

订阅未收到下载文件的响应

来自分类Dev

原始套接字多播

来自分类Dev

Java代理-无法收到来自主机/套接字问题的响应

来自分类Dev

UDP套接字无响应

来自分类Dev

Python套接字垃圾响应

来自分类Dev

带有原始套接字的python套接字setsockopt

来自分类Dev

RAW ICMP套接字:recvfrom()不接收任何数据

来自分类Dev

套接字,收到序列化异常

来自分类Dev

python urllib收到[Errno套接字错误]

来自分类Dev

在套接字Java上收到多个错误

来自分类Dev

套接字c ++,收到的信息很奇怪

来自分类Dev

Python:在OSX中使用原始套接字

来自分类Dev

Golang的原始套接字嗅探

来自分类Dev

使用原始套接字的ping请求失败

来自分类Dev

Linux内核中原始套接字的工作