「ルーター自作でわかるパケットの流れ」を読む
1年の春ごろから読みたかった本だが、3年の春であるいま、手に取ることにしたt6o_o6t.icon
ネットワークスペシャリスト試験の勉強を経て読みやすくなったと思う
初版第2刷
t6o_o6t.icon
本の全般について
サンプルコードと、その解説を交互に行う形式で進んでいく
ただ、基礎知識をまんべんなく解説してくれるわけではない。
たとえば、サンプルコードには ifreq や ioctl といった構造体や関数が登場する。しかし、それらが何を意味するのかは本文中では述べられていない。
したがって、サンプルコードのもつ背景に疑問をもち、調べながら読んでいくことを意識すると、本書の内容をより詳しく理解できると考えられる。
1章: ネットワーク機器は何をしているのか
ネットワークの基礎知識については、本書でも触れられている
ただ、概念と実装(Linux上のヘッダーファイル)が入り混じった説明になっているので、完全な初学者が理解しようとすると実装部分がノイズになる可能性がある。
先に他のネットワークの本を使って、概念を一通り理解しておくと、Linuxのヘッダーファイルの内容と自分の記憶とをリンクさせることができる。
2章: リンクレイヤープログラミングの基本
基本的な流れは、次の通り。
1. socket() によって、ソケットを作成する
2. ioctl() によって、特定の名前のインターフェースのifindex(インターフェース番号)を取得する。
3. 取得したifindexをもとに、sockaddr_llを生成する。
4. bind()を呼び出して、ソケットを特定のインターフェースにソケットをバインドする。
ソケットの作成
socket()の引数が何を意味しているのかは、man を確認すると良い。
https://manpages.ubuntu.com/manpages/focal/ja/man2/socket.2.html
ifindexの取得
流れ
1. ifreq構造体のifr_nameに、インターフェース名をセットする。
ここで使用する名前は、ifconfigコマンドで表示されるインターフェース一覧のなかから、ふだんインターネットとの通信に使っているものを探すとよいだろう
2. ioctl()関数を呼び出す
3. ifreqのifr_ifindexの値が、取得したifindexを表している。
実験: straceによる呼び出し内容の確認
実験: ifindexはどのような値なのだろうか?
sockaddr_llの生成
TCP/IPのネットワークプログラミングを経験したひとは、sockaddr_inという構造体の存在を思い出してほしい。
sockaddr_inは、TCP/IPプロトコルでの通信において、接続先のIPアドレスとポート番号を設定し、bind()に渡すものだった。
つまり、sockaddr_*は接続に必要な情報をまとめた構造体である。
sockaddr_llは、どのインターフェースにソケットをバインドするのかを含んだ構造体である。
「どのインターフェースなのか」をifindexで指し示す。
ソケットのバインド
bindにソケットとsockaddr_llを渡して、ソケットを特定のインターフェースにバインドする。
この章では、以上の流れがサンプルコードで示されている。
bindしたソケットにはEthernet frameが流れてくるので、read()で読み取ったバイト列をeh_header構造体にキャストすればヘッダ情報を取得できる。
3章:
たぶんlestというのはrestの誤字かな...
Analyze*系の関数は、基本的にバイト列の先頭部分だけを特定の構造体のキャストにしてPrint*系の関数に渡すだけ。
ただ、AnalyzeIpだけは、IPヘッダのプロトコル情報(iphdr.protocol)に応じて、適切なAnalyze*系関数を呼び出す実装も必要になってくる。
つまり、リンクレイヤでのプログラミングは、Ethernetフレーム → IPやIPv6やARP → TCPやUDPという構造を意識して行わなければならない。
プログラミング自体は、バッファを順番に見ていくだけで問題ないので、難しくない。
4章
ブリッジを作るときは、ソケットをインターフェースごとに作ってpollで監視する。
読み取ったデータを、対向するインターフェースのソケットに書き込むだけ。
5章
まとめ
Ethernetフレームを層ごとに分解する方法が分かれば、あとはブリッジやルーターの特性をプログラムに落としこむだけ!