イーサネットトンネルの例に関するFreeBSD Netgraph

みなさんこんにちは。



FreeBSDで作業する多くのシステム管理者は、Netgraphカーネルサブシステムの存在を知っていると思います。 しかし、それがどのように機能し、それから何を構築できるかを知っている/理解している人は多くありません。



それが何であるかを説明します。また、簡単な例を使用して、インターネット上のイーサネットブリッジのアセンブリを分析します。







ちょっとした理論。



ウィキペディアはhttp://ru.wikipedia.org/wiki/Netgraphに伝えます



Netgraphは、グラフの原理に基づいたFreeBSDカーネルのモジュラーネットワークサブシステムです。 Netgraphでは、グラフはさまざまなタイプのノードから構築され、各タイプのノードには多数の入力/出力(以降、フック)があります。 netgraphノードを使用すると、通過するパッケージに対して特定のアクションを実行できます。 一部のNetgraphノードは、L2TP、PPTP、PPPoE、PPP、ATM、bluetoothなどのさまざまなプロトコル、カプセル化のサポートを提供します。その他のモジュールは、bpf、splitなどのnetgraphノード間のモジュールとソート/ルーティングをバンドルします。



Netgraphサブシステムは、それぞれ固有のタスクを持つモジュールのセットです。 これは非常に少数のレゴブロックであり、いつでも接続できます。



多くのnetgraphモジュールの使用の鮮明な例は、PPPoE、PPTP、PPPなどのクライアントトンネルを終了するためにロシアの小さなプロバイダーによって大量に使用されるmpdデーモンです。 mpdブランチのバージョン3では、彼は多くの操作を自分で行い、現在のバージョン5では主にnetgraphモジュールの切り替えを扱っています。



キューブを見てみましょう。



[root@bsd1] /boot/kernel/> ls | grep ng_

ng_UI.ko

ng_async.ko

ng_atm.ko

ng_atmllc.ko

ng_bluetooth.ko

ng_bpf.ko

ng_bridge.ko

ng_bt3c.ko

ng_btsocket.ko

ng_car.ko

ng_ccatm.ko

ng_cisco.ko

ng_deflate.ko

ng_device.ko

ng_echo.ko

ng_eiface.ko

ng_etf.ko

ng_ether.ko

ng_fec.ko

ng_frame_relay.ko

ng_gif.ko

ng_gif_demux.ko

ng_h4.ko

ng_hci.ko

ng_hole.ko

ng_hub.ko

ng_iface.ko

ng_ip_input.ko

ng_ipfw.ko

ng_ksocket.ko

ng_l2cap.ko

ng_l2tp.ko

ng_lmi.ko

ng_mppc.ko

ng_nat.ko

ng_netflow.ko

ng_one2many.ko

ng_ppp.ko

ng_pppoe.ko

ng_pptpgre.ko

ng_pred1.ko

ng_rfc1490.ko

ng_socket.ko

ng_source.ko

ng_split.ko

ng_sppp.ko

ng_sscfu.ko

ng_sscop.ko

ng_sync_ar.ko

ng_sync_sr.ko

ng_tag.ko

ng_tcpmss.ko

ng_tee.ko

ng_tty.ko

ng_ubt.ko

ng_uni.ko

ng_vjc.ko

ng_vlan.ko

[root@bsd1] /boot/kernel/> ls | grep ng_ | wc -l

58

[root@bsd1] /boot/kernel/>







現在、58個のモジュール+ 1つのメインモジュールnetgraph.ko(FreeBSD 7.3)があります。 それらはすべて、カーネルに静的に組み込むことも、動的にロードすることもできます。 モジュールは核であるため、カーネル内で直接動作するため、パフォーマンスで驚くべき結果を得ることができます。



各モジュールには、モジュールの機能、入力/出力(フック、フック、フック、以下「フック」と呼びます)、受信した制御メッセージ、およびそれらのアクションを説明する人がいます。

netgraphサブシステムを管理するために、list、mkpeer、connect、nameなどのさまざまなコマンドを受け入れるngctlユーティリティがあります。これらについては、man ngctlで個別に読むことができます。



チャレンジ。



簡単なタスクを実行します。インターネット経由で2つの物理イーサネットネットワークを接続する必要があります。

2台のサーバーがあります。



BSD1

2つのネットワークインターフェイス

インターフェイス1 em0-外部IP 1.1.1.1

インターフェース2 em1-内部IP 192.168.1.1



BSD2

2つのネットワークインターフェイス

インターフェイス1 em0-外部IP 2.2.2.2

インターフェース2 em1-内部IP 192.168.1.2



サーバー間では、外部インターフェースを介したルーティング可能なインターネット接続があります。

両方のサーバーのem1インターフェイスでネットワークを接続する必要があります。



キューブを見てみましょう。



以下が必要です。



netgraph.koが基盤です。



ng_ether.gif ng_ether.ko-物理ネットワークインターフェイスに接続するためのモジュール。 このモジュールをグラフ空間にロードすると、このインターフェースの名前を持つ物理インターフェースごとに1つのノードが自動的に作成されます。 この場合、「em0:」、「em1:」になります。 各ng_etherノードには、下、上、孤児の3つのフックがあります。 下のフックは、ネットワークインターフェイスのドライバーの場所への直接の出口であり、そこからデバイスがパケットを送受信します。 上部フックは、ネットワークデバイスドライバーがパケットを送信または受信するシステムのカーネルの場所への直接出口です。 孤立フックは同じ下位で、ネットワークデバイスからの誤った損傷パケットのみが入ります。 考慮に入れていません。



簡単に言えば、ng_etherを接続すると、カーネルとネットワークカードドライバー間のやり取りにギャップが生じます。 カーネル側では、このギャップは上部と呼ばれ、ネットワークデバイスドライバー側では下部と呼ばれます。 このギャップは、netgraphサブシステムの何かが下位または上位に接続するまで接続されます。





ng_bridge.gif ng_bridge.koは、実際のイーサネットスイッチのモジュールです。 誰でも、イーサネットポート、点滅する電球、テーブルの上に立ってコンピューターをネットワークに接続するボックスを想像することができます(そして、誰かはまだそれを見ています)。 ng_bridgeは、netgraphサブシステムの同じボックスです。 ng_bridgeは単純なイーサネットスイッチの実装で、arpテーブルと単純なループ検出アルゴリズムを備えています。 Ng_bridgeフックはlink0、link1などと呼ばれます。 ng_etherのフックをそれらに接続します。 モジュールは、setconfig、getconfig、reset、getstats、clrstats、getclrstats、gettableなどの制御メッセージを受け取ります。 メッセージアクションは直感的で、詳細はman ng_bridgeにあります。





ng_ksocket.gif ng_ksocket.ko-カーネル(ソケット()システム関数でサポートされている任意のソケット)から直接リッスンするためのソケットを開き、別のソケットに接続できるモジュール。 単一のフック接続「<family> / <type> / <proto>」を受け入れます。 manソケットは、ファミリ、タイプ、プロトが何であるかを示します。 この場合、「inet / dgram / udp」になります。 inet-ipv4、dgram-データグラム、proto-udp。 モジュールは、バインド、リッスン、接続、受け入れ、getname、getpeername、setopt、getoptなどのいくつかの制御メッセージも受信します。 詳細については、man ng_ksocketを参照してください。 「接続」というメッセージを使用して、着信フック「inet / dgram / udp」をリモートフックに接続できます。リモートフックは「bind」コマンドを使用して同様に作成されました。



グラフを作成します。



グラフシステムの動作をよりよく理解するには、システムに組み込む前にグラフを描画することをお勧めします。



ethernet_over_udp_scheme.gif








1. em1インターフェースのng_etherモジュールの両方のフックをng_bridgeに接続して、インターフェースに接続された物理ネットワークと仮想ネットワークの両方をシステムが認識できるようにします。 両方のサーバーで同じように行われます。

2.次の空きリンクng_bridgeは、パラメータinet / dgram / udpを使用してng_ksocketに接続されます。 (なぜUDPを選択したのですか?IPプロトコルと同様に、誰もケーブルまたは無線ネットワークで信号の正常な配信を保証しないため、誰もUDPの配信を保証しません。)両方のサーバーで同じことが行われます。

3. ng_ksocketモジュールにコマンドを送信して、特定のIPアドレスで特定のポートを取得し、目的のポートでリモートIPに接続します。 両方のサーバーでこれを行いますが、IPアドレスとポートが互いに反対に変更される点が異なります。





システムでグラフを収集します。



上記でngctlユーティリティについてはすでに言及しました。 その助けを借りて、システムにモジュールを作成し、それらをリンクします。

mkpeer、connect、name、msgコマンドが必要になります。 それらを簡単な用語で説明します。



Mkpeerチーム。

構文: ngctl mkpeer module1 module_type2 hook1 hook2

指定されたタイプで「module2」を作成し、そのフック「hook2」をモジュール「module1」の「hook1」に接続します



接続コマンド

構文: ngctl connect module1 module2 hook1 hook2

モジュール「module1」の「hook1」をモジュール「module2」の「hook2」に接続します。



名前コマンド。

構文: ngctl name module:フック名

mkpeerで作成されたモジュールに名前を付けます。



Msgコマンド

構文: ngctl msg module:メッセージパラメーター

コントロール「メッセージ」を「パラメータ」とともに「モジュール」に送信します。



さて、「bsd1」サーバーのグラフはライブのように見えます。 トンネルには、udpポート7777を使用します



ng_bridgeノードを作成し、そのフック「link0」にネットワークインターフェイス「em1」「lower」のフックを接続します。

ngctl mkpeer em1:ブリッジ下位link0

新しく作成したノードに「switch」という名前を付けます。これは、「em1:lower」というパスにあります。

ngctl name em1:下位スイッチ

「スイッチ」上位ネットワークインターフェイス「em1」の「link1」に接続します。

ngctl connectスイッチ:em1:link1 upper

ng_ksocketノードを作成し、「スイッチ」のフック「inet / dgram / udp」「link2」に接続します

ngctl mkpeerスイッチ:ksocket link2 inet / dgram / udp

新しく作成されたksocket「switch_socket」を呼び出します。パス「switch:link2」にあります。

ngctl nameスイッチ:link2 switch_socket

パラメーターを指定して、bindコマンドをswitch_socketに送信します。 ksocketはIP 1.1.1.1でポート7777を使用します。

ngctl msg switch_socket:bind inet / 1.1.1.1:7777

パラメータを指定して「switch_socket」に「connect」コマンドを送信します。 ksocketは、IPアドレス2.2.2.2のポート7777に接続します。

ngctl msg switch_socket:接続inet / 2.2.2.2:7777

em1ネットワークインターフェイスのng_etherモジュールにコマンドを送信して、それ宛てではないパケットの盗聴モードに切り替えます。 結局のところ、仮想ネットワークにあるデバイスのパケットを受信する必要があります。

ngctl msg em1:setpromisc 1

ngctl msg em1:setautosrc 0



bsd2サーバーの場合、bindとconnectコマンドのパラメーターを交換するだけです。



使いやすくするために、これらすべてをshスクリプトで設計しました。

スクリプトは別のngctl shutdownコマンドを使用します。 このコマンドは、パラメーターで指定されたモジュールに特別な制御メッセージを送信します。 このメッセージは各モジュールで受信されます。詳細は「manモジュール」です。 通常、このコマンドにより、モジュールが破壊され、すべての接続が切断されます。



  #!/ bin / sh
 self = 1.1.1.1
ピア= 2.2.2.2
ポート= 7777
 if = em1

ケース「$ 1」
        開始)
             echo "netgraphスイッチの開始。"
             ngctl mkpeer $ {if}:ブリッジ下位link0
             ngctl name $ {if}:下位スイッチ
             ngctl接続スイッチ:$ {if}:link1 upper
             ngctl mkpeerスイッチ:ksocket link2 inet / dgram / udp
             ngctl nameスイッチ:link2 switch_socket
             ngctl msg switch_socket:bind inet / $ {self}:$ {port}
             ngctl msg switch_socket:接続inet / $ {peer}:$ {port}
             ngctl msg $ {if}:setpromisc 1
             ngctl msg $ {if}:setautosrc 0
            エコー「OK」
            出口0
             ;;
        停止)
             echo "netgraphスイッチを停止しています。"
             ngctl shutdown switch_socket:
             ngctlシャットダウンスイッチ:
             ngctl shutdown $ {if}:
            エコー「OK」
            出口0
             ;;
        再起動)
             sh $ 0ストップ
             sh $ 0スタート
             ;;
         *)
             echo "Usage:` basename $ 0` {start | stop | restart} "
             64番出口
             ;;
エサック


何が起こったのか見てみましょう

[root@bsd1] /usr/local/etc/rc.d/> ngctl list

There are 5 total nodes:

Name: em0 Type: ether ID: 00000001 Num hooks: 0

Name: em1 Type: ether ID: 00000002 Num hooks: 2

Name: switch Type: bridge ID: 000000f6 Num hooks: 3

Name: ngctl16408 Type: socket ID: 00000100 Num hooks: 0

Name: switch_socket Type: ksocket ID: 000000fa Num hooks: 1









タイプソケットを持つ「ngctl16408」モジュールは、制御のためにngctlによって使用されます。注意しないでください。



バッグの実行:

 [root @ bsd1] / root /> ping 192.168.1.2
 PING 192.168.1.2(192.168.1.2):56データバイト
 192.168.1.2から64バイト:icmp_seq = 0 ttl = 64 time = 3.760 ms
 192.168.1.2から64バイト:icmp_seq = 1 ttl = 64 time = 3.527 ms
 192.168.1.2から64バイト:icmp_seq = 2 ttl = 64 time = 3.479 ms
 192.168.1.2から64バイト:icmp_seq = 3 ttl = 64 time = 4.052 ms

 [root @ bsd1] / root /> ngctl msgスイッチ:getstats 2
 「[f6]:」からの応答「getstats」(4):
引数:{recvOctets = 49333 recvPackets = 532 recvMulticast = 467 recvBroadcast = 63 xmitOctets = 580 xmitPackets = 12 xmitMulticasts = 10 xmitBroadcasts = 1}




終わり



たくさんのテキストを書いたので、理解できるといいのですが。 実際には、そのようなトンネルは誰かに保護されていないように見えます。なぜなら、パケットはインターネットで開かれますが、VPN接続内でこのトンネルを転送する人は誰もいないからです。 たとえば、この束を使用して、IPTVマルチキャストを動作中のネットワークに転送します。



次の記事では、さらにいくつかのnetgraphモジュールについて説明します-ng_netflowトラフィックカウント、ng_natトラフィック、ng_carを介したシェーピングを結果のスキームに追加する、または他の興味深いものを思いつくかもしれません。



ご清聴ありがとうございました。



All Articles