【技術書まとめ】Linuxで動かしながら学ぶTCP/IP入門

docker環境構築はこちらを参考にしました
https://qiita.com/kobori_akira/items/cfde4179d899f5872562

## TCP/IPとは

- プロトコルは「決めごと」
- グローバルIPアドレスは一意
    - `8.8.8.8`はGoogleのDNSサーバ以外にない
- パケットが宅配便ならヘッダは送り状伝票
    - ![スクリーンショット 2021-12-28 16.45.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/282504/108894b1-2046-2799-618a-c51488656b7c.png)
        - SourceAddressが差出人、DestinationAddressが宛先、ProtocolにはPayloadとして格納されているプロトコル情報が入る（ICMPなど）
- IPアドレスはネットワークインターフェイスに付与される
    - `lo`に`127.0.0.1`など
- `127.0.0.1`はループバックアドレス
    - 自分自身を表すIPアドレス
- pingコマンドはICMPのエコーリクエストとエコーリプライというメッセージをやりとりする
- `traceroute`でパケットが通過したルータがわかる
    - ルータの台数はホップ数と呼ぶ
    - 仕組み
        - 意図的にTTLに小さな値を設定してパケットを送る
            - ルータごとにICMP時間切れメッセージが届く
                - TTLを1つずつ増やして送る
                    - ルータのIPアドレスを並べる
                        - 経路がわかる
                            - `* * *`は時間切れメッセージを送ってこないルータ
- IPアドレスを持ってネットに繋がっているルータでないコンピュータをホストと呼ぶ
    - ルータとホストを区別せずネットに繋がったコンピュータの総称をノードと呼ぶ
- バケツリレーの次の相手は`ip route show`で表示されるルーティングテーブルで管理されている
    - `via 10.0.2.2`など次のパケットを渡す相手はNext Hopと呼ぶ


## Network Namespaces

- ルータが必要になるのは違うセグメントの相手と通信したいときだけ
    - `192.0.2.1`と`192.0.2.2`は同じセグメント
- IPv4のIPアドレスは32ビット
    - 2進数だと`11000000000000000000001000000001`
        - 8ビットごとにすると`11000000.00000000.00000010.00000001`
            - 10進数にすると`192.0.2.1`
                - ネットワーク部は24ビット目だと
                - `192.0.2`まで、ホスト部は`.1`
                    - このネットワーク部がセグメントと呼ばれる
                    - `192.0.2.1/24`の`/24`が切る場所を示す
                        - この長さをプレフィクス長と呼ぶ
                        - サブネットマスクでは`255.255.255.0`となる
- 仮想ネットワークを作る
    - `sudo ip netns add ns1`でNework Namespaceを作る
    - `sudo ip link add ns1-veth0 type veth name ns2-veth0`でネットワークインターフェイスを作る
    - `ip link show | grep veth`で作られているか確認できる
    - `sudo ip link set ns1-veth0 netns ns1`でvethインターフェイスがNetwork Namespaceで使えるようになる
    - `sudo ip netns exec ns1 ip address add 192.0.2.1/24 dev ns1-veth0`でNetwork NamespaceのvethインターフェイスにIPアドレスを付与する
    - `sudo ip netns exec ns1 ip link show ns1-veth0 | grep state`でstateがDOWNになっているのを確認できる
    - `sudo ip netns exec ns1 ip link set ns1-veth0 up`でUPできる
    - `sudo ip netns exec ns1 ping -c 3 192.0.2.2`でpingの疎通が確認できる
    - `sudo ip netns exec ns1 ip route add default via 192.0.2.254`でデフォルトルートとなるルーティングエントリを追加できる
        - 手で行うのはスタティックルーティングと呼ばれる
    - `sudo ip netns exec router sysctl net.ipv4.ip_forward=1`でルータとして動作するように設定する



## イーサネット

- イーサネットの役割は「近隣までの荷物の配達」
    - IPのパケットが荷物の入った段ボールならイーサネットはそれを運ぶトラック
        - 段ボールの中の荷物はICMPメッセージなど
        - トラック一台分をフレームと呼ぶ
            - パケットは異なるフレームに積み替えられながら目的地まで運ばれていく
                - フレームの送信先と送信元をMACアドレスで表す
                    - 原則的にはそれぞれの機器ごとに一意とする
                        - `00:00:5E:00:53:01`など
                    - ブロードキャストアドレスは`ff:ff:ff:ff:ff:ff`
                        - 「このフレームが届く限りの範囲で、すべての機器に聞いてほしい」


## トランスポート層のプロトコル

- トランスポート層が解決すること
    - どのパケットかどのアプリケーションで利用されるかわからない
        - IPのヘッダだけではわからない
            - UDPヘッダのポートで区別する
                - マンションの部屋番号のようなもの
                    - 住人はTCP/IP通信のアプリケーション
    - パケットが途中で破棄される恐れがある
        - 配達する郵便局のヤギさんが勝手に食べてしまう
- UDP
    - ポート
        - `Source Port`と`Destination Port`
            - 通信を受けるならサーバとみなせる
                - 送信元はアプリケーションが通信するプロトコルによってポート番号が決まる
                    - DNSでは53
                - 送信先はサーバならクライアントのエフェメラルポートがヘッダに入っている
            - 自分から通信を始めるならクライアント
                - 送信元はOSが自動的にポート番号を割り当てる
                    - エフェメラルポートと呼ばれる
                - DNSなら53が送信先
    - データはペイロードとしてData部分に入る
    - 送ったら送りっぱなしのプロトコル
- TCP
    - データが相手までちゃんと届いたか確認しながら通信をする
        - コネクション型プロトコル
    - スリーウェイハンドシェイクでお互いに挨拶と握手を交わす
        - SYN（Synchronize sequence numbers)でシーケンス番号を同期する
            - どこまでちゃんと送れているか、順番が正しいかをシーケンス番号でチェックする
        - ACK（Acknowledgment field significant）でちゃんと受け取ったことを示す


## アプリケーション層のプロトコル

- TCP/IPではOSI参照モデルのセッション層から上3つの層をすべてアプリケーション層としてまとめている
- HTTP
    - （Hypertext Transfer Protocol）
    - TCPの80番ポートを使ってやりとりされるテキストベースのプロトコル
        - トランスポート層でncコマンドで通信するのと本質的に変わらない
- DNS
    - （Domain Name System）
    - UDPの53番ポートを使ってやりとりされるバイナリベースのプロトコル
    - ブラウザはDNSによって解決されたIPアドレスを使ってサーバに接続している
        - これを名前解決という
            - 人間はIPアドレスを覚えられないから意味を持った文字列を代わりに使う
        - `ping -c 3 localhost`が`127.0.0.1`に向けて打たれるのと同じ
        - ![スクリーンショット 2022-01-04 18.05.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/282504/4170c8e0-1a41-ff76-26cc-ca66b6d97824.png)
            - ドメイン名の解決はリゾルバが担当する
                - コンピュータ内の`hosts`や外部サーバに問い合わせる
                    - `hosts`には`localhost`など必要最低限のドメイン名しかない
                    - なければ加入しているISPや公開DNSなどのネームサーバに問い合わせる
                        - ネームサーバやフォワーダのIPアドレスは一般的なUnix系OSでは`/etc/resolv.conf`にある
- DHCP
    - （Dynamic Host Configuration Protocol）
    - TCP/IPネットワークを使うのに必要な設定作業を自動化するプロトコル
        - ネットワークインターフェイスにIPアドレスを付与する
        - デフォルトルートのルーティングエントリをルーティングテーブルに追加する
        - 名前解決に利用するネームサーバを指定するなど
    - デフォルトルートになるルータがDHCPサーバを兼任することが多い
        - 配布するIPアドレスの範囲やデフォルトルートの情報を指定しておく
            - `dnsmasq`を使って配布する


## NAT

- （Network Address Translation）
- 「IPアドレスを変換する」という概念
    - パケットヘッダにおいて、IPアドレスの入ったフィールドの値を書き換える
- 背景
    - IPv4は32ビット正の整数なので2の32乗の43億が総数となる
        - 足りなくなった
            - NATを使えば少数のグローバルアドレスを多数のプライベートアドレスで共有できる
                - 必要になるグローバルアドレスを減らせる
            - インターネットレジストリという組織が管理している
- プライベートアドレスとして予約されているIPアドレス
    - 10.0.0.0/8
    - 172.16.0.0/12
    - 192.168.0.0/16
- Source NAT（SNAT）
    - 単純にNATやNAPTとある場合はこれ
        - Source NAT の実装は`IPマスカレード`と呼ばれる
    - 送信元のプライベートアドレスをグローバルアドレスに書き換える
        - ルータが自身のインターフェイスに付与されているグローバルアドレスに書き換える
            - インターネットでもルーティングできるようになる
                - 「行き」のバケットはこれでいいが「戻り」も必要
                    - 戻りの分配はポートを含めてグローバルアドレスをプライベートアドレスに書き換えて記憶しておく（NAPT)
                        - 対応関係をセッションとよんで管理する
                            - ICMPのエコーリクエストとエコーリプライのヘッダにあるIdentifierフィールドをポートの代わりにする
    - `iptables`で設定する
- Destination NAT
    - 「ポートを空ける」という行為
        - Source NATではインターネットからLANに向けた一方的な通信はできない
            - プライベートIPアドレスだから
                - Destination NATを使う
                    - 送信先がルータのグローバルアドレスになっているパケットを、プライベートアドレスに書き換える
                        - 送信先のポートが特定の値になっているTCPやUDPのパケットだけ


## ソケットプログラミング

- HTTPクライアント
    - pythonでsocketをインポートして作る
        - `socket()`でどんな種類の通信をするか指定する
        - `connect()`で通信したいサーバとポートを指定して接続を開く
        - `send()/recv()`でバイト列を送受信する
        - `close()`接続を閉じる
- エコーサーバ
    - `bind()`でどのIPアドレスとポートを使ってクライアントからの接続を待ち受けるか指定する
    - `listen()`で待ち受ける
    - `accept()`で接続してきたクライアントを処理する
- バイナリベースのプロトコルを実装したサーバとクライアント
    - TCP/IPではバイトオーダーをビッグエンディアンに統一している
        - ネットワークバイトオーダーと呼ぶ
            - コンピュータ内部はホストバイトオーダーと呼ばれる
                - 変換が必要