diff --git a/features/open_flow13/nicira_learn.feature b/features/open_flow13/nicira_learn.feature new file mode 100644 index 00000000..0b7a2f61 --- /dev/null +++ b/features/open_flow13/nicira_learn.feature @@ -0,0 +1,22 @@ +@openflow13 +Feature: NiciraLearn + + This action adds or modifies a flow in an OpenFlow table, similar to + OFPT_FLOW_MOD with OFPFC_MODIFY_STRICT as 'command'. + + Scenario: new({}) + When I try to create an OpenFlow action with: + """ + Pio::OpenFlow::NiciraLearn.new({}) + """ + Then it should finish successfully + And the action has the following fields and values: + | field | value | + | idle_timeout | 0 | + | hard_timeout | 0 | + | priority | 65535 | + | cookie | 0 | + | flags | [] | + | table_id | 0 | + | fin_idle_timeout | 0 | + | fin_hard_timeout | 0 | diff --git a/lib/pio/open_flow13.rb b/lib/pio/open_flow13.rb index 5294c99f..d8581da3 100644 --- a/lib/pio/open_flow13.rb +++ b/lib/pio/open_flow13.rb @@ -23,6 +23,8 @@ require 'pio/open_flow13/nicira_send_out_port' require 'pio/open_flow13/nicira_stack_pop' require 'pio/open_flow13/nicira_stack_push' +require 'pio/open_flow13/nicira_learn' +require 'pio/open_flow13/learn_match' require 'pio/open_flow13/send_out_port' require 'pio/open_flow13/set_arp_operation' require 'pio/open_flow13/set_arp_sender_hardware_address' diff --git a/lib/pio/open_flow13/learn_match.rb b/lib/pio/open_flow13/learn_match.rb new file mode 100644 index 00000000..ad07a7c0 --- /dev/null +++ b/lib/pio/open_flow13/learn_match.rb @@ -0,0 +1,114 @@ +require 'bindata' +require 'forwardable' +require 'pio/open_flow13/match' + +module Pio + module OpenFlow13 + class LearnMatch + extend Forwardable + + # rubocop:disable MethodLength + def initialize(arguments) + @source = arguments.fetch(:source) + @destination = arguments.fetch(:destination) + registers = { _source: { oxm_class: source_oxm_class, + oxm_field: source_oxm_field, + oxm_length: source_oxm_length }, + _destination: { oxm_class: destination_oxm_class, + oxm_field: destination_oxm_field, + oxm_length: destination_oxm_length } } + options = [:n_bits, + :source_offset, + :destination_offset].each_with_object({}) do |each, opts| + opts[each] = arguments[each] if arguments[each] + end + @frame = Field.new(registers.merge(options)) + end + # rubocop:enable MethodLength + + attr_reader :source + attr_reader :destination + + def_delegator :@frame, :to_binary_s + + def self.read(raw_data) + begin + frame = Field.read(raw_data) + rescue + raise Pio::ParseError, $ERROR_INFO.message + end + + field = allocate + field.instance_variable_set :@frame, frame + field + end + + private + + def source_oxm_class + source_class.const_get(:OXM_CLASS) + end + + def source_oxm_field + source_class.const_get(:OXM_FIELD) + end + + def source_oxm_length + source_class.new.length + end + + def source_class + Match.const_get(@source.to_s + .split('_') + .map(&:capitalize) + .join) + end + + def destination_oxm_class + destination_class.const_get(:OXM_CLASS) + end + + def destination_oxm_field + destination_class.const_get(:OXM_FIELD) + end + + def destination_oxm_length + destination_class.new.length + end + + def destination_class + Match.const_get(@destination.to_s + .split('_') + .map(&:capitalize) + .join) + end + + class Field < BinData::Record + endian :big + + bit2 :zero + hide :zero + bit1 :src_type, value: 0 + hide :src_type + bit2 :dst_type, value: 0 + hide :dst_type + + bit11 :_n_bits + struct :_source do + uint16 :oxm_class + bit7 :oxm_field + bit1 :oxm_hasmask, value: 0 + uint8 :oxm_length + end + uint16 :source_offset + struct :_destination do + uint16 :oxm_class + bit7 :oxm_field + bit1 :oxm_hasmask, value: 0 + uint8 :oxm_length + end + uint16 :destination_offset + end + end + end +end diff --git a/lib/pio/open_flow13/nicira_learn.rb b/lib/pio/open_flow13/nicira_learn.rb new file mode 100644 index 00000000..cd7b547d --- /dev/null +++ b/lib/pio/open_flow13/nicira_learn.rb @@ -0,0 +1,42 @@ +require 'pio/open_flow' + +module Pio + module OpenFlow13 + # NXAST_LEARN action + class NiciraLearn < OpenFlow::NiciraAction + extend OpenFlow::Flags + + flags_16bit :flags, [:send_flow_rem, + :delete_learned] + + nicira_action_header action_type: 0xffff, + action_length: 24, + subtype: 16 + uint16 :idle_timeout + uint16 :hard_timeout + uint16 :priority, initial_value: 0xffff + uint64 :cookie + flags :flags + uint8 :table_id + string :padding1, length: 1 + hide :padding1 + uint16 :fin_idle_timeout + uint16 :fin_hard_timeout + array :flow_mod_specs, type: :uint16, initial_length: 0 + string :padding2, length: 2 + hide :padding2 + + def fms_length + if flow_mod_specs.size > 0 + flow_mod_specs.map(&:length).inject(&:+) + else + 0 + end + end + + def padding_length + (22 + 7) / 8 * 8 - 22 + end + end + end +end