一覧NW-03-2 · スイッチとVLAN

VLANの仕組みとタグ

accessとtrunkで同じフレームがどのVLANとして運ばれるかを確認します。

説明

VLANは一つのスイッチ網の中でブロードキャスト領域を分ける仕組みです。端末側でタグを持たないフレームでも、スイッチは受信ポートの設定からVLANを判断できます。

VLAN100の端末が送ったフレームをtrunkへ出し、802.1Qタグの各値を取り出します。許可VLANと送信先ポートを変え、タグを外す場合や、両端のタグなしVLAN設定が違う場合の扱いを調べます。

1accessとtrunkの設定

aはVLAN100、bはVLAN200のaccessポート、tは100と200を運ぶtrunkとします。このtrunkでは100と200を両方タグ付きにします。

access = {"a": 100, "b": 200}; allowed = {100, 200}ポートの所属と許可VLAN; ingress_vlan = access["a"]受信側の分類; print(ingress_vlan, sorted(allowed))内部VLANとtrunkの条件

aからのタグなしフレームはVLAN100へ分類されます。タグがないことはVLANに属さないという意味ではありません。受信ポートのPVID等の設定が分類に使われます。

2元のEthernetフレーム

宛先6・送信元6・EtherType2の後にDATAを置いた試料を作ります。比較するバイト列はFCSを含めず、パディングを含めて60バイトです。

frame = bytes.fromhex("02000000000b02000000000a88b5") + b"DATA" + bytes(42)タグなし試料; print(len(frame), frame[:18].hex(" "))長さと先頭部分

EtherTypeの88 b5は12〜13バイト目にあります。タグを追加すると、この位置へTPIDが入り、元のEtherTypeは4バイト後ろへ移ります。FCSは送信するフレーム内容に合わせて再計算されます。

3802.1Qタグを挿入

PCPを5、DEIを0、VIDを100にしてTCIを作り、TPID 0x8100とともに挿入します。

pcp, dei, vid = 5, 0, ingress_vlanタグの各値; tci = (pcp << 13) | (dei << 12) | vidTCIのビット配置; tagged = frame[:12] + bytes.fromhex("8100") + tci.to_bytes(2,"big") + frame[12:]送信元の後へタグ挿入; print(tagged[:22].hex(" "), len(tagged))追加したバイトと長さ

81 00 a0 64という4バイトが増え、記録長は64バイトです。PCP3ビット、DEI1ビット、VID12ビットをTCIへ詰めています。PCPは優先度の情報であり、値を付けただけで帯域が予約されるものではありません。

4タグを読み取って元の値へ戻す

受信側としてTCIからPCP・DEI・VIDを取り出し、元のEtherTypeも確認します。

received_tci = int.from_bytes(tagged[14:16],"big")TCIを読み取る; received_vid = received_tci & 0xfff下位12ビットのVID; print(received_tci >> 13, (received_tci >> 12) & 1, received_vid, hex(int.from_bytes(tagged[16:18],"big")))各欄を復号

5、0、100、0x88b5です。VIDは100であり、PCPを含む0xa064全体をVLAN番号として扱ってはいけません。タグを解析しても、許可されるVLANかどうかは受信ポートの設定で確認します。

5trunkで許可していないVLAN

許可一覧を100・200から200だけへ変えた二つの条件で、VID100の受信を判定します。

print(received_vid in allowed)元の許可一覧; restricted = {200}; print(received_vid in restricted)100を許可しない条件

最初はTrue、後はFalseです。フレームに正しい形式のタグが付いていても、対象trunkがそのVLANを運ぶ設定でなければ通りません。この変更でVLAN100が200へ読み替えられるわけではありません。

6access側へ出すときにタグを外す

受信を許可した条件に戻り、VLAN100のaccessポートへ送るフレームを作ります。

egress_ports = [port for port,vlan in access.items() if vlan == received_vid]同じVLANの出力候補; untagged = tagged[:12] + tagged[16:]4バイトのタグを除く; print(egress_ports, untagged == frame)ポートと元フレームの一致

候補はaだけで、タグを外したバイト列は元のframeと一致します。VLAN200のbへは同じVLAN内の転送として出しません。実際の出力先は、このVLAN分類に加えてMAC学習などでも絞られます。

7タグなしの分類が両端で違う場合

別のtrunkで、送信側がタグなしVLANを100、受信側が200としている不一致を考えます。

sender_native, receiver_pvid = 100, 200両端の不一致; wire_has_tag = False線上ではタグなし; classified = receiver_pvid if not wire_has_tag else sender_native受信側の分類; print(sender_native, classified)所属の食い違い

送信側で100として出したタグなしフレームが、受信側では200へ分類されるモデルです。タグなしのフレームには元のVIDがないため、両端の設定を合わせる必要があります。機器によって不一致の検出や遮断もあるので、実際の動作条件を確認します。

8VLANが違えば同じMACも別の検索キー

同じMACが二つのVLANに存在する転送表を作り、VIDごとの参照先を表示します。

mac = "02:00:00:00:00:0b"; fdb = {(100,mac): "a", (200,mac): "b"}VLAN別の対応表; print(fdb[100,mac], fdb[200,mac])VLANとMACで検索

同じMACでもVLAN100ではa、200ではbです。このモデルはVLANごとに学習する方式です。VLAN間の通信にはL3の経路等が必要で、trunkを通しただけで二つのVLANが一つになるわけではありません。

まとめ

タグなしフレームも受信ポートの設定でVLANへ分類されます。

802.1QのTCIをPCP・DEI・VIDへ分け、許可VLANと照合します。

タグの付け外し、タグなしの分類、VLAN別の学習を両端で対応させます。

操作履歴と解説を振り返れます。

Python 3

1
access = {"a": 100, "b": 200}; allowed = {100, 200}; ingress_vlan = access["a"]; print(ingress_vlan, sorted(allowed))
100 [100, 200]
2
frame = bytes.fromhex("02000000000b02000000000a88b5") + b"DATA" + bytes(42); print(len(frame), frame[:18].hex(" "))
60 02 00 00 00 00 0b 02 00 00 00 00 0a 88 b5 44 41 54 41
3
pcp, dei, vid = 5, 0, ingress_vlan; tci = (pcp << 13) | (dei << 12) | vid; tagged = frame[:12] + bytes.fromhex("8100") + tci.to_bytes(2,"big") + frame[12:]; print(tagged[:22].hex(" "), len(tagged))
02 00 00 00 00 0b 02 00 00 00 00 0a 81 00 a0 64 88 b5 44 41 54 41 64
4
received_tci = int.from_bytes(tagged[14:16],"big"); received_vid = received_tci & 0xfff; print(received_tci >> 13, (received_tci >> 12) & 1, received_vid, hex(int.from_bytes(tagged[16:18],"big")))
5 0 100 0x88b5
5
print(received_vid in allowed); restricted = {200}; print(received_vid in restricted)
True
False
6
egress_ports = [port for port,vlan in access.items() if vlan == received_vid]; untagged = tagged[:12] + tagged[16:]; print(egress_ports, untagged == frame)
['a'] True
7
sender_native, receiver_pvid = 100, 200; wire_has_tag = False; classified = receiver_pvid if not wire_has_tag else sender_native; print(sender_native, classified)
100 200
8
mac = "02:00:00:00:00:0b"; fdb = {(100,mac): "a", (200,mac): "b"}; print(fdb[100,mac], fdb[200,mac])
a b

演習完了