Skip to content

Commit

Permalink
Merge pull request #123 from trema/feature/exact_match
Browse files Browse the repository at this point in the history
Add Pio::ExactMatch.
  • Loading branch information
yasuhito committed Feb 5, 2015
2 parents cbb4da1 + b3f68d6 commit 9eda07b
Show file tree
Hide file tree
Showing 13 changed files with 197 additions and 28 deletions.
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -13,6 +13,7 @@ spec/reports
test/tmp
test/version_tmp
tmp
vendor/

# YARD artifacts
.yardoc
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
@@ -1,5 +1,11 @@
# Changelog

## develop (unreleased)

### New features
* [#123](https://github.com/trema/pio/pull/123): Add new class `Pio::ExactMatch`.


## 0.11.2 (1/29/2015)

### Bugs fixed
Expand Down
38 changes: 38 additions & 0 deletions features/exact_match.feature
@@ -0,0 +1,38 @@
Feature: Pio::ExactMatch.new
Scenario: packet_in_arp_request.raw
Given a packet data file "packet_in_arp_request.raw"
When I try to create an exact match from the packet
And the parsed data have the following field and value:
| field | value |
| wildcards | {} |
| in_port | 1 |
| dl_src | ac:5d:10:31:37:79 |
| dl_dst | ff:ff:ff:ff:ff:ff |
| dl_vlan | 65535 |
| dl_vlan_pcp | 0 |
| dl_type | 2054 |
| nw_tos | 0 |
| nw_proto | 1 |
| nw_src | 192.168.2.254 |
| nw_dst | 192.168.2.5 |
| tp_src | 0 |
| tp_dst | 0 |

Scenario: packet_in_cbench.raw (ARP request)
Given a packet data file "packet_in_cbench.raw"
When I try to create an exact match from the packet
And the parsed data have the following field and value:
| field | value |
| wildcards | {} |
| in_port | 1 |
| dl_src | 00:00:00:00:00:01 |
| dl_dst | 80:00:00:00:00:01 |
| dl_vlan | 65535 |
| dl_vlan_pcp | 0 |
| dl_type | 2048 |
| nw_tos | 0 |
| nw_proto | 255 |
| nw_src | 192.168.0.40 |
| nw_dst | 192.168.1.40 |
| tp_src | 31256 |
| tp_dst | 22635 |
File renamed without changes.
Binary file added features/packet_data/packet_in_cbench.raw
Binary file not shown.
4 changes: 2 additions & 2 deletions features/packet_in_read.feature
@@ -1,6 +1,6 @@
Feature: Pio::PacketIn.read
Scenario: packet_in.raw
Given a packet data file "packet_in.raw"
Scenario: packet_in_arp_request.raw
Given a packet data file "packet_in_arp_request.raw"
When I try to parse the file with "PacketIn" class
Then it should finish successfully
And the parsed data have the following field and value:
Expand Down
4 changes: 4 additions & 0 deletions features/step_definitions/packet_data_steps.rb
Expand Up @@ -25,6 +25,10 @@
end
end

When(/^I try to create an exact match from the packet$/) do
@result = Pio::ExactMatch.new(Pio::PacketIn.read(IO.read(@raw)))
end

Then(/^it should finish successfully$/) do
# Noop.
end
Expand Down
1 change: 1 addition & 0 deletions lib/pio.rb
Expand Up @@ -3,6 +3,7 @@
require 'pio/arp'
require 'pio/dhcp'
require 'pio/echo'
require 'pio/exact_match'
require 'pio/features'
require 'pio/flow_mod'
require 'pio/hello'
Expand Down
21 changes: 4 additions & 17 deletions lib/pio/arp/message.rb
@@ -1,32 +1,19 @@
require 'forwardable'
require 'pio/arp/format'

module Pio
class Arp
# Base class of ARP Request and Reply
class Message
extend Forwardable

def_delegators :@format, :destination_mac
def_delegators :@format, :source_mac
def_delegators :@format, :ether_type
def_delegators :@format, :hardware_type
def_delegators :@format, :protocol_type
def_delegators :@format, :hardware_length
def_delegators :@format, :protocol_length
def_delegators :@format, :operation
def_delegators :@format, :sender_hardware_address
def_delegators :@format, :sender_protocol_address
def_delegators :@format, :target_hardware_address
def_delegators :@format, :target_protocol_address
def_delegators :@format, :to_binary

private_class_method :new

def initialize(user_options)
options = self.class.const_get(:Options).new(user_options.dup.freeze)
@format = Arp::Format.new(options.to_hash)
end

def method_missing(method, *args)
@format.__send__ method, *args
end
end
end
end
106 changes: 106 additions & 0 deletions lib/pio/exact_match.rb
@@ -0,0 +1,106 @@
require 'pio/match'
require 'pio/type/ethernet_header'

module Pio
# OpenFlow 1.0 exact match
class ExactMatch
# Pio::PacketIn#data parser
class PacketInDataParser
# Ethernet header parser
class EthernetHeaderParser < BinData::Record
extend Pio::Type::EthernetHeader

endian :big

ethernet_header
rest :payload
end

# IPv4 packet parser
class IPv4Packet < BinData::Record
extend Pio::Type::EthernetHeader
extend Type::IPv4Header

endian :big

ethernet_header
ipv4_header

uint16 :transport_source_port
uint16 :transport_destination_port
rest :rest
end

def self.read(raw_data)
ethernet_header = EthernetHeaderParser.read(raw_data)
case ethernet_header.ether_type
when 0x0800
IPv4Packet.read raw_data
when 0x0806
Pio::Arp.read raw_data
else
fail 'Failed to parse packet_in data.'
end
end
end

extend Forwardable

# rubocop:disable MethodLength
# rubocop:disable AbcSize
def initialize(packet_in)
data = PacketInDataParser.read(packet_in.data)
case data
when PacketInDataParser::IPv4Packet
options = {
in_port: packet_in.in_port,
dl_src: data.source_mac,
dl_dst: data.destination_mac,
dl_vlan: data.vlan_vid,
dl_vlan_pcp: data.vlan_pcp,
dl_type: data.ether_type,
nw_tos: data.ip_type_of_service,
nw_proto: data.ip_protocol,
nw_src: data.ip_source_address,
nw_dst: data.ip_destination_address,
tp_src: data.transport_source_port,
tp_dst: data.transport_destination_port
}
when Arp::Request
options = {
in_port: packet_in.in_port,
dl_src: data.source_mac,
dl_dst: data.destination_mac,
dl_vlan: data.vlan_vid,
dl_vlan_pcp: data.vlan_pcp,
dl_type: data.ether_type,
nw_tos: 0,
nw_proto: data.operation,
nw_src: data.sender_protocol_address,
nw_dst: data.target_protocol_address,
tp_src: 0,
tp_dst: 0
}
end
@match = Pio::Match.new(options)
end
# rubocop:enable MethodLength
# rubocop:enable AbcSize

def_delegator :@match, :wildcards
def_delegator :@match, :in_port
def_delegator :@match, :dl_src
def_delegator :@match, :dl_dst
def_delegator :@match, :dl_vlan
def_delegator :@match, :dl_vlan_pcp
def_delegator :@match, :dl_type
def_delegator :@match, :nw_tos
def_delegator :@match, :nw_proto
def_delegator :@match, :nw_src
def_delegator :@match, :nw_dst
def_delegator :@match, :tp_src
def_delegator :@match, :tp_dst
def_delegator :@match, :to_binary_s
def_delegator :@match, :to_binary_s, :to_binary
end
end
4 changes: 1 addition & 3 deletions lib/pio/ipv4_address.rb
Expand Up @@ -26,10 +26,8 @@ def initialize(addr)
@value = IPAddr.new(addr, Socket::AF_INET)
when String
@value = IPAddr.new(addr)
when IPv4Address
@value = addr.value
else
fail TypeError, "Invalid IPv4 address: #{addr.inspect}"
@value = addr.value
end
end

Expand Down
31 changes: 29 additions & 2 deletions lib/pio/type/ethernet_header.rb
Expand Up @@ -4,13 +4,40 @@ module Pio
module Type
# Adds ethernet_header macro.
module EthernetHeader
def ethernet_header(options)
# rubocop:disable MethodLength
# rubocop:disable AbcSize
def ethernet_header(options = { ether_type: 0x0800 })
class_eval do
mac_address :destination_mac
mac_address :source_mac
uint16 :ether_type, value: options[:ether_type]
uint16 :ether_type_internal, initial_value: options[:ether_type]

bit3 :vlan_pcp_internal, onlyif: -> { vlan? }
bit1 :vlan_cfi, onlyif: -> { vlan? }
bit12 :vlan_vid_internal, onlyif: -> { vlan? }

uint16 :ether_type_vlan, onlyif: -> { vlan? },
initial_value: options[:ether_type]

def ether_type
ether_type_internal
end

def vlan_vid
vlan? ? vlan_vid_internal : 0xffff
end

def vlan_pcp
vlan? ? vlan_pcp_internal : 0
end

def vlan?
ether_type == 0x8100
end
end
end
# rubocop:enable MethodLength
# rubocop:enable AbcSize
end
end
end
9 changes: 5 additions & 4 deletions lib/pio/type/ipv4_header.rb
Expand Up @@ -7,22 +7,23 @@ module IPv4Header
# This method smells of :reek:TooManyStatements
# rubocop:disable MethodLength
# rubocop:disable AbcSize
def ipv4_header(options)
def ipv4_header(options = {})
class_eval do
bit4 :ip_version, initial_value: 0x4
bit4 :ip_header_length, initial_value: 0x5
uint8 :ip_type_of_service, initial_value: 0
uint16be :ip_total_length,
initial_value: options[:ip_total_length]
initial_value: options[:ip_total_length] || 0
uint16be :ip_identifier, initial_value: 0
bit3 :ip_flag, initial_value: 0
bit13 :ip_fragment, initial_value: 0
uint8 :ip_ttl, initial_value: 128
uint8 :ip_protocol, value: options[:ip_protocol]
uint8 :ip_protocol, initial_value: options[:ip_protocol] || 0
uint16be :ip_header_checksum,
initial_value: options[:ip_header_checksum]
initial_value: options[:ip_header_checksum] || 0
ip_address :ip_source_address
ip_address :ip_destination_address
string :option, read_length: -> { 20 - ip_header_length * 4 }
end
end
# rubocop:enable AbcSize
Expand Down

0 comments on commit 9eda07b

Please sign in to comment.