ネットワークを作って理解しようとする(Ethernet編)

こんにちは. 今週末は新型コロナの影響で外出自粛なので暇を持て余しております. 暇なのでNetflixで鬼滅の刃を見始めました.面白いですね〜.

ネットワークを作って理解する

最近の興味としてネットワークの仕組みを理解したいというのがあり,プロトコルスタック自作なるものを知りました.とはいえ僕はC言語が得意でないのでGo言語で作ってみようと思い作成を始めました.というわけて何回かに分けて紹介したいと思います.

OSI参照モデル

OSI参照モデルとはコンピュータの通信機能を階層構造に分割したモデルです. 各階層にはそれぞれが担うべき機能が定義されています. データリンク層では隣接するノード間のデータの通信をサポートします. osi-model

開発環境

開発環境は以下の通りです.ioctlなどのシステムコールを扱うためprivilegeオプションを有効にしたLinuxコンテナを作成してプログラムをビルドします. また,実行はコンテナの中でネットワーク名前空間を分離して行います.

  • Mac OS Catalina
  • VSCode
  • Docker version 19.03.5, build 633a0ea

実装

リポジトリはこちら

物理層からデータを受け取る

今回のプログラムでは生のパケットを受け取る必要があります.golangの標準パッケージでは生のパケットを扱うことができないため別の方法で生のパケットを取得しなければいけません.そこで今回のプログラムでは以下の二つの方法で生のパケットを取得します.

  • PF_PACKET
  • Tun/Tapデバイス

PF_PACKET

PF_PACKETはLinuxのsocketシステムコールで生のパケットを扱うためのドメインです.syscallパッケージのSocket関数を用いて以下のようにソケットを開きます.

protocol := hton16(syscall.ETH_P_ALL)
fd, err := syscall.Socket(syscall.AF_PACKET, syscall.SOCK_RAW, int(protocol))
if err != nil {
    return -1, err
}

開いたソケットを用いてPFPacket構造体を定義します.

type PFPacket struct {
	fd                 int
	name               string
	address            ethernet.HardwareAddress
	netInfo            ip.IPSubnetMask
	registeredProtocol []LinkNetProtocol
	MTU                int
	buffer             chan *ethernet.EthernetFrame
}

Tun/Tap

Tun/TapはUnixで使用できる仮想ネットワークデバイスです.Tun/Tapデバイスに届いたパケットは直接ユーザープログラムに送られます. Tun/Tapデバイスは以下のように開きます.

const device = "/dev/net/tun"

file, err := os.OpenFile(device, os.O_RDWR, 0600)
	if err != nil {
		return "", nil, err
	}

開いたファイルを用いてTun構造体を定義します.

type Tun struct {
	file               io.ReadWriteCloser
	name               string
	address            ethernet.HardwareAddress
	netInfo            ip.IPSubnetMask
	registeredProtocol []LinkNetProtocol
	MTU                int
	buffer             chan *ethernet.EthernetFrame
}

Deviceインターフェース

DeviceインターフェースでPF_PACKETとTunデバイスの差を吸収します.

[Read More]