こちらのサイト と『マスタリングTCP/IP』を参考にゆるふわなまとめ.

OSI参照モデルとカプセル化

/ レイヤー データ形式
7 アプリケーション X
6 プレゼンテーション X
5 セッション X
4 トランスポート セグメント
3 ネットワーク パケット
2 データリンク フレーム
1 物理 ビット

LANの機器

NIC(Network Interface Card)がPCとネットワークの窓口.

レイヤー 機器 説明
ネットワーク層 ルーター 送信先までの経路を決定
データリンク層 ブリッジ
スイッチングハブ
近くの機器との伝送制御
物理層 リピーター
ハブ
A/D伝送,モデム

物理層

同軸ケーブルはご存じの通り.銅線を絶縁体で覆ったもの.

ツイストペアケーブルは8本の銅線を2x4でより合わせている.そうすることで磁気の干渉をキャンセルしている.

その他光ファイバなど.

データリンク層

メディアアクセス制御(MAC)を行う.具体的には,通信方式(TMDA,CMDA)に応じた誤り検出の情報などをパケットに付加してフレームをつくる.

MACアドレスはNICに各ベンダーが製造時に割り当てた固有のIDである.MACアドレスは48bit,つまり16進数で12bitである.表記するときは2x6で区切るので,dc:fb:48:14:0d:16のように表記する.

ここではLAN上のイーサネット内部で完結する通信に話を限定する.イーサネット上の情報はそのバス内すべてに伝達される仕組みになっている.つまりブロードキャストされる.しかし同軸ケーブルはベースバンド方式であるため,あるノードがブロードキャストしているときに別のノードがブロードキャストしてしまうと,信号がケーブル上で混合されてしまう.これを防ぐためにCSMA/CD(Career Sense Multiple Access Collision Detection)という制御を行う.

キャリア検知では,初めにイーサネット上に他の信号が来ていないことを確認する.そして一定時間待ったあと自分の信号を送り始める.フレームを多数送らないといけないノードは,一つ一つのフレームを一定時間待ちながら送ることになる.送信中に別のノードからのフレームを受信してしまったら,自分の送信したフレームと干渉してしまっているので,いったんフレームの送信を中止する.

イーサネット内での通信はブロードキャストされるので,当然XがYに送ったフレームはZにも届いてしまう.フレームには送信先のMACアドレスが記されているので,Zは自分宛てに届いたものなのかを確認することができる.

ネットワーク層

ネットワーク層では,パケットに送信元と受信先のIPアドレスをヘッダーに付加する.またpingで出てくるTTLも付加される.

IPv4ではIPアドレスは32bitである.IPv6では64bitである.

IPv4

RFC791での定義 による.

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Version|  IHL  |Type of Service|          Total Length         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|         Identification        |Flags|      Fragment Offset    |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|  Time to Live |    Protocol   |         Header Checksum       |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                       Source Address                          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                    Destination Address                        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                    Options                    |    Padding    |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

そのパケットの合計サイズ,TTL,送信先・元のIPアドレスが付加される.

クラスフルアドレッシング

32bitのIPアドレスのうち,何ビットをネットワーク番号とホスト番号に使うかによってクラスが定められている.


クラス 1stオクテット 2ndオクテット 3rdオクテット 4thオクテット
クラスA ネットワーク番号 ホスト番号 ホスト番号 ホスト番号
クラスB ネットワーク番号 ネットワーク番号 ホスト番号 ホスト番号
クラスC ネットワーク番号 ネットワーク番号 ネットワーク番号 ホスト番号

受信側は,第一オクテットを見ることによりそのアドレスはどのクラスに属するのかを識別する.それぞれのクラスではIPアドレスは以下のようになる.


クラス 1stオクテット IPアドレス
A 0xxxxxxx 0.host.host.host - 127.host.host.host
B 10xxxxxx 128.network.host.host - 191.network.host.host
C 110xxxxx 192.network.network.host - 223.network.network.host

予約済みアドレス

ホスト番号が00000000または11111111になるもの.あるネットワークを外から見たとき,そのネットワーク自体を大雑把に指定する際に利用する.ブロードキャストアドレスと呼ばれる.

プライベートIPアドレス

最近はNATが普通に使われているからよく見るIPアドレスだと思う.


1stオクテット 2ndオクテット 3rdオクテット 4thオクテット 表記
10(00001010) x x x 10/8
172(10101100) 16 - 31 x x 172.16/12
192(11000000) 168 x x 192.168/16

サブネットワーク

クラスAのネットワークの管理をしているとすると,その管理者は1700万のIPアドレスを自由に割り当てることができる.しかしそれらを1から割り当てることはしない.住所のように「階層」をつくるものである.そこでホスト番号の先頭にサブネット番号と呼ばれるものを付ける.

ホスト番号の先頭を利用する.けどどこまでがサブネットワークを示しているのか分かるのだろうか?

この疑問に答えるのがサブネットマスクである.これはネットワーク部 + サブネットワーク部までのビットを1で,残りを0で埋めた32bit列である.

$ ifconfig
enp0s3: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 10.0.2.15  netmask 255.255.255.0  broadcast 10.0.2.255
        inet6 fe80::53ca:a4fe:732d:1e18  prefixlen 64  scopeid 0x20<link>
        ether 08:00:27:a4:cf:a8  txqueuelen 1000
        RX packets 29394  bytes 6371743 (6.3 MB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 19539  bytes 6732159 (6.7 MB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

上は仮想マシン上でifconfigコマンドの出力を見たものである.これより

  • IPアドレスは10.0.2.15つまりタイプ1のプライベートアドレスである
  • よってネットワーク部は1stカルテットである(10)
  • 一方サブネットマスクが3rdカルテットまで255(b'1)で埋められている
  • すなわちサブネットワーク部は2nd,3rdカルテットの0.2である
  • そして純粋なホスト部は15である

ことが分かる.

ネットワーク サブネットワーク サブネットワーク ホスト 表記
10. 0. 2. 15 10.0.2/24

もう一つ例題.172.20.100.52/26の場合,52 = 00110100の上位2bit00までがネットワーク部である.よって172.20.100.0-63が取りうるアドレスの範囲である.したがって

  • IPアドレスは        172.20.100.52/26
  • ネットワークアドレスは   172.20.100.0 /26
  • ブロードキャストアドレスは 172.20.100.63/26

である.

DHCP

ここまでで,パケットからフレームを作るときに

  • 送信元IPアドレス
  • 送信先IPアドレス
  • 送信元MACアドレス
  • 送信先MACアドレス

の4つが必要であることが分かる.MACアドレスは初めから各機器に固有であるが,IPアドレスは属するネットワークによって異なる.そのため愚直にこれらを揃えるには利用者がstaticにIPアドレスを決め打ちする必要がある.しかしそれでは拡張性などが損なわれるので動的にIPアドレスを取得するためのプロトコルとしてDHCPというものが用いられている.これはサーバーに「IPアドレスが欲しい」という要求を送信元IPアドレスがない状態で送るためのプロトコルである.

DHCPクライアントとサーバー

DHCPサーバーはあらかじめ割り当てるIPアドレスの範囲のプールを持っている.また割り当てるIPアドレスのリース期限も定めている.

クライアントがLAN内においてDHCPメッセージをブロードキャストすることで,サーバーがそれに応答して以下のようにやり取りを行う.

Client <-> Server 送信先MAC 送信元MAC 送信先IP 送信元IP DHCPメッセージ
Client
-> Server
とりあえず探す
ffff.ffff.ffff
ブロードキャスト
Clientの
MAC
255.255.
255.255
ブロードキャスト
0.0.0.0
割り当てられていない
DISCOVER
Clnt IP: 0.0.0.0
Srv IP: 0.0.0.0
Server
-> Client
候補を与える
ffff.ffff.ffff
ブロードキャスト
Serverの
MAC
255.255.
255.255
ブロードキャスト
192.168.1.100 OFFER
Clnt IP: 192.168.1.10
Srv IP: 192.168.1.100
Client
-> Server
その候補でOK!
ffff.ffff.ffff
ブロードキャスト
Clientの
MAC
255.255.
255.255
ブロードキャスト
0.0.0.0
割り当てられていない
REQUEST
192.168.1.10を要求!
Clnt IP: 0.0.0.0
Srv IP: 192.168.1.100
Server
-> Client
確定
ffff.ffff.ffff
ブロードキャスト
Serverの
MAC
255.255.
255.255
ブロードキャスト
192.168.1.100 ACK
サブネット情報など
Clnt IP: 192.168.1.10
Srv IP: 192.168.1.100

ARP

Webブラウザでhttps://google.co.jpをリクエストするとDNSサーバーがそのIPアドレスを教えてくれる.しかしその時点でまだGoogleのサーバーのMACアドレスは不明だからIP通信は成立しない.そこでARPという仕組みを用いてGoogleのサーバーのMACアドレスを知ることができる(<-説明の便宜上こう書いているが,実は嘘.デフォルトゲートウェイのMACアドレスを使う.わざわざGoogleのサーバーのMACアドレスを調べはしない).

まず(普通はGoogleのMACアドレスを初めから知っていることはないけどルールとして)クライアント自身が持つARPテーブルを参照する.

$ arp -a
_gateway (10.0.2.2) at 52:54:00:12:35:02 [ether] on enp0s3

(当然)ここにはGoogleのサーバーのIPアドレスはないので探す必要がある.

ARP要求

/ ARPパケット内 ARPパケット内 ARPパケット内 ARPパケット内 /
Client <-> Server 送信先MAC 送信元MAC 送信先IP 送信元IP 動作
Client
-> Server
0.0.0.0
まだ不明
ClientのMAC DNSから得たIP addr ClientのIP addr REQUEST
ブロードキャスト
して調べる
Server
-> Client
ClientのMAC ServerのMAC ClientのIP addr ServerのIP addr リクエストされた送信先IP
を持つサーバーが応答

ARP要求をやり取りした後,お互いにARPテーブルを更新してお互いを登録する.なおGoogleのサーバーのMACアドレスを知ろうなんて愚直なことは実際にはやらない.そもそもブロードキャストメッセージはLANの外にまで出ていかない(そんなことしたらトラヒックがひっ迫する).

DNSと通信の流れ

コンピュータにはNICが取り付けられているので,それで送信元MACアドレスが決定する.次にLANに接続するとDHCPによりLANでのIPアドレスが決定する.次にDNSによりアクセスしたいFQDNのIPアドレスが分かり,ARPによりそのFQDNのサーバーのMACアドレスが分かる.これにより必要なアドレスが全て判明したので通信を行うことができる.

ルーティング

デフォルトゲートウェイ

ルーターの重要な役割は以下の2つである.

  • ブロードキャストを外に出さないこと
  • 他ネットワークへの転送

1つ目は2つ目ほど目立たないが重要である.これによりネットワークがブロードキャストメッセージによりひっ迫するのを防ぐ.基本的にブロードキャストされているメッセージはそのLANの内部にしか流されず,他のネットワークには転送されない.そのためARP要求をネットワーク外のWebサーバーにまで届けることができない.

そこでクライアントからのARP要求に対して,デフォルトゲートウェイはそのルーター自身のMACアドレスを返答する.つまりクライアントはARP応答で受け取ったMACアドレスを,Webサーバーのものと勘違いしている状態である.これでARPは完了したことにして,パケットの送信を始める.

以降クライアントがpublishするパケットにおける送信先MACアドレスは,デフォルトゲートウェイのMACアドレスになる.そのパケットは以下のようにしてWebサーバーにまで届けられるとしよう.

(ホスト) ===> (ルーターA) ===> (ルーターB) ===> (Webサーバー)

最終的にパケットがWebサーバーに届くまでに,いくつかのルーターの間のLANを経由する.ルーティングは経路表を見て行われ,ルーターAからルーターBへとルーティングが行われる.その時,パケットの送信元/送信先MACアドレスは以下のように書き換えられる.

/ 送信元MAC 送信先MAC
通過前 ホストのMAC ルーターAのMAC
通過後 ルーターAのMAC ルーターBのMAC

ルーターAがルーターBのMACアドレスを知るには,ARP要求を出す,またはARPテーブルを参照すればよい.これがWebサーバーに至るまで繰り返され,最終的に送信が完了する.

ルーティングテーブル

ルーティングテーブルには送信先ネットワークまでの距離と,そのネットワークへパケットを転送する際に利用するポート番号が記されている.ルーターはパケットが届いてきたらそれに応じて転送を行う(MACアドレスを書き換えながら).

以下はWindows上のWSLにおいてrouteコマンド(経路表を表示する)を実行した結果である.

受信先サイト    ゲートウェイ    ネットマスク   フラグ Metric Ref 使用数 インタフェース
127.0.0.0       0.0.0.0         255.0.0.0       U     256    0        0 lo
127.0.0.1       0.0.0.0         255.255.255.255 U     256    0        0 lo
127.255.255.255 0.0.0.0         255.255.255.255 U     256    0        0 lo
224.0.0.0       0.0.0.0         240.0.0.0       U     256    0        0 lo
255.255.255.255 0.0.0.0         255.255.255.255 U     256    0        0 lo
255.255.255.255 0.0.0.0         255.255.255.255 U     0      0        0 eth2
224.0.0.0       0.0.0.0         240.0.0.0       U     0      0        0 eth2
0.0.0.0         buffalo.setup   255.255.255.255 U     0      0        0 eth2
192.168.11.255  0.0.0.0         255.255.255.255 U     0      0        0 eth2
192.168.11.0    0.0.0.0         255.255.255.0   U     0      0        0 eth2
192.168.11.6    0.0.0.0         255.255.255.255 U     0      0        0 eth2

192.168.11.0がネットワークアドレスである.順番に見てみよう.

  • 255.255.255.255はDHCPで使うようなブロードキャストアドレスである
  • 192.168.11.255はそのネットワーク内におけるブロードキャストに利用する
  • 0.0.0.0/0はすべてのネットワークに用いる.これはデフォルトルートと呼ばれ,0.0.0.0/0の代わりにdefaultと表記されることもある(IPアドレスを指してはいないため)

ルーターはパケットが届いたらその送信先IPアドレスを調べ,それに最も一致するネットワークアドレスを持つルーターに転送する.例えば172.20.100.52172.20/16172.20.100/24の両方にマッチするが,この場合より一致する部分が長い172.20.100/24を選択する.

ルーティングプロトコル

静的ルーティングでは管理者が決め打ちでルーティングを決める.動的ルーティングはルーティングプロトコルに従ってルーター間で決定される.ルーティングテーブルは適用される範囲によって2種類に分けられる.AS内部で用いられる or AS間で用いられるである.

ASとは ネットワークにおける自律システム(AS)とはICANNが管理するネットワークのグループ.日本ではJPNICが管理しており,ここで で一覧を見ることができるようだ

以下のプロトコルが有名.

AS内部(IGP) AS間(EGP)
RIP : ディスタンスベクタ
OSPF : リンクステート
BGP

ルーティングを決定する際にはルーター間の距離を基準に最短経路を求める.その距離関数をメトリックといい,プロトコルによって異なる.RIPではルーター間のホップ数を用いるが,OSPFでは回線速度を用いる.

RIP

下のようなネットワークを例にしてみる.

RIPの例

初めのルーティングテーブルは各ルーターが直接接続されているネットワークのみ登録されている.

ルーター 送信先 NextHop Metric Port
A 192.168.2/24 0 P1
192.168.1/24 0 P2
192.168.10/24 0 P3
B 192.168.1/24 0 P1
192.168.100/24 0 P2
C 192.168.2/24 0 P1
192.168.200/24 0 P2

ルーティングテーブルは連想配列のようなものだと思えばいい.まず初めにAからテーブルの情報を送ると,BCは自身のテーブルに含まれていないエントリーを,メトリックをインクリメントして追加する.すでに含まれているエントリーについては更新しない.

ルーター 送信先 NextHop Metric Port
B 192.168.1/24 0 P1
192.168.100/24 0 P2
192.168.10/24 A 1 P1
192.168.2/24 A 1 P1
C 192.168.2/24 0 P1
192.168.200/24 0 P2
192.168.1/24 1 P1
192.168.10/24 1 P1

さらにBとCからもAに送信する.

ルーター 送信先 NextHop Metric Port
A 192.168.2/24 0 P1
192.168.1/24 0 P2
192.168.10/24 0 P3
192.168.100/24 B 1 P2
192.168.200/24 C 1 P1

再度Aから送信する.

ルーター 送信先 NextHop Metric Port
B 192.168.1/24 0 P1
192.168.100/24 0 P2
192.168.10/24 A 1 P1
192.168.2/24 A 1 P1
192.168.200/24 A 2 P1
C 192.168.2/24 0 P1
192.168.200/24 0 P2
192.168.1/24 A 1 P1
192.168.10/24 A 1 P1
192.168.100/24 A 2 P2

ルーティングテーブルが最適な状態に達した後にネットワークのトポロジーが変化すると,アップデート情報を受け取ったルーターは,それによりメトリックが低下するネットワークについては更新を行う.以下を参照.

link

ルーティングループ

ディスタンスベクタ型のルーティングプロトコルでは起こりうる問題である.

ルーティングループ

先ほどの状態で,上の図のようにルーターBとルーターCが接続されているとしよう.そして192.168.200/24において障害が起きたとしよう.まずルーターCでは192.168.200/24へのエントリーは削除されるので,以下の状態からスタート.

ルーター 送信先 NextHop Metric Port
B 192.168.1/24 0 P1
192.168.100/24 0 P2
192.168.10/24 A 1 P1
192.168.2/24 A 1 P1
192.168.200/24 C 1 P3
C 192.168.2/24 0 P1
~192.168.200/24~ ~0~ ~P2~
192.168.1/24 A 1 P1
192.168.10/24 A 1 P1
192.168.100/24 B 1 P3

ルーターBは192.168.200/24へのルートがあると勘違いしていることに注意.そしてBとCが同時に更新情報を送ったとしよう.この時各ルーターがとる行動は

  • BがCから受け取るテーブルには192.168.200/24へのエントリーがないので,Bでも削除
  • CがBから受け取るテーブルには192.168.200/24という未知のネットワークへのエントリーがあるので,メトリックをインクリメントして追加

である.よって以下のようなテーブルに更新されてしまう.

ルーター 送信先 NextHop Metric Port
B 192.168.1/24 0 P1
192.168.100/24 0 P2
192.168.10/24 A 1 P1
192.168.2/24 A 1 P1
~192.168.200/24~ ~C~ ~1~ ~P3~
C 192.168.2/24 0 P1
192.168.1/24 A 1 P1
192.168.10/24 A 1 P1
192.168.100/24 A 1 P2
192.168.200/24 B 2 P3

再度交換し合うと,メトリックが加算され続けてしまう.

ルーター 送信先 NextHop Metric Port
B 192.168.1/24 0 P1
192.168.100/24 0 P2
192.168.10/24 A 1 P1
192.168.2/24 A 1 P1
192.168.200/24 C 3 P3
C 192.168.2/24 0 P1
192.168.1/24 A 1 P1
192.168.10/24 A 1 P1
192.168.100/24 A 1 P2
~192.168.200/24~ ~B~ ~2~ ~P3~

ルーターBが更新情報を送る前にCが192.168.200/24への接続障害を先にBに送ることができるのであれば,この問題は生じない.ディスタンスベクタ型のルーティングプロトコルではすべてのルーターが同じ情報を持つまで更新が収束しない.

これを防ぐ方法として,スプリットホライズンというものが知られている.スプリットホライズンを有効にすると,ルーターXから教えてもらったエントリー(NextHopがXになっているエントリー)はXに送る更新のメッセージから削除される.それにより送信するエントリー数を減らすことができるという仕組みだが,障害が発生している場合は無限カウントを防ぐことができる.上の例だと,ルーターBはNextHopがCになっている192.168.200/24のエントリーをCに再送はしない.

ICMP

ICMP(Internet Control Message Protocol)はネットワークの状態を管理するのに利用されるプロトコルである.ICMPメッセージはIPヘッダ+ICMPメッセージのみである.トランスポート層の情報は含まれない.

ICMPメッセージの中でも重要なのは以下の5つである.

タイプ 説明 意味 種類
0 Echo Reply Echo応答 Query
3 Destination
Unreachable
IPアドレスの
宛先到達不能
Error
5 Redirect 最適(ベターな)経路を通知 Error
8 Echo Request Echo要求 Query
11 Time Exceeded 時間超過による
パケット破棄
Error

パケットを受け取ったルーターが何かエラーが起きたり要求を受け取ると,ICMPメッセージを発行する.

  • Destination Unreachable: 宛先IPアドレスにパケットを送ることができない
  • Redirect: ホストがyyy.yyy.yyy.yyyへのパケットを送ろうとしており,デフォルトゲートウェイであるルーターAにパケットを送る.しかしルーターAを経由してからさらにルーターB(ホストのLAN内にある)を経由した方がメトリックが低下することが分かったとしよう.するとルーターAは「yyy.yyy.yyy.yyyへのデフォルトゲートウェイをルーターBに設定するよう経路表の変更を行う」旨のICMPメッセージをホストに送信する.
  • 宛先のホストYのIPアドレスを指定してホストXからRequestを送る.それが届いたらホストYはホストX宛てにReplyを送る.pingではひたすらRequestを送るコマンドであり,TTLやDestination Unreachableを知ることができる.

pingはレイヤー3のプロトコルである.もしpingが上手くいくのに繋がらないという事態が発生した場合,それはレイヤー4以上で何らかのエラーが発生しているということである.

TTLは通過できるゲートウェイの個数である.ゲートウェイを通過するたびにデクリメントされる.あるルーターでゼロになるとそのパケットは破棄される.パケットを廃棄したルーターは送信元にICMP::11"Time Exceeded"を送信する.

TCP

TCPヘッダ

TCPヘッダーは以下のようになる(RFC793より ).

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|          Source Port          |       Destination Port        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                        Sequence Number                        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                    Acknowledgment Number                      |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|  Data |           |U|A|P|R|S|F|                               |
| Offset| Reserved  |R|C|S|S|Y|I|            Window             |
|       |           |G|K|H|T|N|N|                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|           Checksum            |         Urgent Pointer        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                    Options                    |    Padding    |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                             data                              |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

サイズは4バイトx5=20バイトである.

制御番号のうちACKSYNFINはよく用いられる.TCPにおけるアプリケーション間のコネクションの確立方法はthree-way handshakeと呼ばれる.ホストXからホストYに向けてコネクションの確立を行う際は以下の手順をとる.

X <-> Y 説明 ACK SYN
X -> Y Yへ接続要求 0 1
@Y 受け取る / /
Y -> X XへとACK
&
Xへ接続要求
1 1
@X 受け取る / /
X -> Y Yに応答する 1 0

シーケンス番号

シーケンス番号はデータ本体が初めから数えて何バイト目であるかを示す番号である.

TCPでは送る一つ一つのパケットにシーケンス番号をつける.一つのパケットが何バイトであるかはMTU,そのうちデータ部が何バイトであるかはMSSとして定められている.サーバーは次に何バイト目のデータを要求するかを確認応答で提示する.

あるパケットのサイズを1020バイトとしよう.つまりデータ本体は1000バイトである.そのシーケンス番号が1であった場合,そのパケットを受け取ったサーバーは確認応答(ACK)により1001を要求する.クライアントは同様にシーケンス番号1001から初めて1020バイトのデータを送る,というのを繰り返す.

再送タイムアウト時間

基本的にサーバーは「パケットを送る」「ACKを待つ」「次のパケットを送る」の繰り返しを行うが,途中でACKが消失してしまうこともあり得る.そのため再送タイムアウト時間だけ待ってもACKがこない場合はもう一度そのパケットを送りなおす.TCPではクライアントはパケットを送るたびに,(そのパケットに対応する)確認応答が来るまでの時間RTT(Round Trip Time)を測定している.RTTの分散(ジッタ)を加算して再送タイムアウト時間を更新する.

MSSの決定

TCPではパケットにおけるデータの最大サイズ(バイト)をMSSという.スリーウェイハンドシェイクを行う際に,サーバーとクライアントはTCPヘッダに自分のインターフェース(FDDIなのかEtherなのか)の最大MSSをセットして送る.双方のうち小さいほうをMSSとして採用する.

ウィンドウ制御

一つのパケットを送ってACKを待ち,またパケットを送るというのは効率が悪い.そこで一度に送るパケットの個数を多重化するのがウィンドウ制御である.

Sliding Window

ACKを待たずに送信できるデータのサイズをウィンドウサイズという.上の図の場合ウィンドウサイズは4セグメントとなっている.

ウィンドウ制御と再送制御

シーケンス番号が

  • 1-1000
  • 1001-2000
  • 2001-3000
  • ~3001-4000~
  • 4001-5000 のデータがサーバーに届いたとする.そのときサーバーは4001-5000のデータが届いた時も(まだ3001への要求が応えられていないため)3001に対するACKを返答する.そのため3001-4000が届くまでACKでは3001を要求し続ける.以下の図は途中で消失したパケットに対してACKが何度も送られる様子である.

Retransmission

図のようにまだ受け取っていないパケット(シーケンス番号)に対するACKが複数届くことを重複確認応答という.3つの重複確認応答が確認されるとクライアントはそのパケットを再送する.one-by-oneで送ってタイムアウト処理を行うよりはこの方がまだ高速である.

  • 1-1000
  • 1001-2000
  • 2001-3000
  • 3001-4000
  • 4001-5000

のパケットを送ったときにACKは4001-5000の分しか届かなかったとしよう.しかしそれだけでも4000までのパケットが届いていることは確認できている.ウィンドウ制御によりACKが必ずしも全て届く必要がないことが分かる.

フロー制御

送信側が勝手に決めたMSSでデータを送りつけると受信側のバッファーがパンクしてしまう可能性がある.TCPのヘッダーにはウィンドウサイズのフィールドがあるため,受信ホストは自分の受信能力に応じてこの値を設定してウィンドウ更新通知により送信ホストに伝える.これによりMSSはadaptiveに変化する.

ふくそう制御

いくらウィンドウ制御によって多重化してパケットを送ることができるとしても,回線のリンク速度や混雑具合によってはネットワークをひっ迫させる可能性がある.そこでTCPではスロースタートと呼ばれるアルゴリズムによってデータの送信量を調節する.

まず初めにウィンドウサイズを初期値(1,2,3などが多そう,リンクによる)に設定し,ACKが来るたびにインクリメントする.ウィンドウサイズがf(n)のときkf(n)個のACKが返ってくるとすると(0 < k < 1)

f(n+1) = f(n) + k * f(n)

よりふくそうウィンドウは指数関数的に増加することが分かる.実際に送るデータサイズには,送信側のウィンドウサイズと受信側の(ウィンドウ更新通知で知らせた)ウィンドウサイズのうち小さいほうを採用する.

ただ指数関数的に増加し続けるのは防がねばならないため,スロースタート閾値(SST,Slow Start Threshold)が定められている.そしてウィンドウサイズf(n)がSSTを超えたらACKが来るたびに以下の値だけインクリメントする.

MSS * MSS
---------
  f(n)

f(n)が増えるほどACKの数も増えるので上の式によるインクリメントもより多く行われるようになるが,一回一回のインクリメント量は小さくなる.結果的にウィンドウサイズは直線的に増えることになる.

SSTの決め方

以下ではウィンドウサイズをcwnd(n)(Congetion Window Size)と表記する.

Congestion Window Control

初めはSSTは定義されていない.そしてcwnd(n)を指数関数的に増加させていく.そのうちタイムアウトが起きるので,その時は以下のように更新する.

SST <- cwnd(n)/2
cwnd(n+1) <- 1

次からはSSTを超えた後はcwnd(n)を「少しづつインクリメント」するようになる.それでもいずれタイムアウトまたは重複確認応答がくることになる.もしタイムアウトが起きた場合

SST <- cwnd(n)/2
cwnd(n+1) <- 1

とするが,もし重複確認応答がきた場合は以下のようにcwnd(n)を更新する.

SST <- cwnd(n)/2
cwnd(n+1) <- cwnd(n)/2 + 3

タイムアウトの場合と異なり,この場合は少なくともACKが3回届くだけの帯域幅は確保されているためこのような処理を行う.

DNS

TLDとゾーン

ドメイン名の一番後ろをTLD(Top Level Domain)という..com.org.netなどトップレベルドメインが3文字以上になっているのはgTLDと呼ばれるドメインで,もともとはアメリカ国内で使われていた.これは.jpとか.nlと組み合わせる必要がないドメインである.

一つ一つのドメインに対してそのネームサーバーが存在する.各ネームサーバーは自身とその子ノードのIPアドレスを知っている.この範囲をゾーンという.

リソースレコード

ネームサーバーが各ノードについて有している情報をリソースレコードという.例えばgithub.ioにあるsoblinのリソースレコードは以下のようになる感じ.

所有者 名前 タイプ クラス TTL RD長 RDATA
github.io soblin 0001 0001 86400 4 xxx.xxx.xxx.xxx

このようにしてsoblin.github.ioのIPアドレスを教えている.タイプは以下のものが有名である.

タイプ 1 2 5 6 12 13
名前 A NS CNAME SOA PTR MX
意味 ホストの
IPアドレス
ネームサーバー ホスト
エイリアス
ゾーンの
情報
ポインタ
(逆引き)
メールサーバー

Aレコードは上に示したsoblin.github.ioに対するIPアドレスを直接記述しているフィールドである.

NSレコード

NSレコードはネームサーバーのIPアドレスを伝えるフィールドである.soblin.netlify.comにアクセスする際は

  • .comのネームサーバーにアクセスしてnetlifyのネームサーバーのアドレスを知る
  • .netlify.comにアクセスしてsoblinのIPアドレスを知る

ことになる.そのためnetlify.comに対応する部分のネームサーバーのIPアドレスを知る必要がある.以下のように(適当)NSレコードを記述する.

名前 タイプ クラス RDATA
github.io NS IN ns.github.io
soblin NS IN ns.soblin.github.io

そしてns.github.ioなどのAレコードも合わせて記述する.

名前 タイプ クラス RDATA
ns.github.io A IN xxx.xxx.
xxx.xxx
ns.soblin.github.io A IN xxx.xxx.
xxx.xxx

その他

CNAMEはCanonical Nameの略称.ドメイン名にエイリアスを付ける.

SOAはDNSのキャッシュに使われる.

PTRはIPアドレスにドメイン名を対応付ける.逆引きに使われる.

MXはメールサーバーを教える.

名前 タイプ クラス RDATA
soblin.com MX IN NN mail.soblin.com
mail A IN xxx.xxx.
xxx.xxx

NNには優先値を示す数値が入る.

DNSレゾルバー

DNSのやり取りはUDPを介して行われる(ポート番号53).DNSサーバーにはフルサービスサーバーコンテンツサーバー(権威サーバー)の2種類が存在する.DNSサーバーに問い合わせるクライアント(スタブレゾルバー)は初めにフルサービスレゾルバーに再帰問合わせを行う.もしフルサービスレゾルバーがそのクエリをキャッシュしていなければ,次にフルサービスレゾルバーは権威サーバーに反復問合わせを行う.

DNS Resolver

プロバイダーのネームサーバーはフルサービスレゾルバーにあたる.ブログやサイトを公開しているサーバーがコンテンツサーバーにあたる.コンテンツサーバーはフルサービスレゾルバーから得たレコード情報をキャッシュするのでキャッシュサーバーとも呼ばれる.

家にあるようなブロードバンドルーターが反復問合わせを行うのは非効率的である.そのようなルーター内部のDNSサーバーはスレーブサーバーとして外部にあるフォワーダーと呼ばれるDNSサーバーに名前解決を依頼する.

DNSメッセージ

RFC1035 によりDNSメッセージが定義されている.

                                1  1  1  1  1  1
  0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|                      ID                       |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|QR|   Opcode  |AA|TC|RD|RA|   Z    |   RCODE   |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|                    QDCOUNT                    |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|                    ANCOUNT                    |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|                    NSCOUNT                    |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|                    ARCOUNT                    |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

フラグには以下8通りである.

bit Query Value
1 QR
問合わせ・応答
1 : ネームサーバーからの応答
0 : 問合わせ
4 OPcode
オペコード
0 : 正引き
1 : 逆引き
2 : サーバー状態要求
1 AA
オーソリティー応答
1 : 「当たり」のコンテンツサーバーからの応答
その他
1 TX
切り捨て
/
1 RD
再帰要望
1 : レゾルバー <-> フルサービスレゾルバー
0 : その他
1 RA
再帰有効
1 : 再帰応答可能
0 : 不可能
3 予約 /
4 Rcode
戻りコード
0 : 応答成功

HTTP

リクエスト開始行

HTTPはクライアント・サーバーモデルであり,クライアントはユーザーエージェント(UA),サーバーはWebサーバー(apache,nginxなど)である.HTTPのメッセージの形式は以下の通りである.

開始行 + メッセージヘッダー(+CRLF) + (CRLF + ) メッセージ本体

メッセージヘッダーの終わりとメッセージ本体の直前にCRLF(\r\n)がある.開始行はGET soblin.netlify.com HTTP/1.0のような感じである.

メソッド + リクエストURI + HTTPバージョン

GETの部分は一般的にメソッドと呼ばれ,以下の3つがよく使われる.

  • GET
  • HEAD
  • POST

HEADはヘッダーのみ要求する.GETは(html)ファイル名を要求する(データはないよ).POSTはCGIに送るデータなども加えて送る.

レスポンス開始行

レスポンスも同様に以下のような形式である.

開始行 + メッセージヘッダー(+CRLF) + (CRLF + ) メッセージ本体

レスポンスの開始行は以下の形式である.

HTTPバージョン + ステータスコード + 応答フレーズ

ステータスコードと応答フレーズは以下が有名.

ステータスコード 応答フレーズ 説明
200 OK OK
301 Moved Permanently リクエストURIは別のURIに割り当てられている
400 Bad Request HTTPの書式が間違っている
403 Forbidden リクエストは受け取ったが実行できない
404 Not Found リクエストURIは存在しない
500 Internal Server Error サーバーがリクエストの処理でエラー状態

メッセージヘッダー

メッセージヘッダーは以下の形式である.

Field: val
Field: val

よく使われるのは以下の通り.

ヘッダー 説明
Host 宛先のドメイン名
User-Agent ブラウザ情報など
Referer ハイパーリンクする前のページ
Accept 取り扱い可能なファイル拡張子
Accept/Content
Encoding
圧縮方式
Last-Modified 応答の最終更新日

GET

GETは基本的にリクエストURIをサーバーに教えるのみである.特徴的なGETメソッドとしては条件付きGETが挙げられる.ユーザーエージェントが取得しているページの更新日時をIf-Modified-SinceにセットしてGETすることで,サーバーは「その日時以降に該当URIが更新されていたら」ページを送信して,もし更新されていなかったら304 Not Modifiedを返答する(ページは送信しない).

またURIがCGIである場合,GETにより取得されるページはそのプログラムではなくそれにより生成されたHTMLである.

POST

これはGETに加えて要求メッセージに何らかのデータを付加するメソッドである.卑近な例だと入力ボックスの入力データも付加してサーバーにリクエストを送ることで,レスポンスのHTMLファイルを動的に変化させることができる.

telnetでGETしてみる

仮想マシン上のUbuntuでApacheを動かしている.

$ telnet
telnet> open localhost 80
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
GET /index.html HTTP/1.0

HTTP/1.1 200 OK
Date: Wed, 22 Apr 2020 18:06:00 GMT
Server: Apache/2.4.29 (Ubuntu)
Last-Modified: Wed, 22 Apr 2020 14:31:24 GMT
Etags: "2d35-5a3e1fc963046"
Accept-Ranges: bytes
Content-Length: 11573
Vary: Accept-Encoding
Connection: close
Content-Type: text/html


<!DOCTYPE html>
<!--[if IE 8]><html class="no-js lt-ie9" lang="en" > <![endif]-->
<!--[if gt IE 8]><!--> <html class="no-js" lang="en" > <!--<![endif]-->
<head>
  <meta charset="utf-8">
    <script type="text/javascript">

セッションとCookie

OSI参照モデルで出てくるセッション層はプロセス間の通信である.HTTPでは一回のGETからレスポンスまでが一つのセッションであり,その間にデータ転送のコネクションが何度か行われる.しかし一度のセッションの情報はHTTPでは保存されない(セッションステートレス).そのため同一ホストから何度もリクエストがある場合にそれが「同一のホストから何度もリクエストされている」ことに気づけない.例えばECサイトではXさんが注文を追加してページを遷移するたびに「新しいユーザーが注文してきた」と勘違いしてしまうため,同一のユーザーの買い物かごに注文を追加するみたいなことができない.そこであるリクエストが来た時にサーバー側からクライアントにCookieと呼ばれるIDを渡して,以降それを携帯してセッションを行う.

  • 初めにクライアントからリクエストがあると,サーバーはSet-Cookieをメッセージヘッダーにセットして返答
  • 以降クライアントはCookieをその値にセットしてメッセージヘッダーを送信する

Cookieには以下の情報が保存される.

  • 名前(userid)
  • 有効期限(expire)
  • パス属性(path)
  • サーバードメイン名(domain)
  • その他

もしExpireが設定されているのであればそのCookieはセッションの終了後もファイルとして保存される(永続化される).設定されていないCookieはブラウザが閉じると消去されるためセッションCookieと呼ばれる.

ドメイン名とパスはやり取りをするサーバーを特定するためにセットされる(ドメイン名が同じでも各サブディレクトリは別々のサーバーによって管理されている可能性があるため).

プロキシサーバー

クライアントはWebサーバーに接続する代わりにプロキシーサーバーに接続する(というよりDNSで教えてもらったサーバーが実はプロキシーになっていて,本体のWebサーバーには直接接続しないようにするのでは).そしてプロキシーサーバーはWebサーバーに接続してレスポンスを取得し,クライアントに横流しする.

その時プロキシーはファイルをキャッシュするので,クライアントからのすべてのリクエストに対していちいちWebサーバーに問い合わせる必要がなくなる.その他,クライアントからの要求のうち一部をブロックするコンテンツフィルターの役割も果たすことがある.

認証

HTTPではベーシック認証とダイジェスト認証が利用できる.特定のWebサーバーにアクセスするときにサーバーのレスポンスヘッダーにWWW-Authorizeがセットされている場合,その値(Basic or Digest)に基づいてユーザー名とパスワードを求められるようになっている.クライアントはリクエストヘッダーにおいてAuthorizationをユーザー名とパスワードに設定して再度アクセスする.ベーシック認証ではユーザー名とパスワードはBASE64により暗号化されて送られる.しかしHTTPヘッダーにユーザー名とパスワードが(暗号化されていはいるものの)そのまま書いてあるようなものである(暗号化データから直接(他のパラメータなしに)解読される可能性がある).

そこでダイジェスト認証ではハッシュ関数を使う.まずサーバーはランダムな文字列を生成してそれをクライアントに送る.クライアントはその文字列とユーザー名とパスワードをハッシュ関数により一つのビット列に変換し,それをサーバーに送る(ランダムな文字列がある分解読しにくい,あと当然ハッシュ関数は共通だよ).サーバーはそのハッシュ値と,手元で計算したハッシュ値(ランダムな文字列と正解のユーザー名,パスワードから計算)と比較し,一致すれば認証成功とする.このときのハッシュ関数としてはMD5やSHA(いろいろバージョンがある)が知られている.実際SSL/TLSの方がセキュリティーは高いが.

メール

初期の電子メールでは送信者と受信者がTCPで直接コネクションを作りメールの送受信をお互いのハードディスク上で行っていた.それだと(特に)受信者が電源をOFFにしているときにメールを送信することができないので,メールサーバーにメールを送信するようになった.そして受信者はメールサーバーからPOP(IMAP)というプロトコルによってメールをフェッチする形式になった.下の図のようにメール本体をローカル(から送信|にフェッチ)するクライアントをMUA,転送を行うサーバーをMTAという.

MUA and MTU

MXレコード

MUAからxxx@yyy.com宛てのメールを受け取ったMTAは,DNSにその宛先を問い合わせる.具体的にはyyy.comのサーバーのMXレコードを問合わせ,そのAレコードと合わせて取得する.

Domain Record Addr
yyy.com MX mail.yyy.com
mail.yyy.com A xxx.xxx.
xxx.xxx

そしてその宛先のIPアドレスのサーバーにメールを転送する.

SMTPとPOP/IMAP

MTAがMUAからメールを受け取る,またはMTAへ転送するときに使うのがSMTPである.MUAがMTAにメールをフェッチする時に使われるのがPOP/IMAPである.

大雑把に言うとメールを送る(配信と転送)のに使うのがSMTP,受け取るの(受信,フェッチ)に使うのがPOP/IMAPである

メールの内容はMIMEと呼ばれる形式によってフォーマットされている.メタ情報を含んだヘッダ,空行を挟んでコンテンツが続く.

POPはメールの送受信をサーバーを経由する方法である.送信者はSMTPプロトコルによってPOPサーバーまでメールを送り,受信者はPOP(IMAP)プロトコルによってユーザーの認証などを行いながらメールをフェッチする.メールの管理は送信者側で行う.

IMAPではメールの管理をサーバー上で行うため既読や未読といったメールボックスの情報をクラウド上で管理してくれる.