doufei7516 2015-09-14 23:43
浏览 136

在MacOSX上使用Go 1.5创建RAW数据包

I am trying to do some basic packet crafting for a testing tool I am working on, but I can not seem to get the packet crafting to work (I am using Go 1.5 on OSX and am running as root.)

I am using the following code (taken from here) to try and create an ICMP packet, but when I try to specify say specific options in the IP header it does not seem to work. Further when I look at this packet in wireshark it shows up as protocol 255 (unknown).

I have read that on Linux system you can use AF_PACKET but on OSX systems you need to use BPF, however the sample code I found is using "syscall.Socket(syscall.AF_INET, syscall.SOCK_RAW, syscall.IPPROTO_RAW)" and I am not sure how to get started with BPF. I have also seen some people try to use gopacket instead of the x/net/ipv4 package.

package main

import (
    "golang.org/x/net/ipv4"
    "net"
    "syscall"
)

func main() {
    var err error
    fd, _ := syscall.Socket(syscall.AF_INET, syscall.SOCK_RAW, syscall.IPPROTO_RAW)

    addr := syscall.SockaddrInet4{
        Port: 0,
        Addr: [4]byte{127, 0, 0, 1},
    }
    p := pkt()

    _ = syscall.Sendto(fd, p, 0, &addr)
}

func pkt() []byte {
    h := ipv4.Header{
        Version:  4,
        Len:      20,
        TOS:      0,
        TotalLen: 85, // I can not seem to change this
        ID:       2,  // I can not seem to change this
        TTL:      64, // I can not seem to change this
        Protocol: 1,  // ICMP, This does not seem to work
        Dst:      net.IPv4(127, 0, 0, 1),
    }

    icmp := []byte{
        8, // type: echo request
        0, // code: not used by echo request
        0, // checksum (16 bit), we fill in below
        0,
        0, // identifier (16 bit). zero allowed.
        0,
        0, // sequence number (16 bit). zero allowed.
        0,
        0xC0, // Optional data. ping puts time packet sent here
        0xDE,
    }
    cs := csum(icmp)
    icmp[2] = byte(cs)
    icmp[3] = byte(cs >> 8)

    out, _ := h.Marshal()

    return append(out, icmp...)
}

func csum(b []byte) uint16 {
    var s uint32
    for i := 0; i < len(b); i += 2 {
        s += uint32(b[i+1])<<8 | uint32(b[i])
    }
    // add back the carry
    s = s>>16 + s&0xffff
    s = s + s>>16
    return uint16(^s)
}

If I print out the p variable that contains the packet data in Main() after the data comes back from pkt() it looks right:

DEBUG: (decimal) [69 0 60 0 0 0 0 0 64 1 0 0 0 0 0 0 127 0 0 1 8 0 55 33 0 0 0 0 192 222]
DEBUG: (hex)      45 0 3c 0 0 0 0 0 40 1 0 0 0 0 0 0 7f 0 0 1 8 0 37 21 0 0 0 0 c0 de 

And you can see that the protocol is set for "1" in the 10th byte. But when we look at this packet in wireshark it looks like:

wireshark screen shot

  • 写回答

2条回答 默认 最新

  • douliang4858 2015-09-16 04:23
    关注

    Ok I was able to get this to work on OS X now. You need to make sure you are setting IP_HDRINCL socket option syscall.SetsockoptInt(s, syscall.IPPROTO_IP, syscall.IP_HDRINCL, 1) and then you need to be careful building the packet. One trick that caught me for a LONG TIME was that, for some reason, for Sendto OS X/BSD want the IP length in host byte order, which in my case was LittleEndian, not BigEndian which is the typical network order. If you look at this code (I just kinda built the IP header myself, you can build it another way) it runs as expected.

    package main
    
    import (
        "encoding/binary"
        "fmt"
        "syscall"
    )
    
    func main() {
        s, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_RAW, syscall.IPPROTO_RAW)
        if err != nil {
            panic(err)
        }
    
        err = syscall.SetsockoptInt(s, syscall.IPPROTO_IP, syscall.IP_HDRINCL, 1)
        if err != nil {
            panic(err)
        }
        addr := syscall.SockaddrInet4{Addr: [4]byte{127, 0, 0, 1}}
    
        data := makepacket()
    
        for _, v := range data {
            if v == 0 {
                fmt.Printf("00 ")
                continue
            } else if v < 0xf {
                fmt.Printf("0%x ", v)
                continue
            }
            fmt.Printf("%x ", v)
        }
        fmt.Printf("
    ")
        err = syscall.Sendto(s, data, 0, &addr)
        if err != nil {
            panic(err)
        }   
    }       
    
    func makepacket() []byte {
        icmp := []byte{
            8, // type: echo request
            0, // code: not used by echo request
            0, // checksum (16 bit), we fill in below
            0,
            0, // identifier (16 bit). zero allowed.
            0,
            0, // sequence number (16 bit). zero allowed.
            0,
            0xC0, // Optional data. ping puts time packet sent here
            0xDE, 
        }
        cs := csum(icmp)
        icmp[2] = byte(cs)
        icmp[3] = byte(cs >> 8)
    
        buf := []byte{0x45, 0x00, 0x00, 0x00, 0x95, 0x13, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0x7f, 0x00, 0x0
    0, 0x01, 0x7f, 0x00, 0x00, 0x01}
        binary.LittleEndian.PutUint16(buf[2:4], uint16(len(icmp) + len(buf)))
        return append(buf, icmp...)
    }
    
    func csum(b []byte) uint16 {
        var s uint32
        for i := 0; i < len(b); i += 2 {
            s += uint32(b[i+1])<<8 | uint32(b[i])
    

    This code gives me this output in # tcpdump -X -i lo0

    20:05:24.016465 IP localhost > localhost: ICMP echo request, id 0, seq 0, length 10
        0x0000:  4500 001e 9513 0000 4001 0000 7f00 0001  E.......@.......
        0x0010:  7f00 0001 0800 3721 0000 0000 c0de       ......7!......
    20:05:24.016495 IP localhost > localhost: ICMP echo reply, id 0, seq 0, length 10
        0x0000:  4500 001e 3e4f 0000 4001 0000 7f00 0001  E...>O..@.......
        0x0010:  7f00 0001 0000 3f21 0000 0000 c0de       ......?!......
    
    评论

报告相同问题?

悬赏问题

  • ¥15 微信会员卡接入微信支付商户号收款
  • ¥15 如何获取烟草零售终端数据
  • ¥15 数学建模招标中位数问题
  • ¥15 phython路径名过长报错 不知道什么问题
  • ¥15 深度学习中模型转换该怎么实现
  • ¥15 HLs设计手写数字识别程序编译通不过
  • ¥15 Stata外部命令安装问题求帮助!
  • ¥15 从键盘随机输入A-H中的一串字符串,用七段数码管方法进行绘制。提交代码及运行截图。
  • ¥15 TYPCE母转母,插入认方向
  • ¥15 如何用python向钉钉机器人发送可以放大的图片?