自分で作るTCP/IPスタック入門!第1回:EthernetとARPの基礎を徹底解説
引用元:https://news.ycombinator.com/item?id=43250093
昔、Cでユーザースペースのネットワークスタックを作ろうとしたんだけど、TUNインターフェースを通して生のパケットを処理するところまではできた。IPアドレスやルートの設定ができるシェルもあって、mbufとsk_bufを参考にした構造体でネットワークパケットを保持してる。UDPの実装までは進んだけど、TCPの実装はやる気がなくて。興味があったらこちらからどうぞ:
https://github.com/cakturk/unet
昔、純粋なbashでpcap/tcpdumpのパーサーを書いたことがあるんだ。プログラムを書くのがそれしかできなかったからね。もちろん、超遅くて脆弱なものだったけど、実際に動いたし、ちょっと楽しかった。今でもそのコードがあったらいいなぁ。
多くの組み込みデバイスはlwipのTCP/IP実装を使ってる。lwipの’POSIX port’も同じことをしていて、TUN/TAPデバイスから生のEthernetバイトを受け取るんだ。
https://github.com/lwip-tcpip/lwip/blob/master/contrib/ports…
最小のLinuxカーネルをTCP/IPスタックなしでコンパイルすると約400KB。TCP/IPスタックを追加すると800KBになる。温度を送信するプロジェクトのために、ユーザースペースの小さなCプログラムでUDPメッセージに値を送ったら、スペースと複雑さが減ったよ。
そりゃすごい!何も知らない私からすると、それってTCP/IPの部分がカーネル全体の半分のソースコードってことじゃないよね?
Linuxカーネルのソースコードの大半はデバイスドライバーだよ。ほとんどはデフォルトでカーネルイメージに含まれてないけど、必要に応じてカーネルモジュールとして使えるんだ。たとえば、サーモスタットはわざわざ珍しいゲームコントローラーのドライバーは必要ないから、そんなのは入ってないけど、必要なら追加できる。
IPスタックってなんでそんなに大きいの?400KBのバイナリってかなりのコード量だよ。大規模サーバー用に最適化されてるの?
現代のTCP/IPスタックには、アンチスプーフィングやパフォーマンス向上(ハードウェアネットワークカードとのゼロコピー統合など)、攻撃防止策(SYNフラッドやシーケンス番号のランダム化など)、さまざまなハードウェアオフロード(チェックサムオフロードなどを行うネットワークカードも含む)、IPv6(もともとIPSecの統合も要求されていた)や低レイヤー2プロトコルのサポートが含まれているよ。
ARPを無効にすると、同じネットワークに同じIPを持つサーバーをグループ化できる!もし、ルーティングフロントエンドがMACアドレスでバックエンドサーバーのネットワークインターフェースにパケットを転送できるなら、バックエンドサーバーは自分自身を宛先として認識して、IPを入れ替えてクライアントに直接返事するんだ(ルーティングフロントエンドには戻らない)。同じことは、ARPを無効にすることなく、ループバックインターフェースに共通IPアドレスをエイリアスとして追加することで実現できる。このトリックは90年代から00年代にかけてIBMのWebSphereソフトウェアロードバランサーで使われてた。
>このトリックは90年代から00年代にかけてIBMのWebSphereソフトウェアロードバランサーで使われてた。”Cisco IOS SLB”も似たように動作することができるよ。各サーバーにループバックのエイリアスとして追加された仮想IPを使うんだ。一般的なL3バランシングに比べて、IPパケットのヘッダーを再構成する必要が無いという利点がある。
DSR(Direct Server Return)って呼ばれてるよ。詳しいことはブログ見てみて。
ARPを無効にすれば同じIPのサーバーをグループで使えるけど、スイッチはMACアドレスを学習しないからパケットが全ポートにバンバン流れちゃうんだ。ちゃんとしたVLAN作った方がいいよ!
ARPはLANデバイスのためのもので、L2スイッチはARPを使わずに送信元MACで転送テーブルを作れるんだよ。行き先MACが分からない時はブロードキャストするけど、全デバイスが1回はフレームを送ると止まる。
F5にはARPプロキシ設定があって、それ使えば手間が省けるけど、DHCPが壊れることがあるから注意してね。
低レベルのことをやるならdpdkをいじるのもアリだよ。ARPはデフォルトで無効になってるし。
Pythonで似たようなことをやったよ。多分、あんまりうまく書けてないけど。ICMPでインターネットホストにpingも飛ばせた。自分のはノートに収まってるから、全体を把握しやすいかな。でも、TCPのとこは難しすぎて興味が薄れた。プログラミングに興味あるならやってみると面白いよ!
昔、原発でクライアントサイドの開発をしてた時、TCP/IPの経験で雇われたんだ。でも、そのプラントのコンピュータにはTCP/IPスタックがなかったから、作らなきゃいけなかった。
記事の最初で、「dmacとsmacは自己説明的なフィールド」って書いてあるけど、これで分からない人は読む気失うよね。
完全な引用は「dmacとsmacは自己説明的なフィールドで、通信する当事者のMACアドレスを含んでいる」って感じ。記事はネットワークスタックを作る内容だから、ちょっとは知識がある人が読むべきかな。
多分、更新されたんだと思うけど、次の文ではちゃんと説明があったよ。「通信する当事者のMACアドレスを含んでいる」って書いてある。
もっとコメントを表示(1)
関連:
“Let’s code a TCP/IP stack (2016)”
-
https://news.ycombinator.com/item?id=27654182
- 2021年6月(コメント49件)
“Let’s code a TCP/IP stack, 1: Ethernet & ARP (2016)”
-
https://news.ycombinator.com/item?id=17316487
- 2018年6月(コメント47件)
“Let’s Code a TCP/IP Stack: TCP Retransmission”
-
https://news.ycombinator.com/item?id=14701199
- 2017年7月(コメント30件)
“Let’s code a TCP/IP stack, 1: Ethernet and ARP”
-
https://news.ycombinator.com/item?id=11234229
- 2016年3月(コメント49件)
著者がARP解決テストに使った10.0.0.4ってIPアドレスの出どころが分からないんだけど、これは何のアドレスなの?作ったEthernetデバイスがアクセスする偽のデバイス?それとも実際に著者のネットワークにあるデバイスなの? 誰か教えて。
記事に書かれてないけど、著者はインターフェース初期化時にこれをハードコーディングしてるよ:
>“https://github.com/saminiir/level-ip/blob/e9ceb08f01a5499b85…”
TAPデバイスはソフトウェアでエミュレートされたEthernetリンクみたいなもので、パケットを送るとユーザーレベルのプログラムに直接届く。プログラムがどのIPアドレスを持つかやARPの返信をどうするか決めるのはプログラム次第。通常こういうことはOSが扱うから、インターフェースにIPアドレスを追加するにはroot権限が必要なんだよね。
ああ、ありがとう!記事でこれを明示しなかったのは大きな見落としだと思う。ARPの部分が重要な情報が抜けてるみたいに感じられるし、実は前の部分が何かを欠いているんだよね。再度ありがとう!
確か、ARPは自分のローカルセグメントでしか機能しないよね。ルーターが自分のアドレスを埋めてパケットを転送するんだ。それとRARPもあって、これは‘ネットワーク’に自分のIPアドレスを尋ねる方法の一つだよ。RARPが現実でまだ使えるかは知らないけど。
記事の非仮定的説明が非常に良いと思った。よくできてる。