2012年5月3日木曜日

OpenFlow Controllerフレームワーク(Python/POX, Trema/Ruby, C, Java)を試す(Simple Learning L2 Switch)


OpenFlow Controllerフレームワークを試すメモです。
すごく簡単なL2スイッチのコントローラを似た感じの実装で並べてみます。(たいてい、各フレームワークにはサンプルでL2スイッチがついてますが、それとは別で。)

まずは、POX(Python)とTrema(Ruby)で。(CはNOXを並べておきたいけど、おいおい。JavaはFloodlightもbeaconも微妙で悩み中、後者かなあ。)

OpenFlow Swithを使った環境の準備方法は、Mininetとか、OpenvSwitchとKVMを使うとか、PC(サーバー)に多ポートNICつけてみるとか。

0. プログラム概要


一台のOpenFlow Switch用のシンプルな学習L2スイッチ。(学習: スイッチのどのポートにどのMacアドレスの通信機器がついているかを保存しておいて、それなりに振舞う。)
このプログラムは、並べて眺めるのが目的なので、まじめに使っちゃ駄目です。

プログラム構成(色分け)
フレームワークや言語のしきたり
Macアドレスと ポートの辞書
OpenFlow Controller(本プログラム)スタート時の準備
OpenFlow Switchが接続した・切断した時の振る舞い
OpenFlow Switchからパケット扱いについて問い合わせがきた時の振る舞い
OpenFlow Switchへの命令(フローテーブル内容変更とか、パケット送出しろとか)

あと、ちょっと珍しい用語として
・datapathは、ざっくりOpenFlow Switchのことみたいな。(そのIDをdatapath_idだとかdpidだとか。)
・FLOODは、ざっくりブロードキャストみたいな。

1. POX(Python)


プログラムファイル: ofc1.py
from pox.core import *
from pox.lib.util import dpidToStr
import pox.openflow.libopenflow_01 as of

log = core.getLogger()
macPortDic = {}

def launch():
  log.info("Controller Launch")
  core.openflow.addListenerByName("PacketIn", packet_in)
  core.openflow.addListenerByName("ConnectionUp", connection_up)
  core.openflow.addListenerByName("ConnectionDown", connection_down)

def connection_up(event):
  log.info("Connection Up: datapath_id=%s" % dpidToStr(event.dpid))

def connection_down(event):
  log.info("Connection Down: datapath_id=%s" % dpidToStr(event.dpid))

def packet_in(event):
  packet = event.parse()
  log.info("Packet-in: datapath_id=%s, port=%d, src mac=%s, dst mac=%s" %
      (dpidToStr(event.dpid), event.port, packet.src, packet.dst))

  macPortDic[packet.src] = event.port

  if packet.dst in macPortDic:
    port = macPortDic[packet.dst]
    flow_mod(event, packet, port)
    packet_out(event, port)
  else:
    packet_out(event, of.OFPP_FLOOD)

# Controller-to-Switch Modify-State(Flow Table Modification)
def flow_mod(event, packet, port):
  msg = of.ofp_flow_mod()
  msg.match = of.ofp_match.from_packet(packet)
  msg.actions.append(of.ofp_action_output(port = port))
  msg.buffer_id = event.ofp.buffer_id
  event.connection.send(msg)

# Controller-to-Switch Send-Packet
def packet_out(event, port):
  msg = of.ofp_packet_out()
  msg.actions.append(of.ofp_action_output(port = port))
  msg.buffer_id = event.ofp.buffer_id
  msg.in_port = event.port
  event.connection.send(msg)

実行する
pox.py ofc1

2. Trema(Ruby)


プログラムファイル: ofc1.rb
class OFC1 < Controller

  def start
    info "Controller Launch"
    @macportdic = Hash::new
  end

  def switch_ready datapath_id
    info "Connection Up: datapathid=#{ datapath_id.to_hex }"
  end

  def switch_disconnected datapath_id
    info "Connection Down: datapathid=#{ datapath_id.to_hex }"
  end

  def packet_in datapath_id, message
    info "Packet In: datapath_id=%s, port=%d, src mac=%s, dst mac=%s",
        datapath_id.to_hex, message.in_port, message.macsa,  message.macda

    @macportdic[message.macsa] = message.in_port

    if @macportdic.has_key?(message.macda)
      port_no = @macportdic[message.macda]
      flow_mod datapath_id, message, port_no
      packet_out datapath_id, message, port_no
    else
      packet_out datapath_id, message, OFPP_FLOOD
    end
  end

# Controller-to-Switch Modify-State(Flow Table Modification)
  def flow_mod datapath_id, message, port_no
    send_flow_mod_add(
      datapath_id,
      :match => ExactMatch.from( message ),
      :actions => ActionOutput.new( :port => port_no )
    )
  end

# Controller-to-Switch Send-Packet
  def packet_out datapath_id, message, port_no
    send_packet_out(
      datapath_id,
      :packet_in => message,
      :actions => ActionOutput.new( :port => port_no )
    )
  end

end


実行する
trema run ofc1.rb

--
以上

0 件のコメント:

コメントを投稿