mininetとは
おじいさんなので昔話から始める。むかしむかし、複数のネットワーク機器を使った実験をしようとすれば、当然物理のネットワーク機器を複数台用意しなければならず、ネットワーク資源の潤沢な研究室でもなければそんな研究はできなかったもんさ (諸説あり) 。
そのうち仮想化技術のおかげで、仮想計算機にネットワーク・インターフェイス・カード (Network Interface Card: NIC) をぽこぽこ作成し、異なる仮想計算機のそれらを相互に接続すれば仮想的なネットワークが簡単にできるようになったのじゃよ。まあ素敵。しかし仮想計算機はそれなりにメモリや補助記憶装置の容量を占有するので、数十個、数百個の機器が接続された仮想ネットワークを構築しようとすると、素敵ではあるのだけれど無駄が多いわね、というのが正直なお気持ちだったもんさ。
そこでコンテナじゃよ。コンテナでは計算機そのものをエミュレートする愚を犯さず、プロセスやメモリ空間、ファイルシステムやネットワークを分離するするので、無駄なく仮想ネットワークを構築できる。その実装が mininet というわけさ。
SRv6とは
おじいさんなので昔話から以下略。IP ネットワークでは、とある始点 (Source: Src.) でオギャァと生まれた IP パケットには目的地 (Destination: Dst.) となる IP アドレスが記載されておる。この IP パケットがどのような経路をたどって Dst. まで到達するかというと、Dst. までに存在する経路制御装置がその役割を担う。経路制御装置は静的に、あるいは動的に経路制御表を作成し、ある経路制御装置に到着した IP パケットはこの経路制御表にしたがって次の経路制御装置なりに送り届けられる。日本シリーズで優勝したチームの監督が胴上げされているのを見るとき、監督は空中遊泳しているように見えるかもしれないが (そんな純粋な認知をする人はこの記事を読んでいないだろうが) 実際はそうでないように、IP パケットは自分の意志で Dst. へ向けて進んでいるように見えて実際は経路制御装置に運ばれているだけなのである。
Internet Protocol の仕様が記載されている RFC 791 を紐解くと、その頃から「経路制御装置と経路制御プロトコルにまかせることなく、Src. で経路を制御したい!」という需要があったことが分かる。strict source and record route (SSRR) と loose source and record route (LSRR) がそれである。これら始点制御 (source routing) はその名の通り、Src. で経路を制御する手法であり、途中の経路制御装置がどのような経路制御表を持っていようと始点で指定された経路でパケットは転送される。これとて IP パケットが自分の意志で動いているわけではないのだが、自由意志とは何かという議論になりかねないので、それはさておいておこう。source routing はしかし、当時の経路制御装置の資源的・機能的制約があり、セキュリティ上の懸念も指摘され、広まることはなかった。とはいえ僕の兄弟子にあたる先生は遺伝的アルゴリズムと始点制御で博士論文を書いていたりする。それはともかく。
2000 年代に入っても始点制御でトラフィック制御を行おう、経路の割り当てを効率的に行うことでトラフィック要求をより多く許容できるようにしよう、といった提案や研究が行われてきた。Active Networking と呼ばれる手法であり、後に Software Defined Network へと発展する、と繋げて良いのかどうかは議論の余地があるかもしれない。IETF の Source Packet Routing in Networking (spring) ワーキンググループで議論され 2014 年に Internet-Draft である Segment Routing Architecture が提案された。これが Segment Routing (SR) である。具体的な実装の順番としては MPLS で SR を実現する SR-MPLS (RFC 8660, 8663) が、ついで IPv6 での実装である SRv6 (RFC 8402, 8754, 8986) が提案された。
SRv6 は Linux Kernel 4.10 でマージされたので、これ以降のカーネルを持つ Linux であれば SRv6 の実験をすることができる。たとえば Ubuntu Linux でいえば 2017 年に公開された Ubuntu 17.04 が Linux Kernel 4.10 を採用しているので、これ以降の Ubuntu であれば SRv6 を利用することが可能である。というわけで、今時の Ubuntu 24.04 LTS であれば全く問題なく SRv6 を利用することができる。
いまどきの Apple Sillicon を登載した Mac 製品で Ubuntu 24.04 LTS を利用しようとするとき、UTM を使うのがおすすめである。Ubuntu Server で ARM アーキテクチャを選択して ISO イメージをダウンロードすれば、あとは一般的な手続きで Ubuntu 24.04 LTS をインストールすることができる。この手順についてはまた別口で記事を書こう。いたってシンプルに仮想マシンを作ることができるのだけれども、初めての人には手引き書も必要だろうから。
mininet のインストール
Ubuntu 24.04 で mininet をインストールする。これは大変に簡単で、
% sudo apt install mininet
これでインストールできてしまう。
ネットワークトポロジの作成
実験ネットワークを構築するにあたり、以下の構成要素が必要となる。
- ホスト: Dst. や Src. に相当する、パケットを送信する、受信する計算機。
- スイッチ: L2 でホストやルータの機器を接続するネットワーク機器。
- ルータ: 異なる L3 ネットワークを接続する。
このうち mininet のデフォルトで用意されているのは、ホストとスイッチだけであり、ルータは自分で作らなければならない。そこでまず、ホストとスイッチだけからなるネットワークを作ってみることにしよう。この、どのように機器がつながっているのかという形のことをネットワークトポロジという。
ホストとホストをスイッチで接続する。
mininet は Python のライブラリの形で様々な環境が提供されている。そのため、ネットワークトポロジを作るのも、Python で記述することになる。トポロジについて記述しようとすると、mininet.topo というモジュールを読み込む必要がある。ホストやスイッチについて記述しようとすると、mininet.node というモジュールを読み込む必要がある。
ということで、Python スクリプトの最初でモジュールの読み込みを行うことから始める。
from mininet.topo import Topo
from mininet.node import Node, OVSBridge
これは実際に mininet の topo.py (mininet/mininet/topo.py) を読んでみると良い。
#!/usr/bin/env python
"""@package topo
Network topology creation.
@author Brandon Heller (brandonh@stanford.edu)
This package includes code to represent network topologies.
A Topo object can be a topology database for NOX, can represent a physical
setup for testing, and can even be emulated with the Mininet package.
"""
from mininet.util import irange, natural, naturalSeq
このようにはじまる。そして読み進めてみると、
class Topo( object ):
"Data center network representation for structured multi-trees."
def __init__( self, *args, **params ):
"""Topo object.
Optional named parameters:
hinfo: default host options
sopts: default switch options
lopts: default link options
calls build()"""
self.g = MultiGraph()
self.hopts = params.pop( 'hopts', {} )
self.sopts = params.pop( 'sopts', {} )
self.lopts = params.pop( 'lopts', {} )
# ports[src][dst][sport] is port on dst that connects to src
self.ports = {}
self.build( *args, **params )
のように続く。Python で from mininet.topo import Topo とすることで、topo.py から class Topo だけを抜き出して利用することができる。同様にして from mininet.node import Node, OVSBridge も理解できるだろう。
topo.py で定義される Topo クラスのサブクラスを作り、そのインスタンスを作ることで mininet で扱えるトポロジが定義される。ではサブクラスを作ってトポロジを定義してみよう。たとえば以下のようなホスト1 (h1) とホスト2 (h2) がスイッチ1 (s1) で接続されているトポロジを作ってみる。
これを実現するサブクラスはこのように記述する。
class MyTopology(Topo):
def build(self):
s1 = self.addSwitch('s1', cls=OVSBridge)
h1 = self.addHost('h1')
h2 = self.addHost('h2')
self.addLink(h1, s1)
self.addLink(s1, h2)
スイッチの追加は topo モジュールで定義されている addSwitch メソッドを利用する。node モジュールを読むと OVSBridge クラスが記述されており、addSwitch メソッド呼出しの第 2 引数で指定されている cls=OVSBridge がどのように処理されているかは topo.py と node.py の記述を辿ってみるとよい。
同様にホストの追加は addHost メソッドであり、こちらにはオプションの指定をすることはできない。最後にこれらスイッチとホストを addLink メソッドで接続する。
最後にこのサブクラスに対する key を与えて、topos という辞書に登録する。たとえば以下のように。指定した key (ここでは mytopology) は後に使うので覚えておこう。
topos = { 'mytopology': MyTopology }
このようにして作ったトポロジファイル全体は以下のようになる。simple_L2_topology.py とでもファイル名をつけて保存しておこう。
from mininet.topo import Topo
from mininet.node import Node, OVSBridge
class MyTopology(Topo):
def build(self):
s1 = self.addSwitch('s1', cls=OVSBridge)
h1 = self.addHost('h1')
h2 = self.addHost('h2')
self.addLink(h1, s1)
self.addLink(s1, h2)
topos = { 'mytopology': MyTopology }
さて、このトポロジーファイルで mininet するためには mininet のコマンドラインランチャーである mn コマンドを使う。mn コマンドの実行には管理者権限が必要であり、--custom オプションと --topo オプションを使うことで、自作したトポロジーファイルをシミュレートする環境が整う。mn コマンドのオプションについては mn --help を実行することで参照することができる。--custom オプションは続けて Python のファイルを指定することにより、そのファイルに記述されたカスタムのクラスやパラメータを読み込むことができる。--topo オプションは、mininet で用意された各種トポロジを指定するオプションだが、--custom オプションで独自のクラスが読み込まれている場合、そのクラスの key をここで指定することで、そのクラスをインスタンス化することができる。
% sudo mn --custom simple_L2_topology.py --topo mytopology
するとこうなる。
*** No default OpenFlow controller found for default switch!
*** Falling back to OVS Bridge
*** Creating network
*** Adding controller
*** Adding hosts:
h1 h2
*** Adding switches:
s1
*** Adding links:
(h1, s1) (s1, h2)
*** Configuring hosts
h1 h2
*** Starting controller
*** Starting 1 switches
s1 ...
*** Starting CLI:
mininet>
mininet の利用 (1) ping してみる。
作られたトポロジで、たとえば h1 から h2 に 5 発 ping してみたいと思ったら mininet> プロンプトで h1 ping -c 5 h2 のようにする。
mininet> h1 ping -c 5 h2
PING 10.0.0.2 (10.0.0.2) 56(84) bytes of data.
64 bytes from 10.0.0.2: icmp_seq=1 ttl=64 time=1.37 ms
64 bytes from 10.0.0.2: icmp_seq=2 ttl=64 time=0.230 ms
64 bytes from 10.0.0.2: icmp_seq=3 ttl=64 time=0.134 ms
64 bytes from 10.0.0.2: icmp_seq=4 ttl=64 time=0.289 ms
64 bytes from 10.0.0.2: icmp_seq=5 ttl=64 time=0.194 ms
--- 10.0.0.2 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4100ms
rtt min/avg/max/mdev = 0.134/0.443/1.372/0.466 ms
つづく!(クリスマスに間に合わせるためにとりあえずここまででっちあげた)
