「ルーター自作で分かるパケットの流れ」でRust入門してみたお話、その1。

そのままだと長いので、何回かに分けていきます。

なぜRustなのか

Twitterのフォロワーたちがめっちゃ「Rustいいぞ」と言ってくるので、とりあえず試したみたいなと思って手頃な対象を見つけたから。調べてみると、結構扱いやすそうなデータリンク層の操作するライブラリがあったので、それを使ってRustに入門したいと思います。

準備

ソースコードへの追記

Cargoでプロジェクトを作成したら、libpnetというライブラリを使うために、Cargo.tomlに追記します。

[dependencies.pnet]
version = "0.21.0"

github.com

github.com

仮想ネットワークデバイスについて

手元でルーターの実験をするには、Linuxの基本的な仮想ネットワークについてもおさらいしておいたほうがいいかなと思います。まぁ、前提知識がある人じゃないと見てもわからないと思いますが。。。

netns

netnsはネットネームスペースのことです。まぁ、よくnetnsはL3のスイッチとかルーターとかって解説されることも多いですが、それのためには、適切にveth間のパケット転送をしてくれる何かしら(iptablesだったり、これから作成するルータだったり)が必要になるのでちょっと説明が下手だと思ってたりもするんですが。まずはじめにちゃんとネームスペースを分離する方法からやっていきたいと思います。

# add
sudo ip netns add [name]

# list
sudo ip netns list

# delete
sudo ip netns delete [name]

# exec
sudo ip netns exec [name] [cmd]

# join interface
sudo ip link set [name-device] netns [name]

veth

vethは仮想のLANケーブルのイメージです。

# add
sudo ip link add name [name1] type veth peer name [name2]

# delete
sudo ip link delete name [name1]

linux bridge

linux bridgeは仮想のL2ブリッジです。ipコマンドからも作成できるのですが、ipコマンドからはvethをattachする方法がわからないので、brctlを使っていきたいと思います。

# add
sudo brctl addbr [name]

# delete
sudo brctl delbr [name]

# attach interface
sudo brctl addif [name] [name-veth]

# detach interface
sudo brctl delif [name] [name-veth]

libpnet

libpnetはrustのクロスプラットフォームな低レイヤーなネットワークのライブラリです。本来、こいつ自体がパケットの解析もできるはずなのですが、その機能はあえて車輪の再発明をしていきます。

第2章 リンクレイヤープログラミングの基本

この章では、ざっくり言ってしまうとEthernetヘッダの解析をするという感じなので、そこまで作っていきたいと思います。まずは、環境を作っていきたいと思います。

sudo ip netns add ns1
sudo ip netns add ns2

sudo ip link add name ns1-veth type veth peer name ns2-veth

sudo ip link set ns1-veth netns ns1
sudo ip link set ns2-veth netns ns2

sudo ip netns exec ns1 ip link set ns1-veth up
sudo ip netns exec ns1 ip addr add 10.0.0.1/24 dev ns1-veth
sudo ip netns exec ns2 ip link set ns2-veth up
sudo ip netns exec ns2 ip addr add 10.0.0.2/24 dev ns2-veth

ここまでコマンドを実行して、ちゃんとpingが通ればOKです。

sudo ip netns exec ns1 ping 10.0.0.2

そして、ここでプログラムをns2内で実行する。

sudo ip netns exec ns2 ./target/debug/router

github.com

すると、こんな具合に、パケットがu8の配列として見れると思います。

$ cargo build && sudo ip netns exec ns2 ./target/debug/router
   Compiling router v0.1.0 (file:///home/proelbtn/Workspaces/Rust/router)
    Finished dev [unoptimized + debuginfo] target(s) in 0.66 secs
[82, 52, 83, 131, 223, 215, 110, 150, 55, 154, 213, 138, 8, 0, 69, 0, 0, 84, 214, 243, 64, 0, 64, 1, 79, 179, 10, 0, 0, 1,10, 0, 0, 2, 8, 0, 206, 70, 36, 125, 0, 1, 21, 145, 183, 90, 0, 0, 0, 0, 116, 124, 5, 0, 0, 0, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55]
[110, 150, 55, 154, 213, 138, 82, 52, 83, 131, 223, 215, 8, 0, 69, 0, 0, 84, 190, 43, 0, 0, 64, 1, 168, 123, 10, 0, 0, 2, 10, 0, 0, 1, 0, 0, 214, 70, 36, 125, 0, 1, 21, 145, 183, 90, 0, 0, 0, 0, 116, 124, 5, 0, 0, 0, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55]
[110, 150, 55, 154, 213, 138, 82, 52, 83, 131, 223, 215, 8, 6, 0, 1, 8, 0, 6, 4, 0, 1, 82, 52, 83, 131, 223, 215, 10, 0, 0, 2, 0, 0, 0, 0, 0, 0, 10, 0, 0, 1]
[82, 52, 83, 131, 223, 215, 110, 150, 55, 154, 213, 138, 8, 6, 0, 1, 8, 0, 6, 4, 0, 1, 110, 150, 55, 154, 213, 138, 10, 0,0, 1, 0, 0, 0, 0, 0, 0, 10, 0, 0, 2]
[110, 150, 55, 154, 213, 138, 82, 52, 83, 131, 223, 215, 8, 6, 0, 1, 8, 0, 6, 4, 0, 2, 82, 52, 83, 131, 223, 215, 10, 0, 0, 2, 110, 150, 55, 154, 213, 138, 10, 0, 0, 1]
[82, 52, 83, 131, 223, 215, 110, 150, 55, 154, 213, 138, 8, 6, 0, 1, 8, 0, 6, 4, 0, 2, 110, 150, 55, 154, 213, 138, 10, 0,0, 1, 82, 52, 83, 131, 223, 215, 10, 0, 0, 2]

これから、この数値を順番にパースしていく感じですね。。。若干だるそう。