# Full TCP/IP for 8-Bit Architectures
> [!abstract] 概要
> 本稿では、完全なホスト間相互運用性に必要な RFC1122 要件のサブセットを満たす、小型で移植性の高い 2 つの TCP/IP 実装について述べる。
> これらの TCP/IP 実装は、緊急データや輻輳制御など TCP の機構を犠牲にしていない。
> IP フラグメント再構成をサポートし、同時に開ける接続数は利用可能な RAM によってのみ制限される。
> 小型で単純であるにもかかわらず、複雑で大規模なスタックを持つ相手だけでなく、同程度に軽量なスタックを実行する相手とも通信できる。
> コードサイズはおよそ 10 キロバイトで、RAM 使用量は数百バイトまで構成できる。
## 論文情報
- 著者: [[Adam Dunkels]]
- 所属: [[Swedish Institute of Computer Science]]
- 媒体: MobiSys 2003, *The First International Conference on Mobile Systems, Applications, and Services*
- 開催地・日付: San Francisco, 2003-05-05〜2003-05-08
- 公式ページ: https://www.usenix.org/conference/mobisys2003/full-tcpip-8-bit-architectures
- 実装: [[lwIP]](lightweight IP)、[[uIP]](micro IP)
## 概要
従来の TCP/IP 実装は数百キロバイトのコードと数百キロバイトの RAM を必要とし、数十キロバイトの RAM と 100 キロバイト未満のコード領域しか持たない 8 ビット・16 ビット組み込みシステムには大きすぎた。
本論文は、RFC1122 のうちホスト間相互運用性に関わる要件を維持しながら、実装のインターフェース、メモリ管理、接続状態の表現を小さくする設計を示す。
完全な機能を優先する [[lwIP]] と、絶対最小の資源量を優先する [[uIP]] を同じプロトコル実装として比較し、コードサイズ・RAM 使用量・スループットの関係を測定する。
## 問題設定
組み込み機器をインターネット上の「第一級のネットワーク市民」として扱うには、特定のウェブサーバー用途に特化した簡略実装ではなく、異なるホストとの汎用的な TCP/IP 通信が必要である。
しかし、8 ビット CPU では 32 ビット演算、動的メモリ、スライディングウィンドウのための送信キューが高コストになる。
相手がワークステーション級の完全なスタックだけだと仮定して機構を削除すると、別の小型機器とのピアツーピア通信で必要な再送、輻輳制御、IP フラグメント再構成などを失う。
論文は、以下を設計上の制約とする。
- TCP/IP のプロトコル機構を削って相互運用性を損なわないこと
- 8 ビット・16 ビットシステムの限られたコード領域と RAM に収まること
- 複数の同時接続を、固定された小さな接続数に制限しないこと
- BSD ソケット API のタスク・コンテキスト切り替え負荷を避けること
## 提案手法
### 2 つの実装の設計目標
[[lwIP]] は、IP・ICMP・UDP・TCP を備えた、完全な機能を持つが簡略化された TCP/IP 実装である。
複数のネットワークインターフェース、複数の TCP 接続、UDP、スライディング TCP ウィンドウ、TCP 輻輳制御を実装し、プロトコルごとのモジュール分離と柔軟な構成を保つ。
[[uIP]] は、完全な TCP/IP スタックとして必要な絶対最小の機構だけを実装する。
ネットワークインターフェースは 1 つに限定し、UDP を実装せず、IP・ICMP・TCP に集中する。
スライディングウィンドウを持たず、接続ごとに未確認の TCP セグメントを 1 個だけ許すことで、32 ビット演算と送信済みデータの保持を避ける。
![[_attachments/dunkels-tcp-ip-8-bit-architectures/fig01-tcpip-input.png]]
**Figure 1: TCP/IP input processing.**
ネットワークインターフェースから到着したパケットを TCP/IP スタックが接続ごとに多重分離し、ウェブサーバー、メール送信、データロガーなどのアプリケーションへ配送する構成を示す。(Source: Figure 1.)
![[_attachments/dunkels-tcp-ip-8-bit-architectures/fig02-tcpip-output.png]]
**Figure 2: TCP/IP output processing.**
複数のアプリケーションから渡されたデータを TCP/IP スタックが集約し、送信パケットとしてネットワークインターフェースへ渡す構成を示す。(Source: Figure 2.)
### メモリとバッファ管理
lwIP は、グローバルなメモリプールから固定サイズのバッファを動的に割り当てる。
1 パケットを複数バッファに分割でき、参照カウンタによって TCP/IP スタック、アプリケーション、再送キューが同じバッファを共有できる。
送信データが volatile かどうかに応じてコピーまたはポインタ参照を選び、受信確認まで再送キューのバッファを解放しない。
uIP は明示的な動的メモリ割り当てを行わず、最大サイズのパケットを収容できる単一のグローバルバッファと固定長の接続テーブルだけを使う。
受信データは次のパケットで上書きされるため、アプリケーションはイベント処理中に直ちに消費するか、必要な部分を別バッファへコピーする。
送信済みデータは再送のために保持せず、再送時にはアプリケーションが同じデータを再生成する。
### アプリケーション・インターフェース
BSD ソケット API は停止待ちの意味論を持ち、タスク管理、コンテキスト切り替え、スタック領域の割り当てを必要とするため対象 CPU には重い。
両実装はイベント駆動 API を採用し、データ到着、接続要求、スタックからのポーリングなどのイベントに応じてアプリケーションを呼び出す。
アプリケーションは受信データを処理した直後に応答でき、低性能なシステムでも応答時間を短くできる。
### メイン制御ループとプロトコル処理
lwIP と uIP は、マルチタスクシステムのタスクとしても、単一タスクシステムのメインプログラムとしても動作する。
メインループは、ネットワークからパケットが到着したか、周期タイムアウトが発生したかを繰り返し確認する。
パケット到着時には入力ハンドラを呼び、タイムアウト時には遅延 ACK、再送、RTT 推定などのタイマー依存機構を処理する。
![[_attachments/dunkels-tcp-ip-8-bit-architectures/fig03-main-control-loop.png]]
**Figure 3: The main control loop.**
パケット到着の処理と周期タイムアウトの処理を交互に確認し、それぞれがアプリケーションイベントと出力パケット生成へ進むイベントループを示す。(Source: Figure 3.)
### RFC1122 への適合
RFC1122 の要件を、ホスト間通信に影響する要件と、アプリケーションとスタックの間だけに影響する要件へ分ける。
両実装は前者を実装し、後者ではソフトエラー報告や動的な Type-of-Service ビット設定など、利用するアプリケーションが少なく相互運用性に影響しない機構を省略する。
| 機能 | uIP | lwIP |
|---|---:|---:|
| IP・TCP チェックサム | ✓ | ✓ |
| IP フラグメント再構成 | ✓ | ✓ |
| IP オプション | — | — |
| 複数インターフェース | — | ✓ |
| UDP | — | ✓ |
| 複数 TCP 接続 | ✓ | ✓ |
| TCP オプション | ✓ | ✓ |
| 可変 TCP MSS | ✓ | ✓ |
| RTT 推定 | ✓ | ✓ |
| TCP フロー制御 | ✓ | ✓ |
| TCP スライディングウィンドウ | — | ✓ |
| TCP 輻輳制御 | 不要 | ✓ |
| 順序外 TCP データ | — | ✓ |
| TCP 緊急データ | ✓ | ✓ |
| 再送用データバッファ | — | ✓ |
(Table 1. TCP/IP features implemented by uIP and lwIP. Source: Table 1.)
IP フラグメント再構成は専用バッファと受信済みフラグメントのビットマップで行う。
ただし同時に再構成できるパケットは 1 個だけであり、指定時間内に全フラグメントが届かなければ破棄する。
ICMP はエコー要求・応答に限定し、IP オプション、Path MTU Discovery、ICMP Redirect は実装しない。
## 実験設定
実験は、450 MHz Pentium III 上の FreeBSD 4.7 ホストと、14.7456 MHz の Atmel Atmega128 AVR、128 キロバイトのフラッシュ ROM、32 キロバイトの RAM を持つ [[Ethernut]] ボードを、専用の 10 Mbit/s Ethernet で接続して行った。
ホストでは [[Dummynet]] を用いて通信遅延を制御した。
組み込み側では uIP と lwIP の上に単純なウェブサーバーを実行し、ホストの `fetch` でヌルバイトのファイルを 10 回取得した。
uIP のファイルサイズは 200 キロバイト、lwIP は 200 メガバイトとし、組み込み側でファイル全体を保持せずウェブサーバーが送信時に生成した。
uIP は送信ウィンドウを 50〜1,450 バイト、lwIP は 500〜11,000 バイトの範囲で変化させ、TCP/IP の総 RAM 使用量と平均スループットの関係を測定した。
コードサイズは gcc 2.95.3(x86) と gcc 3.3(AVR)で最適化を有効にしてコンパイルした。
## 実験結果
### 遅延 ACK とスループット
受信側の遅延 ACK は通常、2 セグメントごとに ACK を返し、次のセグメントが届かなければ最大 200〜500 ms 待つ。
未確認セグメントを 1 個しかネットワーク上に置けない uIP はこのアルゴリズムと相性が悪く、送信ウィンドウ 1,000 バイト、RTT 40 ms、遅延 ACK タイムアウト 200 ms の場合、最大スループットは
`p = s / (t + td) = 4,166 bytes/second`
となる。
遅延 ACK を無効にすれば同じ条件で 25,000 bytes/second まで上がる。
![[_attachments/dunkels-tcp-ip-8-bit-architectures/fig04-uip-delayed-ack.png]]
**Figure 4: uIP sending data with 10 ms emulated delay.**
10 ms のエミュレート遅延を加えた場合、遅延 ACK を有効にした曲線は送信ウィンドウを増やしても低いままであり、無効時の曲線との差が大きくなる。(Source: Figure 4.)
![[_attachments/dunkels-tcp-ip-8-bit-architectures/fig05-uip-no-delay.png]]
**Figure 5: uIP sending data without emulated delay.**
遅延 ACK を無効にした場合、uIP のスループットは送信ウィンドウの増加に伴って上昇し、およそ 1,400 バイト付近で 315,000 bytes/second に近づく。一方、有効時は数万 bytes/second に留まる。(Source: Figure 5.)
![[_attachments/dunkels-tcp-ip-8-bit-architectures/fig06-rtt-packet-size.png]]
**Figure 6: Round-trip time as a function of packet size.**
パケットサイズの増加に伴って RTT も増加する。測定値にはパケット入力と出力でそれぞれ一度ずつ行うコピー処理のコストが含まれる。(Source: Figure 6.)
![[_attachments/dunkels-tcp-ip-8-bit-architectures/fig07-lwip-delays.png]]
**Figure 7: lwIP sending data with and without emulated delays.**
lwIP は送信ウィンドウが 3,000 バイトを超えると 1 RTT あたり 2 セグメントを送信でき、遅延 ACK による低下を回避する。遅延なしではおよそ 415 キロバイト/秒で頭打ちになり、10 ms と 20 ms の遅延では低下する。(Source: Figure 7.)
### コードサイズ
| 関数 | コードサイズ(bytes) |
|---|---:|
| uIP(x86) チェックサム計算 | 464 |
| uIP(x86) IP・ICMP・TCP | 4,724 |
| uIP(x86) 合計 | 5,188 |
(Table 2. Code size for uIP (x86). Source: Table 2.)
| 関数 | コードサイズ(bytes) |
|---|---:|
| uIP(AVR) チェックサム計算 | 712 |
| uIP(AVR) IP・ICMP・TCP | 4,452 |
| uIP(AVR) 合計 | 5,164 |
(Table 3. Code size for uIP (AVR). Source: Table 3.)
| 関数 | コードサイズ(bytes) |
|---|---:|
| lwIP(x86) メモリ管理 | 2,512 |
| lwIP(x86) チェックサム計算 | 504 |
| lwIP(x86) ネットワークインターフェース | 364 |
| lwIP(x86) IP | 1,624 |
| lwIP(x86) ICMP | 392 |
| lwIP(x86) TCP | 9,192 |
| lwIP(x86) 合計 | 14,588 |
(Table 4. Code size for lwIP (x86). Source: Table 4.)
| 関数 | コードサイズ(bytes) |
|---|---:|
| lwIP(AVR) メモリ管理 | 3,142 |
| lwIP(AVR) チェックサム計算 | 1,116 |
| lwIP(AVR) ネットワークインターフェース | 458 |
| lwIP(AVR) IP | 2,216 |
| lwIP(AVR) ICMP | 594 |
| lwIP(AVR) TCP | 14,230 |
| lwIP(AVR) 合計 | 21,756 |
(Table 5. Code size for lwIP (AVR). Source: Table 5.)
uIP は x86 で 5,188 バイト、AVR で 5,164 バイトとほぼ同じコードサイズである。
lwIP は x86 の 14,588 バイトから AVR の 21,756 バイトへ増えるが、これは 32 ビット演算が多く、8 ビット CPU では一つの演算が多数の命令へ展開されるためである。
RAM 使用量は uIP では 400 バイトから 3 キロバイト、lwIP では 5〜16 キロバイトの範囲で測定された。
## 考察
この論文の中心的な設計判断は、プロトコル機構を削るのではなく、機能を実装する境界を狭めることで資源量を削減することである。
uIP は送信済みデータの保持とスライディングウィンドウをアプリケーション側へ移し、単一バッファとイベント駆動 API を用いる。
lwIP はプロトコルスタック側にバッファ、再送キュー、複数セグメントのウィンドウ管理を残し、その代償としてコードサイズと RAM 使用量を増やす。
両者の違いは「完全な TCP/IP か簡略 TCP/IP か」ではなく、RFC1122 のホスト間要件を共通に保ったまま、状態と責任をどの層へ置くかの違いである。
uIP の構成は小さな送信量のセンサーや制御機器に向き、lwIP の構成は複数接続・大きな送信量・UDP・複数インターフェースが必要な機器に向く。
論文はセキュリティ、IP アドレス自動設定、エネルギー消費を扱わず、これらを組み込み TCP/IP の残る課題として挙げる。
## 強み / 弱点・課題
### 強み
- 小型実装でも IP フラグメント再構成、TCP 緊急データ、RTT 推定、フロー制御などの相互運用性に必要な機構を保持した点
- 同一のプロトコル要件に対して、機能性重視の lwIP と資源量重視の uIP を並べ、コードサイズ・RAM・スループットの設計空間を示した点
- AVR、H8S/300、8051、Z80、ARM、M16c、x86 へ移植できる C 実装として、特定アプリケーションへの過度な特化を避けた点
### 弱点・課題
- 実験は Ethernut/Atmega128 と FreeBSD 4.7 の構成に限られ、他のネットワークインターフェース、CPU、ワークロードでの再現性は評価していない
- uIP の再送データ再生成をアプリケーションへ要求するため、アプリケーションが送信データを決定論的に再生成できない場合の扱いが難しい
- uIP は遅延 ACK と相性が悪く、送信ウィンドウを増やしても単一セグメント制約が送信スループットを制限する
- セキュリティ、アドレス自動設定、エネルギー消費はスコープ外であり、インターネット接続する組み込み機器の運用条件は別途設計する必要がある
## 出典
- [[.raw/papers/dunkels-tcp-ip-8-bit-architectures.pdf]]
- [[.raw/papers/dunkels-tcp-ip-8-bit-architectures.txt]]
- [[@2003__MobiSys__Full TCP IP for 8-Bit Architectures]] — MobiSys 2003 掲載論文