【実務・中級編】 イーサネットフレームのMAC宛先アドレスとMAC送信元アドレス – インフラ・物理&L2/L3ネットワーク基礎実践ガイド

パケットの旅はここから始まる:L2レイヤーの主役「MACアドレス」とフレーム転送の深層

こんにちは。日夜、不可解なパケットロスやL2ループの波に立ち向かっているインフラエンジニアの皆さん、お疲れ様です。

Web APIの設計やクラウドでのコンテナネットワーク構築に夢中になっていると、私たちが普段何気なく使っているHTTPリクエストが、実際にはどのような「乗り物」に乗って物理世界を移動しているのかを忘れがちになりませんか?

「PythonのrequestsでAPIを叩いた」「curlでエンドポイントを疎通確認した」。これらはすべてOSI参照モデルの第7層(アプリケーション層)から第4層(トランスポート層)の出来事です。しかし、データセンターのスイッチやルーターの背後で、実際にビットを運んでいるのはL2(データリンク層)のイーサネットフレームに他なりません。

今回は、そのイーサネットフレームの最前線に陣取る「MAC宛先アドレス」と「MAC送信元アドレス」にスポットを当て、パケットがスイッチングハブをどう駆け抜けていくのか、そのリアルな挙動と実務で役立つ知見を徹底的に解説していきます。

—

1. イーサネットフレームの構造とMACアドレスの正体

私たちが日常的に目にするIPアドレス(L3)やポート番号(L4)は、いわば「宛先の国名や住所」です。しかし、同じLANセグメント(同一のブロードキャストドメイン)内での直接的な会話において、 NIC(Network Interface Card)同士が「誰から誰へ」を正確に認識するために使われるのが、48ビット(6オクテット)のMACアドレス(Media Access Control Address)です。

MACアドレスのバイナリ構造とユニークな意味

MACアドレスは、一般的に A1:B2:C3:D4:E5:F6 のようなコロン区切りの16進数6オクテットで表現されます。しかし、その内部構造をビットレベルで覗いてみると、単なるランダムな識別子ではないことがわかります。

[ MACアドレスの構造 (48ビット) ]
  
   1オクテット目                    3オクテット目   4〜6オクテット目
+-----------------+-----------------+-----------------+-----------------+
| I/G | U/L | ... |      OUI        |      OUI        |   ベンダー固有  |
+-----------------+-----------------+-----------------+-----------------+
  ^     ^
  |     +--- 0: グローバルユニーク(OUI) / 1: ローカル管理アドレス
  +--------- 0: ユニキャスト / 1: マルチキャスト(ブロードキャスト含む)
  • I/G(Individual/Group)ビット:

最上位バイトの最下位ビット(Bit 0)。これが 0 ならユニキャスト(特定の一台のNIC宛て)、1 ならマルチキャスト(またはブロードキャスト)を示します。全ビットが 1 の FF:FF:FF:FF:FF:FF は、お馴染みのレイヤー2ブロードキャストアドレスです。

  • U/L(Universal/Local)ビット:

最上位バイトの第2ビット(Bit 1)。0 ならIEEEが管理するグローバルユニークなMACアドレス(OUI)、1 なら管理者が手動で割り当てたローカル管理アドレスです。

—

2. スイッチングの心臓部:MACアドレステーブルとフレーム転送のメカニズム

L2スイッチ(レイヤー2スイッチ)は、届いたイーサネットフレームの「MAC送信元アドレス」を学習し、「MAC宛先アドレス」を元にフォワーディング(転送)するという、極めてシンプルかつ高速な処理をハードウェア(ASIC)で行っています。

実際のネットワーク上で、フレームがどのように処理されていくのか、その王道のシーケンスを見てみましょう。

[PC A (Sender)]             [L2 Switch]             [PC B (Receiver)]
      |                          |                           |
      |--- 1. フレーム送信 ------->|                           |
      |    (Src: MAC_A,          |                           |
      |     Dst: MAC_B)          |                           |
      |                          |-- 2. MAC学習 -------------|
      |                          |   Port 1 = MAC_A          |
      |                          |                           |
      |                          |-- 3. フラッディング ------| (MAC_B未学習の場合)
      |                          |   (Port 1 以外へ転送) ---->|
      |                          |                           |
      |                          |<-- 4. 返信フレーム -------|
      |                          |   (Src: MAC_B,            |
      |                          |    Dst: MAC_A)            |
      |                          |-- 5. MAC学習 -------------|
      |                          |   Port 2 = MAC_B          |
      |                          |                           |
      |<-- 6. ユニキャスト転送 --|                           |
      |    (Port 1 へ直接送信) --|                           |

現場で役立つTips:フラッディングとMACアドレステーブル溢れの恐怖

スイッチが「MAC宛先アドレス」を自分のMACアドレステーブル(CAMテーブル)に見つけられない場合、受信ポート以外のすべてのポートへフレームをばら撒く「フラッディング(Flooding)」を行います。

もし、悪意ある攻撃者やバグったスクリプトが、存在しないランダムなMAC送信元アドレスを持つフレームを大量に送出するとどうなるでしょうか?
スイッチのMACアドレステーブルは有限です。テーブルが溢れる(MACテーブル・オーバーフロー)と、スイッチは正常なユニキャストフレームまでもすべてフラッディングせざるを得なくなり、実質的にハブと同じ動作になってネットワーク全体のパフォーマンスが急降下します。これがL2セキュリティにおける「MACフラッディング攻撃」の脅威です。

—

3. 実務で触れるコードとインフラ的アプローチ

Webエンジニアであっても、DockerのブリッジネットワークやKubernetesのCNI(Container Network Interface)、あるいはクラウド上のVPC内部で仮想NIC(vNIC)を扱う際、MACアドレスの挙動を理解しているかどうかがトラブルシューティングの明暗を分けます。

ここでは、Pythonを用いてローカルマシンのインターフェース情報を取得し、MACアドレスを確認する実用的なスクリプトを見てみましょう。

PythonによるMACアドレス・インターフェース情報の取得

標準ライブラリやサードパーティライブラリ(psutil)を使用することで、サーバーが持つ物理・仮想NICのMACアドレスをプログラムから正確に把握できます。

import psutil

def print_network_interfaces():
    """
    ホストに存在するすべてのネットワークインターフェースの
    MACアドレスとステータスを詳細に表示する実用スクリプト
    """
    print(f"{'Interface Name':<20} | {'MAC Address':<18} | {'Status':<10}")
    print("-" * 56)

    # psutilを用いてネットワークアドレス情報を取得
    addrs = psutil.net_if_addrs()
    stats = psutil.net_if_stats()

    for interface_name, address_list in addrs.items():
        mac_address = "N/A"
        is_up = "DOWN"

        # インターフェースの稼働状態を取得
        if interface_name in stats:
            is_up = "UP" if stats[interface_name].isup else "DOWN"

        # アドレスリストからMACアドレス(AF_LINK または AF_PACKET)を抽出
        for addr in address_list:
            # 家族(family)がOS依存のMACアドレス型であるかをチェック
            if addr.family == psutil.AF_LINK or getattr(psutil, 'AF_PACKET', None) == addr.family:
                mac_address = addr.address
                break

        print(f"{interface_name:<20} | {mac_address:<18} | {is_up:<10}")

if __name__ == "__main__":
    print_network_interfaces()

クラウド・コンテナ環境におけるMACアドレスの重要性

AWSのENI(Elastic Network Interface)やKubernetesのPodネットワーク(CalicoやFlannelなど)を設計する際、仮想的なMACアドレスが動的または静的にアサインされます。
例えば、ロードバランサー(ALB/NLB)の背後にあるターゲットグループで、インスタンスがフェイルオーバーする際、GARP(Gratuitous ARP)やL2の再学習がスムーズに行われないと、通信が数秒間ブラックホールに吸い込まれるような現象(パケットロス)が発生します。

「なぜかAPIの応答が数秒間途切れる」という障害に直面したとき、L3のルーティングだけでなく、L2レイヤーのMACアドレステーブルの更新タイミング(老化タイマー:Aging Timer)に目を向けられるかどうかが、シニアエンジニアの腕の見せ所です。

—

4. まとめ

イーサネットフレームの「MAC宛先アドレス」と「MAC送信元アドレス」は、一見するとローカルな脇役に思えるかもしれません。しかし、パケットが世界中のネットワークを渡り歩く(あるいは同一セグメント内で高速に転送される)ための最初のパスポートであり、すべての通信の土台です。

  • MACアドレスは48ビットの識別子であり、ユニキャストとマルチキャスト、グローバルとローカルの属性を持つ。
  • L2スイッチはMAC送信元を学習し、MAC宛先を元に転送・フラッディングを行う。
  • Webやクラウドのインフラトラブルにおいても、L2のMAC学習やテーブル溢れ、仮想NICの挙動を疑う視点を持つことで、複雑な障害の根本原因(Root Cause)に素早くたどり着くことができる。

インフラの基礎をなすこの「目に見えないパケットの宛書」を深く理解し、より堅牢でスケーラブルなシステムを構築していきましょう。

それでは、また次回の深淵なるプロトコルの世界でお会いしましょう!

コメント

タイトルとURLをコピーしました