使用原始套接字发送自定义以太网数据包

huangapple go评论75阅读模式
英文:

Send custom ethernet packet using raw socket

问题

我搜索了如何使用自定义以太网类型发送原始以太网数据包,但很多人都在谈论TCP和UDP协议。
我需要打开一个原始套接字,接收所有具有自定义以太网类型的数据包,读取有效载荷,并发送一个具有不同自定义以太网类型的数据包。

我收到了数据包,但是f.Write(myBuf)返回了以下错误:Error: write fd 5: no such device or address
我做错了什么?

英文:

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?

答案1

得分: 3

使用os.Write将在后台执行一个针对实际文件的write系统调用。要将数据“写入”网络套接字,您需要使用sendto系统调用。

以下示例使用自定义以太网类型发送数据。因此,只是一个带有一些数据的以太网数据包。

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, // 以太网地址长度为6字节
		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, // 自定义以太网类型
	}

	// 您的自定义数据
	p := append(ethHeader, []byte("Hello World")...)

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

// htons将一个short(uint16)从主机字节顺序转换为网络字节顺序。
func htons(i uint16) uint16 {
	return (i<<8)&0xff00 | i>>8
}
英文:

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 (
	&quot;log&quot;
	&quot;net&quot;
	&quot;os&quot;
	&quot;syscall&quot;
)

func main() {
	ifname := os.Args[1]
	iface, err := net.InterfaceByName(ifname)
	if err != nil {
		log.Fatal(&quot;get link by name:&quot;, 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(&quot;Hello World&quot;)...)

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

// htons converts a short (uint16) from host-to-network byte order.
func htons(i uint16) uint16 {
	return (i&lt;&lt;8)&amp;0xff00 | i&gt;&gt;8
}

huangapple
  • 本文由 发表于 2022年1月11日 00:19:36
  • 转载请务必保留本文链接:https://go.coder-hub.com/70655602.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定