Code Monkey home page Code Monkey logo

pio's Introduction

Welcome to Trema

Build Status Code Climate Coverage Status Dependency Status

Trema is an OpenFlow controller programming framework that provides everything needed to create OpenFlow controllers in Ruby. It provides a high-level OpenFlow library and also a network emulator that can create OpenFlow-based networks for testing on your PC. This self-contained environment helps streamlines the entire process of development and testing.

Prerequisites

  • Ruby 2.0.0 or higher (RVM).
  • Open vSwitch (apt-get install openvswitch-switch).

Documentation

See https://relishapp.com/trema/trema/docs for links to documentation for all APIs.

Sample Code

Study sample code for implementation examples of Trema features. Each sample code project is executable source example of how to write a OpenFlow controller using Trema Ruby API.

Contributors

Special thanks to all contributors for submitting patches. A full list of contributors including their patches can be found at:

https://github.com/trema/trema/contributors

License

Trema is released under the GNU General Public License version 2.0 or MIT License:

pio's People

Contributors

hkwi avatar otahi avatar rrrene avatar shun159 avatar yasuhito avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

pio's Issues

`.new` options macro.

@shun159 .new するときの必須オプションチェックの部分などをマクロにしたので,
DHCP もこれを使うように修正おねがいできますか? たとえば Pio::Lldp.new
オプションはこんな仕様です.

  • 必須
    • :dpid
    • :port_number
  • オプション
    • :destination_mac: デフォルト = '01:80:c2:00:00:0e'
    • :source_mac: デフォルト = '01:02:03:04:05:06'

新しいマクロを使うと, これは次のように書けます.

# lib/pio/lldp/options.rb
require 'pio/options'

module Pio
  class Lldp
    class Options < Pio::Options
      mandatory_option :dpid
      mandatory_option :port_number
      option :destination_mac
      option :source_mac

      DEFAULT_DESTINATION_MAC = '01:80:c2:00:00:0e'
      DEFAULT_SOURCE_MAC = '01:02:03:04:05:06'

      def initialize(options)
        validate_options(options)
        @dpid = options[:dpid]
        @port_id = options[:port_number]
        @destination_mac =
          Mac.new(options[:destination_mac] || DEFAULT_DESTINATION_MAC)
        @source_mac = Mac.new(options[:source_mac] || DEFAULT_SOURCE_MAC)
      end

      def to_hash
        {
         chassis_id: @dpid,
         port_id: @port_id,
         destination_mac: @destination_mac,
         source_mac: @source_mac
        }
      end
    end
  end
end


# lib/pio/lldp.rb
module Pio
  class Lldp

    # ...

    def initialize(options)
      @frame = Frame.new(Options.new(options).to_hash)
    end

Add cucumber scenarios

.pcap ファイルを使ってパーサをテストする仕組みを作りました。実際のパケットデータがきちんとパースできるかのテストです。

@shun159 DHCP パーサについて、なるべく多くの .pcap でテストを追加してください。

使いかた:

  1. features/pcap/ に .pcap ファイルを足す
  2. features/*.feature を書く (features/arp_read.feature を参考に)
  3. rake cucumber でテストを実行

.pcap はたとえば以下からダウンロードできます。

他にもいい場所があったら教えてください。

StripVlanHeaderを含むバイナリをパースした場合に、SystemStackErrorが起こる。

@yasuhito

下のバイナリをパースした際、SystemStackErrorが起こりました。
バイナリデータ誤りありましたら申し訳ありませんが、よろしくお願いします。

require 'bindata'

module Pio
  # An action to strip the 802.1q header.
  class StripVlanHeader < BinData::Record
    endian :big

    uint16 :type, value: 3
    uint16 :message_length, value: 8
    uint32 :padding
    hide :padding

    alias_method :to_binary, :to_binary_s

    def snapshot
      self # <= ここでエラー?
    end
  end
end
binary = [1,13,0,200,0,0,0,0,255,255,255,255,255,255,0,120,0,1,0,8,0,10,0,0,0,2,0,8,0,0,0,0,0,3,0,8,0,0,0,0,0,4,0,16,0,0,0,0,0,1,0,0,0,0,0,0,0,5,0,16,0,0,0,0,0,2,0,0,0,0,0,0,0,6,0,8,10,0,0,1,0,7,0,8,20,0,0,2,0,8,0,8,184,0,0,0,0,9,0,8,0,8,0,0,0,10,0,8,0,0,0,0,0,11,0,16,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,8,0,1,0,128,0,38,130,235,234,209,0,22,157,29,156,196,8,0,69,0,0,50,0,0,0,0,128,1,18,121,192,168,83,3,192,168,83,254,8,0,246,255,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0].pack "C*"

出力以下の通りです。

 pp PacketOut.read binary
#<Pio::PacketOut:0x00000002ffbe08
 @format=
  {:open_flow_header=>
    {:ofp_version=>1,
     :message_type=>13,
     :message_length=>200,
     :transaction_id=>0},
   :body=>
    {:buffer_id=>4294967295,
     :in_port=>65535,
     :actions=>
      [#<Pio::SetVlanVid:0x00000002ff92c0
        @format={:type=>1, :message_length=>8, :vlan_id=>10}>,
       #<Pio::SetVlanPriority:0x00000002ff3280
        @format={:type=>2, :message_length=>8, :vlan_priority=>0}>,
       #<Pio::StripVlanHeader:0x00000002ff0af8 ...>,
       #<Pio::SetEthSrcAddr:0x00000002fdf0a0
        @format=
         {:type=>4,
          :message_length=>16,
          :mac_address=>#<Pio::Mac:25093420 "00:00:00:00:00:01">}>,
       #<Pio::SetEthDstAddr:0x00000002fdc198
        @format=
         {:type=>5,
          :message_length=>16,
          :mac_address=>#<Pio::Mac:25069920 "00:00:00:00:00:02">}>,
       #<Pio::SetIpSrcAddr:0x00000002fd0d48
        @format=
         {:type=>6,
          :message_length=>8,
          :ip_address=>
           #<Pio::IPv4Address:0x00000002fc9c00
            @value=#<IPAddr: IPv4:10.0.0.1/255.255.255.255>>}>,
       #<Pio::SetIpDstAddr:0x00000002fc94a8
        @format=
         {:type=>7,
          :message_length=>8,
          :ip_address=>
           #<Pio::IPv4Address:0x00000002fbedc8
            @value=#<IPAddr: IPv4:20.0.0.2/255.255.255.255>>}>,
       #<Pio::SetIpTos:0x00000002fbe350
        @format={:type=>8, :message_length=>8, :type_of_service=>184}>,
       #<Pio::SetTransportSrcPort:0x00000002fbc730
        @format={:type=>9, :message_length=>8, :port_number=>8}>,
       #<Pio::SetTransportDstPort:0x00000002fb3838
        @format={:type=>10, :message_length=>8, :port_number=>0}>,
       #<Pio::Enqueue:0x00000002fb1290
        @format=
         {:type=>11, :message_length=>16, :port_number=>1, :queue_id=>1}>,
       #<Pio::SendOutPort:0x00000002f90c98
        @format=
         {:type=>0, :message_length=>8, :port_number=>1, :max_len=>128}>],
     :data=>
      "\x00&\x82\xEB\xEA\xD1\x00\x16\x9D\x1D\x9C\xC4\b\x00E\x00\x002\x00\x00\x00\x00\x80\x01\x12y\xC0\xA8S\x03\xC0\xA8S\xFE\b\x00\xF6\xFF\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"}}>
SystemStackError: stack level too deep
        from /usr/local/rvm/gems/ruby-2.0.0-p247/gems/pio-0.9.0/lib/pio/strip_vlan_header.rb:16

仮想ポートをシンボルで扱う

以下の仮想ポートをSymbolで扱えますか。

OFPP_MAX = 0xff00
OFPP_IN_PORT = 0xfff8
OFPP_TABLE = 0xfff9
OFPP_NORMAL = 0xfffa
OFPP_FLOOD = 0xfffb
OFPP_ALL = 0xfffc
OFPP_CONTROLLER = 0xfffd
OFPP_LOCAL = 0xfffe
OFPP_NONE = 0xffff

イメージはこうでしょうか

in_port: :flood

Support cisco-style MAC addresses

Hi,

it would be great if you would also support cisco style MAC addresses as input for Pio::Mac.new out of the box.

They look e.g. like "002a.6abc.a12c"

A bit ugly but working for me (I added the elsif-part):

def parse_mac_string(mac)
  octet_regex = '[0-9a-fA-F][0-9a-fA-F]'
  if /^(#{ octet_regex }:){5}(#{ octet_regex })$/ =~ mac
    mac.gsub(':', '').hex
  elsif /^(#{ octet_regex+octet_regex }.){2}(#{ octet_regex+octet_regex })$/ =~ mac
    mac.gsub('.', '').hex
  else
    fail ArgumentError
  end
end
  • Sebbb

LLDPフレーム構築の方法

lldp = Pio::Lldp.new(dpid: 0x123, port_number: 12)
lldp.to_binary

以上のコードでLLDPフレームを構築できることは確認しましたが、Chassis ID TLVやPort ID TLV以外のTLVを生成する方法が分かりません。
例えば、System name TLVやManagement Address TLVを生成する方法を教えていただけますでしょうか。

よろしくお願いいたします。

" error: undefined method `key?' for nil:NilClass " のエラーの原因について

サンプルプログラムの”trema/repeater_hub”を、trema.confを用いたエミュレートではなく、
KVM上で同じトポロジーを作成して実行したところ、下記のエラーが出ました。

[ 実行環境 ]
ホストOS : Ubuntu12.04
ゲストOS : Centos7

エラー内容
error: undefined method `key?' for nil:NilClass
/usr/local/rvm/gems/ruby-2.2.1/gems/pio-0.23.1/lib/pio/open_flow10/match.rb:137:in `block in initialize': undefined method `key?' for nil:NilClass (NoMethodError)
        from /usr/local/rvm/gems/ruby-2.2.1/gems/pio-0.23.1/lib/pio/open_flow10/match.rb:136:in `each'
        from /usr/local/rvm/gems/ruby-2.2.1/gems/pio-0.23.1/lib/pio/open_flow10/match.rb:136:in `each_with_object'
        from /usr/local/rvm/gems/ruby-2.2.1/gems/pio-0.23.1/lib/pio/open_flow10/match.rb:136:in `initialize'
        from /usr/local/rvm/gems/ruby-2.2.1/gems/pio-0.23.1/lib/pio/open_flow10/exact_match.rb:47:in `new'
        from /usr/local/rvm/gems/ruby-2.2.1/gems/pio-0.23.1/lib/pio/open_flow10/exact_match.rb:47:in `initialize'
        from /root/repeater_hub/lib/repeater_hub.rb:12:in `new'
        from /root/repeater_hub/lib/repeater_hub.rb:12:in `packet_in'
        from /usr/local/rvm/gems/ruby-2.2.1/bundler/gems/trema-63d81024e1a2/lib/trema/controller.rb:319:in `block in send_handler'
        from /usr/local/rvm/gems/ruby-2.2.1/bundler/gems/trema-63d81024e1a2/lib/trema/controller.rb:319:in `synchronize'
        from /usr/local/rvm/gems/ruby-2.2.1/bundler/gems/trema-63d81024e1a2/lib/trema/controller.rb:319:in `send_handler'
        from /usr/local/rvm/gems/ruby-2.2.1/bundler/gems/trema-63d81024e1a2/lib/trema/controller.rb:324:in `maybe_send_handler'
        from /usr/local/rvm/gems/ruby-2.2.1/bundler/gems/trema-63d81024e1a2/lib/trema/controller.rb:296:in `handle_openflow_message'
        from /usr/local/rvm/gems/ruby-2.2.1/bundler/gems/trema-63d81024e1a2/lib/trema/controller.rb:264:in `block in start_switch_main'
        from /usr/local/rvm/gems/ruby-2.2.1/bundler/gems/trema-63d81024e1a2/lib/trema/controller.rb:264:in `loop'
        from /usr/local/rvm/gems/ruby-2.2.1/bundler/gems/trema-63d81024e1a2/lib/trema/controller.rb:264:in `start_switch_main'
        from /usr/local/rvm/gems/ruby-2.2.1/bundler/gems/trema-63d81024e1a2/lib/trema/controller.rb:256:in `block in start_switch_thread'

そこで、"/usr/local/rvm/gems/ruby-2.2.1/gems/pio-0.23.1/lib/pio/open_flow10/exact_match.rb"のコンストラクタのcase文に下記のコードを加えたところ正常に動きました。

加えたコード
      when Pio::Arp::Reply
        options = {
          in_port: packet_in.in_port,
          ether_source_address: packet_in.source_mac,
          ether_destination_address: packet_in.destination_mac,
          vlan_vid: data.vlan_vid,
          vlan_priority: data.vlan_pcp,
          ether_type: data.ether_type,
          ip_tos: 0,
          ip_protocol: data.operation,
          ip_source_address: data.sender_protocol_address,
          ip_destination_address: data.target_protocol_address,
          transport_source_port: 0,
          transport_destination_port: 0
        }

(*) ARP requestのcaseをコピーしてreplyに変更しただけです。

このことから、ARP replyに対する処理ができていなかったものと思われますが、どうしてでしょうか?
また、ほかに解決方法はあるでしょうか?

回答よろしくお願いいたします。

バイナリ変換ツール

皆様、

取得したデータを変換するツールについて、
こちらで議論・相談させてください。

宜しくお願い致します。

Refactor DHCP specs using rspec_given

@shun159 たてつづけにすいません.

最近テストを整理するために rspec_given というやつでテストをすべて書き直したので, DHCP もその流儀で修正お願いできますか?

書きかたは ARP とかの部分を見てもらえれば何となく分かると思いますが, rspec_given とは テストの Given, When, Then を _spec.rb に明示的に書けるようにして,読みやすくしたものです.

rake flogでエラー

お疲れ様です。
近藤です。

表題の通りですが、
rake flogを実行すると下記のようなエラーになりました。
https://travis-ci.org/trema/pio/jobs/13373275#L135

Coverage is at 99.2%.
Coverage report sent to Coveralls.
0 total warnings
rake aborted!
can't convert Array into Float
/home/travis/build/trema/pio/Rakefile:43:in `printf'
/home/travis/build/trema/pio/Rakefile:43:in `block (2 levels) in <top (required)>'
/home/travis/build/trema/pio/Rakefile:42:in `each'
/home/travis/build/trema/pio/Rakefile:42:in `block in <top (required)>'
/home/travis/.rvm/gems/ruby-1.9.3-p448/bin/ruby_noexec_wrapper:14:in `eval'
/home/travis/.rvm/gems/ruby-1.9.3-p448/bin/ruby_noexec_wrapper:14:in `<main>'

Tasks: TOP => travis => quality => flog

(See full trace by running task with --trace)

下記の行が該当していそうに見えたのですが、
間違いでしたら、もうしわけありません。

https://github.com/trema/pio/blob/develop/Rakefile#L43

    printf "%8.1f: %s\n", [score, name]

宜しくお願いします。

Pio::FlowModについて

※誤りありましたらすみません…

このようなデータを与え、Match.newをしました。

{:in_port=>1,
 :dl_src=>"00:00:00:00:00:0a",
 :dl_dst=>"00:00:00:00:00:14",
 :dl_vlan=>0,
 :dl_vlan_pcp=>0,
 :dl_type=>2048,
 :nw_tos=>0,
 :nw_proto=>1,
 :nw_src=>"10.0.0.0/8",
 :nw_dst=>"20.0.0.0/8",
 :tp_src=>8,
 :tp_dst=>0}

pp Match.new match
{:wildcards=>
  [:dl_vlan, :tp_dst, {:nw_src=>24}, {:nw_dst=>24}, :dl_vlan_pcp, :nw_tos],
 :in_port=>1,
 :dl_src=>#<Pio::Mac:21221080 "00:00:00:00:00:0a">,
 :dl_dst=>#<Pio::Mac:21217820 "00:00:00:00:00:14">,
 :dl_vlan=>0,
 :dl_vlan_pcp=>0,
 :dl_type=>2048,
 :nw_tos=>0,
 :nw_proto=>1,
 :nw_src=>
  #<Pio::IPv4Address:0x00000002882230
   @value=#<IPAddr: IPv4:10.0.0.0/255.255.255.255>>,
 :nw_dst=>
  #<Pio::IPv4Address:0x00000002880138
   @value=#<IPAddr: IPv4:20.0.0.0/255.255.255.255>>,
 :tp_src=>8,
 :tp_dst=>0}

Trema::Matchでは以下のとおりとなりますので、
wildcardsの表示が違うように見えます。

ruby
> match =  Match.new( {:in_port=>1, :dl_src=>"00:00:00:00:00:0a", :dl_dst=>"00:00:00:00:00:14", :dl_vlan=>0, :dl_vlan_pcp=>0, :dl_type=>2048, :nw_tos=>0, :nw_proto=>1, :nw_src=>"10.0.0.0/8", :nw_dst=>"20.0.0.0/8", :tp_src=>8, :tp_dst=>0} )
> match.to_s
 => "wildcards = 0x61800(nw_src(24)|nw_dst(24)), in_port = 1, dl_src = 00:00:00:00:00:0a, dl_dst = 00:00:00:00:00:14, dl_vlan = 0, dl_vlan_pcp = 0, dl_type = 0x800, nw_tos = 0, nw_proto = 1, nw_src = 10.0.0.0/8, nw_dst = 20.0.0.0/8, tp_src = 8, tp_dst = 0"

となっております。

また、以下をするとRuntimeErrorを起こします。

match =  Match.new( {:in_port=>1, :dl_src=>"00:00:00:00:00:0a", :dl_dst=>"00:00:00:00:00:14", :dl_vlan=>0, :dl_vlan_pcp=>0, :dl_type=>2048, :nw_tos=>0, :nw_proto=>1, :nw_src=>"10.0.0.0/8", :nw_dst=>"20.0.0.0/8", :tp_src=>8, :tp_dst=>0} )
 match_bin = match.to_binary_s
# => RuntimeError: Invalid flag: {:nw_src=>24}
        from /usr/local/rvm/gems/ruby-2.0.0-p598/gems/pio-0.10.1/lib/pio/match.rb:70:in `block in set'
        from /usr/local/rvm/gems/ruby-2.0.0-p598/gems/pio-0.10.1/lib/pio/match.rb:69:in `each'
        from /usr/local/rvm/gems/ruby-2.0.0-p598/gems/pio-0.10.1/lib/pio/match.rb:69:in `set'
        from /usr/local/rvm/gems/ruby-2.0.0-p598/gems/bindata-2.1.0/lib/bindata/primitive.rb:98:in `do_write'
        from /usr/local/rvm/gems/ruby-2.0.0-p598/gems/bindata-2.1.0/lib/bindata/struct.rb:136:in `block in do_write'
        from /usr/local/rvm/gems/ruby-2.0.0-p598/gems/bindata-2.1.0/lib/bindata/struct.rb:136:in `each'
        from /usr/local/rvm/gems/ruby-2.0.0-p598/gems/bindata-2.1.0/lib/bindata/struct.rb:136:in `do_write'
        from /usr/local/rvm/gems/ruby-2.0.0-p598/gems/bindata-2.1.0/lib/bindata/base.rb:167:in `write'
        from /usr/local/rvm/gems/ruby-2.0.0-p598/gems/bindata-2.1.0/lib/bindata/base.rb:180:in `to_binary_s'
        from (irb):5
        from /usr/local/rvm/rubies/ruby-2.0.0-p598/bin/irb:12:in `<main>'

また、FlowMod.readについて、バイナリデータを作成し
readさせました。出力は以下のとおりです。
※ IPアドレスのマスクが反映されていないように見えます

bianry =  [1,14,0,192,0,0,0,0,0,6,24,0,0,1,0,0,0,0,0,10,0,0,0,0,0,20,0,0,0,0,8,0,0,1,0,0,10,0,0,0,20,0,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,0,1,0,1,0,8,0,10,0,0,0,2,0,8,0,0,0,0,0,3,0,8,0,0,0,0,0,4,0,16,0,0,0,0,0,1,0,0,0,0,0,0,0,5,0,16,0,0,0,0,0,2,0,0,0,0,0,0,0,6,0,8,10,0,0,1,0,7,0,8,20,0,0,2,0,8,0,8,184,0,0,0,0,9,0,8,0,8,0,0,0,10,0,8,0,0,0,0,0,11,0,16,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,8,0,1,0,128].pack("C*")

pp FlowMod.read bianry
#<Pio::FlowMod:0x00000002a50620
 @format=
  {:open_flow_header=>
    {:ofp_version=>1,
     :message_type=>14,
     :message_length=>192,
     :transaction_id=>0},
   :body=>
    {:match=>
      {:wildcards=>[{:nw_src=>24}, {:nw_dst=>24}],
       :in_port=>1,
       :dl_src=>#<Pio::Mac:21891620 "00:00:00:00:00:0a">,
       :dl_dst=>#<Pio::Mac:21845160 "00:00:00:00:00:14">,
       :dl_vlan=>0,
       :dl_vlan_pcp=>0,
       :dl_type=>2048,
       :nw_tos=>0,
       :nw_proto=>1,
       :nw_src=>
        #<Pio::IPv4Address:0x000000029a8c18
         @value=#<IPAddr: IPv4:10.0.0.0/255.255.255.255>>,
       :nw_dst=>
        #<Pio::IPv4Address:0x00000002987860
         @value=#<IPAddr: IPv4:20.0.0.0/255.255.255.255>>,
       :tp_src=>8,
       :tp_dst=>0},
     :cookie=>0,
     :command=>:add,
     :idle_timeout=>0,
     :hard_timeout=>0,
     :priority=>65535,
     :buffer_id=>4294967295,
     :out_port=>65535,
     :flags=>[:send_flow_rem],
     :actions=>
      [#<Pio::SetVlanVid:0x00000002984700
        @format={:type=>1, :message_length=>8, :vlan_id=>10}>,
       #<Pio::SetVlanPriority:0x00000002979558
        @format={:type=>2, :message_length=>8, :vlan_priority=>0}>,
       #<Pio::StripVlanHeader:0x00000002962218
        @format={:type=>3, :message_length=>8}>,
       #<Pio::SetEthSrcAddr:0x0000000295fae0
        @format=
         {:type=>4,
          :message_length=>16,
          :mac_address=>#<Pio::Mac:21685520 "00:00:00:00:00:01">}>,
       #<Pio::SetEthDstAddr:0x00000002957e80
        @format=
         {:type=>5,
          :message_length=>16,
          :mac_address=>#<Pio::Mac:21668840 "00:00:00:00:00:02">}>,
       #<Pio::SetIpSrcAddr:0x00000002954190
        @format=
         {:type=>6,
          :message_length=>8,
          :ip_address=>
           #<Pio::IPv4Address:0x00000002940988
            @value=#<IPAddr: IPv4:10.0.0.1/255.255.255.255>>}>,
       #<Pio::SetIpDstAddr:0x0000000292fb38
        @format=
         {:type=>7,
          :message_length=>8,
          :ip_address=>
           #<Pio::IPv4Address:0x0000000292b5b0
            @value=#<IPAddr: IPv4:20.0.0.2/255.255.255.255>>}>,
       #<Pio::SetIpTos:0x0000000292aae8
        @format={:type=>8, :message_length=>8, :type_of_service=>184}>,
       #<Pio::SetTransportSrcPort:0x0000000291b4f8
        @format={:type=>9, :message_length=>8, :port_number=>8}>,
       #<Pio::SetTransportDstPort:0x0000000290b170
        @format={:type=>10, :message_length=>8, :port_number=>0}>,
       #<Pio::Enqueue:0x00000002908f60
        @format=
         {:type=>11, :message_length=>16, :port_number=>1, :queue_id=>1}>,
       #<Pio::SendOutPort:0x000000028fde30
        @format=
         {:type=>0, :message_length=>8, :port_number=>1, :max_len=>128}>]}}>

flow_modのデフォルトのmatch

以下のようなコードは

send_flow_mod_add(
  datapath_id, 
  match: Match.new({}),
)

こう書けたほうがよいかとおもいます。

send_flow_mod_add(datapath_id)

Hello

OpenFlow 1.0 Hello message.

Rename dhcp/type/*.rb

dhcp/type/*.rb の中でファイル名とクラス名が違っているので修正おねがいします。

たとえば dhcp/type/dhcp_client_id.rb で定義されているクラスが ClientId だったりします。dhcp/ の中に入っていれば dhcp_ というのは冗長なので消してください。

Support VendorAction

サンプルデータ

 binary = [1,13,0,212,0,0,0,0,255,255,255,255,255,255,0,132,0,1,0,8,0,10,0,0,0,2,0,8,0,0,0,0,0,3,0,8,0,0,0,0,0,4,0,16,0,0,0,0,0,1,0,0,0,0,0,0,0,5,0,16,0,0,0,0,0,2,0,0,0,0,0,0,0,6,0,8,10,0,0,1,0,7,0,8,20,0,0,2,0,8,0,8,184,0,0,0,0,9,0,8,0,8,0,0,0,10,0,8,0,0,0,0,0,11,0,16,0,1,0,0,0,0,0,0,0,0,0,1,255,255,0,12,0,0,4,210,110,97,122,111,0,0,0,8,0,1,0,128,0,38,130,235,234,209,0,22,157,29,156,196,8,0,69,0,0,50,0,0,0,0,128,1,18,121,192,168,83,3,192,168,83,254,8,0,246,255,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0].pack "C*"

以下、内容です。

[{ set_vlan_vid, 10 },
{ set_vlan_priority, 0 },
{ strip_vlan_header },
{ set_eth_src_addr, <<0,0,0,0,0,1>> },
{ set_eth_dst_addr, <<0,0,0,0,0,2>> },
{ set_ip_src_addr, <<10,0,0,1>> },
{ set_ip_dst_addr, <<20,0,0,2>> },
{ set_ip_tos, 16#b8 },
{ set_transport_src_port, 8 },
{ set_transport_dst_port, 0 },
{ enqueue, 1, 1 },
{ vendor_action, 1234, <<"nazo">> },
{ send_out_port, 1, 128 }]

Delete `_tlv` from option name

@shun159 忘れないうちにメモです。DHCP 関連のオプションでいくつか *_tlv というのがありますが、これは直したほうがいいと思います。

TLV は内部データ構造の話でユーザには関係のないことなので、単純に server_identifier などと _tlv なしに指定できたほうがうれしいです。

Delete `_hash' from *_hash methods

inch をかけると Pio::Dhcp::DhcpTlvOptions#dhcp_server_identifier_hash みたいな名前が大量に出てきますが、これは良くないです。

Ruby のように Duck Typing の言語では、メソッド名に型を限定する名前をつけるのは有害です。たとえば中身が将来的に Hash でなくなったときに混乱します。

というわけで別の名前をおねがいします。ひとまずは、こういう命名のメソッドがないかチェックからおねがいします。

DHCP Optional Field の種類

表題の件ですが、
対応させたいTLVなどについて、こちらで相談・議論させてください。

※とりあえず、一旦はよく使うTLVに限定し、すべてを実装しません。

宜しくお願いします。

ICMP パーサ/ジェネレータの仕様

0.3.0 に入れる予定の ICMP パーサ/ジェネレータの仕様をここで決めましょう。
@shun159 思い付く相談アイテムを補足おねがいします 🍺
simple_router でも使うと思うので @kazuyas 先生にも相談させてください。

IPv4Addressの同値性の判定について

細かな部分で大変恐縮ですが、

ipv4_address == otherのようなものを書くとき、

to_sとか#to_iとか、

こういったものを使わなくても比較できるようにしてあげると、
より使いやすくなるのではないでしょうか。

https://github.com/shun159/pio/blob/ipv4_address/eql/spec/pio/ipv4_address_spec.rb#L13

※Macクラスのほぼ丸コピーですが。

先にSpecだけ、追記しましたので、
ご意見お願いいたします。

Circular dependency in Dhcp

@shun159 rake spec すると次の警告が出ています。require が循環しているんだと思います。

/Users/yasuhito/play/pio/spec/pio/dhcp_spec.rb:6: warning: already initialized constant Pio::DHCP
/Users/yasuhito/play/pio/lib/pio/dhcp.rb:26: warning: previous definition of DHCP was here

Many objects are undocumented.

YARD のコメントがついていないものがたくさんあります。API の見直しも兼ねて、ドキュメントを整備しましょう。

ちなみに rake yard:list_undoc でコメントの付いてないものを確認できます。

Remove redundant constants from DHCP::Consts module

#45 で指摘された点についての修正をします。

以下、引用です。

これは別イシューですが、定数は共通するものは Pio::Dhcp 直下、パケット特有のものは対応するクラス (Pio::Dhcp::Offer とか) に定義してください。わざわざ include Consts しなきゃいけないのがまどろっこしいのと、全部大文字なので定数なのは自明で、わざわざ Pio::Dhcp::Consts::XXX と名乗るのは冗長だと思うので。。

WIP: Pio::Dhcp修正

#65 (comment)
での新仕様検討をここで行いますので、こちらに転記します。

更新ごとにつらつらと書いていきます。
@yasuhito 何かありましたら追記をお願いします。

TODO

仕様の検討

  • テストの方針決め
    • 生成のテスト
    • 解析のテスト
    • オプションのテスト
  • メソッドを自動生成する方法を考える
    • 解析結果取得の為のメソッド生成
    • 生成するときにハッシュを生成するメソッド生成
  • .newであたえられるオプションの種類の決定
  • メッセージ別のTLV値の明確化(end_of_tlvも含めすべて)
  • パーズした後に利用できるメソッドの種類の決定
  • デフォルト値についての決定
  • DHCPリレーメッセージの仕様検討

テストケース作成

  • 解析テスト
    • クライアントメッセージ
    • サーバーメッセージ
  • 生成テスト
    • クライアントメッセージ
    • サーバーメッセージ
  • オプションテスト
    • クライアントメッセージ
    • サーバーメッセージ
  • 互換性テスト
    • いろいろなパケットの収集

最終的なリファクタリング

yardドキュメント追加

  • 日本語ドキュメント
  • 上記の英訳

既存の指摘点の対応について:

  • Delete `_hash' from *_hash methods (#60)
  • Make private methods private (#64)
  • Rename *_hash methods to *_tlv (#63)
  • BootRequestOptions => BootRequest::Options (#53)
  • Remove the redundant Dhcp prefix from Dhcp::DhcpField (#52)
  • Refactor DHCP specs using rspec_given (#39)

関連RFC

http://www.zytrax.com/books/dhcp/apc/

方針:

プロトコル概要

クライアントメッセージの振る舞いについて

ブロードキャストとユニキャストの使用

  • ユニキャスト:
    • DHCP サーバーのアドレスを知っている場合、クライアントは DHCPDISCOVER や DHCPREQUEST で IP ブロードキャストアドレスではなく、そのユニキャストアドレスを使用しても良い。
    • クライアントはサーバーに DHCPRELEASE メッセージをユニキャストする
    • 既知の DHCP サーバーに DHCPINFORM メッセージを送信する場合にも、クライアントはユニキャストを使用しても良い。
  • ブロードキャスト:
    • DHCPサーバのアドレスを知らない限り、DHCPクライアントは、DHCPDISCOVER、DHCPREQUEST、DHCPINRFORM の各メッセージをブロードキャストする
    • クライアントがサーバーによって提供された IP アドレスを使用を拒否するなら、クライアントは DHCPDECLINE メッセージをブロードキャストする。

サーバーメッセージの振る舞いについて

ブロードキャストとユニキャストの使用

  • Offer
    送信先アドレスはユニキャストとブロードキャストが選択可能。
  • Ack
    送信先アドレスはユニキャストとブロードキャストが選択可能。
  • Nack
    送信先アドレスはブロードキャストアドレスに限定。

DHCPリレーエージェントの振る舞いについて

http://www.cisco.com/cisco/web/support/JP/100/1007/1007781_100-j.html#dhcprfcref
を参考にする。

Pio::Dhcp 新仕様

クライアントメッセージ別オプション

サーバメッセージ別オプション

質問事項

  • message.rbとかで書いている、def_delegatorspacket_inが提供するメソッドとかぶっています。必要ですか?

    得られる情報が同じものをPioで提供する必要があるかと考えてます。
    たとえば、packet_in.macsaと@frame.source_macって得られる情報同じです。

    たぶん心配しているのは、Trema::PacketIn と Pio でコードの重複が出ないかということだと思いますが、Trema::PacketIn の各メソッドは Pio の各クラスのメソッドを呼ぶだけという実装になるので、コードの重複は出ないはずです。

    たとえば packet_in.macsa は実装上は Pio の dhcp.macsa を呼ぶだけなので、Pio の #macsa はどっちみち必要です。

    なので、Trema::PacketInから得られないL7とかのペイロード情報を提供するに
    しておけば、如何かと。

    こちらも、もしそういうメソッドがあれば PacketIn から参照できるようにしようと思っています。

  • テスト方針について:これまでジェネレータが出したフレームでテストを書くことがありましたが、今回は実際にスニファしたデータをベースにテストを作成したいと考えています。そのため、テストコードからはどのオプションがmandatoryかoptionalかわからなくなるかもしれませんが、その辺ドキュメントすればいいかなと思います。また、そういった場合、最小限・最大のオプションそれぞれのテストをどうしようか、言ったところはありますが、ご意見・ご指摘お願いします。

    テストコードの書きかたで mandatory/optional も表現できるので、コードと分離したドキュメンテーションはいらないはずです。たとえば Arp のテストでやっているように、

    mandatory なオプションを指定しなかったら例外が上がる
    optional なやつを指定しなかったらデフォルトになる
    

    と書けばよいわけで、こうしておけば rspec -fs の実行結果がオプションのドキュメントになります。RSpec > はテストコードを仕様 (spec) として読めるようにという目的のツールなので、こういう使いかたをしましょう。

  • dhcpのtlvデータを読み込むためのメソッドを自動生成などして、行数をうまいこと減らしたいです

      def message_type
        get_tlv_field(Dhcp::MESSAGE_TYPE_TLV)
      end

      def server_identifier
        get_tlv_field(Dhcp::SERVER_IDENTIFIER_TLV)
      end

      def client_identifier
        get_tlv_field(Dhcp::CLIENT_IDENTIFIER_TLV)
      end

これが今後要望などあると、追加するためのメソッドとともに増えてくるので、
行数と修正にかかる時間、デグレーションを減らしたい考えです。いい方法などあれば教えてください。

2014/06/24
以下のようにして自動生成するようにした

      def method_missing(method)
        tlv_methods = Dhcp::Constants.constants(true).map do |const|
          const.downcase
        end
        if dhcp.methods.include?(method)
          dhcp.send(method)
        elsif tlv_methods.include?(method)
          send :get_tlv_field, Dhcp::Constants.const_get(method.upcase)
        end
      end

      def get_tlv(tlv_type)
        tlv = dhcp.optional_tlvs.find do | each |
          each['tlv_type'] == tlv_type
        end
        tlv['tlv_value'] if tlv
      end

      def get_tlv_field(tlv_type)
        tlv = get_tlv(tlv_type)
        tlv.snapshot if tlv
      end

simple_router を Pio::Arp でリファクタリング

@kazuyas さん、
Pio の最新版 0.2.1 で念願の ARP パーサ/ジェネレータが入りました。

現状で ARP を扱っている Trema のサンプルが simple_router ぐらいだと思うので、simple_router の ARP 処理部分にこの Pio::Arp を使ってみてもらえないでしょうか?

使い方で分からない点や、問題点の修正はいつでも聞いてもらえれば大丈夫です。

DHCP パーサ/ジェネレータの仕様

もう少し先の話になりますが、
DHCPの仕様をここで相談させて頂ければと存じます。

私なりで、恐縮ですが、イメージはこのような感じです。

# DHCP Reply.new
:destination_mac => "00:26:82:eb:ea:d1",
:source_mac => "00:16:9d:1d:9c:c4",
:ip_source_address => "192.168.84.1",
:ip_destination_address => "192.168.83.3",
:yip => "192.168.83.3",
:optional_tlvs => [
  {"tlv_type"=>53, "tlv_info_length"=>1, "tlv_value"=>5},
  {"tlv_type"=>58, "tlv_info_length"=>4, "tlv_value"=>345600},
  {"tlv_type"=>59, "tlv_info_length"=>4, "tlv_value"=>604800},
  {"tlv_type"=>51, "tlv_info_length"=>4, "tlv_value"=>691200},
  {"tlv_type"=>54, "tlv_info_length"=>4, "tlv_value"=> [IPv4Address.new( "192.168.84.1" )]},
  {"tlv_type"=>1, "tlv_info_length"=>4, "tlv_value"=> [IPv4Address.new( "255.255.255.0" )]},
  {"tlv_type"=>81, "tlv_info_length"=>3, "tlv_value"=>"\x00\xFF\xFF"},
  {"tlv_type"=>15, "tlv_info_length"=>11, "tlv_value"=>"sied.local\x00"},
  {"tlv_type"=>3, "tlv_info_length"=>4, "tlv_value"=> [IPv4Address.new( "192.168.83.254" )]},
  {"tlv_type"=>6, "tlv_info_length"=>4, "tlv_value"=> [IPv4Address.new( "192.168.84.1" )]},
  {"tlv_type"=>255, "tlv_info_length"=>0, "tlv_value"=>""}
 ]
# DHCP Request.new
 :destination_mac => "24:db:ac:41:e5:5b",
 :source_mac => "00:26:82:eb:ea:d1",
 :ip_source_address => "192.168.1.101",
 :ip_destination_address => "192.168.1.1",
 :optional_tlvs =>
  [
    {"tlv_type"=>53, "tlv_info_length"=>1, "tlv_value"=>3},
    {"tlv_type"=>61, "tlv_info_length"=>7, "tlv_value"=>"\x01\x00&\x82\xEB\xEA\xD1"},
    {"tlv_type"=>12, "tlv_info_length"=>12, "tlv_value"=>"SIED-CLI-013"},
    {"tlv_type"=>81, "tlv_info_length"=>26, "tlv_value"=>"\x00\x00\x00SIED-CLI-013.sied.local"},
    {"tlv_type"=>60, "tlv_info_length"=>8, "tlv_value"=>"MSFT 5.0"},
    {"tlv_type"=>55, "tlv_info_length"=>12, "tlv_value"=>[1, 15, 3, 6, 44, 46, 47, 31, 33, 121, 249, 43]},
    {"tlv_type"=>255, "tlv_info_length"=>0, "tlv_value"=>""}
  ]

ParserのIPv6対応

すこし相談なのですが、parserの実装についてです。

    # rubocop:disable MethodLength
    def self.read(raw_data)
      ethernet_header = EtherTypeParser.read(raw_data)
      case ethernet_header.ether_type
      when EthernetHeader::EtherType::IPV4, EthernetHeader::EtherType::VLAN
        IPv4Packet.read raw_data
      when EthernetHeader::EtherType::ARP
        Pio::Arp.read raw_data
      when EthernetHeader::EtherType::LLDP
        Pio::Lldp.read raw_data
      else
        # fail 'Failed to parse packet_in data.'
        ethernet_header
      end
    end
    # rubocop:enable MethodLength
  end

failにするのではなく、ethernet_headerを返すのはいかがでしょうか??
※よくipv6パケットがコントローラに上がってきた時にparseに失敗して落ちることがありました。

error: undefined method `to_binary' for FlowMod

$ bundle exec trema run lib/learning_switch13.rb --openflow13
LearningSwitch started.
switch_ready 2748
error: undefined method `to_binary' for #<Pio::FlowMod::Body:0x00000002291678>
/var/lib/gems/2.1.0/gems/pio-0.23.0/lib/pio/open_flow13/flow_mod.rb:159:in `method_missing': undefined method `to_binary' for #<Pio::FlowMod::Body:0x00000002291678> (NoMethodError)
    from /var/lib/gems/2.1.0/gems/pio-0.23.0/lib/pio/open_flow13/flow_mod.rb:177:in `method_missing'
    from /home/shun159/.bundler/ruby/2.1.0/trema-b748b48fc92b/lib/trema/switch.rb:37:in `write'
    from /home/shun159/.bundler/ruby/2.1.0/trema-b748b48fc92b/lib/trema/controller.rb:158:in `send_message'
    from /home/shun159/.bundler/ruby/2.1.0/trema-b748b48fc92b/lib/trema/controller.rb:144:in `send_flow_mod_add'
    from lib/learning_switch13.rb:122:in `flow_mod'
    from lib/learning_switch13.rb:99:in `iff_controlling_frame'
    from lib/learning_switch13.rb:34:in `install_builtin_flow'
    from lib/learning_switch13.rb:23:in `switch_ready'
    from /home/shun159/.bundler/ruby/2.1.0/trema-b748b48fc92b/lib/trema/controller.rb:280:in `block in send_handler'
    from /home/shun159/.bundler/ruby/2.1.0/trema-b748b48fc92b/lib/trema/controller.rb:280:in `synchronize'
    from /home/shun159/.bundler/ruby/2.1.0/trema-b748b48fc92b/lib/trema/controller.rb:280:in `send_handler'
    from /home/shun159/.bundler/ruby/2.1.0/trema-b748b48fc92b/lib/trema/controller.rb:285:in `maybe_send_handler'
    from /home/shun159/.bundler/ruby/2.1.0/trema-b748b48fc92b/lib/trema/controller.rb:224:in `start_switch_main'
    from /home/shun159/.bundler/ruby/2.1.0/trema-b748b48fc92b/lib/trema/controller.rb:217:in `block in start_switch_thread'

Randomize :transaction_id option in Pio::Dhcp::Discover#new

  1. Pio::Dhcp::Discover.new のサンプルで :transaction_id を指定してますが、コードを見たかんじだとこのオプションは必須ではないですよね? サンプルは簡単のために最小限にしたいので、消したほうがいいと思います。
  2. :transaction_id のデフォルトが (たぶん) 0 になってますが、これはランダム生成したほうがいいんではないでしょうか?
discover = Pio::Dhcp::Discover.new(source_mac: ...)
discover.transaction_id #=> ランダムな値

コメントおねがいしまーす。

Pio::ICMP の実環境での動作チェック

@shun159 動作チェックですが、よく考えたら動作チェック用の新しいコードを書かなくても simple_router を使えばいいのかなと思いました。さきほど Arp のほう simple_router に適用して PR を出しときましたので (trema/trema#328) あとは ICMP も同様に適用して動かしてみる感じです。このプランでどうでしょうか?

move methods which is not access from other class, from `public` to `private`.

ドキュメンテーション作業に関連して、
APIとして利用される以外メソッド等、外部に公開する必要がない
メソッドについては、privateへ移動すると、ドキュメンテーション作業の手間が
減らせると考えております。

※こちらは #39 のあと対応させて頂けばと存じます。

まずは、DHCP関連ですが、
後程、こちらでリストします。

ご意見等頂けますと、幸いです。
宜しくお願い致します。

パケットからMatchを生成するメソッド

Arp::Request.match(source_mac: "11:11:11:11:11:11")
UdpHeader.match(ip_destination: "192.168.0.1")

こんな感じで、"カジュアル"にMatchのオブジェクト(インスタンス)を作れるありがたいです!!

DHCP example is out-dated

DHCP exampleの内容が #41 での変更が反映されていません。
#38 マージ後に、修正させて頂きます。

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.