1 of 24

Go言語で作るTCP/IP

terassiy @terassyi_

SYN 1

2 of 24

自己紹介

  • terassyi(@terassyi_)
  • 福岡の学生
  • 好きなもの
    • ネットワーク
    • コンテナ
    • Golang
    • Rust

2

3 of 24

TCP/IPを(Goで)作るモチベーション

  • インターネットの世界がどのように構築されているのか理解したい
  • パケットのやり取りを自分で作ってみたい
  • 通信できると楽しい
  • 自作○○を何かしてみたかった

  • C言語があまり書けない
  • Go言語を絶賛勉強中だった
  • 既存の実装があまりなかった

3

4 of 24

準備

  • 仕様(RFC)を読む
    • Ethernet
    • ARP(RFC826)
    • IPv4(RFC791)
    • ICMP(RFC792)
    • TCP(RFC793)
  • 参考となる実装を読む

4

5 of 24

デモ

5

標準パッケージ(OSのプロトコルスタック)を利用したtcp echoサーバ

gotcpで記述した

tcp echoクライアント

6 of 24

デモ

6

7 of 24

全体図

7

ping応答

Ethernet

ARP

IPv4

ICMP

TCP

アドレス解決問い合わせ

アドレス解決

8 of 24

EthernetからIPv4(ICMP)まで

8

Ethernet

ARP

IPv4

ICMP

TCP

アドレス解決問い合わせ

アドレス解決

ping応答

9 of 24

IPアドレスとMACアドレスの解決

9

ARPテーブルの定義

IPパケット送出処理

ARPテーブルの更新を待つ

新たにARPリクエスト送信

10 of 24

TCP

10

ping応答

Ethernet

ARP

IPv4

ICMP

TCP

アドレス解決問い合わせ

アドレス解決

11 of 24

TCPの機能

  • コネクション管理
  • 再送制御
  • 順序制御
  • 輻輳制御
  • フラグ
  • シーケンス番号
  • 確認応答(ACK)番号
  • 再送タイマ
  • ウィンドウ

11

12 of 24

TCPパケット

12

13 of 24

TCP内部構造体(Task Control Block)

13

TCB(Task Control Block): TCPの内部状態を管理・制御する構造体

14 of 24

TCPの状態遷移

14

  • Active open
    • Client側のコネクション確立処理
  • Passive open
    • Server側のコネクション確立処理
  • Active close
    • Client側のコネクション終了処理
  • Passive close
    • Server側のコネクション終了処理

15 of 24

コネクションの確立

Server(passive open)

Client(active open)

15

Listen()

Listen

Accept()

SYN_RECVD

ESTABLISHED

getConnection()

Conn{}

Dial()

SYN_SENT

ESTABLISHED

getConnection()

Conn{}

SYN

SYN|ACK

ACK

16 of 24

コネクションの確立(Client)

16

17 of 24

コネクションの確立(Server)

17

18 of 24

セグメントの処理

18

19 of 24

再送処理

19

20 of 24

コネクションの終了

  • ハーフクローズ
    • コネクションを片方づつ終了する
    • 通常クライアントからクローズ(active close)
    • active close側は最後のAckのあと一定時間

待ってAckの伝達を確認する

    • passive close側は最後のAckが来なければ

Finを再送する

20

Client(active close)

Server(passive close)

FIN|ACK

ACK

ACK

FIN|ACK

wait

21 of 24

コネクションの終了

Server(passive close)

Client(active close)

21

ESTABLISHED

handle()

passiveClose()

CLOSE_WAIT

LAST_ACK

CLOSED

FIN_WAIT1

activeClose()

FIN_WAIT2

TIME_WAIT

CLOSED

※別ルートの遷移もあり

22 of 24

GotcpのTCPクライアントのコード

22

  • TcpInit()で各プロトコルの初期化とパケットハンドリング用goroutineを起動
  • インターフェースはできるだけ標準パッケージに寄せて実装

23 of 24

はまりどころ

  • チェックサム計算
    • TCPパケットだけでなくアドレスなども含めたバイト列として計算しなければならない
  • RFC793の仕様の現実の実装が異なる箇所がある
    • PSHフラグ
    • Readの挙動
    • 当初のTCPの仕様では能動的にアプリケーションにデータを渡すようになっている
    • 現在のsocket APIはアプリケーションが任意のタイミングでデータを取りに来る(TCPは能動的にデータを渡せない)
  • seq/ack番号の計算のタイミングや整合性

23

もう少し詳しいハマりどころの説明→自作TCP/IPデバッグの辛み

24 of 24

まとめ

  • TCPの仕組みを理解することができた
  • Go言語を使用することでロジックに集中して実装できた
  • EthernetからTCPまで全部で約4000行程度の分量
  • Window制御は未実装

  • 実際にパケットのやり取りができてwiresharkできれいにストリームが見えると楽しい

FIN|ACK 24