lib/pio/open_flow13/flow_mod.rb

Summary

Maintainability
A
50 mins
Test Coverage
# frozen_string_literal: true

require 'pio/open_flow'
require 'pio/open_flow/buffer_id'
require 'pio/open_flow13/match'

module Pio
  module OpenFlow13
    # OpenFlow 1.3 FlowMod message parser and generator
    class FlowMod < OpenFlow::Message
      # enum ofp_flow_mod_command
      class Command < BinData::Primitive
        COMMANDS = {
          add: 0,
          modify: 1,
          modify_strict: 2,
          delete: 3,
          delete_strict: 4
        }.freeze

        uint8 :command

        def get
          COMMANDS.invert.fetch(command)
        end

        def set(value)
          self.command = COMMANDS.fetch(value)
        end
      end

      # For delete commands, require matching entries to include this as
      # an output port.
      class OutPort < BinData::Primitive
        ANY = 0xffffffff

        endian :big
        uint32 :out_port, initial_value: ANY

        def get
          out_port == ANY ? :any : out_port
        end

        def set(value)
          self.out_port = (value == :any ? ANY : value)
        end
      end

      # For delete commands, require matching entries to include this as
      # an output group.
      class OutGroup < BinData::Primitive
        ANY = 0xffffffff

        endian :big
        uint32 :out_group, initial_value: ANY

        def get
          out_group == ANY ? :any : out_group
        end

        def set(value)
          self.out_group = (value == :any ? ANY : value)
        end
      end

      # OpenFlow 1.3 instructions
      class Instructions < BinData::Primitive
        endian :big

        count_bytes_remaining :instructions_length
        string :instructions, read_length: :instructions_length

        def set(instructions)
          self.instructions = Array(instructions).map(&:to_binary_s).join
        end

        # rubocop:disable MethodLength
        def get
          list = []
          tmp = instructions
          until tmp.empty?
            instruction_type = BinData::Uint16be.read(tmp)
            instruction = case instruction_type
                          when 1
                            GotoTable.read(tmp)
                          when 2
                            WriteMetadata.read(tmp)
                          when 4
                            Apply.read(tmp)
                          when 6
                            Meter.read(tmp)
                          else
                            raise "Unsupported instruction #{instruction_type}"
                          end
            tmp = tmp[instruction.instruction_length..-1]
            list << instruction
          end
          list
        end
        # rubocop:enable MethodLength

        def length
          instructions.length
        end
      end

      open_flow_header(version: 4, type: 14,
                       length: lambda do
                         48 + match.length + instructions.length
                       end)
      uint64 :cookie
      uint64 :cookie_mask
      uint8 :table_id
      command :command
      uint16 :idle_timeout
      uint16 :hard_timeout
      uint16 :priority, initial_value: 0xffff
      buffer_id :buffer_id
      out_port :out_port
      out_group :out_group
      flags_16bit :flags,
                  %i[send_flow_rem
                     check_overwrap
                     reset_counts
                     no_packet_counts
                     no_byte_counts]
      string :padding, length: 2
      hide :padding
      oxm :match
      instructions :instructions
    end
  end
end