2

I searched how to send a raw ethernet packet using a custom ethertype but a lot of people talks abouts about tcp and udp protocols. I need to open a raw socket, take all the packets that have my custom ether type, read the payload and send back a packet with a different custom ether type.

func main() {
    //set promiscuos mode
    cmd := exec.Command("ifconfig", "eth0", "promisc")
    err := cmd.Run()
    if err != nil {
        fmt.Println("Error: " + err.Error())
        return
    }
    //open raw socket with custom ethertype_1 and bind to interface
    fd, err := syscall.Socket(syscall.AF_PACKET, syscall.SOCK_RAW, Htons(ETHER_TYPE_1))
    if err != nil {
        fmt.Println("Error: " + err.Error())
        return
    }
    err = syscall.BindToDevice(fd, "eth0")
    if err != nil {
        fmt.Println("Error: " + err.Error())
        return
    }
    f := os.NewFile(uintptr(fd), fmt.Sprintf("fd %d", fd))

    for {
        buf := make([]byte, 1024)
        numRead, err := f.Read(buf)
        if err != nil {
            fmt.Println(err)
        }
        go ReadSocket(buf, numRead)
    }

func ReadSocket(buf []byte, numRead int) {
    packet := BufToEthPacket(buf, numRead)

    fmt.Printf("Destination MAC: % X\n", packet.dstMac)
    fmt.Printf("Source MAC:      % X\n", packet.srcMac)
    fmt.Printf("ether type:      %X\n", packet.ethType)
    fmt.Printf("Payload:         % X\n", packet.payload)

    var myPacket EthPacket
    myPacket.srcMac = packet.dstMac
    myPacket.dstMac = packet.srcMac
    myPacket.ethType = ETHER_TYPE_2
    myPacket.payload = packet.payload

    var myBuf = EthPacketToBuf(myPacket)

    //open raw socket with custom ethertype_2 and bind to interface
    fd, err := syscall.Socket(syscall.AF_PACKET, syscall.SOCK_RAW, Htons(ETHER_TYPE_2))
    if err != nil {
        fmt.Println("Error: " + err.Error())
        return
    }
    err = syscall.BindToDevice(fd, "eth0")
    if err != nil {
        fmt.Println("Error: " + err.Error())
        return
    }
    f := os.NewFile(uintptr(fd), fmt.Sprintf("fd %d", fd))

    n, err := f.Write(myBuf)
    if err != nil {
        fmt.Println("Error: " + err.Error())
        return
    }
    if n != numRead {
        fmt.Println("Error: byte length not equal")
        return
    }
}

I received the packet but the f.Write(myBuf) return me the following error: Error: write fd 5: no such device or address

What I'm doing wrong?

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189

1 Answers1

3

Using os.Write will execute a write syscall in the background, which is meant for actual files. To "write" data to a network socket you need to use the sendto syscall.

The following example sends data with a custom ether type. So just an ethernet packet with some data.

package main

import (
    "log"
    "net"
    "os"
    "syscall"
)

func main() {
    ifname := os.Args[1]
    iface, err := net.InterfaceByName(ifname)
    if err != nil {
        log.Fatal("get link by name:", err)
    }

    srcMac := iface.HardwareAddr
    if len(srcMac) == 0 {
        srcMac = []byte{0, 0, 0, 0, 0, 0}
    }
    dstMac := []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05}

    fd, _ := syscall.Socket(syscall.AF_PACKET, syscall.SOCK_RAW, int(htons(syscall.ETH_P_ALL)))
    addr := syscall.SockaddrLinklayer{
        Ifindex: iface.Index,
        Halen:   6, // Ethernet address length is 6 bytes
        Addr: [8]uint8{
            dstMac[0],
            dstMac[1],
            dstMac[2],
            dstMac[3],
            dstMac[4],
            dstMac[5],
        },
    }

    ethHeader := []byte{
        dstMac[0], dstMac[1], dstMac[2], dstMac[3], dstMac[4], dstMac[5],
        srcMac[0], srcMac[1], srcMac[2], srcMac[3], srcMac[4], srcMac[5],
        0x12, 0x34, // your custom ethertype
    }

    // Your custom data
    p := append(ethHeader, []byte("Hello World")...)

    err = syscall.Sendto(fd, p, 0, &addr)
    if err != nil {
        log.Fatal("Sendto:", err)
    }
}

// htons converts a short (uint16) from host-to-network byte order.
func htons(i uint16) uint16 {
    return (i<<8)&0xff00 | i>>8
}
Dylan Reimerink
  • 5,874
  • 2
  • 15
  • 21
  • Here we still have the IP layer. On this interface there are no ip assigned. Anyway I tested it and still not working, the error is ```Sendto: invalid argument``` – Andrea Romano Jan 11 '22 at 08:22
  • You are right, changed the example to one where we construct our own ethernet packet, hope this helps – Dylan Reimerink Jan 11 '22 at 19:29
  • Still not working, same error ```Sendto: invalid argument``` I'm using a usb adapter with two ethernet interfaces connected by a cable. Does this loop a problem? – Andrea Romano Jan 12 '22 at 08:58
  • That is strage, the exact program does run om my machine. "invalid argument" is the EINVAL error number. Most occurrences I can find have it because of an incorrect length parameter. But in our case the syscall package calculates this for us, so we can't make this mistake. What happens if you attempt to send on "lo" instead of on an actual device? – Dylan Reimerink Jan 12 '22 at 12:09
  • I've tested exactly your code and there are no error, so the problem can be the usb adapter or the fact that server and client are on the same machine. I will test this on a second PC – Andrea Romano Jan 13 '22 at 07:50