ローリングコンバットピッチなう!

AIとか仮想化とかペーパークラフトとか

単一L2スイッチの配下にVLANを使わずに複数のIPサブネットを収容する

VLANを使わずにL2スイッチ配下に複数のIPサブネットを配置可能か?

これを読んで「出来るじゃん」と普通に思った人は多分この記事を読む必要は無いです。
「VLANは必須じゃないの?」と思った人は興味があれば読んで下さい。

図1の様にL2スイッチの下に4台のPCをぶら下げます。各々固定でIPを振り、PC0は192.168.1.1,PC1は192.168.1.2,PC2は192.168.2.1,PC3は192.168.2.2とします。
VLANは使いません。IPサブネットは各々/24とします。
この状況下で同じサブネット配下のPC0-PC1及びPC2-PC3はお互いにIP通信可能でしょうか?

図1:L2スイッチの下に192.168.1.0/24と192.168.2.0/24を混在させる

答えは「可能」です。
L2スイッチなので、パケットの配送はIPレイヤーではなくMACレイヤー(Etherヘッダ)の宛先アドレス(MACアドレス)で制御されます。
例えばPC0からPC1宛にパケットを送信する場合、PC0が送出するパケットのEthernetヘッダの宛先MACがPC1のものになっていれば、L2スイッチは正しくパケットをPC1に転送します。
同様にPC2からPC3宛にパケットを送信する場合、PC2が送出するパケットのEthernetヘッダの宛先MACがPC3のものになっていれば、パケットはPC3に転送されます。
この際、パケット内のIPレイヤーの送信元、宛先IPアドレスはL2スイッチは関与しません。

パケットはTCP/IPであれば[Etherヘッダ][IPヘッダ][TCPヘッダ][TCP payload」の様にパケット内にヘッダとpayload(送受信するデータの中身)が配置されており、L2スイッチは先頭のEtherヘッダのみを参照してパケットの送信先を決めます。

Etherヘッダの構造概要は下記のwikipediaに記載があります。先頭6バイトに送信先MAC,次の6バイトに送信元MACアドレスが格納されており、L2スイッチは先頭の送信先MACを見て、パケットの転送先ポートを決定します。
イーサネットフレーム - Wikipedia

MACアドレスだけでL2スイッチがパケット転送を行うため、単一L2スイッチ配下に異なるIPサブネットが収容されていても、単に配下の端末同士がIP通信するだけなら問題無く通信可能です。じゃあ、VLAN要らないの?というと、セキュリティや通信効率を考えないならVLAN無しでもとにかく通信は出来ます。

MACアドレスの解決(PC,通信端末上)

上記の話が成立するには、PC0(192.168.1.1)が予めPC1(192.168.1.2)のMACアドレスを知る必要があります。
しかし通常は事前にMACアドレスは判っていません。
PCでもスマフォでもあるいはその他ネットワーク機器でも、内部には通常ARPテーブルと呼ばれるテーブルを持っています。(IPv4の場合)

詳細な構成や管理仕様はOS(Windows,MAC OS,Linux etc)によって異なりますが、IPアドレスとそれに紐づくMACアドレス、更に通信に使用するネットワークインターフェイスの識別子(インターフェイス名等)がセットで格納されています。

こんなイメージです。
PC0がPC1と一度もIP通信を行っていない場合はARPテーブルには192.168.1.2のエントリは格納されていません。
PC0が初めてPC1とIP通信を行おうとする際に、ARPリクエストというパケットを送信します。
ARPリクエストはIPアドレスを指定して(この場合PC1のIPアドレス192.168.1.2)、そのIPアドレスを持つ端末のMACアドレスを返信してもらうためのパケットです。どの端末が指定のIPアドレスを持っているか事前には判らないので、このリクエストはブロードキャストパケットとして送出されます。送信先MACアドレスとしてはFF:FF:FF:FF:FF:FFが使用され、このパケットを受け取ったL2スイッチはリンクアップしている全てのポートにこのARPリクエストを送出します。


ARPリクエストは図1の例では、PC1だけではなくPC2やPC3にも送信されますが、192.168.1.2のIPアドレスを持っているのはPC1だけなので、PC1がこのリクエストに応答してARPリプライというパケットをPC0に返します。ARPリプライにはPC1のMACアドレスが入っているので、PC1はこれを受け取って、上述の様なARPテーブルのエントリを作成、これに従って以後、PC2宛ての送信先MACアドレスをこのテーブルから探して、パケット送信します。


PC0が発したARPリクエストが異なるIPサブネットに属するPC2やPC3に届くのは無駄であるし、IPサブネット的にはPC2やPC3には見えないはずのPC0の存在が見えてしまう...また、PC2やPC3が偽のARPリプライを送ってPC1になりすます危険性等が存在します。PC0,PC1とPC2,PC3をポートVLANで別にVLANに収容すればこの様なことは発生しません。これが通常、単一L2スイッチ配下に複数のIPサブネットをぶら下げる際にVLANを分ける理由ですが、単に通信可能か、不可能かで言えば通信は可能です。


以下はLinuxマシン上で、ARPテーブルを表示(arpコマンド)させた後、ARPテーブル上に存在しないマシンに対してpingを実行した後、再度ARPテーブルを表示させた例です。(表示されているMACアドレスの下位3バイトはXXとマスクしています)

最初、ARPテーブルには192.169.11.5が存在していません。

次にpingで192.168.11.5にICMP ECHOパケットを送ります。この時、先ほど説明したARPリクエストとリプライのやり取りがこのマシンと192.168.11.5の間で発生し、ARPテーブルに192.168.11.5が登録された後にICMP ECHOパケットが送出されます。
pingの後のARPテーブル表示には192.168.11.5が存在します。

$ arp
アドレス               HWタイプ  HWアドレス         フラグ マスク インタフェース
localhost.lan            ether   86:5e:4a:xx:xx:xx   C                     xenbr0
192.168.11.1             ether   10:6f:3f:xx:xx:xx   C                     xenbr0
$ ping 192.168.11.5
PING 192.168.11.5 (192.168.11.5) 56(84) bytes of data.
64 bytes from 192.168.11.5: icmp_seq=1 ttl=64 time=32.3 ms
64 bytes from 192.168.11.5: icmp_seq=2 ttl=64 time=56.3 ms
^C
--- 192.168.11.5 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 32.343/44.370/56.397/12.027 ms
$ arp
アドレス               HWタイプ  HWアドレス         フラグ マスク インタフェース
localhost.lan            ether   86:5e:4a:xx:xx:xx   C                     xenbr0
192.168.11.1             ether   10:6f:3f:xx:xx:xx   C                     xenbr0
192.168.11.5             ether   fc:18:3c:xx:xx:xx   C                     xenbr0
$ 

MACアドレスの解決(L2スイッチ上)

ではL2スイッチはどの様にして接続された各PCのMACアドレスを知るのでしょうか?
通常L2スイッチにもARPテーブルと似たMACアドレスを記憶するテーブルがあります。ただし、L2スイッチはL3すなわちIPレイヤーの情報は見ないので、IPアドレスはこのテーブルには含まれません。


MACアドレスポート番号
00:11:22:33:44:550
AA:BB:CC:DD:EE:FF1

こんな感じで、ポート番号とそのポートに接続されたPC等の通信機器のMACアドレスが記録されています。
L2スイッチはいずれかのポートからパケットを受信すると、そのパケットのEtherヘッダの送信先MACアドレスを利用してこのテーブルを検索し、パケット送出先のポートを決定します。このMACアドレステーブルをFDB(Forwarding Database)等と呼びます。

通信機器をL2スイッチに接続した直後はL2スイッチはその機器のMACアドレスが判りません。
L2スイッチは接続した機器から何かパケットを受信した際に、そのパケットの送信元MACアドレスを取得して、MACアドレステーブルに記録します。
もし接続した機器が何もパケットを送信しないとMACアドレステーブルには記録されないままです。

では、受信したパケットの送信先MACが見つからない場合は、受信ポート以外の全てのポートにパケットをコピーして転送します。これをフラッディングと呼びます。もし送信先MACアドレスを持つ機器がいずれかのポートに繋がっていれば、そのパケットは受信され、更に受信した機器は大抵の場合何らかのレスポンスを返すので、それによりMACアドレスMACテーブルに新規登録され、以後はフラッディングは発生しません。
もちろん存在しない宛先に誰かがパケットを送信し続けるとフラッディングが継続するという迷惑な状態になります。この場合もVLANをサブネット毎に分けておけば、他のサブネットには迷惑は掛かりません。

MACアドレステーブルあるいはFDBはL2スイッチの1ポートあたり1MACアドレスを保持するわけではなく、L2スイッチがカスケード接続されているケースや1台の機器が複数のMACアドレスを持っているケース(仮想化で内部で仮想マシンをブリッジしている場合等)もあるため、ポートあたり複数のMACが記憶出来るのが普通です。

1ポートあたりいくつのMACアドレスを抱えられるかはL2スイッチ全体でxxエントリとか、ポートあたりyyエントリ等、L2スイッチ毎のスペックに依存します。

1台PCに複数のIPアドレスを設定する

図1の応用版として図2の様に1台のPCの1つのネットワークIFに2つのIPアドレス(異なるサブネット)を付与し、サブネット毎に通信させる事も可能です。
Linuxだとネットワークインターフェイスに対してアドレスエリアスが簡単に設定できるので、下記構成で192.168.1.0/24と192.168.2.0/24の2つのサブネットを使い分けてPC0,PC1間を通信させる事も出来ます。

図2: 1台のPCが1つのIFで複数のIPサブネットに所属

下記はPC1をUbuntu 18.04 LTS, PC2をraspberry piとしてそれぞれの同じネットワークIFに2つのサブネットを割り振って通信させた例です。
(IPアドレスの割り当てが上の図と異なっているのでちょっと判りにくいのはご容赦を)

1台目: Ubuntu 18.04 LTS(192.168.11.252と192.168.20.1)

$ sudo ip addr add local 192.168.20.1 dev ens3 label ens3:1 ★IF ens3に192.168.20.1を追加
$ ip addr show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: ens3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 52:54:00:xx:xx:xx brd ff:ff:ff:ff:ff:ff
    inet 192.168.11.252/24 brd 192.168.11.255 scope global ens3 ★ens3の元のIP
       valid_lft forever preferred_lft forever
    inet 192.168.20.1/32 scope global ens3:1 ★ens3に追加されたIP
       valid_lft forever preferred_lft forever
    inet6 fe80::5054:ff:fexx:xxxx/64 scope link 
       valid_lft forever preferred_lft forever
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default 
    link/ether 02:42:e7:xx:xx:xx brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
       valid_lft forever preferred_lft forever

2台目: Raspberry pi(192.168.11.200と192.168.20.2)

pi@raspberrypi:~ $ ip addr show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether b8:27:eb:xx:xx:xx brd ff:ff:ff:ff:ff:ff
    inet 192.168.11.200/24 brd 192.168.11.255 scope global eth0 ★eth0のIPアドレス 192.168.11.200
       valid_lft forever preferred_lft forever
    inet6 fe80::9fb8:5960:727a:a5b0/64 scope link 
       valid_lft forever preferred_lft forever
pi@raspberrypi:~ $ sudo ip addr add local 192.168.20.2/24 dev eth0 label eth0:1 ★eth0に129.168.20.2を追加
pi@raspberrypi:~ $ ifconfig eth0:1 ★追加結果を確認
eth0:1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.20.2  netmask 255.255.255.0  broadcast 0.0.0.0
        ether b8:27:eb:xx:xx:xx  txqueuelen 1000  (イーサネット)

pi@raspberrypi:~ $ ping 192.168.20.1 ★1台目の追加アドレスにping
PING 192.168.20.1 (192.168.20.1) 56(84) bytes of data.
64 bytes from 192.168.20.1: icmp_seq=1 ttl=64 time=1.40 ms
64 bytes from 192.168.20.1: icmp_seq=2 ttl=64 time=1.13 ms
64 bytes from 192.168.20.1: icmp_seq=3 ttl=64 time=0.875 ms
64 bytes from 192.168.20.1: icmp_seq=4 ttl=64 time=0.971 ms
^C
--- 192.168.20.1 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3004ms
rtt min/avg/max/mdev = 0.875/1.096/1.405/0.205 ms
pi@raspberrypi:~ $ ping 192.168.11.252 ★1台目のオリジナルアドレスにping
PING 192.168.11.252 (192.168.11.252) 56(84) bytes of data.
64 bytes from 192.168.11.252: icmp_seq=1 ttl=64 time=2.22 ms
64 bytes from 192.168.11.252: icmp_seq=2 ttl=64 time=1.08 ms
64 bytes from 192.168.11.252: icmp_seq=3 ttl=64 time=1.09 ms
^C
--- 192.168.11.252 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2003ms
rtt min/avg/max/mdev = 1.086/1.469/2.226/0.536 ms
pi@raspberrypi:~ $ 
pi@raspberrypi:~ $ ip route show ★raspberry piのルーティングテーブル
default via 192.168.11.1 dev eth0 src 192.168.11.200 metric 202 
192.168.11.0/24 dev eth0 proto kernel scope link src 192.168.11.200 metric 202 
192.168.20.0/24 dev eth0 proto kernel scope link src 192.168.20.2 
pi@raspberrypi:~ $ arp ★raspberry piのARPテーブル
アドレス               HWタイプ  HWアドレス         フラグ マスク インタフェース
192.168.11.252           ether   52:54:00:xx:xx:xx   C                     eth0 ★1台目のオリジナルアドレス
192.168.11.9             ether   80:fa:5b:xx:xx:xx  C                     eth0
localhost.lan            ether   86:5e:4a:xx:xx:xx   C                     eth0
192.168.11.1             ether   10:6f:3f:xx:xx:xx   C                     eth0
192.168.20.1             ether   52:54:00:xx:xx:xx   C                     eth0 ★1台目の追加アドレス

2台を192.168.11.0/24と192.168.20.0/24の2つのサブネットで疎通させた後、2台目のraspberry piARPテーブルを見ると、1台目の2つのIPアドレスが同じMACアドレスに紐づく形で登録されています。

何の役に立つの?

先に書いた通り、セキュリティや通信効率考えるならVLAN分ける方が良いです。
例えば個人が家庭内で、家電量販店で3000円くらいで売っている様なスイッチングハブを購入し、raspberry piとかで実験的に通常のインターネット接続用とは別のスタンドアローンなサブネットを組みたい場合、物理的には全部同じスイッチングハブに繋ぐ等も可能です。使い方間違えると無駄なフラッディングとか発生しますが、ファイヤーウォール/NATの内側で全機器を自分が管理するならセキュリティ云々もあまり考えなくても良いのでお手軽です。