🐱

GoでEtherCATパケットを生成する

2023/12/17に公開

はじめに

この記事は CISTアドベントカレンダー2023の17日目です

EtherCATのペイロードを生成してgopacketで送信してみました。

EtherCATのアーキテクチャ等は解説しないので、もし興味がありましたらベッコフが提供しているドキュメントを見ることをお勧めします

EtherCATとは

EtherCATはベッコフオートメーションが開発したリアルタイム性のある産業イーサネット技術です。EtherCATプロトコルがIEC規格IEC61158で公開され、オートメーション技術、試験・計測技術およびその他の多数のアプリケーション分野に適したハードリアルタイムおよびソフトリアルタイム性能を提供します。

EtherCATを開発する際に最も留意したことは、短周期のサイクルタイム(≤ 100 µs)、精度の高い時刻同期(≤ 1 µs)を可能とする低ジッタおよび低ハードウェアコスト化です。

EtherCATは2003年4月に公表され、その年の11月にEtherCAT Technology Groupを設立しました。その後ETGは世界最大の産業用イーサネット・フィールドバス団体に成長しました。ETGはデバイスメーカとユーザを結びつけ、両者が技術作業部会でEtherCAT技術の進展に貢献できるように注力しています。

EtherCAT - イーサネットフィールドバスより引用

工場や製造現場にある機器を制御するときに使われている産業用ネットワークの1つです

サンプルコード

動かすためには以下が必要です

  • goの開発環境(1.21以上)
  • libpcap-dev(※)
  • gopacket(パケットに関わるライブラリ)
  • goecat(EtherCATのバイトデータを生成するライブラリ)

※ aptやbrewで提供されているパッケージ名です。それ以外の環境だと名前が異なる可能性があります

package main

import (
	"encoding/hex"
	"fmt"
	"log"
	"time"

	"github.com/Aruminium/goecat/pkg/ethercat/command"
	"github.com/Aruminium/goecat/pkg/ethercat/datagram"
	"github.com/Aruminium/goecat/tools/packet"
	"github.com/google/gopacket"
	"github.com/google/gopacket/pcap"
)

var (
	device       string                    = "en7"
	snapshot_len int32                     = 1024
	promiscuous  bool                      = false
	timeout      time.Duration             = 30 * time.Second
	options      gopacket.SerializeOptions = gopacket.SerializeOptions{
		ComputeChecksums: true,
		FixLengths:       true,
	}
	handle *pcap.Handle
	err    error
)

func main() {
	handle, err = pcap.OpenLive(device, snapshot_len, promiscuous, timeout)
	if err != nil {
		log.Fatal(err)
	}
	defer handle.Close()

	packet, err := packet.NewEtherCATPacket(device)
	if err != nil {
		log.Fatal(err)
	}

	ecatDatagram := datagram.Datagram{
		Command: command.BRD,
		Index:   uint8(0x00),
		Address: uint32(0x00000000),
		LRCM:    datagram.NewLrcm(false, false, 1),
		IRQ:     uint16(0x0000),
		Data:    []byte{0x00},
		WKC:     uint16(0x0000),
	}
	packet.Ecat.AppendDatagram(ecatDatagram)

	data, err := packet.Send(handle, options)
	if err != nil {
		fmt.Printf("[-] Error while sending: %s\n", err.Error())
	}
	fmt.Println(hex.Dump(data))
}

LRCMは、Len+Reserved+Circulating+Moreの頭文字です
ドキュメントだと、Len, R, C, Mと表現されているのでわかりにくいです

実行結果

実行するとセグメントにパケットが流れるのでWiresharkでキャプチャしてあげます

最後に

今回はGoを用いてEtherCATパケットを生成してみました。

goecatは私の初OSSとして公開したものになります
ちょっと面倒なbit処理やDatagramsのLength計算をやってくれます。

先のソースコードで言うとこちらです

	lrcm := datagram.NewLrcm(0, 0, 1)
	ecatDatagram := datagram.Datagram{
		Command: command.BRD,
		Index:   uint8(0x00),
		Address: uint32(0x00000000),
		LRCM:    lrcm,
		IRQ:     uint16(0x0000),
		Data:    []byte{0x00},
		WKC:     uint16(0x0000),
	}
	packet.Ecat.AppendDatagram(ecatDatagram)

	data, err := packet.Send(handle, options)

Githubはgoecatです

Discussion