スイッチングハブ
SedMacFreamTest
プロジェクトをもう一度実行してみてください。端末Bへ送りたいだけなのにも関わらず、端末CとDも受け取ってしまい、不必要なので、いちいち受信データを破棄していることが確認できます。これは非常に非効率的ですし、この状態ではセキュリティ上、好ましくありません。その点を改善するために作られたのがスイッチングハブです。 スイッチングハブは一般にFDB(Forwarding Database:転送データベース)と呼ばれるものを保持しており、このデータベースを基に効率的にデータを直接送信します。
この動きを体験するためにVlanTest
プロジェクトをスタートアッププロジェクトに設定してから実行してください。このサンプルコードを実行すると、リピータハブと呼ばれる通常のハブよりも効率的にデータが送られていることを実感できると思います。 では、スイッチングハブ動作の概要をこれから解説します。ただし、メーカー毎に細部は異なりますので参考程度にしてください。 サンプルコードを参考にしながら解説を読んでください。
Public Overrides Sub SendEnter() '送信元アドレスとVIDのセットがFDBに登録チェック Dim sa As FdbElement = Me.SearchFDB(Me.savaDeata.SourceAddress, _ Me.savaDeata.VTagInfo.VID) '登録されている場合は寿命を延ばし、 '登録されていない場合は登録する If sa Is Nothing Then sa = New FdbElement() sa.Address = Me.savaDeata.SourceAddress sa.VID = Me.savaDeata.VTagInfo.VID sa.PortNumber = Me.sender.ID sa.Cable = Me.sender sa.Aging = DateTime.Now.AddHours(10) '数値は架空のもの Me.fdb.Add(sa) '新しい要素をFDBへ追加 Else sa.Aging.AddMinutes(10) '数値は架空のもの End If '送信元アドレスと宛先アドレスが同じ場合はフレームを破棄して処理を終える If Me.savaDeata.SourceAddress = Me.savaDeata.DestinationAddress Then Return End If '送信先アドレスとVIDのセットがFDBに登録チェック Dim da As FdbElement = Me.SearchFDB(Me.savaDeata.DestinationAddress, _ Me.savaDeata.VTagInfo.VID) '送信処理 If da Is Nothing Then '宛先が分からない状態 Flooding() Else '宛先が分かる状態 da.Cable.SendDataToNic(Me.savaDeata) End If End Sub
public override void SendEnter() { //送信元アドレスとVIDのセットがFDBに登録チェック FdbElement sa = this.SearchFDB( this.savaDeata.SourceAddress, this.savaDeata.VTagInfo.VID ); //登録されている場合は寿命を延ばし、 //登録されていない場合は登録する if ( sa == null ) { sa = new FdbElement( ); sa.Address = this.savaDeata.SourceAddress; sa.VID = this.savaDeata.VTagInfo.VID; sa.PortNumber = this.sender.ID; sa.Cable = this.sender; sa.Aging = DateTime.Now.AddHours( 10 ); //数値は架空のもの this.fdb.Add( sa ); //新しい要素をFDBへ追加 } else { sa.Aging.AddMinutes( 10 ); //数値は架空のもの } //送信元アドレスと宛先アドレスが同じ場合はフレームを破棄して処理を終える if ( this.savaDeata.SourceAddress == this.savaDeata.DestinationAddress ) { return; } //送信先アドレスとVIDのセットがFDBに登録チェック FdbElement da = this.SearchFDB( this.savaDeata.DestinationAddress, this.savaDeata.VTagInfo.VID ); //送信処理 if ( da == null ) { //宛先が分からない状態 Flooding( ); } else { //宛先が分かる状態 da.Cable.SendDataToNic( this.savaDeata ); } }
1. フレームから必要な情報を取り出す
一番最初にすることは、宛先アドレス・送信元アドレス・仮想LAN識別子の3つの情報を取り出すことです。ここで、10ギガビットEthernetでは、MACフレームが変更されていることに注意してください。10ギガビットEthernetでは、送信元アドレスとType/Lengthフィールドの間に4バイトのVLANタグが付加されています。2バイト目はTypeフィールドで0x8100の固定値です。
次に、VLANタグがあって、各フィールドは、優先順位(3ビット)・CFI(1ビット)・VID(12ビット)となっています。このため、VLAN対応のNICを使用してVLANタグが識別できない古いハブを使用すると通信ができません。この点に注意して下さい。
なお、CFIとは Canonical Format Indicatorの略称で、このフィールドはネットワークオーダーを表すフラグです。CFI=0がビッグエンディアン、1がリトルエンディアンです。「ネットワークバイトオーダー」とは何かというと、一言で言えばビットの並べ方です。覚えている方もいると思いますが、CPUにはリトルエンディアンとビッグエンディアンと呼ばれるバイトの並びがあります。それと同じ概念で、ネットワークプロトコルにも、最下位ビットからデータを伝送する(リトルエンディアン)方式と、最上位ビットからデータを伝送する(ビッグエンディアン)方式があります。 ちなみに、TCP/IPではビッグエンディアンを使用しています。ですからEthernet同士の通信ではCFIの値は必ず0です。
2. 送信元アドレス+VIDでFDBを検索
次にすることは、送信元アドレスの登録、もしくは寿命の更新です。送信元アドレスの内容を追加する理由は、効率よく通信するために端末のデータを記録するためです。寿命の更新については、少々なれない概念だと思いますのでこれから説明します。
ネットワークでは色々な理由で、新しい端末が追加されたり取り除かれたりします。そこで、スイッチングハブでは、一定期間が過ぎたら使われていない端末のデータを削除しています。ですから、この段階で使用されている端末データの寿命を延ばします。
private FdbElement SearchFDB( MacAddress address, ushort vid ) { FdbElement target = null; foreach ( FdbElement element in this.fdb ) { if ( element.VID == vid && element.Address == address ) { target = element; break; } } return target; }
Private Function SearchFDB(ByVal address As MacAddress, ByVal vid As UShort) As FdbElement Dim target As FdbElement = Nothing For Each element As FdbElement In Me.fdb If element.VID = vid AndAlso element.Address = address Then target = element End If Next Return target End Function
3. 宛先アドレス+VIDでFDBを検索
最後はデータを目的地に届けるために送信元の情報を使ってFDBを検索します。そして検索した結果、端末のデータが見つかったら目的地にデータを直接送ります。もし見つからなかった場合は、仕方がないので送信元ポート以外の全ポートへデータを送ります。この動作を「フラッディング」(Flooding:洪水)と呼びます。
Private Function SearchFDB(ByVal address As MacAddress, ByVal vid As UShort) As FdbElement Dim target As FdbElement = Nothing For Each element As FdbElement In Me.fdb If element.VID = vid AndAlso element.Address = address Then target = element End If Next Return target End Function
private void Flooding() { foreach ( LanCable cable in this.ConnectCables ) { VlanNic nic = (VlanNic)cable.ConnectNic; if ( nic.Address != this.savaDeata.SourceAddress ) { cable.SendDataToNic( this.savaDeata ); } } }
一つ注意して欲しいことがあります。今回は説明を簡略化するためにFDBへの登録は手順1のみでしたが、実際はスパニング・ツリー・アルゴリズムなどのネットワーク経路制御プロトコルにより更新されることがあります。ネットワーク経路の制御については他層の説明も必要なので今回はしません。
まとめ
いかがだったでしょうか? 今回はネットワークアクセス層で重要な事柄について、サンプルコードを交えながら解説しました。この記事がネットワークアクセス層の理解の一助になれば幸いです。次回はインターネット層について解説します。お楽しみに。
参考資料
書籍
- 『詳解TCP/IP Vol.1 プロトコル』 W.Richard Stevens 著、橘康雄・井上尚司 訳、ピアソンエデュケーション、2000年12月
- 『マスタリングTCP/IP 入門編 第4版』 竹下隆史・村山公保・荒井透・苅田幸雄 著、オーム社、2007年2月
- 『10ギガビットEthernet教科書』 石田修、瀬戸康一郎 著、IDGジャパン、2002年4月
- 『802.11 高速無線LAN教科書』 松江英明・守倉正博 著、IDGジャパン、2003年3月
Webページ
- @IT 『Master of IP Network』