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

时间:2015-09-14 23:43:43

标签: macos sockets go packet

我正在尝试为我正在处理的测试工具做一些基本的数据包制作,但我似乎无法使数据包制作工作(我在OSX上使用Go 1.5并且以root身份运行。)

我正在使用以下代码(taken from here)来尝试创建ICMP数据包,但是当我尝试在IP标头中指定特定选项时,它似乎不起作用。此外,当我在wireshark中查看此数据包时,它显示为协议255(未知)。

我已经读过在Linux系统上你可以使用AF_PACKET但是在OSX系统上你需要使用BPF,但是我找到的示例代码是使用“syscall.Socket(syscall.AF_INET,syscall.SOCK_RAW,syscall.IPPROTO_RAW)”我不知道如何开始使用BPF。我也看到有些人尝试使用gopacket而不是x / net / ipv4包。

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)
}

如果我从pkt()返回数据后打印出包含Main()中数据包数据的p变量,它看起来是正确的:

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 

您可以看到协议在第10个字节中设置为“1”。但是当我们在wireshark中查看这个包时,它看起来像:

wireshark screen shot

2 个答案:

答案 0 :(得分:1)

好的,我现在可以让它在OS X上运行了。您需要确保设置IP_HDRINCL套接字选项syscall.SetsockoptInt(s, syscall.IPPROTO_IP, syscall.IP_HDRINCL, 1),然后您需要小心构建数据包。让我度过长时间的一个诀窍是,出于某种原因,Sendto OS X/BSD want the IP length in host byte order,在我的情况下是LittleEndian,而不是BigEndian,这是典型的网络顺序。如果你看一下这段代码(我只是自己构建了IP头,你可以用另一种方式构建它)它按预期运行。

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("\n")
    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])

此代码在# 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       ......?!......

答案 1 :(得分:1)

这是我在Mik项目上从Mikio回来的答案。我在这里为其他可能正在寻找解决此问题的人添加它。

package main

import (
    "fmt"
    "golang.org/x/net/ipv4"
    "log"
    "net"
)

func main() {
    ip := net.ParseIP("127.0.0.1")
    proto := 1

    c, err := net.ListenPacket(fmt.Sprintf("ip4:%d", proto), "0.0.0.0")
    if err != nil {
        log.Fatal(err)
    }
    defer c.Close()

    p, err := ipv4.NewRawConn(c)
    if err != nil {
        log.Fatal(err)
    }

    b := []byte("HELLO-R-U-THERE")
    h := &ipv4.Header{
        Version:  ipv4.Version,
        Len:      ipv4.HeaderLen,
        TotalLen: ipv4.HeaderLen + len(b),
        ID:       12345,
        Protocol: proto,
        Dst:      ip.To4(),
    }
    if err := p.WriteTo(h, b, nil); err != nil {
        log.Println(err)
    }
}