Skip to content

Commit

Permalink
Merge d12e46b into 1798e6c
Browse files Browse the repository at this point in the history
  • Loading branch information
yasuhito committed Jul 7, 2016
2 parents 1798e6c + d12e46b commit c0b3d73
Show file tree
Hide file tree
Showing 15 changed files with 280 additions and 59 deletions.
2 changes: 1 addition & 1 deletion features/arp_reply.feature
Original file line number Diff line number Diff line change
Expand Up @@ -50,5 +50,5 @@ Feature: Arp::Reply
0xc0, 0xa8, 0x53, 0xfe, # sender_protocol_address
0x00, 0x26, 0x82, 0xeb, 0xea, 0xd1, # target_hardware_address
0xc0, 0xa8, 0x53, 0x03, # target_protocol_address
].pack('C*')
].pack('C42')
"""
2 changes: 1 addition & 1 deletion features/arp_request.feature
Original file line number Diff line number Diff line change
Expand Up @@ -48,5 +48,5 @@ Feature: Arp::Request
0xc0, 0xa8, 0x53, 0x03, # sender_protocol_address
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, # target_hardware_address
0xc0, 0xa8, 0x53, 0xfe, # target_protocol_address
].pack('C*')
].pack('C42')
"""
149 changes: 149 additions & 0 deletions features/ethernet_header.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
Feature: EthernetHeader
Scenario: create an Ethernet header
When I create a packet with:
"""ruby
Pio::EthernetHeader.new(
destination_mac: 'ff:ff:ff:ff:ff:ff',
source_mac: '00:26:82:eb:ea:d1',
ether_type: Pio::Ethernet::Type::IPV4
)
"""
Then the packet has the following fields and values:
| field | value |
| class | Pio::EthernetHeader |
| destination_mac | ff:ff:ff:ff:ff:ff |
| source_mac | 00:26:82:eb:ea:d1 |
| ether_type.to_hex | 0x08, 0x00 |

Scenario: create a VLAN-tagged Ethernet header
When I create a packet with:
"""ruby
Pio::EthernetHeader.new(
destination_mac: 'ff:ff:ff:ff:ff:ff',
source_mac: '00:26:82:eb:ea:d1',
ether_type: Pio::Ethernet::Type::VLAN,
vlan_pcp: 5,
vlan_cfi: 0,
vlan_vid: 100
)
"""
Then the packet has the following fields and values:
| field | value |
| class | Pio::EthernetHeader |
| destination_mac | ff:ff:ff:ff:ff:ff |
| source_mac | 00:26:82:eb:ea:d1 |
| ether_type.to_hex | 0x81, 0x00 |
| vlan_pcp | 5 |
| vlan_cfi | 0 |
| vlan_vid | 100 |

Scenario: read an Ethernet header
Given I use the fixture "ethernet_header"
When I create a packet with:
"""ruby
Pio::EthernetHeader.read(eval(IO.read('ethernet_header.rb')))
"""
Then the packet has the following fields and values:
| field | value |
| class | Pio::EthernetHeader |
| destination_mac | ff:ff:ff:ff:ff:ff |
| source_mac | 00:26:82:eb:ea:d1 |
| ether_type.to_hex | 0x08, 0x00 |

Scenario: read a VLAN-tagged Ethernet header
Given I use the fixture "ethernet_header"
When I create a packet with:
"""ruby
Pio::EthernetHeader.read(eval(IO.read('vlan_ethernet_header.rb')))
"""
Then the packet has the following fields and values:
| field | value |
| class | Pio::EthernetHeader |
| destination_mac | ff:ff:ff:ff:ff:ff |
| source_mac | 00:26:82:eb:ea:d1 |
| ether_type.to_hex | 0x81, 0x00 |
| vlan_pcp | 5 |
| vlan_cfi | 0 |
| vlan_vid | 100 |

Scenario: convert Ethernet header to Ruby code
When I eval the following Ruby code:
"""ruby
Pio::EthernetHeader.new(
destination_mac: 'ff:ff:ff:ff:ff:ff',
source_mac: '00:26:82:eb:ea:d1',
ether_type: Pio::Ethernet::Type::IPV4
).to_ruby
"""
Then the result of eval should be:
"""ruby
[
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, # destination_mac
0x00, 0x26, 0x82, 0xeb, 0xea, 0xd1, # source_mac
0x08, 0x00, # ether_type
].pack('C14')
"""

Scenario: convert VLAN-tagged Ethernet header to Ruby code
When I eval the following Ruby code:
"""ruby
Pio::EthernetHeader.new(
destination_mac: 'ff:ff:ff:ff:ff:ff',
source_mac: '00:26:82:eb:ea:d1',
ether_type: Pio::Ethernet::Type::VLAN,
vlan_pcp: 5,
vlan_cfi: 0,
vlan_vid: 100
).to_ruby
"""
Then the result of eval should be:
"""ruby
[
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, # destination_mac
0x00, 0x26, 0x82, 0xeb, 0xea, 0xd1, # source_mac
0x81, 0x00, # ether_type
0b101_0_000001100100, # vlan_pcp, vlan_cfi, vlan_vid
0x81, 0x00, # ether_type_vlan
].pack('C14nC2')
"""

Scenario: EthernetHeader instance inspection
When I eval the following Ruby code:
"""ruby
Pio::EthernetHeader.new(
destination_mac: 'ff:ff:ff:ff:ff:ff',
source_mac: '00:26:82:eb:ea:d1',
ether_type: Pio::Ethernet::Type::IPV4,
).inspect
"""
Then the result of eval should be:
"""
#<EthernetHeader destination_mac: "ff:ff:ff:ff:ff:ff", source_mac: "00:26:82:eb:ea:d1", ether_type: 0x0800>
"""

Scenario: VLAN-tagged EthernetHeader instance inspection
When I eval the following Ruby code:
"""ruby
Pio::EthernetHeader.new(
destination_mac: 'ff:ff:ff:ff:ff:ff',
source_mac: '00:26:82:eb:ea:d1',
ether_type: Pio::Ethernet::Type::VLAN,
vlan_pcp: 5,
vlan_cfi: 0,
vlan_vid: 100
).inspect
"""
Then the result of eval should be:
"""
#<EthernetHeader destination_mac: "ff:ff:ff:ff:ff:ff", source_mac: "00:26:82:eb:ea:d1", ether_type: 0x8100, vlan_pcp: 5, vlan_cfi: 0, vlan_vid: 100>
"""

Scenario: EthernetHeader class inspection
When I eval the following Ruby code:
"""ruby
Pio::EthernetHeader.inspect
"""
Then the result of eval should be:
"""
EthernetHeader(destination_mac: mac_address, source_mac: mac_address, ether_type: uint16, vlan_pcp: bit3, vlan_cfi: bit1, vlan_vid: bit12)
"""
5 changes: 5 additions & 0 deletions fixtures/ethernet_header/ethernet_header.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, # destination_mac
0x00, 0x26, 0x82, 0xeb, 0xea, 0xd1, # source_mac
0x08, 0x00, # ether_type
].pack('C*')
7 changes: 7 additions & 0 deletions fixtures/ethernet_header/vlan_ethernet_header.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, # destination_mac
0x00, 0x26, 0x82, 0xeb, 0xea, 0xd1, # source_mac
0x81, 0x00, # ether_type
0b101_0_000001100100, # vlan_pcp, vlan_cfi, vlan_vid
0x81, 0x00, # ether_type_vlan
].pack('C14nC2')
6 changes: 3 additions & 3 deletions lib/pio/arp/format.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ module Pio
class Arp
# ARP parser.
class Format < BinData::Record
include EthernetHeader
include Ethernet

endian :big

ethernet_header ether_type: EtherType::ARP
ethernet_header ether_type: Ethernet::Type::ARP
uint16 :hardware_type, value: 1
uint16 :protocol_type, value: 0x0800
uint8 :hardware_length, value: 6
Expand All @@ -33,7 +33,7 @@ def to_exact_match(in_port)
in_port: in_port,
source_mac_address: source_mac,
destination_mac_address: destination_mac,
vlan_vid: vlan_vid,
vlan_vid: 0xffff,
vlan_priority: vlan_pcp,
ether_type: ether_type,
tos: 0,
Expand Down
11 changes: 2 additions & 9 deletions lib/pio/arp/message.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
require 'pio/arp/format'
require 'pio/ruby_dumper'

module Pio
class Arp
# Base class of ARP Request and Reply
class Message
include RubyDumper
private_class_method :new

def initialize(user_options)
Expand All @@ -14,15 +16,6 @@ def initialize(user_options)
def method_missing(method, *args)
@format.__send__ method, *args
end

# Returns a Ruby code representation of this packet, such that
# it can be eval'ed and sent later.
def to_ruby
hexes = @format.field_names.map do |each|
' ' + __send__(each).to_hex + ", # #{each}" if __send__("#{each}?")
end.compact
['[', *hexes, "].pack('C*')"].join("\n")
end
end
end
end
4 changes: 2 additions & 2 deletions lib/pio/dhcp/frame.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ class Frame < BinData::Record

OPTION_FIELD_LENGTH = 60

include EthernetHeader
include Ethernet
include IPv4Header
include UdpHeader

endian :big
ethernet_header ether_type: EtherType::IPV4
ethernet_header ether_type: Ethernet::Type::IPV4
ipv4_header ip_protocol: ProtocolNumber::UDP
udp_header
dhcp_field :dhcp
Expand Down
70 changes: 39 additions & 31 deletions lib/pio/ethernet_header.rb
Original file line number Diff line number Diff line change
@@ -1,58 +1,66 @@
require 'pio/ruby_dumper'
require 'pio/type/mac_address'

module Pio
# Adds ethernet_header macro.
module EthernetHeader
# EtherType constants for ethernet_header.ether_type.
module EtherType
module Ethernet
# EtherType constants
module Type
ARP = 0x0806
IPV4 = 0x0800
VLAN = 0x8100
LLDP = 0x88cc
end

# Ethernet header parser
class Parser < BinData::Record
endian :big

mac_address :destination_mac
mac_address :source_mac
uint16 :ether_type
bit3 :vlan_pcp_internal, onlyif: :vlan?
bit1 :vlan_cfi, onlyif: :vlan?
bit12 :vlan_vid_internal, onlyif: :vlan?
uint16 :ether_type_vlan, value: :ether_type, onlyif: :vlan?
end

# This method smells of :reek:TooManyStatements
# rubocop:disable MethodLength
def self.included(klass)
def klass.ethernet_header(options)
def klass.ethernet_header(options = nil)
mac_address :destination_mac
mac_address :source_mac
uint16 :ether_type, value: options.fetch(:ether_type)
bit3 :vlan_pcp_internal, onlyif: :vlan?
if options
uint16 :ether_type, value: options.fetch(:ether_type)
else
uint16 :ether_type
end
bit3 :vlan_pcp, onlyif: :vlan?
bit1 :vlan_cfi, onlyif: :vlan?
bit12 :vlan_vid_internal, onlyif: :vlan?
bit12 :vlan_vid, onlyif: :vlan?
uint16 :ether_type_vlan, value: :ether_type, onlyif: :vlan?
end
end
# rubocop:enable MethodLength

def ethernet_header
Parser.new(destination_mac: destination_mac,
source_mac: source_mac,
ether_type: ether_type)
end
private

def vlan_vid
vlan? ? vlan_vid_internal : 0xffff
def vlan?
ether_type == Type::VLAN
end
end

def vlan_pcp
vlan? ? vlan_pcp_internal : 0
# Ethernet header generator/parser
class EthernetHeader < BinData::Record
include Ethernet
include RubyDumper

endian :big

ethernet_header

# rubocop:disable LineLength
def self.inspect
'EthernetHeader(destination_mac: mac_address, source_mac: mac_address, ether_type: uint16, vlan_pcp: bit3, vlan_cfi: bit1, vlan_vid: bit12)'
end
# rubocop:enable LineLength

def vlan?
ether_type == EtherType::VLAN
# rubocop:disable LineLength
def inspect
if vlan?
%(#<EthernetHeader destination_mac: "#{destination_mac}", source_mac: "#{source_mac}", ether_type: #{format('0x%04x', ether_type)}, vlan_pcp: #{vlan_pcp}, vlan_cfi: #{vlan_cfi}, vlan_vid: #{vlan_vid}>)
else
%(#<EthernetHeader destination_mac: "#{destination_mac}", source_mac: "#{source_mac}", ether_type: #{format('0x%04x', ether_type)}>)
end
end
# rubocop:enable LineLength
end
end
4 changes: 2 additions & 2 deletions lib/pio/icmp/format.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ class Icmp
class Format < BinData::Record
MINIMUM_IP_PACKET_LENGTH = 50

include EthernetHeader
include Ethernet
include IPv4Header

endian :big

ethernet_header ether_type: EtherType::IPV4
ethernet_header ether_type: Ethernet::Type::IPV4
ipv4_header ip_protocol: ProtocolNumber::ICMP
uint8 :icmp_type
uint8 :icmp_code, initial_value: 0
Expand Down
2 changes: 1 addition & 1 deletion lib/pio/ipv4_header.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def to_exact_match(in_port)
in_port: in_port,
source_mac_address: source_mac,
destination_mac_address: destination_mac,
vlan_vid: vlan_vid,
vlan_vid: vlan? ? vlan_vid : 0xffff,
vlan_priority: vlan_pcp,
ether_type: ether_type,
tos: ip_type_of_service,
Expand Down
4 changes: 2 additions & 2 deletions lib/pio/lldp/frame.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ module Pio
class Lldp
# LLDP frame
class Frame < BinData::Record
include EthernetHeader
include Ethernet

endian :big

ethernet_header ether_type: EtherType::LLDP
ethernet_header ether_type: Ethernet::Type::LLDP
chassis_id_tlv :chassis_id
port_id_tlv :port_id
ttl_tlv :ttl, initial_value: 120
Expand Down

0 comments on commit c0b3d73

Please sign in to comment.