ドキドキするとき無敵でしょ

映画とプログラミングの話

プロトコルスタックを自作する Part1

まだ改善点は多くあるけど、一通りの実装ができたので忘れないうちにまとめておく。

完成したものはこちら。

github.com

コマンドラインARP テーブルを表示するようなものにした。

gunzi@gunzi-desktop:~/protocol-stack$ make 
gcc -o arp_test -I ./ test/arp_test.c *.c
gunzi@gunzi-desktop:~/protocol-stack$ sudo ./arp_test 
[sudo] gunzi のパスワード: 
Network Address         MAC Address
192.168.0.1             A4-12-42-19-DC-B0
192.168.0.2             00-25-DC-FC-43-C7
192.168.0.8             74-DA-88-FE-61-DC

きっかけ

去年の9,10月ぐらいからネットワークの構築とかちょっとした設計をすることがあったのですが
家でも何かしらネットワークの構築ができるようにルーターを人からもらったりしていました。
ただ、ネットワーク関連でプログラムを書いたことがなく、いいネタないかなと考えていたところ
友達のけんつくんがプロトコルスタック自作インターンに参加していたことを思い出しました。

rabbitfoot141.hatenablog.com

ちなみにこちらが上のインターンのメンターの pandax さんの資料です。

www.slideshare.net

これだ!!!と思い「次回機会があれば参加しようかな...」と一瞬考えたんですが、それをやると負けだと感じたので自分で一からやることにしました。

目標設定

最初、目標は「まずは一つプロトコルを実装してみること」に設定しました。自分としては
最初に ICMP を実装するつもりだったのですが「これもしかしてパケットの宛先がわからないとだめでは?」と気づいたので ARP の実装を行うことへと変化しました。

しかし、ただ実装するにしても面白くないので、いくつかの縛りを設けることにしました。
(ダクソシリーズで好きな素性は持たざるもの派です)

  • プロトコルスタックを実装した人のソースを読まない( Pandax さんやその他の人の実装など)
  • プロトコルスタック実装について書かれてる書籍は読まない(小俣光之さんの書籍やマスタリングTCP/IPシリーズなど)
  • 人に質問しない(完全に手詰まりでどうしようもないときはする)
  • できるだけ man 、 Linux のソースを調べる
  • RFC は時間がかかっても自分で一度翻訳してみる(日本語訳を脳死でみないでちゃんと読んでみる)

なぜ人の実装を読まないのかの理由ですが、自分自身写経が苦手なためコードをそのまま書き写して学ぶと 完全に理解した(わかってない) になってしまうためです。
また、難易度は高いほうが気持ちいいのでハードルを上げました。

ちなみにこれを見たけんつくんには RTA と呼ばれました。

今回実装を行ったのは以下4つです。

進め方

何もないところから生成するのは至難の業なので、パケットを解析するところからはじめました。
まずはこの記事を一通り触ってパケットを解析してみることにしました。

https://www.opensourceforu.com/2015/03/a-guide-to-using-raw-sockets/

ここで使われてる関数を man で調べたり、インクルードされている linux のファイルを調べてソースを読むを繰り返して
ひたすら情報を集めました。 基本的な情報は man にあったのでかなり助かりました。

man7.org

man7.org

RFC はここから引っ張ってきました。

https://tools.ietf.org/rfc/index

実装はパケット解析をするプログラムで使用されているヘッダファイルを置き換えていくようにし、以下の流れを繰り返しました。

  1. RFC を読む
  2. 実装
  3. パケットを解析

inet に属する関数群は man 2 で挙動を確認して類する、もしくは同様の挙動になるように実装を行いました。 あとはデバッグの機能でこんなのあったらいいな、というものを実装していきました。

余談ですが、途中で「バイナリデータを見れるやつがあると便利だよ」「エンディアン周りで注意したほうがいい」と アドバイスしてくれた人もいて非常に助かりました。本当にありがとう

実装を終えて

総評すると、初めて触るものが多すぎて何もわからない状態から始めたのでかなり難しかったです。
ただ、 既存の技術をハックして自分なりに考察して改善する面白さは段違いでした。
そのうえ難易度設定を上げて縛りプレイをしたので、最後まで実装できるか正直わからなかったのですが目標までやりきれました。
一人で誰の目もなくできるのか?という点も本当に心配だったんですが、自分で常にマイルストーンを置いて毎日バイトに行く電車でRFC を読んだり、実装について考えていたので全く問題なかったです。

個人的に一番良かったポイントなんですが 自分の作った ARP リクエストパケットに ARP リプライが返ってきたところを tcpdump で見た時は感動しました。

あと定期的にるくすさんのこの記事を何度も読み返してモチベーションを保っていました。
低レイヤーのモチベーションが落ちてきたら読み返すのがおすすめです。(ユーザースペースで動いてるから低レイヤーではないという主張は無視しておく)

rkx1209.hatenablog.com

あと実装中は evernote で資料とか socket の設定とか色々メモしてたらノートの数が14個になってました。
メモ取っておくとあとで見返した時に楽なのでおすすめです。

難しいと感じた部分

デバッグ関連はかなり苦労した部分が多かったです。そもそもどうやって送信するパケットをデバッグするんだ?とか。
tcpdump やヘキサダンプしたデータ、関数の返り値から推測して行いました。基本的なところで行けば man を参照してなにかをしたことがほとんどなかったので、そこも少し大変でした。

教訓として得たのは風邪のときのように冷静な判断が難しいときは休む、というこです。頭が回らないので勘違いが増えます。これで1週間溶けました

次の目標

粗い部分が多いのでそのへんの修正しなきゃなぁ、というのがまず第一。
ICMP の実装ができたら ping pong するところまでを wiki なりにまとめて資料化する予定です。
Part2 以降の目標として ICMP, TCP, UDP など有名なプロトコルをあといくつか実装してみようと考えています。
それに加えてよりカーネルに近い部分の低レイヤーに手を入れていきたいので、デバイスドライバ本を読み進めて実際にイーサネットポートのドライバを書いてみたいと思います。
あとはハックネタを探して理論と実装の部分を学んで自分の中になるギャップを埋めて IT だけでなく CS に強くなりたいですね。

最後に

普段は通信制の大学に通いつつ(?)、バイトでインフラエンジニアのアシスタントをしているのですが普段の業務では構築などが多く
正直プログラミングは向いてないんじゃないか?と思うことも多々あったのですが、実際に実装しているとそうでもないな、ということに気づけました。
ただ、インターンハッカソンで行ったことではないので客観的な評価は得られないため、そこはちょっとデメリットだなと感じます。
実装中は常に自分が何がわからないか、今何ができて何をすべきか、を解決するために man や RFC を電車での移動中などにひたすら調べていました。
縛りを設けたことで難易度が上がりましたが、それらを実践したことでメタスキル的な部分が磨かれたんじゃないかな、と思います。
あと、これは悔しいポイントなんですが RFC でどうしてもわからない部分があって、そこについて調べるためにググってしまったことですね。
見てからデバッグ方法に気づいたのでそれが本当に悔しかったです。 ICMP では絶対に見ないぞ...と心に誓った。
実装中誰にも頼らずやると決めていたので、正直辛いんじゃないだろうか?と思ったんですが全く辛くなかったです。むしろ限られた情報ソースの中からどこかにヒントがないか、
もっと良い実装があるんじゃないか、といったことを常に考えていられたので純粋に楽しかったです。
あと、一通りやったことでネットワーク技術に興味が湧いたので ARP の実装が終わったあとに EuroBSDcon の資料読んでみたりしました。
次はカーネルに挑戦して低レイヤーに挑むぞ、という気持ちが固まりました。最低限のハードウェアの仕様書を読んでコードを生成できるようになりたいですね。
まだ挑みきれてないコンパイラであったり、エミュレータを書いてみるとか、ちょっとずつ挑戦していけたらなと思います。

あとは就活を終わらせるのが目標ですね......早く終わらせて低レイヤーと CS の勉強に集中できるようになりたい...