From 412f01f1f571260342fc462ef7a810453a53b589 Mon Sep 17 00:00:00 2001 From: Yasuhito Takamiya Date: Mon, 9 Feb 2015 13:57:36 +0900 Subject: [PATCH] Add new example `MultiLearningSwitch`. --- CHANGELOG.md | 3 +- Gemfile.lock | 6 +-- README.md | 18 +++++-- features/multi_learning_switch.feature | 67 ++++++++++++++++++++++++++ lib/learning_switch.rb | 12 +---- lib/multi_learning_switch.rb | 50 +++++++++++++++++++ trema.multi.conf | 17 +++++++ 7 files changed, 154 insertions(+), 19 deletions(-) create mode 100644 features/multi_learning_switch.feature create mode 100644 lib/multi_learning_switch.rb create mode 100644 trema.multi.conf diff --git a/CHANGELOG.md b/CHANGELOG.md index c13ba3d..5b76895 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,10 @@ # Change log ## develop (unreleased) +### New features +* Add new example `MultiLearningSwitch`. ## 0.1.0 (2/9/2015) - ### Misc * The initial release version of hello_trema that runs on [pure-Ruby Trema](https://github.com/trema/trema_ruby). diff --git a/Gemfile.lock b/Gemfile.lock index 76db189..6c1ffcd 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,6 +1,6 @@ GIT remote: git://github.com/trema/phut.git - revision: 91b98a6fa1d2e5342051df2911b0c1ec5d06d262 + revision: 97525671c6963cc22d62af4f1be6735c01cdf4ad branch: develop specs: phut (0.1.0) @@ -10,7 +10,7 @@ GIT GIT remote: git://github.com/trema/trema_ruby.git - revision: 82aaa786bab37f853dd5e2aca3a095be35b99658 + revision: 09cc4ab6078b4a32df06c67f74750ca3609797bc branch: develop specs: trema (0.1.0) @@ -73,7 +73,7 @@ GEM given_core (3.6.0) sorcerer (>= 0.3.7) gli (2.12.2) - guard (2.11.1) + guard (2.12.0) formatador (>= 0.2.4) listen (~> 2.7) lumberjack (~> 1.0) diff --git a/README.md b/README.md index ca68979..9766812 100644 --- a/README.md +++ b/README.md @@ -7,8 +7,7 @@ Learning Switch [![Dependency Status](http://img.shields.io/gemnasium/trema/learning_switch.svg?style=flat)][gemnasium] [![Inline docs](http://inch-ci.org/github/trema/learning_switch.png?branch=develop)][inch] -An OpenFlow controller that controls a single OpenFlow switch and -emulates a layer 2 switch. +An OpenFlow controller that emulates layer 2 switches. [travis]: http://travis-ci.org/trema/learning_switch [coveralls]: https://coveralls.io/r/trema/learning_switch @@ -29,14 +28,15 @@ $ bundle install Play ---- -Run this controller: +The `lib/learning_switch.rb` is an OpenFlow controller implementation +that emulates a layer 2 switch. Run this like so: ``` % bundle exec trema run lib/learning_switch.rb -c trema.conf ``` -Send some packets from host1 to host2, and show received packet stats -of host2: +Then send some packets from host1 to host2, and show received packet +stats of host2: ``` % bundle exec trema send_packets --source host1 --dest host2 --n_pkts 10 @@ -45,4 +45,12 @@ ip_dst,tp_dst,ip_src,tp_src,n_pkts,n_octets 192.168.0.2,1,192.168.0.1,1,10,500 ``` +The `lib/multi_learning_switch.rb` is an extension to +`lib/learning_switch.rb`. It can emulate multiple OpenFlow switches. + +``` +% trema run ./lib/multi_learning_switch.rb -c trema.multi.conf +``` + + Enjoy! diff --git a/features/multi_learning_switch.feature b/features/multi_learning_switch.feature new file mode 100644 index 0000000..ca982bf --- /dev/null +++ b/features/multi_learning_switch.feature @@ -0,0 +1,67 @@ +Feature: "Multi Learning Switch" example + Background: + Given a file named ".trema/config" with: + """ + LOG_DIR: . + PID_DIR: . + SOCKET_DIR: . + """ + Given a file named "trema.conf" with: + """ + vswitch('lsw1') { datapath_id '0x1' } + vswitch('lsw2') { datapath_id '0x2' } + vswitch('lsw3') { datapath_id '0x3' } + vswitch('lsw4') { datapath_id '0x4' } + + vhost('host1') + vhost('host2') + vhost('host3') + vhost('host4') + + link 'lsw1', 'host1' + link 'lsw2', 'host2' + link 'lsw3', 'host3' + link 'lsw4', 'host4' + link 'lsw1', 'lsw2' + link 'lsw2', 'lsw3' + link 'lsw3', 'lsw4' + """ + + Scenario: Run + Given I successfully run `trema run ../../lib/multi_learning_switch.rb -c trema.conf -d` + And I run `sleep 10` + When I successfully run `trema send_packets --source host1 --dest host2 --n_pkts 2` + Then the total number of tx packets should be: + | host1 | host2 | host3 | host4 | + | 2 | 0 | 0 | 0 | + And the total number of rx packets should be: + | host1 | host2 | host3 | host4 | + | 0 | 2 | 0 | 0 | + When I successfully run `trema send_packets --source host3 --dest host4 --n_pkts 3` + Then the total number of tx packets should be: + | host1 | host2 | host3 | host4 | + | 2 | 0 | 3 | 0 | + And the total number of rx packets should be: + | host1 | host2 | host3 | host4 | + | 0 | 2 | 0 | 3 | + When I successfully run `trema send_packets --source host4 --dest host1 --n_pkts 2` + Then the total number of tx packets should be: + | host1 | host2 | host3 | host4 | + | 2 | 0 | 3 | 2 | + And the total number of rx packets should be: + | host1 | host2 | host3 | host4 | + | 2 | 2 | 0 | 3 | + When I successfully run `trema send_packets --source host2 --dest host3 --n_pkts 4` + Then the total number of tx packets should be: + | host1 | host2 | host3 | host4 | + | 2 | 4 | 3 | 2 | + And the total number of rx packets should be: + | host1 | host2 | host3 | host4 | + | 2 | 2 | 4 | 3 | + When I successfully run `trema send_packets --source host1 --dest host4 --n_pkts 1` + Then the total number of tx packets should be: + | host1 | host2 | host3 | host4 | + | 3 | 4 | 3 | 2 | + And the total number of rx packets should be: + | host1 | host2 | host3 | host4 | + | 2 | 2 | 4 | 4 | diff --git a/lib/learning_switch.rb b/lib/learning_switch.rb index 45e3e83..5edd208 100644 --- a/lib/learning_switch.rb +++ b/lib/learning_switch.rb @@ -24,12 +24,8 @@ def age_fdb def flow_mod_and_packet_out(message) port_no = @fdb.lookup(message.destination_mac) - if port_no - flow_mod message, port_no - packet_out message, port_no - else - flood message - end + flow_mod(message, port_no) if port_no + packet_out(message, port_no || :flood) end def flow_mod(message, port_no) @@ -47,8 +43,4 @@ def packet_out(message, port_no) actions: SendOutPort.new(port_no) ) end - - def flood(message) - packet_out message, :flood - end end diff --git a/lib/multi_learning_switch.rb b/lib/multi_learning_switch.rb new file mode 100644 index 0000000..b2bfc45 --- /dev/null +++ b/lib/multi_learning_switch.rb @@ -0,0 +1,50 @@ +$LOAD_PATH.unshift __dir__ + +require 'fdb' + +# An OpenFlow controller that emulates multiple layer-2 switches. +class MultiLearningSwitch < Trema::Controller + timer_event :age_fdbs, interval: 5.sec + + def start(_argv) + @fdbs = {} + end + + def switch_ready(datapath_id) + @fdbs[datapath_id] = FDB.new + end + + def packet_in(datapath_id, message) + return if message.destination_mac.reserved? + @fdbs.fetch(datapath_id).learn(message.source_mac, message.in_port) + flow_mod_and_packet_out message + end + + def age_fdbs + @fdbs.each_value(&:age) + end + + private + + def flow_mod_and_packet_out(message) + port_no = @fdbs.fetch(message.dpid).lookup(message.destination_mac) + flow_mod(message, port_no) if port_no + packet_out(message, port_no || :flood) + end + + def flow_mod(message, port_no) + send_flow_mod_add( + message.datapath_id, + match: ExactMatch.new(message), + actions: SendOutPort.new(port_no) + ) + end + + def packet_out(message, port_no) + send_packet_out( + message.datapath_id, + packet_in: message, + actions: SendOutPort.new(port_no) + ) + end +end diff --git a/trema.multi.conf b/trema.multi.conf new file mode 100644 index 0000000..8d0ce04 --- /dev/null +++ b/trema.multi.conf @@ -0,0 +1,17 @@ +vswitch("lsw1") { datapath_id "0x1" } +vswitch("lsw2") { datapath_id "0x2" } +vswitch("lsw3") { datapath_id "0x3" } +vswitch("lsw4") { datapath_id "0x4" } + +vhost("host1") +vhost("host2") +vhost("host3") +vhost("host4") + +link "lsw1", "host1" +link "lsw2", "host2" +link "lsw3", "host3" +link "lsw4", "host4" +link "lsw1", "lsw2" +link "lsw2", "lsw3" +link "lsw3", "lsw4"