CodeZine(コードジン)

特集ページ一覧

仮想ネットワーク実装でTCP/IPを学ぼう(2)
― ネットワークアクセス層の勘所

仮想ネットワークの実装で学ぶTCP/IP (2)

  • LINEで送る
  • このエントリーをはてなブックマークに追加
2009/03/27 14:00
目次

スイッチングハブ

 SedMacFreamTestプロジェクトをもう一度実行してみてください。端末Bへ送りたいだけなのにも関わらず、端末CとDも受け取ってしまい、不必要なので、いちいち受信データを破棄していることが確認できます。これは非常に非効率的ですし、この状態ではセキュリティ上、好ましくありません。その点を改善するために作られたのがスイッチングハブです。 スイッチングハブは一般にFDB(Forwarding Database:転送データベース)と呼ばれるものを保持しており、このデータベースを基に効率的にデータを直接送信します。

 この動きを体験するためにVlanTestプロジェクトをスタートアッププロジェクトに設定してから実行してください。このサンプルコードを実行すると、リピータハブと呼ばれる通常のハブよりも効率的にデータが送られていることを実感できると思います。 では、スイッチングハブ動作の概要をこれから解説します。ただし、メーカー毎に細部は異なりますので参考程度にしてください。 サンプルコードを参考にしながら解説を読んでください。

データ送信時の処理を表すコード(VB.NET)
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
データ送信時の処理を表すコード(C#)
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を検索

 次にすることは、送信元アドレスの登録、もしくは寿命の更新です。送信元アドレスの内容を追加する理由は、効率よく通信するために端末のデータを記録するためです。寿命の更新については、少々なれない概念だと思いますのでこれから説明します。

 ネットワークでは色々な理由で、新しい端末が追加されたり取り除かれたりします。そこで、スイッチングハブでは、一定期間が過ぎたら使われていない端末のデータを削除しています。ですから、この段階で使用されている端末データの寿命を延ばします。

送信元アドレス+VIDでFDBを検索する様子(VB.NET)
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;
}
送信元アドレス+VIDでFDBを検索する様子(C#)
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:洪水)と呼びます。

フラッディングを表すコード(VB.NET)
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
フラッディングを表すコード(C#)
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のみでしたが、実際はスパニング・ツリー・アルゴリズムなどのネットワーク経路制御プロトコルにより更新されることがあります。ネットワーク経路の制御については他層の説明も必要なので今回はしません。

まとめ

 いかがだったでしょうか? 今回はネットワークアクセス層で重要な事柄について、サンプルコードを交えながら解説しました。この記事がネットワークアクセス層の理解の一助になれば幸いです。次回はインターネット層について解説します。お楽しみに。

参考資料

 書籍

Webページ



  • LINEで送る
  • このエントリーをはてなブックマークに追加

バックナンバー

連載:仮想ネットワークの実装で学ぶTCP/IP

著者プロフィール

  • インドリ(インドリ)

    分析・設計・実装なんでもありのフリーエンジニア。 ブログ「無差別に技術をついばむ鳥(http://indori.blog32.fc2.com/)」の作者です。 アドバイザーをしたり、システム開発したり、情報処理技術を研究したりと色々しています。 座右の銘は温故知新で、新旧関係なく必要だと考えた...

あなたにオススメ

All contents copyright © 2005-2021 Shoeisha Co., Ltd. All rights reserved. ver.1.5