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 LiBeAs的带隙等于0.997eV,计算阴离子的N和P
  • ¥15 关于#windows#的问题:怎么用WIN 11系统的电脑 克隆WIN NT3.51-4.0系统的硬盘
  • ¥15 matlab有关常微分方程的问题求解决
  • ¥15 perl MISA分析p3_in脚本出错
  • ¥15 k8s部署jupyterlab,jupyterlab保存不了文件
  • ¥15 ubuntu虚拟机打包apk错误
  • ¥199 rust编程架构设计的方案 有偿
  • ¥15 回答4f系统的像差计算
  • ¥15 java如何提取出pdf里的文字?
  • ¥100 求三轴之间相互配合画圆以及直线的算法