diff --git a/.gitignore b/.gitignore
index e003a5b1..911dd90e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,7 +2,6 @@
*.rbc
/.config
/.idea/
-/Gemfile.lock
/InstalledFiles
/bin/
/coverage/
diff --git a/.rubocop.yml b/.rubocop.yml
index 7a641972..0a14b786 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -12,8 +12,9 @@ AllCops:
Metrics/LineLength:
Exclude:
+ - 'fixtures/**/*.rb'
- 'spec/support/*.rb'
-
+
Style/StringLiterals:
EnforcedStyle: single_quotes
diff --git a/.travis.yml b/.travis.yml
index 96fcdd0a..3ed51302 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,6 +1,8 @@
language: ruby
-before_install: gem update bundler
+before_install:
+ - gem update bundler
+
bundler_args: --without development
env:
@@ -9,15 +11,15 @@ env:
- secure: cbR8GBZyaDxE54pfGkfzhuTQEh3mpcghrx95dL42df3Ei9s3DHCI2Q/yDChUMY3eeqzHUNAt9fPQeDZsMG/aHUrh775bebh28M8zSkUYzv6x1diPnDtywXGutW7MKGak6jw8F+45J6TW4IxNFYMmuWyBu9HOd5VkqWRKvezvqc8=
rvm:
- - 2.0.0
- - 2.2.0
+ - 2.2
+ - 2.3
- ruby-head
-
+
matrix:
allow_failures:
- rvm: ruby-head
fast_finish: true
-
+
script: bundle exec rake travis
notifications:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9a5dbe08..54d82fa0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,9 @@
# Changelog
## develop (unreleased)
+### New features
+* [#301](https://github.com/trema/pio/pull/301): Add `NiciraConjunction` action.
+* [#300](https://github.com/trema/pio/pull/300): Add new classes `Pio::Match::PacketReg{0..3}`.
## 0.30.0 (11/17/2015)
### New features
diff --git a/Gemfile b/Gemfile
index 6e18d20a..a032b66b 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,3 +1,35 @@
source 'https://rubygems.org'
-gemspec development_group: :test
+gemspec
+
+gem 'rake'
+
+group :development, :test do
+ gem 'aruba', require: false
+ gem 'codeclimate-test-reporter', require: false
+ gem 'coveralls', require: false
+ gem 'cucumber', require: false
+ gem 'flay', require: false
+ gem 'flog', require: false
+ gem 'reek', require: false
+ gem 'rspec', require: false
+ gem 'rspec-given', require: false
+ gem 'rubocop', require: false
+end
+
+group :development do
+ gem 'guard', require: false
+ gem 'guard-bundler', require: false
+ gem 'guard-cucumber', require: false
+ gem 'guard-rspec', require: false
+ gem 'guard-rubocop', require: false
+ gem 'inch', require: false
+ gem 'listen', require: false
+ gem 'pry', require: false
+ gem 'rb-fchange', require: false
+ gem 'rb-fsevent', require: false
+ gem 'rb-inotify', require: false
+ gem 'relish', require: false
+ gem 'terminal-notifier-guard', require: false
+ gem 'yard', require: false
+end
diff --git a/Gemfile.lock b/Gemfile.lock
new file mode 100644
index 00000000..224164c6
--- /dev/null
+++ b/Gemfile.lock
@@ -0,0 +1,245 @@
+PATH
+ remote: .
+ specs:
+ pio (0.30.1)
+ activesupport (~> 5.0.2)
+ bindata (~> 2.1.0)
+
+GEM
+ remote: https://rubygems.org/
+ specs:
+ activesupport (5.0.2)
+ concurrent-ruby (~> 1.0, >= 1.0.2)
+ i18n (~> 0.7)
+ minitest (~> 5.1)
+ tzinfo (~> 1.1)
+ archive-tar-minitar (0.5.2)
+ aruba (0.14.2)
+ childprocess (~> 0.5.6)
+ contracts (~> 0.9)
+ cucumber (>= 1.3.19)
+ ffi (~> 1.9.10)
+ rspec-expectations (>= 2.99)
+ thor (~> 0.19)
+ ast (2.3.0)
+ axiom-types (0.1.1)
+ descendants_tracker (~> 0.0.4)
+ ice_nine (~> 0.11.0)
+ thread_safe (~> 0.3, >= 0.3.1)
+ bindata (2.1.0)
+ builder (3.2.2)
+ childprocess (0.5.9)
+ ffi (~> 1.0, >= 1.0.11)
+ codeclimate-engine-rb (0.3.1)
+ virtus (~> 1.0)
+ codeclimate-test-reporter (0.6.0)
+ simplecov (>= 0.7.1, < 1.0.0)
+ coderay (1.1.1)
+ coercible (1.0.0)
+ descendants_tracker (~> 0.0.1)
+ concurrent-ruby (1.0.5)
+ contracts (0.14.0)
+ coveralls (0.8.15)
+ json (>= 1.8, < 3)
+ simplecov (~> 0.12.0)
+ term-ansicolor (~> 1.3)
+ thor (~> 0.19.1)
+ tins (>= 1.6.0, < 2)
+ cucumber (2.4.0)
+ builder (>= 2.1.2)
+ cucumber-core (~> 1.5.0)
+ cucumber-wire (~> 0.0.1)
+ diff-lcs (>= 1.1.3)
+ gherkin (~> 4.0)
+ multi_json (>= 1.7.5, < 2.0)
+ multi_test (>= 0.1.2)
+ cucumber-core (1.5.0)
+ gherkin (~> 4.0)
+ cucumber-wire (0.0.1)
+ descendants_tracker (0.0.4)
+ thread_safe (~> 0.3, >= 0.3.1)
+ diff-lcs (1.2.5)
+ docile (1.1.5)
+ domain_name (0.5.20160309)
+ unf (>= 0.0.5, < 1.0.0)
+ equalizer (0.0.11)
+ erubis (2.7.0)
+ ffi (1.9.14)
+ flay (2.8.1)
+ erubis (~> 2.7.0)
+ path_expander (~> 1.0)
+ ruby_parser (~> 3.0)
+ sexp_processor (~> 4.0)
+ flog (4.4.0)
+ path_expander (~> 1.0)
+ ruby_parser (~> 3.1, > 3.1.0)
+ sexp_processor (~> 4.4)
+ formatador (0.2.5)
+ gherkin (4.0.0)
+ given_core (3.8.0)
+ sorcerer (>= 0.3.7)
+ guard (2.14.0)
+ formatador (>= 0.2.4)
+ listen (>= 2.7, < 4.0)
+ lumberjack (~> 1.0)
+ nenv (~> 0.1)
+ notiffany (~> 0.0)
+ pry (>= 0.9.12)
+ shellany (~> 0.0)
+ thor (>= 0.18.1)
+ guard-bundler (2.1.0)
+ bundler (~> 1.0)
+ guard (~> 2.2)
+ guard-compat (~> 1.1)
+ guard-compat (1.2.1)
+ guard-cucumber (2.1.2)
+ cucumber (~> 2.0)
+ guard-compat (~> 1.0)
+ nenv (~> 0.1)
+ guard-rspec (4.7.3)
+ guard (~> 2.1)
+ guard-compat (~> 1.1)
+ rspec (>= 2.99.0, < 4.0)
+ guard-rubocop (1.2.0)
+ guard (~> 2.0)
+ rubocop (~> 0.20)
+ http-cookie (1.0.2)
+ domain_name (~> 0.5)
+ i18n (0.8.1)
+ ice_nine (0.11.2)
+ inch (0.7.1)
+ pry
+ sparkr (>= 0.2.0)
+ term-ansicolor
+ yard (~> 0.8.7.5)
+ json (2.0.2)
+ listen (3.1.5)
+ rb-fsevent (~> 0.9, >= 0.9.4)
+ rb-inotify (~> 0.9, >= 0.9.7)
+ ruby_dep (~> 1.2)
+ lumberjack (1.0.10)
+ method_source (0.8.2)
+ mime-types (2.99.1)
+ minitest (5.10.1)
+ multi_json (1.12.1)
+ multi_test (0.1.2)
+ nenv (0.3.0)
+ netrc (0.11.0)
+ notiffany (0.1.1)
+ nenv (~> 0.1)
+ shellany (~> 0.0)
+ parser (2.3.1.4)
+ ast (~> 2.2)
+ path_expander (1.0.0)
+ powerpack (0.1.1)
+ pry (0.10.4)
+ coderay (~> 1.1.0)
+ method_source (~> 0.8.1)
+ slop (~> 3.4)
+ rainbow (2.1.0)
+ rake (11.3.0)
+ rb-fchange (0.0.6)
+ ffi
+ rb-fsevent (0.9.7)
+ rb-inotify (0.9.7)
+ ffi (>= 0.5.0)
+ reek (4.4.2)
+ codeclimate-engine-rb (~> 0.3.1)
+ parser (~> 2.3.1, >= 2.3.1.2)
+ rainbow (~> 2.0)
+ relish (0.7.1)
+ archive-tar-minitar (>= 0.5.2)
+ json (>= 1.4.6)
+ rest-client (>= 1.7.2)
+ rest-client (1.8.0)
+ http-cookie (>= 1.0.2, < 2.0)
+ mime-types (>= 1.16, < 3.0)
+ netrc (~> 0.7)
+ rspec (3.5.0)
+ rspec-core (~> 3.5.0)
+ rspec-expectations (~> 3.5.0)
+ rspec-mocks (~> 3.5.0)
+ rspec-core (3.5.4)
+ rspec-support (~> 3.5.0)
+ rspec-expectations (3.5.0)
+ diff-lcs (>= 1.2.0, < 2.0)
+ rspec-support (~> 3.5.0)
+ rspec-given (3.8.0)
+ given_core (= 3.8.0)
+ rspec (>= 2.14.0)
+ rspec-mocks (3.5.0)
+ diff-lcs (>= 1.2.0, < 2.0)
+ rspec-support (~> 3.5.0)
+ rspec-support (3.5.0)
+ rubocop (0.38.0)
+ parser (>= 2.3.0.6, < 3.0)
+ powerpack (~> 0.1)
+ rainbow (>= 1.99.1, < 3.0)
+ ruby-progressbar (~> 1.7)
+ unicode-display_width (~> 1.0, >= 1.0.1)
+ ruby-progressbar (1.7.5)
+ ruby_dep (1.4.0)
+ ruby_parser (3.8.2)
+ sexp_processor (~> 4.1)
+ sexp_processor (4.7.0)
+ shellany (0.0.1)
+ simplecov (0.12.0)
+ docile (~> 1.1.0)
+ json (>= 1.8, < 3)
+ simplecov-html (~> 0.10.0)
+ simplecov-html (0.10.0)
+ slop (3.6.0)
+ sorcerer (1.0.2)
+ sparkr (0.4.1)
+ term-ansicolor (1.4.0)
+ tins (~> 1.0)
+ terminal-notifier-guard (1.7.0)
+ thor (0.19.1)
+ thread_safe (0.3.5)
+ tins (1.12.0)
+ tzinfo (1.2.3)
+ thread_safe (~> 0.1)
+ unf (0.1.4)
+ unf_ext
+ unf_ext (0.0.7.2)
+ unicode-display_width (1.0.1)
+ virtus (1.0.5)
+ axiom-types (~> 0.1)
+ coercible (~> 1.0)
+ descendants_tracker (~> 0.0, >= 0.0.3)
+ equalizer (~> 0.0, >= 0.0.9)
+ yard (0.8.7.6)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ aruba
+ codeclimate-test-reporter
+ coveralls
+ cucumber
+ flay
+ flog
+ guard
+ guard-bundler
+ guard-cucumber
+ guard-rspec
+ guard-rubocop
+ inch
+ listen
+ pio!
+ pry
+ rake
+ rb-fchange
+ rb-fsevent
+ rb-inotify
+ reek
+ relish
+ rspec
+ rspec-given
+ rubocop
+ terminal-notifier-guard
+ yard
+
+BUNDLED WITH
+ 1.14.6
diff --git a/Guardfile b/Guardfile
index b8cb0737..0709b6d4 100644
--- a/Guardfile
+++ b/Guardfile
@@ -14,7 +14,7 @@ guard :rubocop do
watch(%r{(?:.+/)?\.rubocop\.yml$}) { |m| File.dirname(m[0]) }
end
-guard :cucumber, cli: '--profile default' do
+guard :cucumber, cmd_additional_args: '--profile default' do
watch(%r{^features/.+\.feature$})
watch(%r{^features/support/.+$}) { 'features' }
watch(%r{^features/step_definitions/(.+)_steps\.rb$}) do |m|
diff --git a/LICENSE b/LICENSE
deleted file mode 100644
index c3b05826..00000000
--- a/LICENSE
+++ /dev/null
@@ -1,674 +0,0 @@
-GNU GENERAL PUBLIC LICENSE
- Version 3, 29 June 2007
-
- Copyright (C) 2007 Free Software Foundation, Inc. {http://fsf.org/}
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
- Preamble
-
- The GNU General Public License is a free, copyleft license for
-software and other kinds of works.
-
- The licenses for most software and other practical works are designed
-to take away your freedom to share and change the works. By contrast,
-the GNU General Public License is intended to guarantee your freedom to
-share and change all versions of a program--to make sure it remains free
-software for all its users. We, the Free Software Foundation, use the
-GNU General Public License for most of our software; it applies also to
-any other work released this way by its authors. You can apply it to
-your programs, too.
-
- When we speak of free software, we are referring to freedom, not
-price. Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-them if you wish), that you receive source code or can get it if you
-want it, that you can change the software or use pieces of it in new
-free programs, and that you know you can do these things.
-
- To protect your rights, we need to prevent others from denying you
-these rights or asking you to surrender the rights. Therefore, you have
-certain responsibilities if you distribute copies of the software, or if
-you modify it: responsibilities to respect the freedom of others.
-
- For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must pass on to the recipients the same
-freedoms that you received. You must make sure that they, too, receive
-or can get the source code. And you must show them these terms so they
-know their rights.
-
- Developers that use the GNU GPL protect your rights with two steps:
-(1) assert copyright on the software, and (2) offer you this License
-giving you legal permission to copy, distribute and/or modify it.
-
- For the developers' and authors' protection, the GPL clearly explains
-that there is no warranty for this free software. For both users' and
-authors' sake, the GPL requires that modified versions be marked as
-changed, so that their problems will not be attributed erroneously to
-authors of previous versions.
-
- Some devices are designed to deny users access to install or run
-modified versions of the software inside them, although the manufacturer
-can do so. This is fundamentally incompatible with the aim of
-protecting users' freedom to change the software. The systematic
-pattern of such abuse occurs in the area of products for individuals to
-use, which is precisely where it is most unacceptable. Therefore, we
-have designed this version of the GPL to prohibit the practice for those
-products. If such problems arise substantially in other domains, we
-stand ready to extend this provision to those domains in future versions
-of the GPL, as needed to protect the freedom of users.
-
- Finally, every program is threatened constantly by software patents.
-States should not allow patents to restrict development and use of
-software on general-purpose computers, but in those that do, we wish to
-avoid the special danger that patents applied to a free program could
-make it effectively proprietary. To prevent this, the GPL assures that
-patents cannot be used to render the program non-free.
-
- The precise terms and conditions for copying, distribution and
-modification follow.
-
- TERMS AND CONDITIONS
-
- 0. Definitions.
-
- "This License" refers to version 3 of the GNU General Public License.
-
- "Copyright" also means copyright-like laws that apply to other kinds of
-works, such as semiconductor masks.
-
- "The Program" refers to any copyrightable work licensed under this
-License. Each licensee is addressed as "you". "Licensees" and
-"recipients" may be individuals or organizations.
-
- To "modify" a work means to copy from or adapt all or part of the work
-in a fashion requiring copyright permission, other than the making of an
-exact copy. The resulting work is called a "modified version" of the
-earlier work or a work "based on" the earlier work.
-
- A "covered work" means either the unmodified Program or a work based
-on the Program.
-
- To "propagate" a work means to do anything with it that, without
-permission, would make you directly or secondarily liable for
-infringement under applicable copyright law, except executing it on a
-computer or modifying a private copy. Propagation includes copying,
-distribution (with or without modification), making available to the
-public, and in some countries other activities as well.
-
- To "convey" a work means any kind of propagation that enables other
-parties to make or receive copies. Mere interaction with a user through
-a computer network, with no transfer of a copy, is not conveying.
-
- An interactive user interface displays "Appropriate Legal Notices"
-to the extent that it includes a convenient and prominently visible
-feature that (1) displays an appropriate copyright notice, and (2)
-tells the user that there is no warranty for the work (except to the
-extent that warranties are provided), that licensees may convey the
-work under this License, and how to view a copy of this License. If
-the interface presents a list of user commands or options, such as a
-menu, a prominent item in the list meets this criterion.
-
- 1. Source Code.
-
- The "source code" for a work means the preferred form of the work
-for making modifications to it. "Object code" means any non-source
-form of a work.
-
- A "Standard Interface" means an interface that either is an official
-standard defined by a recognized standards body, or, in the case of
-interfaces specified for a particular programming language, one that
-is widely used among developers working in that language.
-
- The "System Libraries" of an executable work include anything, other
-than the work as a whole, that (a) is included in the normal form of
-packaging a Major Component, but which is not part of that Major
-Component, and (b) serves only to enable use of the work with that
-Major Component, or to implement a Standard Interface for which an
-implementation is available to the public in source code form. A
-"Major Component", in this context, means a major essential component
-(kernel, window system, and so on) of the specific operating system
-(if any) on which the executable work runs, or a compiler used to
-produce the work, or an object code interpreter used to run it.
-
- The "Corresponding Source" for a work in object code form means all
-the source code needed to generate, install, and (for an executable
-work) run the object code and to modify the work, including scripts to
-control those activities. However, it does not include the work's
-System Libraries, or general-purpose tools or generally available free
-programs which are used unmodified in performing those activities but
-which are not part of the work. For example, Corresponding Source
-includes interface definition files associated with source files for
-the work, and the source code for shared libraries and dynamically
-linked subprograms that the work is specifically designed to require,
-such as by intimate data communication or control flow between those
-subprograms and other parts of the work.
-
- The Corresponding Source need not include anything that users
-can regenerate automatically from other parts of the Corresponding
-Source.
-
- The Corresponding Source for a work in source code form is that
-same work.
-
- 2. Basic Permissions.
-
- All rights granted under this License are granted for the term of
-copyright on the Program, and are irrevocable provided the stated
-conditions are met. This License explicitly affirms your unlimited
-permission to run the unmodified Program. The output from running a
-covered work is covered by this License only if the output, given its
-content, constitutes a covered work. This License acknowledges your
-rights of fair use or other equivalent, as provided by copyright law.
-
- You may make, run and propagate covered works that you do not
-convey, without conditions so long as your license otherwise remains
-in force. You may convey covered works to others for the sole purpose
-of having them make modifications exclusively for you, or provide you
-with facilities for running those works, provided that you comply with
-the terms of this License in conveying all material for which you do
-not control copyright. Those thus making or running the covered works
-for you must do so exclusively on your behalf, under your direction
-and control, on terms that prohibit them from making any copies of
-your copyrighted material outside their relationship with you.
-
- Conveying under any other circumstances is permitted solely under
-the conditions stated below. Sublicensing is not allowed; section 10
-makes it unnecessary.
-
- 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
-
- No covered work shall be deemed part of an effective technological
-measure under any applicable law fulfilling obligations under article
-11 of the WIPO copyright treaty adopted on 20 December 1996, or
-similar laws prohibiting or restricting circumvention of such
-measures.
-
- When you convey a covered work, you waive any legal power to forbid
-circumvention of technological measures to the extent such circumvention
-is effected by exercising rights under this License with respect to
-the covered work, and you disclaim any intention to limit operation or
-modification of the work as a means of enforcing, against the work's
-users, your or third parties' legal rights to forbid circumvention of
-technological measures.
-
- 4. Conveying Verbatim Copies.
-
- You may convey verbatim copies of the Program's source code as you
-receive it, in any medium, provided that you conspicuously and
-appropriately publish on each copy an appropriate copyright notice;
-keep intact all notices stating that this License and any
-non-permissive terms added in accord with section 7 apply to the code;
-keep intact all notices of the absence of any warranty; and give all
-recipients a copy of this License along with the Program.
-
- You may charge any price or no price for each copy that you convey,
-and you may offer support or warranty protection for a fee.
-
- 5. Conveying Modified Source Versions.
-
- You may convey a work based on the Program, or the modifications to
-produce it from the Program, in the form of source code under the
-terms of section 4, provided that you also meet all of these conditions:
-
- a) The work must carry prominent notices stating that you modified
- it, and giving a relevant date.
-
- b) The work must carry prominent notices stating that it is
- released under this License and any conditions added under section
- 7. This requirement modifies the requirement in section 4 to
- "keep intact all notices".
-
- c) You must license the entire work, as a whole, under this
- License to anyone who comes into possession of a copy. This
- License will therefore apply, along with any applicable section 7
- additional terms, to the whole of the work, and all its parts,
- regardless of how they are packaged. This License gives no
- permission to license the work in any other way, but it does not
- invalidate such permission if you have separately received it.
-
- d) If the work has interactive user interfaces, each must display
- Appropriate Legal Notices; however, if the Program has interactive
- interfaces that do not display Appropriate Legal Notices, your
- work need not make them do so.
-
- A compilation of a covered work with other separate and independent
-works, which are not by their nature extensions of the covered work,
-and which are not combined with it such as to form a larger program,
-in or on a volume of a storage or distribution medium, is called an
-"aggregate" if the compilation and its resulting copyright are not
-used to limit the access or legal rights of the compilation's users
-beyond what the individual works permit. Inclusion of a covered work
-in an aggregate does not cause this License to apply to the other
-parts of the aggregate.
-
- 6. Conveying Non-Source Forms.
-
- You may convey a covered work in object code form under the terms
-of sections 4 and 5, provided that you also convey the
-machine-readable Corresponding Source under the terms of this License,
-in one of these ways:
-
- a) Convey the object code in, or embodied in, a physical product
- (including a physical distribution medium), accompanied by the
- Corresponding Source fixed on a durable physical medium
- customarily used for software interchange.
-
- b) Convey the object code in, or embodied in, a physical product
- (including a physical distribution medium), accompanied by a
- written offer, valid for at least three years and valid for as
- long as you offer spare parts or customer support for that product
- model, to give anyone who possesses the object code either (1) a
- copy of the Corresponding Source for all the software in the
- product that is covered by this License, on a durable physical
- medium customarily used for software interchange, for a price no
- more than your reasonable cost of physically performing this
- conveying of source, or (2) access to copy the
- Corresponding Source from a network server at no charge.
-
- c) Convey individual copies of the object code with a copy of the
- written offer to provide the Corresponding Source. This
- alternative is allowed only occasionally and noncommercially, and
- only if you received the object code with such an offer, in accord
- with subsection 6b.
-
- d) Convey the object code by offering access from a designated
- place (gratis or for a charge), and offer equivalent access to the
- Corresponding Source in the same way through the same place at no
- further charge. You need not require recipients to copy the
- Corresponding Source along with the object code. If the place to
- copy the object code is a network server, the Corresponding Source
- may be on a different server (operated by you or a third party)
- that supports equivalent copying facilities, provided you maintain
- clear directions next to the object code saying where to find the
- Corresponding Source. Regardless of what server hosts the
- Corresponding Source, you remain obligated to ensure that it is
- available for as long as needed to satisfy these requirements.
-
- e) Convey the object code using peer-to-peer transmission, provided
- you inform other peers where the object code and Corresponding
- Source of the work are being offered to the general public at no
- charge under subsection 6d.
-
- A separable portion of the object code, whose source code is excluded
-from the Corresponding Source as a System Library, need not be
-included in conveying the object code work.
-
- A "User Product" is either (1) a "consumer product", which means any
-tangible personal property which is normally used for personal, family,
-or household purposes, or (2) anything designed or sold for incorporation
-into a dwelling. In determining whether a product is a consumer product,
-doubtful cases shall be resolved in favor of coverage. For a particular
-product received by a particular user, "normally used" refers to a
-typical or common use of that class of product, regardless of the status
-of the particular user or of the way in which the particular user
-actually uses, or expects or is expected to use, the product. A product
-is a consumer product regardless of whether the product has substantial
-commercial, industrial or non-consumer uses, unless such uses represent
-the only significant mode of use of the product.
-
- "Installation Information" for a User Product means any methods,
-procedures, authorization keys, or other information required to install
-and execute modified versions of a covered work in that User Product from
-a modified version of its Corresponding Source. The information must
-suffice to ensure that the continued functioning of the modified object
-code is in no case prevented or interfered with solely because
-modification has been made.
-
- If you convey an object code work under this section in, or with, or
-specifically for use in, a User Product, and the conveying occurs as
-part of a transaction in which the right of possession and use of the
-User Product is transferred to the recipient in perpetuity or for a
-fixed term (regardless of how the transaction is characterized), the
-Corresponding Source conveyed under this section must be accompanied
-by the Installation Information. But this requirement does not apply
-if neither you nor any third party retains the ability to install
-modified object code on the User Product (for example, the work has
-been installed in ROM).
-
- The requirement to provide Installation Information does not include a
-requirement to continue to provide support service, warranty, or updates
-for a work that has been modified or installed by the recipient, or for
-the User Product in which it has been modified or installed. Access to a
-network may be denied when the modification itself materially and
-adversely affects the operation of the network or violates the rules and
-protocols for communication across the network.
-
- Corresponding Source conveyed, and Installation Information provided,
-in accord with this section must be in a format that is publicly
-documented (and with an implementation available to the public in
-source code form), and must require no special password or key for
-unpacking, reading or copying.
-
- 7. Additional Terms.
-
- "Additional permissions" are terms that supplement the terms of this
-License by making exceptions from one or more of its conditions.
-Additional permissions that are applicable to the entire Program shall
-be treated as though they were included in this License, to the extent
-that they are valid under applicable law. If additional permissions
-apply only to part of the Program, that part may be used separately
-under those permissions, but the entire Program remains governed by
-this License without regard to the additional permissions.
-
- When you convey a copy of a covered work, you may at your option
-remove any additional permissions from that copy, or from any part of
-it. (Additional permissions may be written to require their own
-removal in certain cases when you modify the work.) You may place
-additional permissions on material, added by you to a covered work,
-for which you have or can give appropriate copyright permission.
-
- Notwithstanding any other provision of this License, for material you
-add to a covered work, you may (if authorized by the copyright holders of
-that material) supplement the terms of this License with terms:
-
- a) Disclaiming warranty or limiting liability differently from the
- terms of sections 15 and 16 of this License; or
-
- b) Requiring preservation of specified reasonable legal notices or
- author attributions in that material or in the Appropriate Legal
- Notices displayed by works containing it; or
-
- c) Prohibiting misrepresentation of the origin of that material, or
- requiring that modified versions of such material be marked in
- reasonable ways as different from the original version; or
-
- d) Limiting the use for publicity purposes of names of licensors or
- authors of the material; or
-
- e) Declining to grant rights under trademark law for use of some
- trade names, trademarks, or service marks; or
-
- f) Requiring indemnification of licensors and authors of that
- material by anyone who conveys the material (or modified versions of
- it) with contractual assumptions of liability to the recipient, for
- any liability that these contractual assumptions directly impose on
- those licensors and authors.
-
- All other non-permissive additional terms are considered "further
-restrictions" within the meaning of section 10. If the Program as you
-received it, or any part of it, contains a notice stating that it is
-governed by this License along with a term that is a further
-restriction, you may remove that term. If a license document contains
-a further restriction but permits relicensing or conveying under this
-License, you may add to a covered work material governed by the terms
-of that license document, provided that the further restriction does
-not survive such relicensing or conveying.
-
- If you add terms to a covered work in accord with this section, you
-must place, in the relevant source files, a statement of the
-additional terms that apply to those files, or a notice indicating
-where to find the applicable terms.
-
- Additional terms, permissive or non-permissive, may be stated in the
-form of a separately written license, or stated as exceptions;
-the above requirements apply either way.
-
- 8. Termination.
-
- You may not propagate or modify a covered work except as expressly
-provided under this License. Any attempt otherwise to propagate or
-modify it is void, and will automatically terminate your rights under
-this License (including any patent licenses granted under the third
-paragraph of section 11).
-
- However, if you cease all violation of this License, then your
-license from a particular copyright holder is reinstated (a)
-provisionally, unless and until the copyright holder explicitly and
-finally terminates your license, and (b) permanently, if the copyright
-holder fails to notify you of the violation by some reasonable means
-prior to 60 days after the cessation.
-
- Moreover, your license from a particular copyright holder is
-reinstated permanently if the copyright holder notifies you of the
-violation by some reasonable means, this is the first time you have
-received notice of violation of this License (for any work) from that
-copyright holder, and you cure the violation prior to 30 days after
-your receipt of the notice.
-
- Termination of your rights under this section does not terminate the
-licenses of parties who have received copies or rights from you under
-this License. If your rights have been terminated and not permanently
-reinstated, you do not qualify to receive new licenses for the same
-material under section 10.
-
- 9. Acceptance Not Required for Having Copies.
-
- You are not required to accept this License in order to receive or
-run a copy of the Program. Ancillary propagation of a covered work
-occurring solely as a consequence of using peer-to-peer transmission
-to receive a copy likewise does not require acceptance. However,
-nothing other than this License grants you permission to propagate or
-modify any covered work. These actions infringe copyright if you do
-not accept this License. Therefore, by modifying or propagating a
-covered work, you indicate your acceptance of this License to do so.
-
- 10. Automatic Licensing of Downstream Recipients.
-
- Each time you convey a covered work, the recipient automatically
-receives a license from the original licensors, to run, modify and
-propagate that work, subject to this License. You are not responsible
-for enforcing compliance by third parties with this License.
-
- An "entity transaction" is a transaction transferring control of an
-organization, or substantially all assets of one, or subdividing an
-organization, or merging organizations. If propagation of a covered
-work results from an entity transaction, each party to that
-transaction who receives a copy of the work also receives whatever
-licenses to the work the party's predecessor in interest had or could
-give under the previous paragraph, plus a right to possession of the
-Corresponding Source of the work from the predecessor in interest, if
-the predecessor has it or can get it with reasonable efforts.
-
- You may not impose any further restrictions on the exercise of the
-rights granted or affirmed under this License. For example, you may
-not impose a license fee, royalty, or other charge for exercise of
-rights granted under this License, and you may not initiate litigation
-(including a cross-claim or counterclaim in a lawsuit) alleging that
-any patent claim is infringed by making, using, selling, offering for
-sale, or importing the Program or any portion of it.
-
- 11. Patents.
-
- A "contributor" is a copyright holder who authorizes use under this
-License of the Program or a work on which the Program is based. The
-work thus licensed is called the contributor's "contributor version".
-
- A contributor's "essential patent claims" are all patent claims
-owned or controlled by the contributor, whether already acquired or
-hereafter acquired, that would be infringed by some manner, permitted
-by this License, of making, using, or selling its contributor version,
-but do not include claims that would be infringed only as a
-consequence of further modification of the contributor version. For
-purposes of this definition, "control" includes the right to grant
-patent sublicenses in a manner consistent with the requirements of
-this License.
-
- Each contributor grants you a non-exclusive, worldwide, royalty-free
-patent license under the contributor's essential patent claims, to
-make, use, sell, offer for sale, import and otherwise run, modify and
-propagate the contents of its contributor version.
-
- In the following three paragraphs, a "patent license" is any express
-agreement or commitment, however denominated, not to enforce a patent
-(such as an express permission to practice a patent or covenant not to
-sue for patent infringement). To "grant" such a patent license to a
-party means to make such an agreement or commitment not to enforce a
-patent against the party.
-
- If you convey a covered work, knowingly relying on a patent license,
-and the Corresponding Source of the work is not available for anyone
-to copy, free of charge and under the terms of this License, through a
-publicly available network server or other readily accessible means,
-then you must either (1) cause the Corresponding Source to be so
-available, or (2) arrange to deprive yourself of the benefit of the
-patent license for this particular work, or (3) arrange, in a manner
-consistent with the requirements of this License, to extend the patent
-license to downstream recipients. "Knowingly relying" means you have
-actual knowledge that, but for the patent license, your conveying the
-covered work in a country, or your recipient's use of the covered work
-in a country, would infringe one or more identifiable patents in that
-country that you have reason to believe are valid.
-
- If, pursuant to or in connection with a single transaction or
-arrangement, you convey, or propagate by procuring conveyance of, a
-covered work, and grant a patent license to some of the parties
-receiving the covered work authorizing them to use, propagate, modify
-or convey a specific copy of the covered work, then the patent license
-you grant is automatically extended to all recipients of the covered
-work and works based on it.
-
- A patent license is "discriminatory" if it does not include within
-the scope of its coverage, prohibits the exercise of, or is
-conditioned on the non-exercise of one or more of the rights that are
-specifically granted under this License. You may not convey a covered
-work if you are a party to an arrangement with a third party that is
-in the business of distributing software, under which you make payment
-to the third party based on the extent of your activity of conveying
-the work, and under which the third party grants, to any of the
-parties who would receive the covered work from you, a discriminatory
-patent license (a) in connection with copies of the covered work
-conveyed by you (or copies made from those copies), or (b) primarily
-for and in connection with specific products or compilations that
-contain the covered work, unless you entered into that arrangement,
-or that patent license was granted, prior to 28 March 2007.
-
- Nothing in this License shall be construed as excluding or limiting
-any implied license or other defenses to infringement that may
-otherwise be available to you under applicable patent law.
-
- 12. No Surrender of Others' Freedom.
-
- If conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License. If you cannot convey a
-covered work so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you may
-not convey it at all. For example, if you agree to terms that obligate you
-to collect a royalty for further conveying from those to whom you convey
-the Program, the only way you could satisfy both those terms and this
-License would be to refrain entirely from conveying the Program.
-
- 13. Use with the GNU Affero General Public License.
-
- Notwithstanding any other provision of this License, you have
-permission to link or combine any covered work with a work licensed
-under version 3 of the GNU Affero General Public License into a single
-combined work, and to convey the resulting work. The terms of this
-License will continue to apply to the part which is the covered work,
-but the special requirements of the GNU Affero General Public License,
-section 13, concerning interaction through a network will apply to the
-combination as such.
-
- 14. Revised Versions of this License.
-
- The Free Software Foundation may publish revised and/or new versions of
-the GNU General Public License from time to time. Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
- Each version is given a distinguishing version number. If the
-Program specifies that a certain numbered version of the GNU General
-Public License "or any later version" applies to it, you have the
-option of following the terms and conditions either of that numbered
-version or of any later version published by the Free Software
-Foundation. If the Program does not specify a version number of the
-GNU General Public License, you may choose any version ever published
-by the Free Software Foundation.
-
- If the Program specifies that a proxy can decide which future
-versions of the GNU General Public License can be used, that proxy's
-public statement of acceptance of a version permanently authorizes you
-to choose that version for the Program.
-
- Later license versions may give you additional or different
-permissions. However, no additional obligations are imposed on any
-author or copyright holder as a result of your choosing to follow a
-later version.
-
- 15. Disclaimer of Warranty.
-
- THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
-APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
-HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
-OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
-THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
-IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
-ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
-
- 16. Limitation of Liability.
-
- IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
-THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
-GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
-USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
-DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
-PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
-EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGES.
-
- 17. Interpretation of Sections 15 and 16.
-
- If the disclaimer of warranty and limitation of liability provided
-above cannot be given local legal effect according to their terms,
-reviewing courts shall apply local law that most closely approximates
-an absolute waiver of all civil liability in connection with the
-Program, unless a warranty or assumption of liability accompanies a
-copy of the Program in return for a fee.
-
- END OF TERMS AND CONDITIONS
-
- How to Apply These Terms to Your New Programs
-
- If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
- To do so, attach the following notices to the program. It is safest
-to attach them to the start of each source file to most effectively
-state the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
- {one line to give the program's name and a brief idea of what it does.}
- Copyright (C) {year} {name of author}
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see {http://www.gnu.org/licenses/}.
-
-Also add information on how to contact you by electronic and paper mail.
-
- If the program does terminal interaction, make it output a short
-notice like this when it starts in an interactive mode:
-
- pio Copyright (C) 2013 Trema
- This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
- This is free software, and you are welcome to redistribute it
- under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License. Of course, your program's commands
-might be different; for a GUI interface, you would use an "about box".
-
- You should also get your employer (if you work as a programmer) or school,
-if any, to sign a "copyright disclaimer" for the program, if necessary.
-For more information on this, and how to apply and follow the GNU GPL, see
-{http://www.gnu.org/licenses/}.
-
- The GNU General Public License does not permit incorporating your program
-into proprietary programs. If your program is a subroutine library, you
-may consider it more useful to permit linking proprietary applications with
-the library. If this is what you want to do, use the GNU Lesser General
-Public License instead of this License. But first, please read
-{http://www.gnu.org/philosophy/why-not-lgpl.html}.
diff --git a/README.md b/README.md
index 511dc030..009f0d3d 100644
--- a/README.md
+++ b/README.md
@@ -8,8 +8,6 @@
-
-
Pio is a ruby gem to easily parse and generate network packets.
## Features Overview
@@ -22,47 +20,9 @@ Pio is a ruby gem to easily parse and generate network packets.
[BinData](https://github.com/dmendel/bindata)'s declarative binary
format DSL so that it is easy to read and debug by human beings.
-## Supported packet formats
-
-Pio supports the following packet formats:
-
-- [ICMP](https://relishapp.com/trema/pio/docs/pio-icmp)
-- [ARP](https://relishapp.com/trema/pio/docs/pio-arp)
-- [LLDP](https://relishapp.com/trema/pio/docs/pio-lldp)
-- [DHCP](https://relishapp.com/trema/pio/docs/pio-dhcp)
-- [UDP](https://relishapp.com/trema/pio/docs/pio-udp)
-- OpenFlow 1.0
- - [Hello](https://relishapp.com/trema/pio/docs/open-flow10/pio-hello)
- - [HelloFailed](https://relishapp.com/trema/pio/docs/open-flow10/pio-error-hellofailed)
- - [BadRequest](https://relishapp.com/trema/pio/docs/open-flow10/pio-error-badrequest)
- - [Echo Request](https://relishapp.com/trema/pio/docs/open-flow10/pio-echo-request)
- - [Echo Reply](https://relishapp.com/trema/pio/docs/open-flow10/pio-echo-reply)
- - [Features Request](https://relishapp.com/trema/pio/docs/open-flow10/pio-features-request)
- - [Features Reply](https://relishapp.com/trema/pio/docs/open-flow10/pio-features-reply)
- - [Packet In](https://relishapp.com/trema/pio/docs/open-flow10/pio-packetin)
- - [Packet Out](https://relishapp.com/trema/pio/docs/open-flow10/pio-packetout)
- - [Flow Mod](https://relishapp.com/trema/pio/docs/open-flow10/pio-flowmod)
- - [Port Status](https://relishapp.com/trema/pio/docs/open-flow10/pio-portstatus)
- - [Exact Match](https://relishapp.com/trema/pio/docs/open-flow10/pio-exactmatch)
- - [Barrier Request](https://relishapp.com/trema/pio/docs/open-flow10/pio-barrier-request)
- - [Barrier Reply](https://relishapp.com/trema/pio/docs/open-flow10/pio-barrier-reply)
- - [Description Stats Request](https://relishapp.com/trema/pio/docs/open-flow10/pio-descriptionstats-request)
- - [Description Stats Reply](https://relishapp.com/trema/pio/docs/open-flow10/pio-descriptionstats-reply)
- - [Flow Stats Request](https://relishapp.com/trema/pio/docs/open-flow10/pio-flowstats-request)
- - [Flow Stats Reply](https://relishapp.com/trema/pio/docs/open-flow10/pio-flowstats-reply)
- - [Aggregate Stats Request](https://relishapp.com/trema/pio/docs/open-flow10/pio-aggregatestats-request)
- - [Aggregate Stats Reply](https://relishapp.com/trema/pio/docs/open-flow10/pio-aggregatestats-reply)
-- OpenFlow 1.3
- - [Hello](https://relishapp.com/trema/pio/docs/open-flow13/pio-hello)
- - [HelloFailed](https://relishapp.com/trema/pio/docs/open-flow13/pio-error-hellofailed)
- - [BadRequest](https://relishapp.com/trema/pio/docs/open-flow13/pio-error-badrequest)
- - [Echo Request](https://relishapp.com/trema/pio/docs/open-flow13/pio-echo-request)
- - [Echo Reply](https://relishapp.com/trema/pio/docs/open-flow13/pio-echo-reply)
- - [Features Request](https://relishapp.com/trema/pio/docs/open-flow13/pio-features-request)
- - [Features Reply](https://relishapp.com/trema/pio/docs/open-flow13/pio-features-reply)
- - [Packet In](https://relishapp.com/trema/pio/docs/open-flow13/pio-packetin)
- - [Packet Out](https://relishapp.com/trema/pio/docs/open-flow13/pio-packetout)
- - [Flow Mod](https://relishapp.com/trema/pio/docs/open-flow13/pio-flowmod)
+## Documentation
+
+See https://relishapp.com/trema/pio/docs for links to documentation for all APIs.
## Installation
@@ -80,10 +40,6 @@ and install it by running Bundler:
bundle
```
-## Documents
-
-- [API document](https://relishapp.com/trema/pio/docs)
-
## Team
- [Yasuhito Takamiya](https://github.com/yasuhito) ([@yasuhito](https://twitter.com/yasuhito))
@@ -100,6 +56,7 @@ bundle
## License
-Pio is released under the GNU General Public License version 3.0:
+Pio is released under the GNU General Public License version 2.0 or MIT License:
--
+* http://www.gnu.org/licenses/gpl-2.0.html
+* http://www.opensource.org/licenses/MIT
diff --git a/Rakefile b/Rakefile
index f2066f58..4fe0a033 100644
--- a/Rakefile
+++ b/Rakefile
@@ -1,7 +1,7 @@
require 'bundler/gem_tasks'
-RELISH_PROJECT = 'trema/pio'
-FLAY_THRESHOLD = 1343
+RELISH_PROJECT = 'trema/pio'.freeze
+FLAY_THRESHOLD = 1446
task default: :travis
task test: [:spec, :cucumber]
@@ -24,7 +24,7 @@ end
desc 'Dump packet data file in Array'
task :dump do
unless ENV['PACKET_FILE']
- fail 'Usage: rake PACKET_FILE="foobar.{pcap,raw}" dump'
+ raise 'Usage: rake PACKET_FILE="foobar.{pcap,raw}" dump'
end
packet_file =
File.join(File.dirname(__FILE__), 'features/', ENV['PACKET_FILE'])
@@ -38,6 +38,6 @@ task :dump do
end
end
else
- fail "Unsupported file extension: #{ENV['PACKET_FILE']}"
+ raise "Unsupported file extension: #{ENV['PACKET_FILE']}"
end
end
diff --git a/features/.nav b/features/.nav
index 501a3aa4..0c4154a4 100644
--- a/features/.nav
+++ b/features/.nav
@@ -1,5 +1,13 @@
-- icmp.feature
-- arp.feature
+- ethernet_header.feature
+- ipv4_header.feature
+- arp:
+ - arp.feature
+ - arp_request.feature
+ - arp_reply.feature
+- icmp:
+ - icmp.feature
+ - icmp_request.feature
+ - icmp_reply.feature
- lldp.feature
- dhcp.feature
- udp.feature
@@ -12,9 +20,10 @@
- features_request.feature
- features_reply.feature
- packet_in.feature
+ - flow_removed.feature
+ - port_status.feature
- packet_out.feature
- flow_mod.feature
- - port_status.feature
- exact_match.feature
- open_flow13:
- hello.feature
diff --git a/features/arp-storm.pcap b/features/arp-storm.pcap
deleted file mode 100644
index 20a10599..00000000
Binary files a/features/arp-storm.pcap and /dev/null differ
diff --git a/features/arp.pcap b/features/arp.pcap
deleted file mode 100644
index 94280940..00000000
Binary files a/features/arp.pcap and /dev/null differ
diff --git a/features/arp.feature b/features/arp/arp.feature
similarity index 59%
rename from features/arp.feature
rename to features/arp/arp.feature
index 88b59ad1..2dd6731f 100644
--- a/features/arp.feature
+++ b/features/arp/arp.feature
@@ -1,15 +1,13 @@
-Feature: Pio::Arp
- Scenario: create an ARP request
- When I try to create a packet with:
- """
- Pio::Arp::Request.new(
- source_mac: '00:26:82:eb:ea:d1',
- sender_protocol_address: '192.168.83.3',
- target_protocol_address: '192.168.83.254'
- )
+Feature: Arp
+ Background:
+ Given I use the fixture "arp"
+
+ Scenario: read an ARP request packet
+ When I create a packet with:
+ """ruby
+ Pio::Arp.read(eval(IO.read('arp_request.rb')))
"""
- Then it should finish successfully
- And the packet has the following fields and values:
+ Then the packet has the following fields and values:
| field | value |
| class | Pio::Arp::Request |
| destination_mac | ff:ff:ff:ff:ff:ff |
@@ -25,18 +23,12 @@ Feature: Pio::Arp
| target_hardware_address | 00:00:00:00:00:00 |
| target_protocol_address | 192.168.83.254 |
- Scenario: create an ARP reply
- When I try to create a packet with:
- """
- Pio::Arp::Reply.new(
- source_mac: '00:16:9d:1d:9c:c4',
- destination_mac: '00:26:82:eb:ea:d1',
- sender_protocol_address: '192.168.83.254',
- target_protocol_address: '192.168.83.3'
- )
+ Scenario: read an ARP reply packet
+ When I create a packet with:
+ """ruby
+ Pio::Arp.read(eval(IO.read('arp_reply.rb')))
"""
- Then it should finish successfully
- And the packet has the following fields and values:
+ Then the packet has the following fields and values:
| field | value |
| class | Pio::Arp::Reply |
| destination_mac | 00:26:82:eb:ea:d1 |
@@ -51,11 +43,3 @@ Feature: Pio::Arp
| sender_protocol_address | 192.168.83.254 |
| target_hardware_address | 00:26:82:eb:ea:d1 |
| target_protocol_address | 192.168.83.3 |
-
- Scenario: parse arp.pcap
- When I try to parse a file named "arp.pcap" with "Pio::Arp" class
- Then it should finish successfully
-
- Scenario: parse arp-storm.pcap
- When I try to parse a file named "arp-storm.pcap" with "Pio::Arp" class
- Then it should finish successfully
diff --git a/features/arp/arp_reply.feature b/features/arp/arp_reply.feature
new file mode 100644
index 00000000..e1888eb4
--- /dev/null
+++ b/features/arp/arp_reply.feature
@@ -0,0 +1,69 @@
+Feature: Arp::Reply
+ Scenario: create an ARP reply
+ When I create a packet with:
+ """ruby
+ Pio::Arp::Reply.new(
+ destination_mac: '00:26:82:eb:ea:d1',
+ source_mac: '00:16:9d:1d:9c:c4',
+ sender_protocol_address: '192.168.83.254',
+ target_protocol_address: '192.168.83.3'
+ )
+ """
+ Then the packet has the following fields and values:
+ | field | value |
+ | class | Pio::Arp::Reply |
+ | destination_mac | 00:26:82:eb:ea:d1 |
+ | source_mac | 00:16:9d:1d:9c:c4 |
+ | ether_type | 2054 |
+ | hardware_type | 1 |
+ | protocol_type | 2048 |
+ | hardware_length | 6 |
+ | protocol_length | 4 |
+ | operation | 2 |
+ | sender_hardware_address | 00:16:9d:1d:9c:c4 |
+ | sender_protocol_address | 192.168.83.254 |
+ | target_hardware_address | 00:26:82:eb:ea:d1 |
+ | target_protocol_address | 192.168.83.3 |
+
+ Scenario: convert to Ruby code
+ When I eval the following Ruby code:
+ """ruby
+ Pio::Arp::Reply.new(
+ destination_mac: '00:26:82:eb:ea:d1',
+ source_mac: '00:16:9d:1d:9c:c4',
+ sender_protocol_address: '192.168.83.254',
+ target_protocol_address: '192.168.83.3'
+ ).to_ruby
+ """
+ Then the result of eval should be:
+ """ruby
+ [
+ 0x00, 0x26, 0x82, 0xeb, 0xea, 0xd1, # destination_mac
+ 0x00, 0x16, 0x9d, 0x1d, 0x9c, 0xc4, # source_mac
+ 0x08, 0x06, # ether_type
+ 0x00, 0x01, # hardware_type
+ 0x08, 0x00, # protocol_type
+ 0x06, # hardware_length
+ 0x04, # protocol_length
+ 0x00, 0x02, # operation
+ 0x00, 0x16, 0x9d, 0x1d, 0x9c, 0xc4, # sender_hardware_address
+ 0xc0, 0xa8, 0x53, 0xfe, # sender_protocol_address
+ 0x00, 0x26, 0x82, 0xeb, 0xea, 0xd1, # target_hardware_address
+ 0xc0, 0xa8, 0x53, 0x03, # target_protocol_address
+ ].pack('C42')
+ """
+
+ Scenario: ARP reply inspection
+ When I eval the following Ruby code:
+ """ruby
+ Pio::Arp::Reply.new(
+ destination_mac: '00:26:82:eb:ea:d1',
+ source_mac: '00:16:9d:1d:9c:c4',
+ sender_protocol_address: '192.168.83.254',
+ target_protocol_address: '192.168.83.3'
+ ).inspect
+ """
+ Then the result of eval should be:
+ """ruby
+ #
+ """
diff --git a/features/arp/arp_request.feature b/features/arp/arp_request.feature
new file mode 100644
index 00000000..9b6b5b0d
--- /dev/null
+++ b/features/arp/arp_request.feature
@@ -0,0 +1,67 @@
+Feature: Arp::Request
+ Scenario: create an ARP request
+ When I create a packet with:
+ """ruby
+ Pio::Arp::Request.new(
+ source_mac: '00:26:82:eb:ea:d1',
+ sender_protocol_address: '192.168.83.3',
+ target_protocol_address: '192.168.83.254'
+ )
+ """
+ Then the packet has the following fields and values:
+ | field | value |
+ | class | Pio::Arp::Request |
+ | destination_mac | ff:ff:ff:ff:ff:ff |
+ | source_mac | 00:26:82:eb:ea:d1 |
+ | ether_type | 2054 |
+ | hardware_type | 1 |
+ | protocol_type | 2048 |
+ | hardware_length | 6 |
+ | protocol_length | 4 |
+ | operation | 1 |
+ | sender_hardware_address | 00:26:82:eb:ea:d1 |
+ | sender_protocol_address | 192.168.83.3 |
+ | target_hardware_address | 00:00:00:00:00:00 |
+ | target_protocol_address | 192.168.83.254 |
+
+ Scenario: convert to Ruby code
+ When I eval the following Ruby code:
+ """ruby
+ Pio::Arp::Request.new(
+ source_mac: '00:26:82:eb:ea:d1',
+ sender_protocol_address: '192.168.83.3',
+ target_protocol_address: '192.168.83.254'
+ ).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, 0x06, # ether_type
+ 0x00, 0x01, # hardware_type
+ 0x08, 0x00, # protocol_type
+ 0x06, # hardware_length
+ 0x04, # protocol_length
+ 0x00, 0x01, # operation
+ 0x00, 0x26, 0x82, 0xeb, 0xea, 0xd1, # sender_hardware_address
+ 0xc0, 0xa8, 0x53, 0x03, # sender_protocol_address
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, # target_hardware_address
+ 0xc0, 0xa8, 0x53, 0xfe, # target_protocol_address
+ ].pack('C42')
+ """
+
+ Scenario: ARP request inspection
+ When I eval the following Ruby code:
+ """ruby
+ Pio::Arp::Request.new(
+ source_mac: '00:26:82:eb:ea:d1',
+ sender_protocol_address: '192.168.83.3',
+ target_protocol_address: '192.168.83.254'
+ ).inspect
+ """
+ Then the result of eval should be:
+ """
+ #
+ """
+
diff --git a/features/dhcp.feature b/features/dhcp.feature
index dc9d8741..d34c67e5 100644
--- a/features/dhcp.feature
+++ b/features/dhcp.feature
@@ -1,6 +1,6 @@
-Feature: Pio::Dhcp
+Feature: Dhcp
Scenario: create a DHCP Ack
- When I try to create a packet with:
+ When I create a packet with:
"""
Pio::Dhcp::Ack.new(
source_mac: 'aa:bb:cc:dd:ee:ff',
@@ -14,95 +14,93 @@ Feature: Pio::Dhcp
subnet_mask: '255.255.255.0'
)
"""
- Then it should finish successfully
- And the packet has the following fields and values:
- | field | value |
- | class | Pio::Dhcp::Ack |
- | destination_mac | 11:22:33:44:55:66 |
- | source_mac | aa:bb:cc:dd:ee:ff |
- | ether_type | 2048 |
- | ip_version | 4 |
- | ip_header_length | 5 |
- | ip_type_of_service | 0 |
- | ip_total_length | 328 |
- | ip_identifier | 0 |
- | ip_flag | 0 |
- | ip_fragment | 0 |
- | ip_ttl | 128 |
- | ip_protocol | 17 |
- | ip_header_checksum | 47177 |
- | source_ip_address | 192.168.0.10 |
- | destination_ip_address | 192.168.0.1 |
- | udp_source_port | 67 |
- | udp_destination_port | 68 |
- | udp_length | 308 |
- | udp_checksum | 7012 |
- | message_type | 5 |
- | hw_addr_type | 1 |
- | hw_addr_len | 6 |
- | hops | 0 |
- | transaction_id | 3735928559 |
- | seconds | 0 |
- | bootp_flags | 0 |
- | client_ip_address | 0.0.0.0 |
- | your_ip_address | 192.168.0.1 |
- | next_server_ip_address | 0.0.0.0 |
- | relay_agent_ip_address | 0.0.0.0 |
- | client_mac_address | aa:bb:cc:dd:ee:ff |
- | subnet_mask | 255.255.255.0 |
- | server_identifier | 192.168.0.10 |
- | renewal_time_value | 3735928559 |
- | rebinding_time_value | 3735928559 |
- | ip_address_lease_time | 3735928559 |
+ Then the packet has the following fields and values:
+ | field | value |
+ | class | Pio::Dhcp::Ack |
+ | destination_mac | 11:22:33:44:55:66 |
+ | source_mac | aa:bb:cc:dd:ee:ff |
+ | ether_type | 2048 |
+ | ip_version | 4 |
+ | ip_header_length | 5 |
+ | ip_type_of_service | 0 |
+ | ip_total_length | 328 |
+ | ip_identifier | 0 |
+ | ip_flag | 0 |
+ | ip_fragment | 0 |
+ | ip_ttl | 128 |
+ | ip_protocol | 17 |
+ | ip_header_checksum | 47177 |
+ | source_ip_address | 192.168.0.10 |
+ | destination_ip_address | 192.168.0.1 |
+ | udp_source_port | 67 |
+ | udp_destination_port | 68 |
+ | udp_length | 308 |
+ | udp_checksum | 7012 |
+ | message_type | 5 |
+ | hw_addr_type | 1 |
+ | hw_addr_len | 6 |
+ | hops | 0 |
+ | transaction_id | 3735928559 |
+ | seconds | 0 |
+ | bootp_flags | 0 |
+ | client_ip_address | 0.0.0.0 |
+ | your_ip_address | 192.168.0.1 |
+ | next_server_ip_address | 0.0.0.0 |
+ | relay_agent_ip_address | 0.0.0.0 |
+ | client_mac_address | aa:bb:cc:dd:ee:ff |
+ | subnet_mask | 255.255.255.0 |
+ | server_identifier | 192.168.0.10 |
+ | renewal_time_value | 3735928559 |
+ | rebinding_time_value | 3735928559 |
+ | ip_address_lease_time | 3735928559 |
Scenario: create a DHCP Discover
- When I try to create a packet with:
+ When I create a packet with:
"""
Pio::Dhcp::Discover.new(
source_mac: '24:db:ac:41:e5:5b',
transaction_id: 0xdeadbeef
)
"""
- Then it should finish successfully
- And the packet has the following fields and values:
- | field | value |
- | class | Pio::Dhcp::Discover |
- | destination_mac | ff:ff:ff:ff:ff:ff |
- | source_mac | 24:db:ac:41:e5:5b |
- | ether_type | 2048 |
- | ip_version | 4 |
- | ip_header_length | 5 |
- | ip_type_of_service | 0 |
- | ip_total_length | 328 |
- | ip_identifier | 0 |
- | ip_flag | 0 |
- | ip_fragment | 0 |
- | ip_ttl | 128 |
- | ip_protocol | 17 |
- | ip_header_checksum | 14758 |
- | source_ip_address | 0.0.0.0 |
- | destination_ip_address | 255.255.255.255 |
- | udp_source_port | 68 |
- | udp_destination_port | 67 |
- | udp_length | 308 |
- | udp_checksum | 34836 |
- | message_type | 1 |
- | hw_addr_type | 1 |
- | hw_addr_len | 6 |
- | hops | 0 |
- | transaction_id | 3735928559 |
- | seconds | 0 |
- | bootp_flags | 0 |
- | client_ip_address | 0.0.0.0 |
- | your_ip_address | 0.0.0.0 |
- | next_server_ip_address | 0.0.0.0 |
- | relay_agent_ip_address | 0.0.0.0 |
- | client_mac_address | 24:db:ac:41:e5:5b |
- | parameters_list | [1, 3, 6, 42] |
- | client_identifier | 24:db:ac:41:e5:5b |
+ Then the packet has the following fields and values:
+ | field | value |
+ | class | Pio::Dhcp::Discover |
+ | destination_mac | ff:ff:ff:ff:ff:ff |
+ | source_mac | 24:db:ac:41:e5:5b |
+ | ether_type | 2048 |
+ | ip_version | 4 |
+ | ip_header_length | 5 |
+ | ip_type_of_service | 0 |
+ | ip_total_length | 328 |
+ | ip_identifier | 0 |
+ | ip_flag | 0 |
+ | ip_fragment | 0 |
+ | ip_ttl | 128 |
+ | ip_protocol | 17 |
+ | ip_header_checksum | 14758 |
+ | source_ip_address | 0.0.0.0 |
+ | destination_ip_address | 255.255.255.255 |
+ | udp_source_port | 68 |
+ | udp_destination_port | 67 |
+ | udp_length | 308 |
+ | udp_checksum | 34836 |
+ | message_type | 1 |
+ | hw_addr_type | 1 |
+ | hw_addr_len | 6 |
+ | hops | 0 |
+ | transaction_id | 3735928559 |
+ | seconds | 0 |
+ | bootp_flags | 0 |
+ | client_ip_address | 0.0.0.0 |
+ | your_ip_address | 0.0.0.0 |
+ | next_server_ip_address | 0.0.0.0 |
+ | relay_agent_ip_address | 0.0.0.0 |
+ | client_mac_address | 24:db:ac:41:e5:5b |
+ | parameters_list | [1, 3, 6, 42] |
+ | client_identifier | 24:db:ac:41:e5:5b |
Scenario: create a DHCP Request
- When I try to create a packet with:
+ When I create a packet with:
"""
Pio::Dhcp::Request.new(
source_mac: '24:db:ac:41:e5:5b',
@@ -111,47 +109,46 @@ Feature: Pio::Dhcp
requested_ip_address: '192.168.0.10'
)
"""
- Then it should finish successfully
- And the packet has the following fields and values:
- | field | value |
- | class | Pio::Dhcp::Request |
- | destination_mac | ff:ff:ff:ff:ff:ff |
- | source_mac | 24:db:ac:41:e5:5b |
- | ether_type | 2048 |
- | ip_version | 4 |
- | ip_header_length | 5 |
- | ip_type_of_service | 0 |
- | ip_total_length | 328 |
- | ip_identifier | 0 |
- | ip_flag | 0 |
- | ip_fragment | 0 |
- | ip_ttl | 128 |
- | ip_protocol | 17 |
- | ip_header_checksum | 14758 |
- | source_ip_address | 0.0.0.0 |
- | destination_ip_address | 255.255.255.255 |
- | udp_source_port | 68 |
- | udp_destination_port | 67 |
- | udp_length | 308 |
- | udp_checksum | 52915 |
- | message_type | 3 |
- | hw_addr_type | 1 |
- | hw_addr_len | 6 |
- | hops | 0 |
- | transaction_id | 3735928559 |
- | seconds | 0 |
- | bootp_flags | 0 |
- | client_ip_address | 0.0.0.0 |
- | your_ip_address | 0.0.0.0 |
- | next_server_ip_address | 0.0.0.0 |
- | relay_agent_ip_address | 0.0.0.0 |
- | client_mac_address | 24:db:ac:41:e5:5b |
- | parameters_list | [1, 3, 6, 42] |
- | client_identifier | 24:db:ac:41:e5:5b |
- | requested_ip_address | 192.168.0.10 |
+ Then the packet has the following fields and values:
+ | field | value |
+ | class | Pio::Dhcp::Request |
+ | destination_mac | ff:ff:ff:ff:ff:ff |
+ | source_mac | 24:db:ac:41:e5:5b |
+ | ether_type | 2048 |
+ | ip_version | 4 |
+ | ip_header_length | 5 |
+ | ip_type_of_service | 0 |
+ | ip_total_length | 328 |
+ | ip_identifier | 0 |
+ | ip_flag | 0 |
+ | ip_fragment | 0 |
+ | ip_ttl | 128 |
+ | ip_protocol | 17 |
+ | ip_header_checksum | 14758 |
+ | source_ip_address | 0.0.0.0 |
+ | destination_ip_address | 255.255.255.255 |
+ | udp_source_port | 68 |
+ | udp_destination_port | 67 |
+ | udp_length | 308 |
+ | udp_checksum | 52915 |
+ | message_type | 3 |
+ | hw_addr_type | 1 |
+ | hw_addr_len | 6 |
+ | hops | 0 |
+ | transaction_id | 3735928559 |
+ | seconds | 0 |
+ | bootp_flags | 0 |
+ | client_ip_address | 0.0.0.0 |
+ | your_ip_address | 0.0.0.0 |
+ | next_server_ip_address | 0.0.0.0 |
+ | relay_agent_ip_address | 0.0.0.0 |
+ | client_mac_address | 24:db:ac:41:e5:5b |
+ | parameters_list | [1, 3, 6, 42] |
+ | client_identifier | 24:db:ac:41:e5:5b |
+ | requested_ip_address | 192.168.0.10 |
Scenario: create a DHCP Offer
- When I try to create a packet with:
+ When I create a packet with:
"""
Pio::Dhcp::Offer.new(
source_mac: 'aa:bb:cc:dd:ee:ff',
@@ -165,47 +162,45 @@ Feature: Pio::Dhcp
subnet_mask: '255.255.255.0'
)
"""
- Then it should finish successfully
- And the packet has the following fields and values:
- | field | value |
- | class | Pio::Dhcp::Offer |
- | destination_mac | 11:22:33:44:55:66 |
- | source_mac | aa:bb:cc:dd:ee:ff |
- | ether_type | 2048 |
- | ip_version | 4 |
- | ip_header_length | 5 |
- | ip_type_of_service | 0 |
- | ip_total_length | 328 |
- | ip_identifier | 0 |
- | ip_flag | 0 |
- | ip_fragment | 0 |
- | ip_ttl | 128 |
- | ip_protocol | 17 |
- | ip_header_checksum | 47177 |
- | source_ip_address | 192.168.0.10 |
- | destination_ip_address | 192.168.0.1 |
- | udp_source_port | 67 |
- | udp_destination_port | 68 |
- | udp_length | 308 |
- | udp_checksum | 7780 |
- | message_type | 2 |
- | hw_addr_type | 1 |
- | hw_addr_len | 6 |
- | hops | 0 |
- | transaction_id | 3735928559 |
- | seconds | 0 |
- | bootp_flags | 0 |
- | client_ip_address | 0.0.0.0 |
- | your_ip_address | 192.168.0.1 |
- | next_server_ip_address | 0.0.0.0 |
- | relay_agent_ip_address | 0.0.0.0 |
- | client_mac_address | aa:bb:cc:dd:ee:ff |
- | subnet_mask | 255.255.255.0 |
- | server_identifier | 192.168.0.10 |
- | renewal_time_value | 3735928559 |
- | rebinding_time_value | 3735928559 |
- | ip_address_lease_time | 3735928559 |
+ Then the packet has the following fields and values:
+ | field | value |
+ | class | Pio::Dhcp::Offer |
+ | destination_mac | 11:22:33:44:55:66 |
+ | source_mac | aa:bb:cc:dd:ee:ff |
+ | ether_type | 2048 |
+ | ip_version | 4 |
+ | ip_header_length | 5 |
+ | ip_type_of_service | 0 |
+ | ip_total_length | 328 |
+ | ip_identifier | 0 |
+ | ip_flag | 0 |
+ | ip_fragment | 0 |
+ | ip_ttl | 128 |
+ | ip_protocol | 17 |
+ | ip_header_checksum | 47177 |
+ | source_ip_address | 192.168.0.10 |
+ | destination_ip_address | 192.168.0.1 |
+ | udp_source_port | 67 |
+ | udp_destination_port | 68 |
+ | udp_length | 308 |
+ | udp_checksum | 7780 |
+ | message_type | 2 |
+ | hw_addr_type | 1 |
+ | hw_addr_len | 6 |
+ | hops | 0 |
+ | transaction_id | 3735928559 |
+ | seconds | 0 |
+ | bootp_flags | 0 |
+ | client_ip_address | 0.0.0.0 |
+ | your_ip_address | 192.168.0.1 |
+ | next_server_ip_address | 0.0.0.0 |
+ | relay_agent_ip_address | 0.0.0.0 |
+ | client_mac_address | aa:bb:cc:dd:ee:ff |
+ | subnet_mask | 255.255.255.0 |
+ | server_identifier | 192.168.0.10 |
+ | renewal_time_value | 3735928559 |
+ | rebinding_time_value | 3735928559 |
+ | ip_address_lease_time | 3735928559 |
Scenario: parse dhcp.pcap
- When I try to parse a file named "dhcp.pcap" with "Pio::Dhcp" class
- Then it should finish successfully
+ Then I parse a file named "dhcp.pcap" with "Pio::Dhcp" class
diff --git a/features/ethernet_header.feature b/features/ethernet_header.feature
new file mode 100644
index 00000000..8656da83
--- /dev/null
+++ b/features/ethernet_header.feature
@@ -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 | 0x800 |
+
+ 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 | 0x8100 |
+ | 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 | 0x800 |
+
+ 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 | 0x8100 |
+ | 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:
+ """
+ #
+ """
+
+ 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:
+ """
+ #
+ """
+
+ Scenario: EthernetHeader class inspection
+ When I eval the following Ruby code:
+ """ruby
+ Pio::EthernetHeader.inspect
+ """
+ Then the result of eval should be:
+ """
+ Pio::EthernetHeader(destination_mac: mac_address, source_mac: mac_address, ether_type: ether_type, vlan_pcp: bit3, vlan_cfi: bit1, vlan_vid: bit12, ether_type_vlan: uint16)
+ """
diff --git a/features/icmp.feature b/features/icmp.feature
deleted file mode 100644
index f2a530ae..00000000
--- a/features/icmp.feature
+++ /dev/null
@@ -1,130 +0,0 @@
-Feature: Pio::Icmp
- Scenario: create an ICMP request
- When I try to create a packet with:
- """
- Pio::Icmp::Request.new(
- source_mac: '00:16:9d:1d:9c:c4',
- destination_mac: '00:26:82:eb:ea:d1',
- source_ip_address: '192.168.83.3',
- destination_ip_address: '192.168.83.254'
- )
- """
- Then it should finish successfully
- And the packet has the following fields and values:
- | field | value |
- | class | Pio::Icmp::Request |
- | destination_mac | 00:26:82:eb:ea:d1 |
- | source_mac | 00:16:9d:1d:9c:c4 |
- | ether_type | 2048 |
- | ip_version | 4 |
- | ip_header_length | 5 |
- | ip_type_of_service | 0 |
- | ip_total_length | 50 |
- | ip_identifier | 0 |
- | ip_flag | 0 |
- | ip_fragment | 0 |
- | ip_ttl | 128 |
- | ip_protocol | 1 |
- | ip_header_checksum | 4729 |
- | source_ip_address | 192.168.83.3 |
- | destination_ip_address | 192.168.83.254 |
- | ip_option | |
- | icmp_type | 8 |
- | icmp_code | 0 |
- | icmp_checksum | 63231 |
- | icmp_identifier | 256 |
- | icmp_sequence_number | 0 |
- | echo_data | |
-
- Scenario: create an ICMP reply
- When I try to create a packet with:
- """
- Pio::Icmp::Reply.new(
- source_mac: '00:26:82:eb:ea:d1',
- destination_mac: '00:16:9d:1d:9c:c4',
- source_ip_address: '192.168.83.254',
- destination_ip_address: '192.168.83.3',
- identifier: 256,
- sequence_number: 0
- )
- """
- Then it should finish successfully
- And the packet has the following fields and values:
- | field | value |
- | class | Pio::Icmp::Reply |
- | destination_mac | 00:16:9d:1d:9c:c4 |
- | source_mac | 00:26:82:eb:ea:d1 |
- | ether_type | 2048 |
- | ip_version | 4 |
- | ip_header_length | 5 |
- | ip_type_of_service | 0 |
- | ip_total_length | 50 |
- | ip_identifier | 0 |
- | ip_flag | 0 |
- | ip_fragment | 0 |
- | ip_ttl | 128 |
- | ip_protocol | 1 |
- | ip_header_checksum | 4729 |
- | source_ip_address | 192.168.83.254 |
- | destination_ip_address | 192.168.83.3 |
- | ip_option | |
- | icmp_type | 0 |
- | icmp_code | 0 |
- | icmp_checksum | 65279 |
- | icmp_identifier | 256 |
- | icmp_sequence_number | 0 |
- | echo_data | |
-
- Scenario: parse icmp.pcap
- When I try to parse a file named "icmp.pcap" with "Pio::Icmp" class
- Then it should finish successfully
- And the message #1 have the following fields and values:
- | field | value |
- | class | Pio::Icmp::Request |
- | destination_mac | 00:13:46:0b:22:ba |
- | source_mac | 00:16:ce:6e:8b:24 |
- | ether_type | 2048 |
- | ip_version | 4 |
- | ip_header_length | 5 |
- | ip_type_of_service | 0 |
- | ip_total_length | 60 |
- | ip_identifier | 36507 |
- | ip_flag | 0 |
- | ip_fragment | 0 |
- | ip_ttl | 128 |
- | ip_protocol | 1 |
- | ip_header_checksum | 10850 |
- | source_ip_address | 192.168.0.114 |
- | destination_ip_address | 192.168.0.1 |
- | ip_option | |
- | icmp_type | 8 |
- | icmp_code | 0 |
- | icmp_checksum | 18780 |
- | icmp_identifier | 768 |
- | icmp_sequence_number | 256 |
- | echo_data | abcdefghijklmnopqrstuvwabcdefghi |
- And the message #2 have the following fields and values:
- | field | value |
- | class | Pio::Icmp::Reply |
- | destination_mac | 00:16:ce:6e:8b:24 |
- | source_mac | 00:13:46:0b:22:ba |
- | ether_type | 2048 |
- | ip_version | 4 |
- | ip_header_length | 5 |
- | ip_type_of_service | 0 |
- | ip_total_length | 60 |
- | ip_identifier | 24150 |
- | ip_flag | 0 |
- | ip_fragment | 0 |
- | ip_ttl | 127 |
- | ip_protocol | 1 |
- | ip_header_checksum | 23463 |
- | source_ip_address | 192.168.0.1 |
- | destination_ip_address | 192.168.0.114 |
- | ip_option | |
- | icmp_type | 0 |
- | icmp_code | 0 |
- | icmp_checksum | 20828 |
- | icmp_identifier | 768 |
- | icmp_sequence_number | 256 |
- | echo_data | abcdefghijklmnopqrstuvwabcdefghi |
diff --git a/features/icmp.pcap b/features/icmp.pcap
deleted file mode 100644
index c50548b1..00000000
Binary files a/features/icmp.pcap and /dev/null differ
diff --git a/features/icmp/icmp.feature b/features/icmp/icmp.feature
new file mode 100644
index 00000000..eabe4bc5
--- /dev/null
+++ b/features/icmp/icmp.feature
@@ -0,0 +1,47 @@
+Feature: Icmp
+ Background:
+ Given I use the fixture "icmp"
+
+ Scenario: read an ICMP request
+ When I create a packet with:
+ """ruby
+ Pio::Icmp.read(eval(IO.read('icmp_request.rb')))
+ """
+ Then the packet has the following fields and values:
+ | field | value |
+ | class | Pio::Icmp::Request |
+ | destination_mac | 11:22:33:44:55:66 |
+ | source_mac | 66:55:44:33:22:11 |
+ | ether_type.to_hex | 0x800 |
+ | ip_total_length | 54 |
+ | ip_protocol | 1 |
+ | source_ip_address | 192.168.83.3 |
+ | destination_ip_address | 192.168.83.254 |
+ | icmp_type | 8 |
+ | icmp_code | 0 |
+ | icmp_checksum | 26613 |
+ | icmp_identifier | 256 |
+ | icmp_sequence_number | 111 |
+ | echo_data | abcdefghijklmnopqrstuvwxyz |
+
+ Scenario: read an ICMP reply
+ When I create a packet with:
+ """ruby
+ Pio::Icmp.read(eval(IO.read('icmp_reply.rb')))
+ """
+ Then the packet has the following fields and values:
+ | field | value |
+ | class | Pio::Icmp::Reply |
+ | destination_mac | 11:22:33:44:55:66 |
+ | source_mac | 66:55:44:33:22:11 |
+ | ether_type.to_hex | 0x800 |
+ | ip_total_length | 54 |
+ | ip_protocol | 1 |
+ | source_ip_address | 192.168.83.254 |
+ | destination_ip_address | 192.168.83.3 |
+ | icmp_type | 0 |
+ | icmp_code | 0 |
+ | icmp_checksum | 28661 |
+ | icmp_identifier | 256 |
+ | icmp_sequence_number | 111 |
+ | echo_data | abcdefghijklmnopqrstuvwxyz |
diff --git a/features/icmp/icmp_reply.feature b/features/icmp/icmp_reply.feature
new file mode 100644
index 00000000..f46ea2ed
--- /dev/null
+++ b/features/icmp/icmp_reply.feature
@@ -0,0 +1,93 @@
+Feature: Icmp::Reply
+ Scenario: create an ICMP reply
+ When I create a packet with:
+ """
+ Pio::Icmp::Reply.new(
+ destination_mac: '11:22:33:44:55:66',
+ source_mac: '66:55:44:33:22:11',
+ source_ip_address: '192.168.83.254',
+ destination_ip_address: '192.168.83.3',
+ icmp_identifier: 256,
+ icmp_sequence_number: 111,
+ echo_data: 'hello world'
+ )
+ """
+ Then the packet has the following fields and values:
+ | field | value |
+ | destination_mac | 11:22:33:44:55:66 |
+ | source_mac | 66:55:44:33:22:11 |
+ | ether_type.to_hex | 0x800 |
+ | ip_total_length | 50 |
+ | ip_protocol | 1 |
+ | source_ip_address | 192.168.83.254 |
+ | destination_ip_address | 192.168.83.3 |
+ | icmp_type | 0 |
+ | icmp_code | 0 |
+ | icmp_checksum | 53442 |
+ | icmp_identifier | 256 |
+ | icmp_sequence_number | 111 |
+ | echo_data | hello world |
+
+ Scenario: convert an ICMP reply to Ruby code
+ When I eval the following Ruby code:
+ """ruby
+ Pio::Icmp::Reply.new(
+ destination_mac: '11:22:33:44:55:66',
+ source_mac: '66:55:44:33:22:11',
+ source_ip_address: '192.168.83.254',
+ destination_ip_address: '192.168.83.3',
+ icmp_identifier: 256,
+ icmp_sequence_number: 0
+ ).to_ruby
+ """
+ Then the result of eval should be:
+ """ruby
+ [
+ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, # destination_mac
+ 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, # source_mac
+ 0x08, 0x00, # ether_type
+ 0b0100_0101, # ip_version, ip_header_length
+ 0x00, # ip_type_of_service
+ 0x00, 0x32, # ip_total_length
+ 0x00, 0x00, # ip_identifier
+ 0b000_0000000000000, # ip_flag, ip_fragment
+ 0x80, # ip_ttl
+ 0x01, # ip_protocol
+ 0x12, 0x79, # ip_header_checksum
+ 0xc0, 0xa8, 0x53, 0xfe, # source_ip_address
+ 0xc0, 0xa8, 0x53, 0x03, # destination_ip_address
+ 0x00, # icmp_type
+ 0x00, # icmp_code
+ 0xfe, 0xff, # icmp_checksum
+ 0x01, 0x00, # icmp_identifier
+ 0x00, 0x00, # icmp_sequence_number
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, # padding
+ ].pack('C20nC42')
+ """
+
+ Scenario: Icmp::Reply instance inspection
+ When I create a packet with:
+ """
+ Pio::Icmp::Reply.new(
+ destination_mac: '00:26:82:eb:ea:d1',
+ source_mac: '00:16:9d:1d:9c:c4',
+ source_ip_address: '1.2.3.4',
+ destination_ip_address: '4.3.2.1',
+ icmp_identifier: 256,
+ icmp_sequence_number: 0
+ ).inspect
+ """
+ Then the result of eval should be:
+ """
+ #
+ """
+
+ Scenario: Icmp::Reply class inspection
+ When I eval the following Ruby code:
+ """ruby
+ Pio::Icmp::Reply.inspect
+ """
+ Then the result of eval should be:
+ """
+ Pio::Icmp::Reply(destination_mac: mac_address, source_mac: mac_address, ether_type: ether_type, vlan_pcp: bit3, vlan_cfi: bit1, vlan_vid: bit12, ether_type_vlan: uint16, ip_version: bit4, ip_header_length: bit4, ip_type_of_service: uint8, ip_total_length: uint16, ip_identifier: uint16, ip_flag: bit3, ip_fragment: bit13, ip_ttl: uint8, ip_protocol: uint8, ip_header_checksum: uint16, source_ip_address: ip_address, destination_ip_address: ip_address, ip_option: string, icmp_type: uint8, icmp_code: uint8, icmp_checksum: uint16, icmp_identifier: uint16, icmp_sequence_number: uint16, echo_data: string)
+ """
diff --git a/features/icmp/icmp_request.feature b/features/icmp/icmp_request.feature
new file mode 100644
index 00000000..089e5dc5
--- /dev/null
+++ b/features/icmp/icmp_request.feature
@@ -0,0 +1,89 @@
+Feature: Icmp::Request
+ Scenario: create an ICMP request
+ When I create a packet with:
+ """ruby
+ Pio::Icmp::Request.new(
+ source_mac: '11:22:33:44:55:66',
+ destination_mac: '66:55:44:33:22:11',
+ source_ip_address: '192.168.83.3',
+ destination_ip_address: '192.168.83.254',
+ icmp_identifier: 256,
+ icmp_sequence_number: 111,
+ echo_data: 'hello world'
+ )
+ """
+ Then the packet has the following fields and values:
+ | field | value |
+ | source_mac | 11:22:33:44:55:66 |
+ | destination_mac | 66:55:44:33:22:11 |
+ | ether_type.to_hex | 0x800 |
+ | ip_total_length | 50 |
+ | ip_protocol | 1 |
+ | source_ip_address | 192.168.83.3 |
+ | destination_ip_address | 192.168.83.254 |
+ | icmp_type | 8 |
+ | icmp_code | 0 |
+ | icmp_checksum | 51394 |
+ | icmp_identifier | 256 |
+ | icmp_sequence_number | 111 |
+ | echo_data | hello world |
+
+ Scenario: convert an ICMP request to Ruby code
+ When I eval the following Ruby code:
+ """ruby
+ Pio::Icmp::Request.new(
+ destination_mac: '11:22:33:44:55:66',
+ source_mac: '66:55:44:33:22:11',
+ source_ip_address: '192.168.83.3',
+ destination_ip_address: '192.168.83.254'
+ ).to_ruby
+ """
+ Then the result of eval should be:
+ """ruby
+ [
+ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, # destination_mac
+ 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, # source_mac
+ 0x08, 0x00, # ether_type
+ 0b0100_0101, # ip_version, ip_header_length
+ 0x00, # ip_type_of_service
+ 0x00, 0x32, # ip_total_length
+ 0x00, 0x00, # ip_identifier
+ 0b000_0000000000000, # ip_flag, ip_fragment
+ 0x80, # ip_ttl
+ 0x01, # ip_protocol
+ 0x12, 0x79, # ip_header_checksum
+ 0xc0, 0xa8, 0x53, 0x03, # source_ip_address
+ 0xc0, 0xa8, 0x53, 0xfe, # destination_ip_address
+ 0x08, # icmp_type
+ 0x00, # icmp_code
+ 0xf7, 0xff, # icmp_checksum
+ 0x00, 0x00, # icmp_identifier
+ 0x00, 0x00, # icmp_sequence_number
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, # padding
+ ].pack('C20nC42')
+ """
+
+ Scenario: Icmp::Request instance inspection
+ When I create a packet with:
+ """ruby
+ Pio::Icmp::Request.new(
+ destination_mac: '00:26:82:eb:ea:d1',
+ source_mac: '00:16:9d:1d:9c:c4',
+ source_ip_address: '1.2.3.4',
+ destination_ip_address: '4.3.2.1'
+ ).inspect
+ """
+ Then the result of eval should be:
+ """
+ #
+ """
+
+ Scenario: Icmp::Request class inspection
+ When I eval the following Ruby code:
+ """ruby
+ Pio::Icmp::Request.inspect
+ """
+ Then the result of eval should be:
+ """
+ Pio::Icmp::Request(destination_mac: mac_address, source_mac: mac_address, ether_type: ether_type, vlan_pcp: bit3, vlan_cfi: bit1, vlan_vid: bit12, ether_type_vlan: uint16, ip_version: bit4, ip_header_length: bit4, ip_type_of_service: uint8, ip_total_length: uint16, ip_identifier: uint16, ip_flag: bit3, ip_fragment: bit13, ip_ttl: uint8, ip_protocol: uint8, ip_header_checksum: uint16, source_ip_address: ip_address, destination_ip_address: ip_address, ip_option: string, icmp_type: uint8, icmp_code: uint8, icmp_checksum: uint16, icmp_identifier: uint16, icmp_sequence_number: uint16, echo_data: string)
+ """
diff --git a/features/ipv4_header.feature b/features/ipv4_header.feature
new file mode 100644
index 00000000..78c993b4
--- /dev/null
+++ b/features/ipv4_header.feature
@@ -0,0 +1,89 @@
+Feature: IPv4Header
+ Scenario: create an IPv4 header
+ When I create a packet with:
+ """ruby
+ IPv4Header.new(source_ip_address: '1.2.3.4',
+ destination_ip_address: '4.3.2.1')
+ """
+ Then the packet has the following fields and values:
+ | field | value |
+ | class | Pio::IPv4Header |
+ | ip_version | 4 |
+ | ip_header_length | 5 |
+ | ip_type_of_service | 0 |
+ | ip_total_length | 20 |
+ | ip_identifier | 0 |
+ | ip_flag | 0 |
+ | ip_fragment | 0 |
+ | ip_ttl | 128 |
+ | ip_protocol | 0 |
+ | ip_header_checksum | 12513 |
+ | source_ip_address | 1.2.3.4 |
+ | destination_ip_address | 4.3.2.1 |
+ | ip_option | |
+
+ Scenario: read an IPv4 header
+ Given I use the fixture "ipv4_header"
+ When I create a packet with:
+ """ruby
+ Pio::IPv4Header.read(eval(IO.read('ipv4_header.rb')))
+ """
+ Then the packet has the following fields and values:
+ | field | value |
+ | class | Pio::IPv4Header |
+ | ip_version | 4 |
+ | ip_header_length | 5 |
+ | ip_type_of_service | 0 |
+ | ip_total_length | 20 |
+ | ip_identifier | 0 |
+ | ip_flag | 0 |
+ | ip_fragment | 0 |
+ | ip_ttl | 128 |
+ | ip_protocol | 0 |
+ | ip_header_checksum | 12513 |
+ | source_ip_address | 1.2.3.4 |
+ | destination_ip_address | 4.3.2.1 |
+ | ip_option | |
+
+ Scenario: convert IPv4 header to Ruby code
+ When I eval the following Ruby code:
+ """ruby
+ IPv4Header.new(source_ip_address: '1.2.3.4',
+ destination_ip_address: '4.3.2.1').to_ruby
+ """
+ Then the result of eval should be:
+ """ruby
+ [
+ 0b0100_0101, # ip_version, ip_header_length
+ 0x00, # ip_type_of_service
+ 0x00, 0x14, # ip_total_length
+ 0x00, 0x00, # ip_identifier
+ 0b000_0000000000000, # ip_flag, ip_fragment
+ 0x80, # ip_ttl
+ 0x00, # ip_protocol
+ 0x30, 0xe1, # ip_header_checksum
+ 0x01, 0x02, 0x03, 0x04, # source_ip_address
+ 0x04, 0x03, 0x02, 0x01, # destination_ip_address
+ ].pack('C6nC12')
+ """
+
+ Scenario: IPv4Header instance inspection
+ When I eval the following Ruby code:
+ """ruby
+ IPv4Header.new(source_ip_address: '1.2.3.4',
+ destination_ip_address: '4.3.2.1').inspect
+ """
+ Then the result of eval should be:
+ """
+ #
+ """
+
+ Scenario: IPv4Header class inspection
+ When I eval the following Ruby code:
+ """ruby
+ Pio::IPv4Header.inspect
+ """
+ Then the result of eval should be:
+ """
+ IPv4Header(ip_version: bit4, ip_header_length: bit4, ip_type_of_service: uint8, ip_total_length: uint16, ip_identifier: uint16, ip_flag: bit3, ip_fragment: bit13, ip_ttl: uint8, ip_protocol: uint8, ip_header_checksum: uint16, source_ip_address: ip_address, destination_ip_address: ip_address, ip_option: string)
+ """
diff --git a/features/lldp.feature b/features/lldp.feature
index 3375ffe4..e8123e89 100644
--- a/features/lldp.feature
+++ b/features/lldp.feature
@@ -1,11 +1,10 @@
-Feature: Pio::Lldp
+Feature: Lldp
Scenario: create
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::Lldp.new(dpid: 0x123, port_number: 12, source_mac: '11:22:33:44:55:66')
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
| class | Pio::Lldp |
| destination_mac | 01:80:c2:00:00:0e |
@@ -23,8 +22,7 @@ Feature: Pio::Lldp
| organizationally_specific | |
Scenario: parse lldp.minimal.pcap
- When I try to parse a file named "lldp.minimal.pcap" with "Pio::Lldp" class
- Then it should finish successfully
+ When I parse a file named "lldp.minimal.pcap" with "Pio::Lldp" class
Then the message #1 have the following fields and values:
| field | value |
| class | Pio::Lldp |
@@ -43,5 +41,4 @@ Feature: Pio::Lldp
| organizationally_specific | |
Scenario: parse lldp.detailed.pcap
- When I try to parse a file named "lldp.detailed.pcap" with "Pio::Lldp" class
- Then it should finish successfully
+ Then I parse a file named "lldp.detailed.pcap" with "Pio::Lldp" class
diff --git a/features/open_flow/header.feature b/features/open_flow/header.feature
new file mode 100644
index 00000000..ae1dbe1c
--- /dev/null
+++ b/features/open_flow/header.feature
@@ -0,0 +1,13 @@
+Feature: OpenFlow::Header
+
+ Scenario: OpenFlow::Header#to_hex
+ When I create an OpenFlow message with:
+ """
+ Pio::OpenFlow::Header.new(version: :OpenFlow10,
+ type: 10,
+ message_length: 18,
+ transaction_id: 0xff)
+ """
+ Then the message has the following fields and values:
+ | field | value |
+ | to_bytes | 0x01, 0x0a, 0x00, 0x12, 0x00, 0x00, 0x00, 0xff |
diff --git a/features/open_flow/nicira_resubmit.feature b/features/open_flow/nicira_resubmit.feature
index cd586d94..b25983f7 100644
--- a/features/open_flow/nicira_resubmit.feature
+++ b/features/open_flow/nicira_resubmit.feature
@@ -1,29 +1,11 @@
-Feature: Pio::NiciraResubmit
+@open_flow13
+Feature: NiciraResubmit
Scenario: new(1)
- When I try to create an OpenFlow action with:
+ When I create an OpenFlow action with:
"""
Pio::NiciraResubmit.new(1)
"""
- Then it should finish successfully
- And the action has the following fields and values:
+ Then the action has the following fields and values:
| field | value |
- | action_type.to_hex | 0xffff |
- | action_length | 16 |
- | vendor.to_hex | 0x2320 |
- | subtype | 1 |
| in_port | 1 |
-
- Scenario: new(:in_port)
- When I try to create an OpenFlow action with:
- """
- Pio::NiciraResubmit.new(:in_port)
- """
- Then it should finish successfully
- And the action has the following fields and values:
- | field | value |
- | action_type.to_hex | 0xffff |
- | action_length | 16 |
- | vendor.to_hex | 0x2320 |
- | subtype | 1 |
- | in_port | :in_port |
diff --git a/features/open_flow/nicira_resubmit_table.feature b/features/open_flow/nicira_resubmit_table.feature
index d593f6a0..f9bdc3f5 100644
--- a/features/open_flow/nicira_resubmit_table.feature
+++ b/features/open_flow/nicira_resubmit_table.feature
@@ -1,31 +1,21 @@
-Feature: Pio::NiciraResubmitTable
+Feature: NiciraResubmitTable
Scenario: new(in_port: 1, table: 1)
- When I try to create an OpenFlow action with:
+ When I create an OpenFlow action with:
"""
Pio::NiciraResubmitTable.new(in_port: 1, table: 1)
"""
- Then it should finish successfully
- And the action has the following fields and values:
+ Then the action has the following fields and values:
| field | value |
- | action_type.to_hex | 0xffff |
- | action_length | 16 |
- | vendor.to_hex | 0x2320 |
- | subtype | 14 |
| in_port | 1 |
| table | 1 |
- Scenario: new(:in_port)
- When I try to create an OpenFlow action with:
+ Scenario: new(in_port: 1)
+ When I create an OpenFlow action with:
"""
Pio::NiciraResubmitTable.new(in_port: 1)
"""
- Then it should finish successfully
- And the action has the following fields and values:
+ Then the action has the following fields and values:
| field | value |
- | action_type.to_hex | 0xffff |
- | action_length | 16 |
- | vendor.to_hex | 0x2320 |
- | subtype | 14 |
| in_port | 1 |
| table.to_hex | 0xff |
diff --git a/features/open_flow10/aggregate_stats_reply.feature b/features/open_flow10/aggregate_stats_reply.feature
index 82435046..bd112e92 100644
--- a/features/open_flow10/aggregate_stats_reply.feature
+++ b/features/open_flow10/aggregate_stats_reply.feature
@@ -1,13 +1,11 @@
@open_flow10
-Feature: Pio::AggregateStats::Reply
+Feature: AggregateStats::Reply
+
Scenario: read
- When I try to parse a file named "open_flow10/aggregate_stats_reply.raw" with "AggregateStats::Reply" class
- Then it should finish successfully
- And the message has the following fields and values:
+ When I parse a file named "open_flow10/aggregate_stats_reply.raw" with "AggregateStats::Reply" class
+ Then the message has the following fields and values:
| field | value |
- | ofp_version | 1 |
- | message_type | 17 |
- | message_length | 36 |
+ | version | 1 |
| transaction_id | 15 |
| xid | 15 |
| stats_type | :aggregate |
diff --git a/features/open_flow10/aggregate_stats_request.feature b/features/open_flow10/aggregate_stats_request.feature
index cc0071a0..0829604f 100644
--- a/features/open_flow10/aggregate_stats_request.feature
+++ b/features/open_flow10/aggregate_stats_request.feature
@@ -1,16 +1,17 @@
@open_flow10
-Feature: Pio::AggregateStats::Request
+Feature: AggregateStats::Request
+
+ Aggregate information about multiple flows is requested with the
+ Aggregate Stats Request message
+
Scenario: new
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
- Pio::AggregateStats::Request.new(match: Match.new(in_port: 1))
+ Pio::AggregateStats::Request.new(match: Pio::Match.new(in_port: 1))
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
- | ofp_version | 1 |
- | message_type | 16 |
- | message_length | 56 |
+ | version | 1 |
| transaction_id | 0 |
| xid | 0 |
| stats_type | :aggregate |
@@ -32,13 +33,10 @@ Feature: Pio::AggregateStats::Request
Scenario: read
- When I try to parse a file named "open_flow10/aggregate_stats_request.raw" with "AggregateStats::Request" class
- Then it should finish successfully
- And the message has the following fields and values:
+ When I parse a file named "open_flow10/aggregate_stats_request.raw" with "Pio::AggregateStats::Request" class
+ Then the message has the following fields and values:
| field | value |
- | ofp_version | 1 |
- | message_type | 16 |
- | message_length | 56 |
+ | version | 1 |
| transaction_id | 14 |
| xid | 14 |
| stats_type | :aggregate |
diff --git a/features/open_flow10/bad_request.feature b/features/open_flow10/bad_request.feature
index 9c7e61f1..dc7f5b10 100644
--- a/features/open_flow10/bad_request.feature
+++ b/features/open_flow10/bad_request.feature
@@ -1,33 +1,16 @@
@open_flow10
-Feature: Pio::Error::BadRequest
+Feature: Error::BadRequest
- Request was not understood error.
+ Request was not understood error
- Scenario: new (raw_data = Echo request 1.3)
- When I try to create an OpenFlow message with:
+ Scenario: new (error_code: :bad_version, raw_data: EchoRequest 1.3)
+ When I create an OpenFlow message with:
"""
- Pio::Error::BadRequest.new(raw_data: Pio::OpenFlow13::Echo::Request.new.to_binary)
+ Pio::Error::BadRequest.new(error_code: :bad_version,
+ raw_data: Pio::OpenFlow13::Echo::Request.new.to_binary)
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
- | ofp_version | 1 |
- | message_type | 1 |
- | message_length | 20 |
- | transaction_id | 0 |
- | xid | 0 |
- | error_type | :bad_request |
- | error_code | :bad_version |
- | raw_data.length | 8 |
-
- Scenario: read
- When I try to parse a file named "open_flow10/bad_request.raw" with "Pio::Error::BadRequest" class
- Then it should finish successfully
- And the message has the following fields and values:
- | field | value |
- | ofp_version | 1 |
- | message_type | 1 |
- | message_length | 20 |
| transaction_id | 0 |
| xid | 0 |
| error_type | :bad_request |
diff --git a/features/open_flow10/barrier_reply.feature b/features/open_flow10/barrier_reply.feature
index 7859ed1b..ba74a240 100644
--- a/features/open_flow10/barrier_reply.feature
+++ b/features/open_flow10/barrier_reply.feature
@@ -1,43 +1,32 @@
@open_flow10
-Feature: Pio::Barrier::Reply
+Feature: Barrier::Reply
+
+ When the switch received a Barrier Request message, the switch must
+ finish processing all previously-received messages before executing
+ any messages beyond the Barrier Request. When such processing is
+ complete, the switch must send an Barrier Reply message with the xid
+ of the original request.
+
Scenario: new
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::Barrier::Reply.new
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
- | ofp_version | 1 |
- | message_type | 19 |
- | message_length | 8 |
+ | version | 1 |
| transaction_id | 0 |
| xid | 0 |
| body | |
Scenario: new(transaction_id: 123)
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::Barrier::Reply.new(transaction_id: 123)
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
- | ofp_version | 1 |
- | message_type | 19 |
- | message_length | 8 |
+ | version | 1 |
| transaction_id | 123 |
| xid | 123 |
| body | |
-
- Scenario: read
- When I try to parse a file named "open_flow10/barrier_reply.raw" with "Barrier::Reply" class
- Then it should finish successfully
- And the message has the following fields and values:
- | field | value |
- | ofp_version | 1 |
- | message_type | 19 |
- | message_length | 8 |
- | transaction_id | 0 |
- | xid | 0 |
- | body | |
diff --git a/features/open_flow10/barrier_request.feature b/features/open_flow10/barrier_request.feature
index ffbef949..fe93a8db 100644
--- a/features/open_flow10/barrier_request.feature
+++ b/features/open_flow10/barrier_request.feature
@@ -1,43 +1,30 @@
@open_flow10
-Feature: Pio::Barrier::Request
+Feature: Barrier::Request
+
+ When the controller wants to ensure message dependencies have been
+ met or wants to receive notifications for completed operations, it
+ may use an Barrier Request message. This message has no body.
+
Scenario: new
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::Barrier::Request.new
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
- | ofp_version | 1 |
- | message_type | 18 |
- | message_length | 8 |
+ | version | 1 |
| transaction_id | 0 |
| xid | 0 |
| body | |
Scenario: new(transaction_id: 123)
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::Barrier::Request.new(transaction_id: 123)
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
- | ofp_version | 1 |
- | message_type | 18 |
- | message_length | 8 |
+ | version | 1 |
| transaction_id | 123 |
| xid | 123 |
| body | |
-
- Scenario: read
- When I try to parse a file named "open_flow10/barrier_request.raw" with "Barrier::Request" class
- Then it should finish successfully
- And the message has the following fields and values:
- | field | value |
- | ofp_version | 1 |
- | message_type | 18 |
- | message_length | 8 |
- | transaction_id | 0 |
- | xid | 0 |
- | body | |
diff --git a/features/open_flow10/description_stats_reply.feature b/features/open_flow10/description_stats_reply.feature
index 5d9ba2fb..e01b8c0e 100644
--- a/features/open_flow10/description_stats_reply.feature
+++ b/features/open_flow10/description_stats_reply.feature
@@ -1,13 +1,15 @@
@open_flow10
-Feature: Pio::DescriptionStats::Reply
+Feature: DescriptionStats::Reply
+
+ Information about the switch manufacturer, hardware revision,
+ software revision, serial number, and a description field is
+ available from a Description Stats Reply.
+
Scenario: read
- When I try to parse a file named "open_flow10/description_stats_reply.raw" with "DescriptionStats::Reply" class
- Then it should finish successfully
- And the message has the following fields and values:
+ When I parse a file named "open_flow10/description_stats_reply.raw" with "DescriptionStats::Reply" class
+ Then the message has the following fields and values:
| field | value |
- | ofp_version | 1 |
- | message_type | 17 |
- | message_length | 1068 |
+ | version | 1 |
| transaction_id | 12 |
| xid | 12 |
| stats_type | :description |
diff --git a/features/open_flow10/description_stats_request.feature b/features/open_flow10/description_stats_request.feature
index 899df29b..c355bef1 100644
--- a/features/open_flow10/description_stats_request.feature
+++ b/features/open_flow10/description_stats_request.feature
@@ -1,43 +1,30 @@
@open_flow10
-Feature: Pio::DescriptionStats::Request
+Feature: DescriptionStats::Request
+
+ Information about the switch manufacturer, hardware revision,
+ software revision, serial number, and a description field is
+ available by sending a Description Stats Request.
+
Scenario: new
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::DescriptionStats::Request.new
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
- | ofp_version | 1 |
- | message_type | 16 |
- | message_length | 12 |
+ | version | 1 |
| transaction_id | 0 |
| xid | 0 |
| stats_type | :description |
Scenario: new(transaction_id: 123)
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::DescriptionStats::Request.new(transaction_id: 123)
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
- | ofp_version | 1 |
- | message_type | 16 |
- | message_length | 12 |
+ | version | 1 |
| transaction_id | 123 |
| xid | 123 |
| stats_type | :description |
-
- Scenario: read
- When I try to parse a file named "open_flow10/description_stats_request.raw" with "DescriptionStats::Request" class
- Then it should finish successfully
- And the message has the following fields and values:
- | field | value |
- | ofp_version | 1 |
- | message_type | 16 |
- | message_length | 12 |
- | transaction_id | 12 |
- | xid | 12 |
- | stats_type | :description |
diff --git a/features/open_flow10/echo_reply.feature b/features/open_flow10/echo_reply.feature
index e3537264..6a000b7e 100644
--- a/features/open_flow10/echo_reply.feature
+++ b/features/open_flow10/echo_reply.feature
@@ -1,62 +1,41 @@
@open_flow10
-Feature: Pio::Echo::Reply
+Feature: Echo::Reply
+
+ An Echo Reply message consists of an OpenFlow header plus the
+ unmodified data field of an echo request message.
+
Scenario: new
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::Echo::Reply.new
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
- | ofp_version | 1 |
- | message_type | 3 |
- | message_length | 8 |
| transaction_id | 0 |
| xid | 0 |
| body | |
| user_data | |
Scenario: new(transaction_id: 123)
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::Echo::Reply.new(transaction_id: 123)
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
- | ofp_version | 1 |
- | message_type | 3 |
- | message_length | 8 |
| transaction_id | 123 |
| xid | 123 |
| body | |
| user_data | |
Scenario: new(body: 'echo reply body')
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::Echo::Reply.new(body: 'echo reply body')
"""
- Then it should finish successfully
- And the message has the following fields and values:
- | field | value |
- | ofp_version | 1 |
- | message_type | 3 |
- | message_length | 23 |
- | transaction_id | 0 |
- | xid | 0 |
+ Then the message has the following fields and values:
+ | field | value |
+ | transaction_id | 0 |
+ | xid | 0 |
| body | echo reply body |
| user_data | echo reply body |
-
- Scenario: read (no message body)
- When I try to parse a file named "open_flow10/echo_reply.raw" with "Pio::Echo::Reply" class
- Then it should finish successfully
- And the message has the following fields and values:
- | field | value |
- | ofp_version | 1 |
- | message_type | 3 |
- | message_length | 8 |
- | transaction_id | 6 |
- | xid | 6 |
- | body | |
- | user_data | |
diff --git a/features/open_flow10/echo_request.feature b/features/open_flow10/echo_request.feature
index 54ec3d43..ba8d20ba 100644
--- a/features/open_flow10/echo_request.feature
+++ b/features/open_flow10/echo_request.feature
@@ -1,66 +1,43 @@
@open_flow10
-Feature: Pio::Echo::Request
+Feature: Echo::Request
+
+ An Echo Request message consists of an OpenFlow header plus an
+ arbitrary-length data field. The data field might be a message
+ timestamp to check latency, various lengths to measure bandwidth, or
+ zero-size to verify liveness between the switch and controller.
+
Scenario: new
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::Echo::Request.new
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
- | ofp_version | 1 |
- | message_type | 2 |
- | message_length | 8 |
| transaction_id | 0 |
| xid | 0 |
| body | |
| user_data | |
Scenario: new(transaction_id: 123)
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::Echo::Request.new(transaction_id: 123)
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
- | ofp_version | 1 |
- | message_type | 2 |
- | message_length | 8 |
| transaction_id | 123 |
| xid | 123 |
| body | |
| user_data | |
Scenario: new(body: 'echo request body')
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::Echo::Request.new(body: 'echo request body')
"""
- Then it should finish successfully
- And the message has the following fields and values:
- | field | value |
- | ofp_version | 1 |
- | message_type | 2 |
- | message_length | 25 |
- | transaction_id | 0 |
- | xid | 0 |
+ Then the message has the following fields and values:
+ | field | value |
+ | transaction_id | 0 |
+ | xid | 0 |
| body | echo request body |
| user_data | echo request body |
-
- Scenario: read (no message body)
- When I try to parse a file named "open_flow10/echo_request.raw" with "Pio::Echo::Request" class
- Then it should finish successfully
- And the message has the following fields and values:
- | field | value |
- | ofp_version | 1 |
- | message_type | 2 |
- | message_length | 8 |
- | transaction_id | 0 |
- | xid | 0 |
- | body | |
- | user_data | |
-
- Scenario: parse error
- When I try to parse a file named "open_flow10/features_request.raw" with "Pio::Echo::Request" class
- Then it should fail with "Pio::ParseError", "Invalid OpenFlow10 Echo Request message."
diff --git a/features/open_flow10/enqueue.feature b/features/open_flow10/enqueue.feature
index 9bfab741..e58db865 100644
--- a/features/open_flow10/enqueue.feature
+++ b/features/open_flow10/enqueue.feature
@@ -1,13 +1,12 @@
@open_flow10
-Feature: Pio::OpenFlow10::Enqueue
+Feature: Enqueue
Scenario: new(port: 1, queue_id: 2)
- When I try to create an OpenFlow action with:
+ When I create an OpenFlow action with:
"""
Pio::OpenFlow10::Enqueue.new(port: 1, queue_id: 2)
"""
- Then it should finish successfully
- And the action has the following fields and values:
+ Then the action has the following fields and values:
| field | value |
| action_type | 11 |
| action_length | 16 |
diff --git a/features/open_flow10/exact_match.feature b/features/open_flow10/exact_match.feature
index 54c360a1..8e222a8e 100644
--- a/features/open_flow10/exact_match.feature
+++ b/features/open_flow10/exact_match.feature
@@ -1,19 +1,19 @@
-Feature: Pio::ExactMatch
+Feature: ExactMatch
Scenario: new (from ARP request Packet In)
- When I create an exact match from "open_flow10/packet_in_arp_request.raw"
+ When I create an exact match from "open_flow10/packet_in_arp_request.rb"
Then the message has the following fields and values:
| field | value |
| wildcards | {} |
| in_port | 1 |
- | source_mac_address | ac:5d:10:31:37:79 |
+ | source_mac_address | fa:ce:b0:00:00:cc |
| destination_mac_address | ff:ff:ff:ff:ff:ff |
| vlan_vid | 65535 |
| vlan_priority | 0 |
| ether_type | 2054 |
| tos | 0 |
| ip_protocol | 1 |
- | source_ip_address | 192.168.2.254 |
- | destination_ip_address | 192.168.2.5 |
+ | source_ip_address | 192.168.0.1 |
+ | destination_ip_address | 192.168.0.2 |
| transport_source_port | 0 |
| transport_destination_port | 0 |
diff --git a/features/open_flow10/features_reply.feature b/features/open_flow10/features_reply.feature
index f07e68bd..f959b034 100644
--- a/features/open_flow10/features_reply.feature
+++ b/features/open_flow10/features_reply.feature
@@ -1,7 +1,7 @@
@open_flow10
-Feature: Pio::Features::Reply
+Feature: Features::Reply
Scenario: new
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::Features::Reply.new(
datapath_id: 0x123,
@@ -9,80 +9,35 @@ Feature: Pio::Features::Reply
n_tables: 0xfe,
capabilities: [:flow_stats, :table_stats, :port_stats, :queue_stats, :arp_match_ip],
actions: [:output, :set_source_mac_address, :set_destination_mac_address],
- ports: [{ port_no: 1,
- hardware_address: '11:22:33:44:55:66',
+ ports: [{ number: 1,
+ mac_address: '11:22:33:44:55:66',
name: 'port123',
config: [:port_down],
state: [:link_down],
curr: [:port_10gb_fd, :port_copper] }]
)
"""
- Then it should finish successfully
- And the message has the following fields and values:
- | field | value |
- | ofp_version | 1 |
- | message_type | 6 |
- | message_length | 80 |
- | transaction_id | 0 |
- | xid | 0 |
- | datapath_id | 291 |
- | dpid | 291 |
- | n_buffers | 256 |
- | n_tables | 254 |
- | capabilities | [:flow_stats, :table_stats, :port_stats, :queue_stats, :arp_match_ip] |
- | actions | [:output, :set_source_mac_address, :set_destination_mac_address] |
- | ports.length | 1 |
- | ports[0].datapath_id | 291 |
- | ports[0].port_no | 1 |
- | ports[0].mac_address | 11:22:33:44:55:66 |
- | ports[0].hardware_address | 11:22:33:44:55:66 |
- | ports[0].name | port123 |
- | ports[0].config | [:port_down] |
- | ports[0].state | [:link_down] |
- | ports[0].curr | [:port_10gb_fd, :port_copper] |
- | ports[0].advertised | [] |
- | ports[0].supported | [] |
- | ports[0].peer | [] |
-
- Scenario: read
- When I try to parse a file named "open_flow10/features_reply.raw" with "Features::Reply" class
- Then it should finish successfully
- And the message has the following fields and values:
- | field | value |
- | ofp_version | 1 |
- | message_type | 6 |
- | message_length | 176 |
- | transaction_id | 2 |
- | xid | 2 |
- | datapath_id | 1 |
- | dpid | 1 |
- | n_buffers | 256 |
- | n_tables | 1 |
- | capabilities | [:flow_stats, :table_stats, :port_stats, :arp_match_ip] |
- | actions | [:output, :set_vlan_vid, :set_vlan_pcp, :strip_vlan, :set_source_mac_address, :set_destination_mac_address, :set_source_ip_address, :set_destination_ip_address, :set_tos, :set_transport_source_port, :set_transport_destination_port, :enqueue] |
- | ports.length | 3 |
- | ports[0].datapath_id | 1 |
- | ports[0].port_no | 2 |
- | ports[0].mac_address | 16:7d:a4:37:ba:10 |
- | ports[0].hardware_address | 16:7d:a4:37:ba:10 |
- | ports[0].name | trema0-0 |
- | ports[0].config | [] |
- | ports[0].state | [] |
- | ports[0].curr | [:port_10gb_fd, :port_copper] |
- | ports[0].advertised | [] |
- | ports[0].supported | [] |
- | ports[0].peer | [] |
- | ports[2].port_no | 1 |
- | ports[2].number | 1 |
- | ports[2].mac_address | 62:94:3a:f6:40:db |
- | ports[2].hardware_address | 62:94:3a:f6:40:db |
- | ports[2].name | trema1-0 |
- | ports[2].config | [] |
- | ports[2].state | [] |
- | ports[2].curr | [:port_10gb_fd, :port_copper] |
- | ports[2].advertised | [] |
- | ports[2].supported | [] |
- | ports[2].peer | [] |
- | ports[2].up? | true |
- | ports[2].down? | false |
- | ports[2].local? | false |
+ Then the message has the following fields and values:
+ | field | value |
+ | transaction_id | 0 |
+ | xid | 0 |
+ | datapath_id | 291 |
+ | dpid | 291 |
+ | n_buffers | 256 |
+ | n_tables | 254 |
+ | capabilities | [:flow_stats, :table_stats, :port_stats, :queue_stats, :arp_match_ip] |
+ | actions | [:output, :set_source_mac_address, :set_destination_mac_address] |
+ | ports.length | 1 |
+ | ports[0].datapath_id | 291 |
+ | ports[0].dpid | 291 |
+ | ports[0].number | 1 |
+ | ports[0].local? | false |
+ | ports[0].mac_address | 11:22:33:44:55:66 |
+ | ports[0].name | port123 |
+ | ports[0].config | [:port_down] |
+ | ports[0].state | [:link_down] |
+ | ports[0].down? | true |
+ | ports[0].curr | [:port_10gb_fd, :port_copper] |
+ | ports[0].advertised | [] |
+ | ports[0].supported | [] |
+ | ports[0].peer | [] |
diff --git a/features/open_flow10/features_request.feature b/features/open_flow10/features_request.feature
index 06f23826..b2424b17 100644
--- a/features/open_flow10/features_request.feature
+++ b/features/open_flow10/features_request.feature
@@ -1,43 +1,25 @@
@open_flow10
-Feature: Pio::Features::Request
+Feature: Features::Request
+
+ Upon OpenFlow channel establishment, the controller sends a
+ Features::Request message.
+
Scenario: new
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::Features::Request.new
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
- | ofp_version | 1 |
- | message_type | 5 |
- | message_length | 8 |
| transaction_id | 0 |
| xid | 0 |
- | user_data | |
-
+
Scenario: new(transaction_id: 123)
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::Features::Request.new(transaction_id: 123)
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
- | ofp_version | 1 |
- | message_type | 5 |
- | message_length | 8 |
| transaction_id | 123 |
| xid | 123 |
- | user_data | |
-
- Scenario: read
- When I try to parse a file named "open_flow10/features_request.raw" with "Pio::Features::Request" class
- Then it should finish successfully
- And the message has the following fields and values:
- | field | value |
- | ofp_version | 1 |
- | message_type | 5 |
- | message_length | 8 |
- | transaction_id | 2 |
- | xid | 2 |
- | user_data | |
diff --git a/features/open_flow10/flow_mod.feature b/features/open_flow10/flow_mod.feature
index 47486ba3..316ba0e7 100644
--- a/features/open_flow10/flow_mod.feature
+++ b/features/open_flow10/flow_mod.feature
@@ -1,7 +1,7 @@
@open_flow10
-Feature: Pio::FlowMod
+Feature: FlowMod
Scenario: new
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::FlowMod.new(
actions: [],
@@ -10,16 +10,13 @@ Feature: Pio::FlowMod
flags: [],
hard_timeout: 0,
idle_timeout: 0,
- match: Match.new(),
+ match: Pio::Match.new(),
out_port: 0,
priority: 0
)
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
- | ofp_version | 1 |
- | message_type | 14 |
| actions | [] |
| buffer_id | 0 |
| command | :add |
@@ -41,184 +38,3 @@ Feature: Pio::FlowMod
| match.wildcards.key?(:tos) | true |
| out_port | 0 |
| priority | 0 |
-
- Scenario: read (Flow Mod Add)
- When I try to parse a file named "open_flow10/flow_mod_add.raw" with "Pio::FlowMod" class
- Then it should finish successfully
- And the message has the following fields and values:
- | field | value |
- | ofp_version | 1 |
- | message_type | 14 |
- | message_length | 192 |
- | transaction_id | 0 |
- | xid | 0 |
- | match.wildcards | {:source_ip_address=>24, :destination_ip_address=>24} |
- | match.in_port | 1 |
- | match.source_mac_address | 00:00:00:00:00:0a |
- | match.destination_mac_address | 00:00:00:00:00:14 |
- | match.vlan_vid | 0 |
- | match.vlan_priority | 0 |
- | match.ether_type | 2048 |
- | match.tos | 0 |
- | match.ip_protocol | 1 |
- | match.source_ip_address | 10.0.0.0 |
- | match.source_ip_address.prefixlen | 8 |
- | match.destination_ip_address | 20.0.0.0 |
- | match.destination_ip_address.prefixlen | 8 |
- | match.transport_source_port | 8 |
- | match.transport_destination_port | 0 |
- | cookie | 0 |
- | command | :add |
- | idle_timeout | 0 |
- | hard_timeout | 0 |
- | priority | 65535 |
- | buffer_id | 4294967295 |
- | out_port | 65535 |
- | flags | [:send_flow_rem] |
- | actions.length | 12 |
- | actions.first.class | Pio::OpenFlow10::SetVlanVid |
- | actions.first.vlan_id | 10 |
-
- Scenario: read (Flow Mod Modify)
- When I try to parse a file named "open_flow10/flow_mod_modify.raw" with "Pio::FlowMod" class
- Then it should finish successfully
- And the message has the following fields and values:
- | field | value |
- | ofp_version | 1 |
- | message_type | 14 |
- | message_length | 192 |
- | transaction_id | 0 |
- | xid | 0 |
- | match.wildcards | {:source_ip_address=>24, :destination_ip_address=>24} |
- | match.in_port | 1 |
- | match.source_mac_address | 00:00:00:00:00:0a |
- | match.destination_mac_address | 00:00:00:00:00:14 |
- | match.vlan_vid | 0 |
- | match.vlan_priority | 0 |
- | match.ether_type | 2048 |
- | match.tos | 0 |
- | match.ip_protocol | 1 |
- | match.source_ip_address | 10.0.0.0 |
- | match.source_ip_address.prefixlen | 8 |
- | match.destination_ip_address | 20.0.0.0 |
- | match.destination_ip_address.prefixlen | 8 |
- | match.transport_source_port | 8 |
- | match.transport_destination_port | 0 |
- | cookie | 0 |
- | command | :modify |
- | idle_timeout | 0 |
- | hard_timeout | 0 |
- | priority | 65535 |
- | buffer_id | 4294967295 |
- | out_port | 65535 |
- | flags | [:send_flow_rem] |
- | actions.length | 12 |
- | actions.first.class | Pio::OpenFlow10::SetVlanVid |
- | actions.first.vlan_id | 10 |
-
- Scenario: read (Flow Mod Modify Strict)
- When I try to parse a file named "open_flow10/flow_mod_modify_strict.raw" with "Pio::FlowMod" class
- Then it should finish successfully
- And the message has the following fields and values:
- | field | value |
- | ofp_version | 1 |
- | message_type | 14 |
- | message_length | 192 |
- | transaction_id | 0 |
- | xid | 0 |
- | match.wildcards | {:source_ip_address=>24, :destination_ip_address=>24} |
- | match.in_port | 1 |
- | match.source_mac_address | 00:00:00:00:00:0a |
- | match.destination_mac_address | 00:00:00:00:00:14 |
- | match.vlan_vid | 0 |
- | match.vlan_priority | 0 |
- | match.ether_type | 2048 |
- | match.tos | 0 |
- | match.ip_protocol | 1 |
- | match.source_ip_address | 10.0.0.0 |
- | match.source_ip_address.prefixlen | 8 |
- | match.destination_ip_address | 20.0.0.0 |
- | match.destination_ip_address.prefixlen | 8 |
- | match.transport_source_port | 8 |
- | match.transport_destination_port | 0 |
- | cookie | 0 |
- | command | :modify_strict |
- | idle_timeout | 0 |
- | hard_timeout | 0 |
- | priority | 65535 |
- | buffer_id | 4294967295 |
- | out_port | 65535 |
- | flags | [:send_flow_rem] |
- | actions.length | 12 |
- | actions.first.class | Pio::OpenFlow10::SetVlanVid |
- | actions.first.vlan_id | 10 |
-
- Scenario: read (Flow Mod Delete)
- When I try to parse a file named "open_flow10/flow_mod_delete.raw" with "Pio::FlowMod" class
- Then it should finish successfully
- And the message has the following fields and values:
- | field | value |
- | ofp_version | 1 |
- | message_type | 14 |
- | message_length | 72 |
- | transaction_id | 0 |
- | xid | 0 |
- | match.wildcards | {:source_ip_address=>24, :destination_ip_address=>24} |
- | match.in_port | 1 |
- | match.source_mac_address | 00:00:00:00:00:0a |
- | match.destination_mac_address | 00:00:00:00:00:00 |
- | match.vlan_vid | 0 |
- | match.vlan_priority | 0 |
- | match.ether_type | 2048 |
- | match.tos | 0 |
- | match.ip_protocol | 1 |
- | match.source_ip_address | 10.0.0.0 |
- | match.source_ip_address.prefixlen | 8 |
- | match.destination_ip_address | 20.0.0.0 |
- | match.destination_ip_address.prefixlen | 8 |
- | match.transport_source_port | 8 |
- | match.transport_destination_port | 0 |
- | cookie | 0 |
- | command | :delete |
- | idle_timeout | 0 |
- | hard_timeout | 0 |
- | priority | 65535 |
- | buffer_id | 4294967295 |
- | out_port | 65535 |
- | flags | [] |
- | actions | [] |
-
- Scenario: read (Flow Mod Delete Strict)
- When I try to parse a file named "open_flow10/flow_mod_delete_strict.raw" with "Pio::FlowMod" class
- Then it should finish successfully
- And the message has the following fields and values:
- | field | value |
- | ofp_version | 1 |
- | message_type | 14 |
- | message_length | 72 |
- | transaction_id | 0 |
- | xid | 0 |
- | match.wildcards | {:source_ip_address=>24, :destination_ip_address=>24} |
- | match.in_port | 1 |
- | match.source_mac_address | 00:00:00:00:00:0a |
- | match.destination_mac_address | 00:00:00:00:00:14 |
- | match.vlan_vid | 0 |
- | match.vlan_priority | 0 |
- | match.ether_type | 2048 |
- | match.tos | 0 |
- | match.ip_protocol | 1 |
- | match.source_ip_address | 10.0.0.0 |
- | match.source_ip_address.prefixlen | 8 |
- | match.destination_ip_address | 20.0.0.0 |
- | match.destination_ip_address.prefixlen | 8 |
- | match.transport_source_port | 8 |
- | match.transport_destination_port | 0 |
- | cookie | 1 |
- | command | :delete_strict |
- | idle_timeout | 0 |
- | hard_timeout | 0 |
- | priority | 65535 |
- | buffer_id | 4294967295 |
- | out_port | 65535 |
- | flags | [] |
- | actions | [] |
diff --git a/features/open_flow10/flow_removed.feature b/features/open_flow10/flow_removed.feature
index 0e234302..55b2d5c1 100644
--- a/features/open_flow10/flow_removed.feature
+++ b/features/open_flow10/flow_removed.feature
@@ -1,34 +1,32 @@
@open_flow10
-Feature: Pio::FlowRemoved
+Feature: FlowRemoved
+
+ If the controller has requested to be notified when flows time out, the datapath
+ does this with the FlowRemoved message.
- @open_flow10
Scenario: read
- When I try to parse a file named "open_flow10/flow_removed.raw" with "FlowRemoved" class
- Then it should finish successfully
- And the message has the following fields and values:
- | field | value |
- | ofp_version | 1 |
- | message_type | 11 |
- | message_length | 88 |
- | transaction_id | 0 |
- | xid | 0 |
- | match.wildcards.keys.size | 11 |
- | match.wildcards.fetch(:destination_mac_address) | true |
- | match.wildcards.fetch(:source_mac_address) | true |
- | match.wildcards.fetch(:ether_type) | true |
- | match.wildcards.fetch(:destination_ip_address_all) | true |
- | match.wildcards.fetch(:ip_protocol) | true |
- | match.wildcards.fetch(:source_ip_address_all) | true |
- | match.wildcards.fetch(:tos) | true |
- | match.wildcards.fetch(:transport_destination_port) | true |
- | match.wildcards.fetch(:transport_source_port) | true |
- | match.wildcards.fetch(:vlan_priority) | true |
- | match.wildcards.fetch(:vlan_vid) | true |
- | cookie | 1 |
- | priority | 65535 |
+ When I parse a file named "open_flow10/flow_removed.raw" with "FlowRemoved" class
+ Then the message has the following fields and values:
+ | field | value |
+ | transaction_id | 0 |
+ | xid | 0 |
+ | match.wildcards.keys.size | 11 |
+ | match.wildcards.fetch(:destination_mac_address) | true |
+ | match.wildcards.fetch(:source_mac_address) | true |
+ | match.wildcards.fetch(:ether_type) | true |
+ | match.wildcards.fetch(:destination_ip_address_all) | true |
+ | match.wildcards.fetch(:ip_protocol) | true |
+ | match.wildcards.fetch(:source_ip_address_all) | true |
+ | match.wildcards.fetch(:tos) | true |
+ | match.wildcards.fetch(:transport_destination_port) | true |
+ | match.wildcards.fetch(:transport_source_port) | true |
+ | match.wildcards.fetch(:vlan_priority) | true |
+ | match.wildcards.fetch(:vlan_vid) | true |
+ | cookie | 1 |
+ | priority | 65535 |
| reason | :delete |
- | duration_sec | 0 |
- | duration_nsec | 0 |
- | idle_timeout | 0 |
- | packet_count | 0 |
- | byte_count | 0 |
+ | duration_sec | 0 |
+ | duration_nsec | 0 |
+ | idle_timeout | 0 |
+ | packet_count | 0 |
+ | byte_count | 0 |
diff --git a/features/open_flow10/flow_stats_reply.feature b/features/open_flow10/flow_stats_reply.feature
index c4700a3b..f7d1079f 100644
--- a/features/open_flow10/flow_stats_reply.feature
+++ b/features/open_flow10/flow_stats_reply.feature
@@ -1,16 +1,13 @@
@open_flow10
-Feature: Pio::FlowStats::Reply
+Feature: FlowStats::Reply
Scenario: new
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::FlowStats::Reply.new
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
- | ofp_version | 1 |
- | message_type | 17 |
- | message_length | 12 |
+ | version | 1 |
| transaction_id | 0 |
| xid | 0 |
| stats_type | :flow |
@@ -18,88 +15,7 @@ Feature: Pio::FlowStats::Reply
@wip
Scenario: new(more options)
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::FlowStats::Reply.new(more options)
"""
- Then it should finish successfully
-
- Scenario: read
- When I try to parse a file named "open_flow10/flow_stats_reply.raw" with "FlowStats::Reply" class
- Then it should finish successfully
- And the message has the following fields and values:
- | field | value |
- | ofp_version | 1 |
- | message_type | 17 |
- | message_length | 228 |
- | transaction_id | 6 |
- | xid | 6 |
- | stats_type | :flow |
- | stats.size | 2 |
- | stats[0].entry_length | 104 |
- | stats[0].table_id | 3 |
- | stats[0].match.wildcards.keys.size | 14 |
- | stats[0].match.wildcards.fetch(:destination_mac_address) | true |
- | stats[0].match.wildcards.fetch(:source_mac_address) | true |
- | stats[0].match.wildcards.fetch(:ether_type) | true |
- | stats[0].match.wildcards.fetch(:in_port) | true |
- | stats[0].match.wildcards.fetch(:destination_ip_address) | 31 |
- | stats[0].match.wildcards.fetch(:destination_ip_address_all) | true |
- | stats[0].match.wildcards.fetch(:ip_protocol) | true |
- | stats[0].match.wildcards.fetch(:source_ip_address) | 31 |
- | stats[0].match.wildcards.fetch(:source_ip_address_all) | true |
- | stats[0].match.wildcards.fetch(:tos) | true |
- | stats[0].match.wildcards.fetch(:transport_destination_port) | true |
- | stats[0].match.wildcards.fetch(:transport_source_port) | true |
- | stats[0].match.wildcards.fetch(:vlan_priority) | true |
- | stats[0].match.wildcards.fetch(:vlan_vid) | true |
- | stats[0].duration_sec | 1 |
- | stats[0].duration_nsec | 2 |
- | stats[0].priority | 100 |
- | stats[0].idle_timeout | 5 |
- | stats[0].hard_timeout | 10 |
- | stats[0].cookie.to_hex | 0x123456789abcdef |
- | stats[0].packet_count | 10 |
- | stats[0].byte_count | 1000 |
- | stats[0].actions.length | 2 |
- | stats[0].actions[0].class | Pio::OpenFlow10::SendOutPort |
- | stats[0].actions[0].port | 1 |
- | stats[0].actions[0].max_length | 0 |
- | stats[0].actions[1].class | Pio::OpenFlow10::SendOutPort |
- | stats[0].actions[1].port | 2 |
- | stats[0].actions[1].max_length | 0 |
- | stats[1].entry_length | 112 |
- | stats[1].table_id | 4 |
- | stats[1].match.wildcards.keys.size | 14 |
- | stats[1].match.wildcards.fetch(:destination_mac_address) | true |
- | stats[1].match.wildcards.fetch(:source_mac_address) | true |
- | stats[1].match.wildcards.fetch(:ether_type) | true |
- | stats[1].match.wildcards.fetch(:in_port) | true |
- | stats[1].match.wildcards.fetch(:destination_ip_address) | 31 |
- | stats[1].match.wildcards.fetch(:destination_ip_address_all) | true |
- | stats[1].match.wildcards.fetch(:ip_protocol) | true |
- | stats[1].match.wildcards.fetch(:source_ip_address) | 31 |
- | stats[1].match.wildcards.fetch(:source_ip_address_all) | true |
- | stats[1].match.wildcards.fetch(:tos) | true |
- | stats[1].match.wildcards.fetch(:transport_destination_port) | true |
- | stats[1].match.wildcards.fetch(:transport_source_port) | true |
- | stats[1].match.wildcards.fetch(:vlan_priority) | true |
- | stats[1].match.wildcards.fetch(:vlan_vid) | true |
- | stats[1].duration_sec | 1 |
- | stats[1].duration_nsec | 2 |
- | stats[1].priority | 100 |
- | stats[1].idle_timeout | 5 |
- | stats[1].hard_timeout | 10 |
- | stats[1].cookie.to_hex | 0x123456789abcdef |
- | stats[1].packet_count | 10 |
- | stats[1].byte_count | 1000 |
- | stats[1].actions.length | 3 |
- | stats[1].actions[0].class | Pio::OpenFlow10::SendOutPort |
- | stats[1].actions[0].port | 1 |
- | stats[1].actions[0].max_length | 0 |
- | stats[1].actions[1].class | Pio::OpenFlow10::SendOutPort |
- | stats[1].actions[1].port | 2 |
- | stats[1].actions[1].max_length | 0 |
- | stats[1].actions[2].class | Pio::OpenFlow10::SendOutPort |
- | stats[1].actions[2].port | 3 |
- | stats[1].actions[2].max_length | 0 |
diff --git a/features/open_flow10/flow_stats_request.feature b/features/open_flow10/flow_stats_request.feature
index eb817ac7..f268bc32 100644
--- a/features/open_flow10/flow_stats_request.feature
+++ b/features/open_flow10/flow_stats_request.feature
@@ -1,16 +1,17 @@
@open_flow10
-Feature: Pio::FlowStats::Request
+Feature: FlowStats::Request
+
+ Information about individual flows is requested with a Flow Stats
+ Request message.
+
Scenario: new
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::FlowStats::Request.new
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
- | ofp_version | 1 |
- | message_type | 16 |
- | message_length | 56 |
+ | version | 1 |
| transaction_id | 0 |
| xid | 0 |
| stats_type | :flow |
@@ -31,16 +32,13 @@ Feature: Pio::FlowStats::Request
| out_port | :none |
Scenario: new
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
- Pio::FlowStats::Request.new(match: Match.new(in_port: 1))
+ Pio::FlowStats::Request.new(match: Pio::Match.new(in_port: 1))
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
- | ofp_version | 1 |
- | message_type | 16 |
- | message_length | 56 |
+ | version | 1 |
| transaction_id | 0 |
| xid | 0 |
| stats_type | :flow |
@@ -59,31 +57,3 @@ Feature: Pio::FlowStats::Request
| match.wildcards.fetch(:vlan_vid) | true |
| table_id.to_hex | 0xff |
| out_port | :none |
-
-
- Scenario: read
- When I try to parse a file named "open_flow10/flow_stats_request.raw" with "FlowStats::Request" class
- Then it should finish successfully
- And the message has the following fields and values:
- | field | value |
- | ofp_version | 1 |
- | message_type | 16 |
- | message_length | 56 |
- | transaction_id | 13 |
- | xid | 13 |
- | stats_type | :flow |
- | match.wildcards.keys.size | 12 |
- | match.wildcards.fetch(:destination_mac_address) | true |
- | match.wildcards.fetch(:source_mac_address) | true |
- | match.wildcards.fetch(:ether_type) | true |
- | match.wildcards.fetch(:in_port) | true |
- | match.wildcards.fetch(:destination_ip_address_all) | true |
- | match.wildcards.fetch(:ip_protocol) | true |
- | match.wildcards.fetch(:source_ip_address_all) | true |
- | match.wildcards.fetch(:tos) | true |
- | match.wildcards.fetch(:transport_destination_port) | true |
- | match.wildcards.fetch(:transport_source_port) | true |
- | match.wildcards.fetch(:vlan_priority) | true |
- | match.wildcards.fetch(:vlan_vid) | true |
- | table_id.to_hex | 0xff |
- | out_port | :none |
diff --git a/features/open_flow10/hello.feature b/features/open_flow10/hello.feature
index 606702e8..509dfc17 100644
--- a/features/open_flow10/hello.feature
+++ b/features/open_flow10/hello.feature
@@ -1,50 +1,28 @@
@open_flow10
-Feature: Pio::Hello
+Feature: Hello
Hello messages are exchanged between the switch and controller upon
- connection startup.
+ connection startup. Hello messages have the version field set to the
+ highest OpenFlow protocol version supported by the sender.
Scenario: new
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
- Pio::OpenFlow10::Hello.new
+ Pio::Hello.new
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
- | ofp_version | 1 |
- | message_type | 0 |
- | message_length | 8 |
+ | version | 1 |
| transaction_id | 0 |
| xid | 0 |
- | body | |
- | user_data | |
Scenario: new(transaction_id: 123)
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::Hello.new(transaction_id: 123)
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
- | ofp_version | 1 |
- | message_type | 0 |
- | message_length | 8 |
+ | version | 1 |
| transaction_id | 123 |
| xid | 123 |
- | body | |
- | user_data | |
-
- Scenario: read
- When I try to parse a file named "open_flow10/hello.raw" with "Hello" class
- Then it should finish successfully
- And the message has the following fields and values:
- | field | value |
- | ofp_version | 1 |
- | message_type | 0 |
- | message_length | 8 |
- | transaction_id | 23 |
- | xid | 23 |
- | body | |
- | user_data | |
diff --git a/features/open_flow10/hello_failed.feature b/features/open_flow10/hello_failed.feature
index c70a5195..e1c69256 100644
--- a/features/open_flow10/hello_failed.feature
+++ b/features/open_flow10/hello_failed.feature
@@ -1,69 +1,19 @@
@open_flow10
-Feature: Pio::Error::HelloFailed
+Feature: Error::HelloFailed
- Hello protocol failed
+ Hello protocol failed error
- Scenario: new
- When I try to create an OpenFlow message with:
+ Scenario: new(error_code: :incompatible, description: 'error description')
+ When I create an OpenFlow message with:
"""
- Pio::Error::HelloFailed.new
+ Pio::Error::HelloFailed.new(error_code: :incompatible,
+ description: 'error description')
"""
- Then it should finish successfully
- And the message has the following fields and values:
- | field | value |
- | ofp_version | 1 |
- | message_type | 1 |
- | message_length | 12 |
- | transaction_id | 0 |
- | xid | 0 |
- | error_type | :hello_failed |
- | error_code | :incompatible |
- | description | |
-
- Scenario: new(description: 'error description')
- When I try to create an OpenFlow message with:
- """
- Pio::Error::HelloFailed.new(description: 'error description')
- """
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
- | ofp_version | 1 |
- | message_type | 1 |
- | message_length | 29 |
| transaction_id | 0 |
| xid | 0 |
| error_type | :hello_failed |
| error_code | :incompatible |
| description | error description |
- Scenario: new(error_code: :permissions_error)
- When I try to create an OpenFlow message with:
- """
- Pio::Error::HelloFailed.new(error_code: :permissions_error)
- """
- Then it should finish successfully
- And the message has the following fields and values:
- | field | value |
- | ofp_version | 1 |
- | message_type | 1 |
- | message_length | 12 |
- | transaction_id | 0 |
- | xid | 0 |
- | error_type | :hello_failed |
- | error_code | :permissions_error |
- | description | |
-
- Scenario: read
- When I try to parse a file named "open_flow10/hello_failed.raw" with "Pio::Error::HelloFailed" class
- Then it should finish successfully
- And the message has the following fields and values:
- | field | value |
- | ofp_version | 1 |
- | message_type | 1 |
- | message_length | 29 |
- | transaction_id | 0 |
- | xid | 0 |
- | error_type | :hello_failed |
- | error_code | :incompatible |
- | description | error description |
diff --git a/features/open_flow10/packet_in.feature b/features/open_flow10/packet_in.feature
index 9936882a..00a42567 100644
--- a/features/open_flow10/packet_in.feature
+++ b/features/open_flow10/packet_in.feature
@@ -1,57 +1,138 @@
@open_flow10
-Feature: Pio::PacketIn
+Feature: PacketIn
+
+ When packets are received by the datapath and sent to the
+ controller, they use the PacketIn message.
+
Scenario: new
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
+ """ruby
+ arp_request = Pio::Arp::Request.new(
+ source_mac: 'fa:ce:b0:00:00:cc',
+ sender_protocol_address: '192.168.0.1',
+ target_protocol_address: '192.168.0.2'
+ )
+
+ Pio::PacketIn.new(transaction_id: 0,
+ buffer_id: 0xffffff00,
+ in_port: 1,
+ reason: :no_match,
+ raw_data: arp_request.to_binary)
+ """
+ Then the message has the following fields and values:
+ | field | value |
+ | transaction_id | 0 |
+ | xid | 0 |
+ | buffer_id | 4294967040 |
+ | total_length | 64 |
+ | in_port | 1 |
+ | reason | :no_match |
+ | data.class | Pio::Arp::Request |
+ | source_mac | fa:ce:b0:00:00:cc |
+ | destination_mac | ff:ff:ff:ff:ff:ff |
+
+ Scenario: read an empty PacketIn message
+ Given I use the fixture "open_flow10"
+ When I create a packet with:
+ """ruby
+ Pio::PacketIn.read(eval(IO.read('packet_in.rb')))
+ """
+ Then the packet has the following fields and values:
+ | field | value |
+ | transaction_id | 0 |
+ | xid | 0 |
+ | buffer_id | 4294967040 |
+ | in_port | 1 |
+ | reason | :no_match |
+
+ Scenario: read a PacketIn message (ARP request)
+ Given I use the fixture "open_flow10"
+ When I create a packet with:
+ """ruby
+ Pio::PacketIn.read(eval(IO.read('packet_in_arp_request.rb')))
+ """
+ Then the packet has the following fields and values:
+ | field | value |
+ | transaction_id | 0 |
+ | xid | 0 |
+ | buffer_id | 4294967040 |
+ | total_length | 64 |
+ | in_port | 1 |
+ | reason | :no_match |
+ | data.class | Pio::Arp::Request |
+ | source_mac | fa:ce:b0:00:00:cc |
+ | destination_mac | ff:ff:ff:ff:ff:ff |
+
+ Scenario: convert PacketIn to Ruby code
+ When I eval the following Ruby code:
+ """ruby
+ arp_request = Pio::Arp::Request.new(
+ source_mac: 'fa:ce:b0:00:00:cc',
+ sender_protocol_address: '192.168.0.1',
+ target_protocol_address: '192.168.0.2'
+ )
+
+ Pio::PacketIn.new(transaction_id: 0,
+ buffer_id: 0xffffff00,
+ in_port: 1,
+ reason: :no_match,
+ raw_data: arp_request.to_binary).to_ruby
+ """
+ Then the result of eval should be:
+ """ruby
+ [
+ 0x01, # version
+ 0x0a, # type
+ 0x00, 0x52, # _length
+ 0x00, 0x00, 0x00, 0x00, # transaction_id
+ 0xff, 0xff, 0xff, 0x00, # buffer_id
+ 0x00, 0x40, # total_length
+ 0x00, 0x01, # in_port
+ 0x00, # reason
+ 0x00, # padding
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfa, 0xce, 0xb0, 0x00, 0x00, 0xcc, 0x08, 0x06, 0x00, 0x01, 0x08, 0x00, 0x06, 0x04, 0x00, 0x01, 0xfa, 0xce, 0xb0, 0x00, 0x00, 0xcc, 0xc0, 0xa8, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xa8, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, # raw_data
+ ].pack('C82')
"""
- data_dump = [
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xac, 0x5d, 0x10, 0x31, 0x37,
- 0x79, 0x08, 0x06, 0x00, 0x01, 0x08, 0x00, 0x06, 0x04, 0x00, 0x01,
- 0xac, 0x5d, 0x10, 0x31, 0x37, 0x79, 0xc0, 0xa8, 0x02, 0xfe, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0xa8, 0x02, 0x05, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00
- ].pack('C*')
+
+ Scenario: PacketIn inspection (empty PacketIn)
+ When I eval the following Ruby code:
+ """ruby
+ Pio::PacketIn.new(transaction_id: 0,
+ buffer_id: 0xffffff00,
+ in_port: 1,
+ reason: :no_match).inspect
+ """
+ Then the result of eval should be:
+ """
+ #
+ """
+
+ Scenario: PacketIn inspection (ARP request)
+ When I eval the following Ruby code:
+ """ruby
+ arp_request = Pio::Arp::Request.new(
+ source_mac: 'fa:ce:b0:00:00:cc',
+ sender_protocol_address: '192.168.0.1',
+ target_protocol_address: '192.168.0.2'
+ )
Pio::PacketIn.new(transaction_id: 0,
buffer_id: 0xffffff00,
in_port: 1,
reason: :no_match,
- raw_data: data_dump)
- """
- Then it should finish successfully
- And the message has the following fields and values:
- | field | value |
- | ofp_version | 1 |
- | message_type | 10 |
- | message_length | 78 |
- | transaction_id | 0 |
- | xid | 0 |
- | buffer_id | 4294967040 |
- | total_len | 60 |
- | in_port | 1 |
- | reason | :no_match |
- | raw_data.length | 60 |
- | source_mac | ac:5d:10:31:37:79 |
- | source_mac.class | Pio::Mac |
- | destination_mac | ff:ff:ff:ff:ff:ff |
- | destination_mac.class | Pio::Mac |
-
- Scenario: read
- When I try to parse a file named "open_flow10/packet_in_arp_request.raw" with "PacketIn" class
- Then it should finish successfully
- And the message has the following fields and values:
- | field | value |
- | ofp_version | 1 |
- | message_type | 10 |
- | message_length | 78 |
- | transaction_id | 0 |
- | xid | 0 |
- | buffer_id | 4294967040 |
- | total_len | 60 |
- | in_port | 1 |
- | reason | :no_match |
- | raw_data.length | 60 |
- | source_mac | ac:5d:10:31:37:79 |
- | source_mac.class | Pio::Mac |
- | destination_mac | ff:ff:ff:ff:ff:ff |
- | destination_mac.class | Pio::Mac |
+ raw_data: arp_request.to_binary).inspect
+ """
+ Then the result of eval should be:
+ """
+ #>
+ """
+
+ Scenario: PacketIn class inspection
+ When I eval the following Ruby code:
+ """ruby
+ Pio::PacketIn.inspect
+ """
+ Then the result of eval should be:
+ """
+ PacketIn(open_flow_version: uint8, message_type: uint8, message_length: uint16, transaction_id: uint32, buffer_id: uint32, total_length: uint16, in_port: uint16, reason: symbol, raw_data: string)
+ """
diff --git a/features/open_flow10/packet_in.raw b/features/open_flow10/packet_in.raw
deleted file mode 100644
index 417c0329..00000000
Binary files a/features/open_flow10/packet_in.raw and /dev/null differ
diff --git a/features/open_flow10/packet_in_arp_request.raw b/features/open_flow10/packet_in_arp_request.raw
deleted file mode 100644
index 93b4edd9..00000000
Binary files a/features/open_flow10/packet_in_arp_request.raw and /dev/null differ
diff --git a/features/open_flow10/packet_out.feature b/features/open_flow10/packet_out.feature
index 59381d98..59487a98 100644
--- a/features/open_flow10/packet_out.feature
+++ b/features/open_flow10/packet_out.feature
@@ -1,19 +1,30 @@
@open_flow10
-Feature: Pio::PacketOut
- Scenario: read
- When I try to parse a file named "open_flow10/packet_out.raw" with "PacketOut" class
- Then it should finish successfully
- And the message has the following fields and values:
- | field | value |
- | ofp_version | 1 |
- | message_type | 13 |
- | message_length | 88 |
- | transaction_id | 22 |
- | xid | 22 |
- | buffer_id | 4294967295 |
- | in_port | 65535 |
- | actions.length | 1 |
- | actions.first.class | Pio::OpenFlow10::SendOutPort |
- | actions.first.port | 2 |
- | actions.first.max_length | 65535 |
- | raw_data.length | 64 |
+Feature: PacketOut
+ Scenario: new
+ When I create an OpenFlow message with:
+ """
+ arp_request = Pio::Arp::Request.new(
+ source_mac: 'fa:ce:b0:00:00:cc',
+ sender_protocol_address: '192.168.0.1',
+ target_protocol_address: '192.168.0.2'
+ )
+
+ Pio::PacketOut.new(
+ transaction_id: 0x16,
+ buffer_id: 0xffffffff,
+ in_port: 0xffff,
+ actions: Pio::SendOutPort.new(2),
+ raw_data: arp_request.to_binary
+ )
+ """
+ Then the message has the following fields and values:
+ | field | value |
+ | transaction_id | 22 |
+ | xid | 22 |
+ | buffer_id.to_hex | 0xffffffff |
+ | in_port.to_hex | 0xffff |
+ | actions.length | 1 |
+ | actions[0].class | Pio::OpenFlow10::SendOutPort |
+ | actions[0].port | 2 |
+ | actions[0].max_length | 65535 |
+ | raw_data.length | 64 |
diff --git a/features/open_flow10/port_stats_request.feature b/features/open_flow10/port_stats_request.feature
index 57b45396..df1a1aa8 100644
--- a/features/open_flow10/port_stats_request.feature
+++ b/features/open_flow10/port_stats_request.feature
@@ -1,47 +1,26 @@
-Feature: Pio::OpenFlow10::PortStats::Request
- @open_flow10
+@open_flow10
+Feature: PortStats::Request
Scenario: new(:none)
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::OpenFlow10::PortStats::Request.new(:none)
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
- | ofp_version | 1 |
- | message_type | 16 |
- | message_length | 20 |
+ | version | 1 |
| transaction_id | 0 |
| xid | 0 |
| stats_type | :port |
| port | :none |
- @open_flow10
Scenario: new(:none, transaction_id: 123)
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::OpenFlow10::PortStats::Request.new(:none, transaction_id: 123)
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
- | ofp_version | 1 |
- | message_type | 16 |
- | message_length | 20 |
- | transaction_id | 123 |
- | xid | 123 |
- | stats_type | :port |
- | port | :none |
-
- @open_flow10
- Scenario: read
- When I try to parse a file named "open_flow10/port_stats_request.raw" with "PortStats::Request" class
- Then it should finish successfully
- And the message has the following fields and values:
- | field | value |
- | ofp_version | 1 |
- | message_type | 16 |
- | message_length | 20 |
+ | version | 1 |
| transaction_id | 123 |
| xid | 123 |
| stats_type | :port |
diff --git a/features/open_flow10/port_status.feature b/features/open_flow10/port_status.feature
index ecbc6882..71ec99a2 100644
--- a/features/open_flow10/port_status.feature
+++ b/features/open_flow10/port_status.feature
@@ -1,23 +1,24 @@
-Feature: Pio::PortStatus
+@open_flow10
+Feature: PortStatus
+
+ As physical ports are added, modified, and removed from the
+ datapath, the controller needs to be informed with the PortStatus
+ message.
+
Scenario: read
- When I try to parse a file named "open_flow10/port_status.raw" with "PortStatus" class
- Then it should finish successfully
- And the message has the following fields and values:
- | field | value |
- | class | Pio::OpenFlow10::PortStatus |
- | ofp_version | 1 |
- | message_type | 12 |
- | message_length | 64 |
- | transaction_id | 4 |
- | xid | 4 |
- | reason | :delete |
- | desc.port_no | 65533 |
- | desc.hardware_address | 01:02:03:04:05:06 |
- | desc.name | foo |
- | desc.config | [:no_flood] |
- | desc.state | [:stp_forward, :stp_block] |
- | desc.curr | [:port_10mb_hd] |
- | desc.advertised | [:port_1gb_fd] |
- | desc.supported | [:port_autoneg] |
- | desc.peer | [:port_pause_asym] |
+ When I parse a file named "open_flow10/port_status.raw" with "PortStatus" class
+ Then the message has the following fields and values:
+ | field | value |
+ | transaction_id | 4 |
+ | xid | 4 |
+ | reason | :delete |
+ | number | 65533 |
+ | mac_address | 01:02:03:04:05:06 |
+ | name | foo |
+ | config | [:no_flood] |
+ | state | [:stp_forward, :stp_block] |
+ | curr | [:port_10mb_hd] |
+ | advertised | [:port_1gb_fd] |
+ | supported | [:port_autoneg] |
+ | peer | [:port_pause_asym] |
diff --git a/features/open_flow10/queue_stats_request.feature b/features/open_flow10/queue_stats_request.feature
index a8ddec9a..d4c402e0 100644
--- a/features/open_flow10/queue_stats_request.feature
+++ b/features/open_flow10/queue_stats_request.feature
@@ -1,51 +1,33 @@
-Feature: Pio::OpenFlow10::QueueStats::Request
- @open_flow10
+@open_flow10
+Feature: QueueStats::Request
+
+ Information about queues is requested with a Queue Stats Request
+ message.
+
Scenario: new(port: 1, queue_id: 1)
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::OpenFlow10::QueueStats::Request.new(port: 1, queue_id: 1)
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
- | ofp_version | 1 |
- | message_type | 16 |
- | message_length | 20 |
+ | version | 1 |
| transaction_id | 0 |
| xid | 0 |
| stats_type | :queue |
| port | 1 |
| queue_id | 1 |
- @open_flow10
Scenario: new(port: 1, queue_id: 1, transaction_id: 123)
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::OpenFlow10::QueueStats::Request.new(port: 1, queue_id: 1, transaction_id: 123)
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
- | ofp_version | 1 |
- | message_type | 16 |
- | message_length | 20 |
+ | version | 1 |
| transaction_id | 123 |
| xid | 123 |
| stats_type | :queue |
| port | 1 |
| queue_id | 1 |
-
- @open_flow10
- Scenario: read
- When I try to parse a file named "open_flow10/queue_stats_request.raw" with "QueueStats::Request" class
- Then it should finish successfully
- And the message has the following fields and values:
- | field | value |
- | ofp_version | 1 |
- | message_type | 16 |
- | message_length | 20 |
- | transaction_id | 123 |
- | xid | 123 |
- | stats_type | :queue |
- | port | :all |
- | queue_id | 1 |
diff --git a/features/open_flow10/send_out_port.feature b/features/open_flow10/send_out_port.feature
index ecc8f738..5c72341c 100644
--- a/features/open_flow10/send_out_port.feature
+++ b/features/open_flow10/send_out_port.feature
@@ -1,13 +1,12 @@
@open_flow10
-Feature: Pio::SendOutPort
+Feature: SendOutPort
Scenario: new(1)
- When I try to create an OpenFlow action with:
+ When I create an OpenFlow action with:
"""
Pio::SendOutPort.new(1)
"""
- Then it should finish successfully
- And the action has the following fields and values:
+ Then the action has the following fields and values:
| field | value |
| action_type | 0 |
| action_length | 8 |
@@ -15,12 +14,11 @@ Feature: Pio::SendOutPort
| max_length | 65535 |
Scenario: new(:all)
- When I try to create an OpenFlow action with:
+ When I create an OpenFlow action with:
"""
Pio::SendOutPort.new(:all)
"""
- Then it should finish successfully
- And the action has the following fields and values:
+ Then the action has the following fields and values:
| field | value |
| action_type | 0 |
| action_length | 8 |
@@ -28,12 +26,11 @@ Feature: Pio::SendOutPort
| max_length | 65535 |
Scenario: new(:controller)
- When I try to create an OpenFlow action with:
+ When I create an OpenFlow action with:
"""
Pio::SendOutPort.new(:controller)
"""
- Then it should finish successfully
- And the action has the following fields and values:
+ Then the action has the following fields and values:
| field | value |
| action_type | 0 |
| action_length | 8 |
@@ -41,12 +38,11 @@ Feature: Pio::SendOutPort
| max_length | 65535 |
Scenario: new(:local)
- When I try to create an OpenFlow action with:
+ When I create an OpenFlow action with:
"""
Pio::SendOutPort.new(:local)
"""
- Then it should finish successfully
- And the action has the following fields and values:
+ Then the action has the following fields and values:
| field | value |
| action_type | 0 |
| action_length | 8 |
@@ -54,12 +50,11 @@ Feature: Pio::SendOutPort
| max_length | 65535 |
Scenario: new(:table)
- When I try to create an OpenFlow action with:
+ When I create an OpenFlow action with:
"""
Pio::SendOutPort.new(:table)
"""
- Then it should finish successfully
- And the action has the following fields and values:
+ Then the action has the following fields and values:
| field | value |
| action_type | 0 |
| action_length | 8 |
@@ -67,12 +62,11 @@ Feature: Pio::SendOutPort
| max_length | 65535 |
Scenario: new(:in_port)
- When I try to create an OpenFlow action with:
+ When I create an OpenFlow action with:
"""
Pio::SendOutPort.new(:in_port)
"""
- Then it should finish successfully
- And the action has the following fields and values:
+ Then the action has the following fields and values:
| field | value |
| action_type | 0 |
| action_length | 8 |
@@ -80,12 +74,11 @@ Feature: Pio::SendOutPort
| max_length | 65535 |
Scenario: new(:normal)
- When I try to create an OpenFlow action with:
+ When I create an OpenFlow action with:
"""
Pio::SendOutPort.new(:normal)
"""
- Then it should finish successfully
- And the action has the following fields and values:
+ Then the action has the following fields and values:
| field | value |
| action_type | 0 |
| action_length | 8 |
@@ -93,12 +86,11 @@ Feature: Pio::SendOutPort
| max_length | 65535 |
Scenario: new(:flood)
- When I try to create an OpenFlow action with:
+ When I create an OpenFlow action with:
"""
Pio::SendOutPort.new(:flood)
"""
- Then it should finish successfully
- And the action has the following fields and values:
+ Then the action has the following fields and values:
| field | value |
| action_type | 0 |
| action_length | 8 |
diff --git a/features/open_flow10/set_destination_mac_address.feature b/features/open_flow10/set_destination_mac_address.feature
index 88787e8e..656b93cf 100644
--- a/features/open_flow10/set_destination_mac_address.feature
+++ b/features/open_flow10/set_destination_mac_address.feature
@@ -1,13 +1,12 @@
@open_flow10
-Feature: Pio::SetDestinationMacAddress
+Feature: SetDestinationMacAddress
Scenario: new('11:22:33:44:55:66')
- When I try to create an OpenFlow action with:
+ When I create an OpenFlow action with:
"""
Pio::SetDestinationMacAddress.new('11:22:33:44:55:66')
"""
- Then it should finish successfully
- And the action has the following fields and values:
+ Then the action has the following fields and values:
| field | value |
| action_type | 5 |
| action_length | 16 |
diff --git a/features/open_flow10/set_ip_destination_address.feature b/features/open_flow10/set_ip_destination_address.feature
index 63246ad7..dbffa517 100644
--- a/features/open_flow10/set_ip_destination_address.feature
+++ b/features/open_flow10/set_ip_destination_address.feature
@@ -1,13 +1,12 @@
@open_flow10
-Feature: Pio::OpenFlow10::SetDestinationIpAddress
+Feature: SetDestinationIpAddress
Scenario: new('192.168.0.1')
- When I try to create an OpenFlow action with:
+ When I create an OpenFlow action with:
"""
Pio::OpenFlow10::SetDestinationIpAddress.new('192.168.0.1')
"""
- Then it should finish successfully
- And the action has the following fields and values:
+ Then the action has the following fields and values:
| field | value |
| action_type | 7 |
| action_length | 8 |
diff --git a/features/open_flow10/set_source_ip_address.feature b/features/open_flow10/set_source_ip_address.feature
index 4d8fb805..57d0f2c8 100644
--- a/features/open_flow10/set_source_ip_address.feature
+++ b/features/open_flow10/set_source_ip_address.feature
@@ -1,13 +1,12 @@
@open_flow10
-Feature: Pio::OpenFlow10::SetSourceIpAddress
+Feature: SetSourceIpAddress
Scenario: new('192.168.0.1')
- When I try to create an OpenFlow action with:
+ When I create an OpenFlow action with:
"""
Pio::OpenFlow10::SetSourceIpAddress.new('192.168.0.1')
"""
- Then it should finish successfully
- And the action has the following fields and values:
+ Then the action has the following fields and values:
| field | value |
| action_type | 6 |
| action_length | 8 |
diff --git a/features/open_flow10/set_source_mac_address.feature b/features/open_flow10/set_source_mac_address.feature
index 77690030..dbf3835e 100644
--- a/features/open_flow10/set_source_mac_address.feature
+++ b/features/open_flow10/set_source_mac_address.feature
@@ -1,13 +1,12 @@
@open_flow10
-Feature: Pio::SetSourceMacAddress
+Feature: SetSourceMacAddress
Scenario: new('11:22:33:44:55:66')
- When I try to create an OpenFlow action with:
+ When I create an OpenFlow action with:
"""
Pio::SetSourceMacAddress.new('11:22:33:44:55:66')
"""
- Then it should finish successfully
- And the action has the following fields and values:
+ Then the action has the following fields and values:
| field | value |
| action_type | 4 |
| action_length | 16 |
diff --git a/features/open_flow10/set_tos.feature b/features/open_flow10/set_tos.feature
index b2738109..9f3b3d7e 100644
--- a/features/open_flow10/set_tos.feature
+++ b/features/open_flow10/set_tos.feature
@@ -1,13 +1,12 @@
@open_flow10
-Feature: Pio::OpenFlow10::SetTos
+Feature: SetTos
Scenario: new(0b11111100)
- When I try to create an OpenFlow action with:
+ When I create an OpenFlow action with:
"""
Pio::OpenFlow10::SetTos.new(0b11111100)
"""
- Then it should finish successfully
- And the action has the following fields and values:
+ Then the action has the following fields and values:
| field | value |
| action_type | 8 |
| action_length | 8 |
diff --git a/features/open_flow10/set_transport_destination_port.feature b/features/open_flow10/set_transport_destination_port.feature
index 9013fc09..c802019c 100644
--- a/features/open_flow10/set_transport_destination_port.feature
+++ b/features/open_flow10/set_transport_destination_port.feature
@@ -1,13 +1,12 @@
@open_flow10
-Feature: Pio::OpenFlow10::SetTransportDestinationPort
+Feature: SetTransportDestinationPort
Scenario: new(100)
- When I try to create an OpenFlow action with:
+ When I create an OpenFlow action with:
"""
Pio::OpenFlow10::SetTransportDestinationPort.new(100)
"""
- Then it should finish successfully
- And the action has the following fields and values:
+ Then the action has the following fields and values:
| field | value |
| action_type | 10 |
| action_length | 8 |
diff --git a/features/open_flow10/set_transport_source_port.feature b/features/open_flow10/set_transport_source_port.feature
index 6567620e..4f089566 100644
--- a/features/open_flow10/set_transport_source_port.feature
+++ b/features/open_flow10/set_transport_source_port.feature
@@ -1,13 +1,12 @@
@open_flow10
-Feature: Pio::OpenFlow10::SetTransportSourcePort
+Feature: SetTransportSourcePort
Scenario: new(100)
- When I try to create an OpenFlow action with:
+ When I create an OpenFlow action with:
"""
Pio::OpenFlow10::SetTransportSourcePort.new(100)
"""
- Then it should finish successfully
- And the action has the following fields and values:
+ Then the action has the following fields and values:
| field | value |
| action_type | 9 |
| action_length | 8 |
diff --git a/features/open_flow10/set_vlan_priority.feature b/features/open_flow10/set_vlan_priority.feature
index 15ea17f5..aa3c3ade 100644
--- a/features/open_flow10/set_vlan_priority.feature
+++ b/features/open_flow10/set_vlan_priority.feature
@@ -1,13 +1,12 @@
@open_flow10
-Feature: Pio::OpenFlow10::SetVlanPriority
+Feature: SetVlanPriority
Scenario: new(1)
- When I try to create an OpenFlow action with:
+ When I create an OpenFlow action with:
"""
Pio::OpenFlow10::SetVlanPriority.new(1)
"""
- Then it should finish successfully
- And the action has the following fields and values:
+ Then the action has the following fields and values:
| field | value |
| action_type | 2 |
| action_length | 8 |
diff --git a/features/open_flow10/set_vlan_vid.feature b/features/open_flow10/set_vlan_vid.feature
index 4018e37f..5b07f380 100644
--- a/features/open_flow10/set_vlan_vid.feature
+++ b/features/open_flow10/set_vlan_vid.feature
@@ -1,13 +1,12 @@
@open_flow10
-Feature: Pio::OpenFlow10::SetVlanVid
+Feature: SetVlanVid
Scenario: new(1)
- When I try to create an OpenFlow action with:
+ When I create an OpenFlow action with:
"""
Pio::OpenFlow10::SetVlanVid.new(1)
"""
- Then it should finish successfully
- And the action has the following fields and values:
+ Then the action has the following fields and values:
| field | value |
| action_type | 1 |
| action_length | 8 |
diff --git a/features/open_flow10/strip_vlan_header.feature b/features/open_flow10/strip_vlan_header.feature
index 0dff0d88..7bf02a08 100644
--- a/features/open_flow10/strip_vlan_header.feature
+++ b/features/open_flow10/strip_vlan_header.feature
@@ -1,13 +1,12 @@
@open_flow10
-Feature: Pio::OpenFlow10::StripVlanHeader
+Feature: StripVlanHeader
Scenario: new
- When I try to create an OpenFlow action with:
+ When I create an OpenFlow action with:
"""
Pio::OpenFlow10::StripVlanHeader.new
"""
- Then it should finish successfully
- And the action has the following fields and values:
+ Then the action has the following fields and values:
| field | value |
| action_type | 3 |
| action_length | 8 |
diff --git a/features/open_flow10/table_stats_request.feature b/features/open_flow10/table_stats_request.feature
index d1be8cea..ee032b1b 100644
--- a/features/open_flow10/table_stats_request.feature
+++ b/features/open_flow10/table_stats_request.feature
@@ -1,31 +1,29 @@
@open_flow10
-Feature: Pio::TableStats::Request
+Feature: TableStats::Request
+
+ Information about tables is requested with a Table Stats Request
+ message.
+
Scenario: new
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::TableStats::Request.new
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
- | ofp_version | 1 |
- | message_type | 16 |
- | message_length | 12 |
+ | version | 1 |
| transaction_id | 0 |
| xid | 0 |
| stats_type | :table |
Scenario: new(transaction_id: 123)
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::TableStats::Request.new(transaction_id: 123)
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
- | ofp_version | 1 |
- | message_type | 16 |
- | message_length | 12 |
+ | version | 1 |
| transaction_id | 123 |
| xid | 123 |
| stats_type | :table |
diff --git a/features/open_flow10/vendor_action.feature b/features/open_flow10/vendor_action.feature
index 52624c95..3dff2171 100644
--- a/features/open_flow10/vendor_action.feature
+++ b/features/open_flow10/vendor_action.feature
@@ -1,13 +1,12 @@
@open_flow10
-Feature: Pio::VendorAction
+Feature: VendorAction
Scenario: new(1)
- When I try to create an OpenFlow action with:
+ When I create an OpenFlow action with:
"""
- Pio::VendorAction.new(1)
+ Pio::OpenFlow10::VendorAction.new(1)
"""
- Then it should finish successfully
- And the action has the following fields and values:
+ Then the action has the following fields and values:
| field | value |
| action_type.to_hex | 0xffff |
| length | 8 |
diff --git a/features/open_flow13/apply_actions.feature b/features/open_flow13/apply_actions.feature
index dac26446..d89984f3 100644
--- a/features/open_flow13/apply_actions.feature
+++ b/features/open_flow13/apply_actions.feature
@@ -1,27 +1,25 @@
@open_flow13
-Feature: Apply-Actions instruction.
+Feature: Apply
Scenario: new()
- When I try to create an OpenFlow instruction with:
+ When I create an OpenFlow instruction with:
"""
Pio::Apply.new
"""
- Then it should finish successfully
- And the message has the following fields and values:
- | field | value |
- | class | Pio::Apply |
- | instruction_type | 4 |
- | instruction_length | 8 |
- | actions | [] |
+ Then the message has the following fields and values:
+ | field | value |
+ | class | Pio::OpenFlow13::Apply |
+ | instruction_type | 4 |
+ | instruction_length | 8 |
+ | actions | [] |
Scenario: new(SendOutPort.new(1))
- When I try to create an OpenFlow instruction with:
+ When I create an OpenFlow instruction with:
"""
- Pio::Apply.new(SendOutPort.new(1))
+ Pio::Apply.new(Pio::SendOutPort.new(1))
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
- | class | Pio::Apply |
+ | class | Pio::OpenFlow13::Apply |
| instruction_type | 4 |
| instruction_length | 24 |
| actions.size | 1 |
@@ -29,11 +27,10 @@ Feature: Apply-Actions instruction.
| actions.at(0).port | 1 |
Scenario: read
- When I try to parse a file named "open_flow13/apply_actions.raw" with "Pio::Apply" class
- Then it should finish successfully
- And the message has the following fields and values:
+ When I parse a file named "open_flow13/apply_actions.raw" with "Pio::Apply" class
+ Then the message has the following fields and values:
| field | value |
- | class | Pio::Apply |
+ | class | Pio::OpenFlow13::Apply |
| instruction_type | 4 |
| instruction_length | 24 |
| actions.size | 1 |
diff --git a/features/open_flow13/bad_request.feature b/features/open_flow13/bad_request.feature
index d84f11ef..92417e39 100644
--- a/features/open_flow13/bad_request.feature
+++ b/features/open_flow13/bad_request.feature
@@ -1,19 +1,16 @@
@open_flow13
-Feature: Pio::Error::BadRequest
+Feature: Error::BadRequest
Request was not understood error.
Scenario: new (raw_data = Echo request 1.0)
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::Error::BadRequest.new(raw_data: Pio::OpenFlow10::Echo::Request.new.to_binary)
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
- | ofp_version | 4 |
- | message_type | 1 |
- | message_length | 20 |
+ | version | 4 |
| transaction_id | 0 |
| xid | 0 |
| error_type | :bad_request |
@@ -21,13 +18,10 @@ Feature: Pio::Error::BadRequest
| raw_data.length | 8 |
Scenario: read
- When I try to parse a file named "open_flow13/bad_request.raw" with "Pio::Error::BadRequest" class
- Then it should finish successfully
- And the message has the following fields and values:
+ When I parse a file named "open_flow13/bad_request.raw" with "Pio::Error::BadRequest" class
+ Then the message has the following fields and values:
| field | value |
- | ofp_version | 4 |
- | message_type | 1 |
- | message_length | 20 |
+ | version | 4 |
| transaction_id | 0 |
| xid | 0 |
| error_type | :bad_request |
diff --git a/features/open_flow13/copy_ttl_inwards.feature b/features/open_flow13/copy_ttl_inwards.feature
new file mode 100644
index 00000000..f25539e8
--- /dev/null
+++ b/features/open_flow13/copy_ttl_inwards.feature
@@ -0,0 +1,13 @@
+@open_flow13
+Feature: CopyTtlInwards
+
+ Copies TTL "inwards" -- from outermost to next-to-outermost
+
+ Scenario: new
+ When I create an OpenFlow action with:
+ """
+ Pio::CopyTtlInwards.new
+ """
+ Then the action has the following fields and values:
+ | field | value |
+ | action_type | 12 |
diff --git a/features/open_flow13/copy_ttl_outwards.feature b/features/open_flow13/copy_ttl_outwards.feature
new file mode 100644
index 00000000..eeb7fcd1
--- /dev/null
+++ b/features/open_flow13/copy_ttl_outwards.feature
@@ -0,0 +1,13 @@
+@open_flow13
+Feature: CopyTtlOutwards
+
+ Copies TTL "outwards" -- from next-to-outermost to outermost
+
+ Scenario: new
+ When I create an OpenFlow action with:
+ """
+ Pio::CopyTtlOutwards.new
+ """
+ Then the action has the following fields and values:
+ | field | value |
+ | action_type | 11 |
diff --git a/features/open_flow13/decrement_ip_ttl.feature b/features/open_flow13/decrement_ip_ttl.feature
new file mode 100644
index 00000000..c67a916d
--- /dev/null
+++ b/features/open_flow13/decrement_ip_ttl.feature
@@ -0,0 +1,11 @@
+@open_flow13
+Feature: DecrementIpTtl
+
+ Scenario: new
+ When I create an OpenFlow action with:
+ """
+ Pio::DecrementIpTtl.new
+ """
+ Then the action has the following fields and values:
+ | field | value |
+ | action_type | 24 |
diff --git a/features/open_flow13/echo_reply.feature b/features/open_flow13/echo_reply.feature
index d4585de4..a01f4004 100644
--- a/features/open_flow13/echo_reply.feature
+++ b/features/open_flow13/echo_reply.feature
@@ -1,86 +1,60 @@
@open_flow13
-Feature: Pio::Echo::Reply
+Feature: Echo::Reply
Scenario: new
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::Echo::Reply.new
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
- | ofp_version | 4 |
- | message_type | 3 |
- | message_length | 8 |
+ | version | 4 |
| transaction_id | 0 |
| xid | 0 |
| body | |
| user_data | |
Scenario: new(transaction_id: 123)
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::Echo::Reply.new(transaction_id: 123)
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
- | ofp_version | 4 |
- | message_type | 3 |
- | message_length | 8 |
+ | version | 4 |
| transaction_id | 123 |
| xid | 123 |
| body | |
| user_data | |
Scenario: new(body: 'echo reply body')
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::Echo::Reply.new(body: 'echo reply body')
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
- | ofp_version | 4 |
- | message_type | 3 |
- | message_length | 23 |
+ | version | 4 |
| transaction_id | 0 |
| xid | 0 |
| body | echo reply body |
| user_data | echo reply body |
- Scenario: new(unknown_attr: 'foo') and error
- When I try to create an OpenFlow message with:
- """
- Pio::Echo::Reply.new(unknown_attr: 'foo')
- """
- Then it should fail with "RuntimeError", "Unknown option: unknown_attr"
-
Scenario: read (no message body)
- When I try to parse a file named "open_flow13/echo_reply_no_body.raw" with "Pio::Echo::Reply" class
- Then it should finish successfully
- And the message has the following fields and values:
+ When I parse a file named "open_flow13/echo_reply_no_body.raw" with "Pio::Echo::Reply" class
+ Then the message has the following fields and values:
| field | value |
- | ofp_version | 4 |
- | message_type | 3 |
- | message_length | 8 |
+ | version | 4 |
| transaction_id | 0 |
| xid | 0 |
| body | |
| user_data | |
Scenario: read
- When I try to parse a file named "open_flow13/echo_reply_body.raw" with "Pio::Echo::Reply" class
- Then it should finish successfully
- And the message has the following fields and values:
+ When I parse a file named "open_flow13/echo_reply_body.raw" with "Pio::Echo::Reply" class
+ Then the message has the following fields and values:
| field | value |
- | ofp_version | 4 |
- | message_type | 3 |
- | message_length | 28 |
+ | version | 4 |
| transaction_id | 0 |
| xid | 0 |
| body | hogehogehogehogehoge |
| user_data | hogehogehogehogehoge |
-
- Scenario: parse error
- When I try to parse a file named "open_flow10/features_request.raw" with "Pio::Echo::Reply" class
- Then it should fail with "Pio::ParseError", "Invalid OpenFlow13 Echo Reply message."
diff --git a/features/open_flow13/echo_request.feature b/features/open_flow13/echo_request.feature
index 0b04326f..93e7a3b7 100644
--- a/features/open_flow13/echo_request.feature
+++ b/features/open_flow13/echo_request.feature
@@ -1,86 +1,60 @@
@open_flow13
-Feature: Pio::Echo::Request
+Feature: Echo::Request
Scenario: new
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::Echo::Request.new
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
- | ofp_version | 4 |
- | message_type | 2 |
- | message_length | 8 |
+ | version | 4 |
| transaction_id | 0 |
| xid | 0 |
| body | |
| user_data | |
Scenario: new(transaction_id: 123)
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::Echo::Request.new(transaction_id: 123)
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
- | ofp_version | 4 |
- | message_type | 2 |
- | message_length | 8 |
+ | version | 4 |
| transaction_id | 123 |
| xid | 123 |
| body | |
| user_data | |
Scenario: new(body: 'echo request body')
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::Echo::Request.new(body: 'echo request body')
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
- | ofp_version | 4 |
- | message_type | 2 |
- | message_length | 25 |
+ | version | 4 |
| transaction_id | 0 |
| xid | 0 |
| body | echo request body |
| user_data | echo request body |
- Scenario: new(unknown_attr: 'foo') and error
- When I try to create an OpenFlow message with:
- """
- Pio::Echo::Request.new(unknown_attr: 'foo')
- """
- Then it should fail with "RuntimeError", "Unknown option: unknown_attr"
-
Scenario: read (no message body)
- When I try to parse a file named "open_flow13/echo_request_no_body.raw" with "Pio::Echo::Request" class
- Then it should finish successfully
- And the message has the following fields and values:
+ When I parse a file named "open_flow13/echo_request_no_body.raw" with "Pio::Echo::Request" class
+ Then the message has the following fields and values:
| field | value |
- | ofp_version | 4 |
- | message_type | 2 |
- | message_length | 8 |
+ | version | 4 |
| transaction_id | 0 |
| xid | 0 |
| body | |
| user_data | |
Scenario: read
- When I try to parse a file named "open_flow13/echo_request_body.raw" with "Pio::Echo::Request" class
- Then it should finish successfully
- And the message has the following fields and values:
+ When I parse a file named "open_flow13/echo_request_body.raw" with "Pio::Echo::Request" class
+ Then the message has the following fields and values:
| field | value |
- | ofp_version | 4 |
- | message_type | 2 |
- | message_length | 28 |
+ | version | 4 |
| transaction_id | 0 |
| xid | 0 |
| body | hogehogehogehogehoge |
| user_data | hogehogehogehogehoge |
-
- Scenario: parse error
- When I try to parse a file named "open_flow10/features_request.raw" with "Pio::Echo::Request" class
- Then it should fail with "Pio::ParseError", "Invalid OpenFlow13 Echo Request message."
diff --git a/features/open_flow13/features_reply.feature b/features/open_flow13/features_reply.feature
index 9ebc7825..cb624576 100644
--- a/features/open_flow13/features_reply.feature
+++ b/features/open_flow13/features_reply.feature
@@ -1,7 +1,7 @@
@open_flow13
-Feature: Pio::Features::Reply
+Feature: Features::Reply
Scenario: new
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::Features::Reply.new(
datapath_id: 0x123,
@@ -10,12 +10,9 @@ Feature: Pio::Features::Reply
capabilities: [:flow_stats, :table_stats, :port_stats, :group_stats, :ip_reasm, :queue_stats, :port_blocked]
)
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
- | ofp_version | 4 |
- | message_type | 6 |
- | message_length | 32 |
+ | version | 4 |
| transaction_id | 0 |
| xid | 0 |
| datapath_id | 291 |
@@ -25,21 +22,3 @@ Feature: Pio::Features::Reply
| auxiliary_id | 0 |
| capabilities | [:flow_stats, :table_stats, :port_stats, :group_stats, :ip_reasm, :queue_stats, :port_blocked] |
| reserved | 0 |
-
- Scenario: read
- When I try to parse a file named "open_flow13/features_reply.raw" with "Pio::Features::Reply" class
- Then it should finish successfully
- And the message has the following fields and values:
- | field | value |
- | ofp_version | 4 |
- | message_type | 6 |
- | message_length | 32 |
- | transaction_id | 0 |
- | xid | 0 |
- | datapath_id | 281474976710657 |
- | dpid | 281474976710657 |
- | n_buffers | 256 |
- | n_tables | 1 |
- | auxiliary_id | 0 |
- | capabilities | [:flow_stats, :table_stats, :port_stats, :group_stats, :ip_reasm, :queue_stats, :port_blocked] |
- | reserved | 0 |
diff --git a/features/open_flow13/features_request.feature b/features/open_flow13/features_request.feature
index 761fed56..b77470f0 100644
--- a/features/open_flow13/features_request.feature
+++ b/features/open_flow13/features_request.feature
@@ -1,54 +1,25 @@
@open_flow13
-Feature: Pio::Features::Request
+Feature: Features::Request
Scenario: new
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::Features::Request.new
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
- | ofp_version | 4 |
- | message_type | 5 |
- | message_length | 8 |
+ | version | 4 |
| transaction_id | 0 |
| xid | 0 |
| body | |
Scenario: new(transaction_id: 123)
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::Features::Request.new(transaction_id: 123)
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
- | ofp_version | 4 |
- | message_type | 5 |
- | message_length | 8 |
+ | version | 4 |
| transaction_id | 123 |
| xid | 123 |
| body | |
-
- Scenario: new(unknown_attr: 'foo') and error
- When I try to create an OpenFlow message with:
- """
- Pio::Features::Request.new(unknown_attr: 'foo')
- """
- Then it should fail with "RuntimeError", "Unknown option: unknown_attr"
-
- Scenario: read
- When I try to parse a file named "open_flow13/features_request.raw" with "Pio::Features::Request" class
- Then it should finish successfully
- And the message has the following fields and values:
- | field | value |
- | ofp_version | 4 |
- | message_type | 5 |
- | message_length | 8 |
- | transaction_id | 0 |
- | xid | 0 |
- | body | |
-
- Scenario: parse error
- When I try to parse a file named "open_flow10/hello.raw" with "Pio::Features::Request" class
- Then it should fail with "Pio::ParseError", "Invalid OpenFlow13 Features Request message."
diff --git a/features/open_flow13/flow_mod.feature b/features/open_flow13/flow_mod.feature
index 6bd03f11..39e437f3 100644
--- a/features/open_flow13/flow_mod.feature
+++ b/features/open_flow13/flow_mod.feature
@@ -1,16 +1,13 @@
@open_flow13
-Feature: Pio::FlowMod
+Feature: FlowMod
Scenario: new
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::FlowMod.new
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
- | ofp_version | 4 |
- | message_type | 14 |
- | message_length | 56 |
+ | version | 4 |
| to_binary.length | 56 |
| transaction_id | 0 |
| xid | 0 |
@@ -28,17 +25,14 @@ Feature: Pio::FlowMod
| match.match_fields | [] |
| instructions | [] |
- Scenario: new(instructions: Pio::Apply.new(SendOutPort.new(1)))
- When I try to create an OpenFlow message with:
+ Scenario: new(instructions: Pio::Apply.new(Pio::SendOutPort.new(1)))
+ When I create an OpenFlow message with:
"""
- Pio::FlowMod.new(instructions: Pio::Apply.new(SendOutPort.new(1)))
+ Pio::FlowMod.new(instructions: Pio::Apply.new(Pio::SendOutPort.new(1)))
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
- | ofp_version | 4 |
- | message_type | 14 |
- | message_length | 80 |
+ | version | 4 |
| transaction_id | 0 |
| xid | 0 |
| cookie | 0 |
@@ -54,21 +48,18 @@ Feature: Pio::FlowMod
| flags | [] |
| match.match_fields | [] |
| instructions.size | 1 |
- | instructions.at(0).class | Pio::Apply |
+ | instructions.at(0).class | Pio::OpenFlow13::Apply |
| instructions.at(0).actions.at(0).class | Pio::OpenFlow13::SendOutPort |
| instructions.at(0).actions.at(0).port | 1 |
- Scenario: new(match: Pio::Match.new(in_port: 1), instructions: Pio::Apply.new(SendOutPort.new(1)))
- When I try to create an OpenFlow message with:
+ Scenario: new(match: Pio::Match.new(in_port: 1), instructions: Pio::Apply.new(Pio::SendOutPort.new(1)))
+ When I create an OpenFlow message with:
"""
- Pio::FlowMod.new(match: Pio::Match.new(in_port: 1), instructions: Pio::Apply.new(SendOutPort.new(1)))
+ Pio::FlowMod.new(match: Pio::Match.new(in_port: 1), instructions: Pio::Apply.new(Pio::SendOutPort.new(1)))
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
- | ofp_version | 4 |
- | message_type | 14 |
- | message_length | 88 |
+ | version | 4 |
| transaction_id | 0 |
| xid | 0 |
| cookie | 0 |
@@ -84,57 +75,6 @@ Feature: Pio::FlowMod
| flags | [] |
| match.in_port | 1 |
| instructions.size | 1 |
- | instructions.at(0).class | Pio::Apply |
- | instructions.at(0).actions.at(0).class | Pio::OpenFlow13::SendOutPort |
- | instructions.at(0).actions.at(0).port | 1 |
-
- Scenario: read (no match or instructions)
- When I try to parse a file named "open_flow13/flow_mod_no_match_or_instructions.raw" with "Pio::FlowMod" class
- Then it should finish successfully
- And the message has the following fields and values:
- | field | value |
- | ofp_version | 4 |
- | message_type | 14 |
- | message_length | 56 |
- | transaction_id | 0 |
- | xid | 0 |
- | cookie | 0 |
- | cookie_mask | 0 |
- | table_id | 0 |
- | command | :add |
- | idle_timeout | 0 |
- | hard_timeout | 0 |
- | priority.to_hex | 0xffff |
- | buffer_id | :no_buffer |
- | out_port | :any |
- | out_group | :any |
- | flags | [] |
- | match.match_fields | [] |
- | instructions | [] |
-
- Scenario: read (instruction = apply, action = SendOutPort(port: 1))
- When I try to parse a file named "open_flow13/flow_mod_add_apply_no_match.raw" with "Pio::FlowMod" class
- Then it should finish successfully
- And the message has the following fields and values:
- | field | value |
- | ofp_version | 4 |
- | message_type | 14 |
- | message_length | 80 |
- | transaction_id | 0 |
- | xid | 0 |
- | cookie | 0 |
- | cookie_mask | 0 |
- | table_id | 0 |
- | command | :add |
- | idle_timeout | 0 |
- | hard_timeout | 0 |
- | priority.to_hex | 0xffff |
- | buffer_id | :no_buffer |
- | out_port | :any |
- | out_group | :any |
- | flags | [] |
- | match.match_fields | [] |
- | instructions.size | 1 |
- | instructions.at(0).class | Pio::Apply |
+ | instructions.at(0).class | Pio::OpenFlow13::Apply |
| instructions.at(0).actions.at(0).class | Pio::OpenFlow13::SendOutPort |
| instructions.at(0).actions.at(0).port | 1 |
diff --git a/features/open_flow13/goto_table.feature b/features/open_flow13/goto_table.feature
index 018f2007..2cac27d1 100644
--- a/features/open_flow13/goto_table.feature
+++ b/features/open_flow13/goto_table.feature
@@ -1,26 +1,24 @@
@open_flow13
-Feature: Pio::GotoTable
+Feature: GotoTable
Scenario: new(1)
- When I try to create an OpenFlow instruction with:
+ When I create an OpenFlow instruction with:
"""
- Pio::GotoTable.new(1)
+ Pio::OpenFlow13::GotoTable.new(1)
"""
- Then it should finish successfully
- And the message has the following fields and values:
- | field | value |
- | class | Pio::GotoTable |
- | instruction_type | 1 |
- | instruction_length | 8 |
- | to_binary_s.length | 8 |
- | table_id | 1 |
+ Then the message has the following fields and values:
+ | field | value |
+ | class | Pio::OpenFlow13::GotoTable |
+ | instruction_type | 1 |
+ | instruction_length | 8 |
+ | to_binary_s.length | 8 |
+ | table_id | 1 |
Scenario: read
- When I try to parse a file named "open_flow13/instruction_goto_table.raw" with "Pio::GotoTable" class
- Then it should finish successfully
- And the message has the following fields and values:
- | field | value |
- | class | Pio::GotoTable |
- | instruction_type | 1 |
- | instruction_length | 8 |
- | to_binary_s.length | 8 |
- | table_id | 1 |
+ When I parse a file named "open_flow13/instruction_goto_table.raw" with "Pio::OpenFlow13::GotoTable" class
+ Then the message has the following fields and values:
+ | field | value |
+ | class | Pio::OpenFlow13::GotoTable |
+ | instruction_type | 1 |
+ | instruction_length | 8 |
+ | to_binary_s.length | 8 |
+ | table_id | 1 |
diff --git a/features/open_flow13/hello.feature b/features/open_flow13/hello.feature
index b1dcffae..2b5a9b18 100644
--- a/features/open_flow13/hello.feature
+++ b/features/open_flow13/hello.feature
@@ -1,55 +1,25 @@
@open_flow13
-Feature: Pio::Hello
+Feature: Hello
Scenario: new
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::Hello.new
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
- | ofp_version | 4 |
- | message_type | 0 |
- | message_length | 16 |
+ | version | 4 |
| transaction_id | 0 |
| xid | 0 |
| supported_versions | [:open_flow13] |
Scenario: new(transaction_id: 123)
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::Hello.new(transaction_id: 123)
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
- | ofp_version | 4 |
- | message_type | 0 |
- | message_length | 16 |
+ | version | 4 |
| transaction_id | 123 |
| xid | 123 |
| supported_versions | [:open_flow13] |
-
- Scenario: read (no version bitmap)
- When I try to parse a file named "open_flow13/hello_no_version_bitmap.raw" with "Pio::Hello" class
- Then it should finish successfully
- And the message has the following fields and values:
- | field | value |
- | ofp_version | 4 |
- | message_type | 0 |
- | message_length | 8 |
- | transaction_id | 0 |
- | xid | 0 |
- | supported_versions | [] |
-
- Scenario: read
- When I try to parse a file named "open_flow13/hello_version_bitmap.raw" with "Pio::Hello" class
- Then it should finish successfully
- And the message has the following fields and values:
- | field | value |
- | ofp_version | 4 |
- | message_type | 0 |
- | message_length | 16 |
- | transaction_id | 0 |
- | xid | 0 |
- | supported_versions | [:open_flow10, :open_flow13] |
diff --git a/features/open_flow13/hello_failed.feature b/features/open_flow13/hello_failed.feature
index 065deb74..8fb4a9ad 100644
--- a/features/open_flow13/hello_failed.feature
+++ b/features/open_flow13/hello_failed.feature
@@ -1,19 +1,16 @@
@open_flow13
-Feature: Pio::Error::HelloFailed
+Feature: Error::HelloFailed
Hello protocol failed
Scenario: new
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::Error::HelloFailed.new
"""
- Then it should finish successfully
And the message has the following fields and values:
| field | value |
- | ofp_version | 4 |
- | message_type | 1 |
- | message_length | 12 |
+ | version | 4 |
| transaction_id | 0 |
| xid | 0 |
| error_type | :hello_failed |
@@ -21,16 +18,13 @@ Feature: Pio::Error::HelloFailed
| description | |
Scenario: new(description: 'error description')
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::Error::HelloFailed.new(description: 'error description')
"""
- Then it should finish successfully
And the message has the following fields and values:
| field | value |
- | ofp_version | 4 |
- | message_type | 1 |
- | message_length | 29 |
+ | version | 4 |
| transaction_id | 0 |
| xid | 0 |
| error_type | :hello_failed |
@@ -38,32 +32,15 @@ Feature: Pio::Error::HelloFailed
| description | error description |
Scenario: new(error_code: :permissions_error)
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::Error::HelloFailed.new(error_code: :permissions_error)
"""
- Then it should finish successfully
And the message has the following fields and values:
| field | value |
- | ofp_version | 4 |
- | message_type | 1 |
- | message_length | 12 |
+ | version | 4 |
| transaction_id | 0 |
| xid | 0 |
| error_type | :hello_failed |
| error_code | :permissions_error |
| description | |
-
- Scenario: read
- When I try to parse a file named "open_flow13/hello_failed.raw" with "Pio::Error::HelloFailed" class
- Then it should finish successfully
- And the message has the following fields and values:
- | field | value |
- | ofp_version | 4 |
- | message_type | 1 |
- | message_length | 29 |
- | transaction_id | 0 |
- | xid | 0 |
- | error_type | :hello_failed |
- | error_code | :incompatible |
- | description | error description |
diff --git a/features/open_flow13/match.feature b/features/open_flow13/match.feature
index b6e4de4d..82f097d8 100644
--- a/features/open_flow13/match.feature
+++ b/features/open_flow13/match.feature
@@ -1,789 +1,780 @@
@open_flow13
-Feature: Pio::Match
+Feature: Match
Scenario: new
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::Match.new
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
| match_fields | [] |
Scenario: new(in_port: 1)
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::Match.new(in_port: 1)
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
| in_port | 1 |
Scenario: new(metadata: 1)
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::Match.new(metadata: 1)
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
| metadata | 1 |
Scenario: new(metadata: 1, metadata_mask: 1)
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::Match.new(metadata: 1, metadata_mask: 1)
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
| metadata | 1 |
| metadata_mask | 1 |
Scenario: new(source_mac_address: '01:02:03:04:05:06')
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::Match.new(source_mac_address: '01:02:03:04:05:06')
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
| source_mac_address | 01:02:03:04:05:06 |
Scenario: new(destination_mac_address: '01:02:03:04:05:06')
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::Match.new(destination_mac_address: '01:02:03:04:05:06')
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
| destination_mac_address | 01:02:03:04:05:06 |
Scenario: new(source_mac_address: '01:02:03:04:05:06', source_mac_address_mask: 'ff:ff:ff:00:00:00')
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::Match.new(source_mac_address: '01:02:03:04:05:06', source_mac_address_mask: 'ff:ff:ff:00:00:00')
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
| source_mac_address | 01:02:03:04:05:06 |
| source_mac_address_mask | ff:ff:ff:00:00:00 |
Scenario: new(destination_mac_address: '01:02:03:04:05:06', destination_mac_address_mask: 'ff:ff:ff:00:00:00')
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::Match.new(destination_mac_address: '01:02:03:04:05:06', destination_mac_address_mask: 'ff:ff:ff:00:00:00')
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
| destination_mac_address | 01:02:03:04:05:06 |
| destination_mac_address_mask | ff:ff:ff:00:00:00 |
Scenario: new(ether_type: 0x0800)
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::Match.new(ether_type: 0x0800)
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
| ether_type | 2048 |
Scenario: new(vlan_vid: 10)
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::Match.new(vlan_vid: 10)
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
| vlan_vid | 10 |
Scenario: new(vlan_vid: 10, vlan_pcp: 5)
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::Match.new(vlan_vid: 10, vlan_pcp: 5)
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
| vlan_vid | 10 |
| vlan_pcp | 5 |
Scenario: new(eth_type: 2048, ip_dscp: 46)
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::Match.new(ether_type: 2048, ip_dscp: 46)
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
| ether_type | 2048 |
| ip_dscp | 46 |
Scenario: new(eth_type: 2048, ip_ecn: 3)
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::Match.new(ether_type: 2048, ip_ecn: 46)
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
| ether_type | 2048 |
| ip_ecn | 3 |
Scenario: new(ether_type: 0x0800, ipv4_source_address: '192.168.0.1')
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::Match.new(ether_type: 0x0800, ipv4_source_address: '192.168.0.1')
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
| ether_type | 2048 |
| ipv4_source_address | 192.168.0.1 |
Scenario: new(ether_type: 0x0800, ipv4_destination_address: '192.168.0.1')
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::Match.new(ether_type: 0x0800, ipv4_destination_address: '192.168.0.1')
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
| ether_type | 2048 |
| ipv4_destination_address | 192.168.0.1 |
Scenario: new(ether_type: 0x0800, ipv4_source_address: '192.168.0.1', ivp4_source_address_mask: '255.255.0.0')
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::Match.new(ether_type: 0x0800, ipv4_source_address: '192.168.0.1', ipv4_source_address_mask: '255.255.0.0')
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
| ether_type | 2048 |
| ipv4_source_address | 192.168.0.1 |
| ipv4_source_address_mask | 255.255.0.0 |
Scenario: new(ether_type: 0x0800, ipv4_destination_address: '192.168.0.1', ivp4_destination_address_mask: '255.255.0.0')
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::Match.new(ether_type: 0x0800, ipv4_destination_address: '192.168.0.1', ipv4_destination_address_mask: '255.255.0.0')
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
| ether_type | 2048 |
| ipv4_destination_address | 192.168.0.1 |
| ipv4_destination_address_mask | 255.255.0.0 |
Scenario: new(ether_type: 0x0800, ip_protocol: 6, tcp_source_port: 1111)
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::Match.new(ether_type: 0x0800, ip_protocol: 6, tcp_source_port: 1111)
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
| ether_type | 2048 |
| ip_protocol | 6 |
| tcp_source_port | 1111 |
Scenario: new(ether_type: 0x0800, ip_protocol: 6, tcp_destination_port: 80)
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::Match.new(ether_type: 0x0800, ip_protocol: 6, tcp_destination_port: 80)
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
| ether_type | 2048 |
| ip_protocol | 6 |
| tcp_destination_port | 80 |
Scenario: new(ether_type: 0x0800, ip_protocol: 17, udp_source_port: 2222)
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::Match.new(ether_type: 0x0800, ip_protocol: 17, udp_source_port: 2222)
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
| ether_type | 2048 |
| ip_protocol | 17 |
| udp_source_port | 2222 |
Scenario: new(ether_type: 0x0800, ip_protocol: 17, udp_destination_port: 3333)
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::Match.new(ether_type: 0x0800, ip_protocol: 17, udp_destination_port: 3333)
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
| ether_type | 2048 |
| ip_protocol | 17 |
| udp_destination_port | 3333 |
Scenario: new(ether_type: 0x0800, ip_protocol: 132, sctp_source_port: 22)
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::Match.new(ether_type: 0x0800, ip_protocol: 132, sctp_source_port: 22)
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
| ether_type | 2048 |
| ip_protocol | 132 |
| sctp_source_port | 22 |
Scenario: new(ether_type: 0x0800, ip_protocol: 132, sctp_destination_port: 22)
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::Match.new(ether_type: 0x0800, ip_protocol: 132, sctp_destination_port: 22)
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
| ether_type | 2048 |
| ip_protocol | 132 |
| sctp_destination_port | 22 |
Scenario: new(ether_type: 0x0800, ip_protocol: 1, icmpv4_type: 8)
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::Match.new(ether_type: 0x0800, ip_protocol: 1, icmpv4_type: 8)
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
| ether_type | 2048 |
| ip_protocol | 1 |
| icmpv4_type | 8 |
Scenario: new(ether_type: 0x0800, ip_protocol: 1, icmpv4_code: 0)
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::Match.new(ether_type: 0x0800, ip_protocol: 1, icmpv4_code: 0)
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
| ether_type | 2048 |
| ip_protocol | 1 |
| icmpv4_code | 0 |
Scenario: new(eth_type: 2054, arp_operation: 1)
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::Match.new(ether_type: 2054, arp_operation: 1)
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
| ether_type | 2054 |
| arp_operation | 1 |
Scenario: new(eth_type: 2054, arp_sender_protocol_address: '1.2.3.4')
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::Match.new(ether_type: 2054, arp_sender_protocol_address: '1.2.3.4')
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
| ether_type | 2054 |
| arp_sender_protocol_address | 1.2.3.4 |
Scenario: new(eth_type: 2054, arp_sender_protocol_address: '1.2.3.4', arp_sender_protocol_address_mask: '255.255.0.0')
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::Match.new(ether_type: 2054, arp_sender_protocol_address: '1.2.3.4', arp_sender_protocol_address_mask: '255.255.0.0')
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
| ether_type | 2054 |
| arp_sender_protocol_address | 1.2.3.4 |
| arp_sender_protocol_address_mask | 255.255.0.0 |
Scenario: new(eth_type: 2054, arp_target_protocol_address: '1.2.3.4')
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::Match.new(ether_type: 2054, arp_target_protocol_address: '1.2.3.4')
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
| ether_type | 2054 |
| arp_target_protocol_address | 1.2.3.4 |
Scenario: new(eth_type: 2054, arp_target_protocol_address: '1.2.3.4', arp_target_protocol_address_mask: '255.255.0.0')
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::Match.new(ether_type: 2054, arp_target_protocol_address: '1.2.3.4', arp_target_protocol_address_mask: '255.255.0.0')
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
| ether_type | 2054 |
| arp_target_protocol_address | 1.2.3.4 |
| arp_target_protocol_address_mask | 255.255.0.0 |
Scenario: new(eth_type: 2054, arp_sender_hardware_address: '11:22:33:44:55:66')
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::Match.new(ether_type: 2054, arp_sender_hardware_address: '11:22:33:44:55:66')
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
| ether_type | 2054 |
| arp_sender_hardware_address | 11:22:33:44:55:66 |
Scenario: new(eth_type: 2054, arp_sender_hardware_address: '11:22:33:44:55:66', arp_sender_hardware_address_mask: 'ff:ff:ff:00:00:00')
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::Match.new(ether_type: 2054, arp_sender_hardware_address: '11:22:33:44:55:66', arp_sender_hardware_address_mask: 'ff:ff:ff:00:00:00')
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
| ether_type | 2054 |
| arp_sender_hardware_address | 11:22:33:44:55:66 |
| arp_sender_hardware_address_mask | ff:ff:ff:00:00:00 |
Scenario: new(eth_type: 2054, arp_target_hardware_address: '11:22:33:44:55:66')
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::Match.new(ether_type: 2054, arp_target_hardware_address: '11:22:33:44:55:66')
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
| ether_type | 2054 |
| arp_target_hardware_address | 11:22:33:44:55:66 |
Scenario: new(eth_type: 2054, arp_target_hardware_address: '11:22:33:44:55:66', arp_target_hardware_address_mask: 'ff:ff:ff:00:00:00')
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::Match.new(ether_type: 2054, arp_target_hardware_address: '11:22:33:44:55:66', arp_target_hardware_address_mask: 'ff:ff:ff:00:00:00')
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
| ether_type | 2054 |
| arp_target_hardware_address | 11:22:33:44:55:66 |
| arp_target_hardware_address_mask | ff:ff:ff:00:00:00 |
Scenario: new(ether_type: 0x86dd, ipv6_source_address: '2001:db8:bd05:1d2:288a:1fc0:1:10ee')
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::Match.new(ether_type: 0x86dd, ipv6_source_address: '2001:db8:bd05:1d2:288a:1fc0:1:10ee')
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
| ether_type | 34525 |
| ipv6_source_address | 2001:db8:bd05:1d2:288a:1fc0:1:10ee |
Scenario: new(ether_type: 0x86dd, ipv6_source_address: '2001:db8:bd05:1d2:288a:1fc0:1:10ee', ipv6_source_address_mask: 'ffff:ffff:ffff:ffff::')
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::Match.new(ether_type: 0x86dd, ipv6_source_address: '2001:db8:bd05:1d2:288a:1fc0:1:10ee', ipv6_source_address_mask: 'ffff:ffff:ffff:ffff::')
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
| ether_type | 34525 |
| ipv6_source_address | 2001:db8:bd05:1d2:288a:1fc0:1:10ee |
| ipv6_source_address_mask | ffff:ffff:ffff:ffff:: |
Scenario: new(ether_type: 0x86dd, ipv6_destination_address: '2001:db8:bd05:1d2:288a:1fc0:1:10ee')
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::Match.new(ether_type: 0x86dd, ipv6_destination_address: '2001:db8:bd05:1d2:288a:1fc0:1:10ee')
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
| ether_type | 34525 |
| ipv6_destination_address | 2001:db8:bd05:1d2:288a:1fc0:1:10ee |
Scenario: new(ether_type: 0x86dd, ipv6_destination_address: '2001:db8:bd05:1d2:288a:1fc0:1:10ee', ipv6_destination_address_mask: 'ffff:ffff:ffff:ffff::')
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::Match.new(ether_type: 0x86dd, ipv6_destination_address: '2001:db8:bd05:1d2:288a:1fc0:1:10ee', ipv6_destination_address_mask: 'ffff:ffff:ffff:ffff::')
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
| ether_type | 34525 |
| ipv6_destination_address | 2001:db8:bd05:1d2:288a:1fc0:1:10ee |
| ipv6_destination_address_mask | ffff:ffff:ffff:ffff:: |
Scenario: new(tunnel_id: 1)
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::Match.new(tunnel_id: 1)
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
| tunnel_id | 1 |
Scenario: new(tunnel_id: 1, tunnel_id_mask: 9223372036854775808)
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::Match.new(tunnel_id: 1, tunnel_id_mask: 9223372036854775808)
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
| tunnel_id | 1 |
| tunnel_id_mask | 9223372036854775808 |
+ Scenario: new(packet_reg0: 1)
+ When I create an OpenFlow message with:
+ """
+ Pio::Match.new(packet_reg0: 1)
+ """
+ Then the message has the following fields and values:
+ | field | value |
+ | packet_reg0 | 1 |
+
+ Scenario: new(packet_reg0: 1, packet_reg0_mask: 1)
+ When I create an OpenFlow message with:
+ """
+ Pio::Match.new(packet_reg0: 1, packet_reg0_mask: 1)
+ """
+ Then the message has the following fields and values:
+ | field | value |
+ | packet_reg0 | 1 |
+ | packet_reg0_mask | 1 |
+
+ Scenario: new(packet_reg1: 1)
+ When I create an OpenFlow message with:
+ """
+ Pio::Match.new(packet_reg1: 1)
+ """
+ Then the message has the following fields and values:
+ | field | value |
+ | packet_reg1 | 1 |
+
+ Scenario: new(packet_reg1: 1, packet_reg1_mask: 1)
+ When I create an OpenFlow message with:
+ """
+ Pio::Match.new(packet_reg1: 1, packet_reg1_mask: 1)
+ """
+ Then the message has the following fields and values:
+ | field | value |
+ | packet_reg1 | 1 |
+ | packet_reg1_mask | 1 |
+
+ Scenario: new(packet_reg2: 1)
+ When I create an OpenFlow message with:
+ """
+ Pio::Match.new(packet_reg2: 1)
+ """
+ Then the message has the following fields and values:
+ | field | value |
+ | packet_reg2 | 1 |
+
+ Scenario: new(packet_reg2: 1, packet_reg2_mask: 1)
+ When I create an OpenFlow message with:
+ """
+ Pio::Match.new(packet_reg2: 1, packet_reg2_mask: 1)
+ """
+ Then the message has the following fields and values:
+ | field | value |
+ | packet_reg2 | 1 |
+ | packet_reg2_mask | 1 |
+
+ Scenario: new(packet_reg3: 1)
+ When I create an OpenFlow message with:
+ """
+ Pio::Match.new(packet_reg3: 1)
+ """
+ Then the message has the following fields and values:
+ | field | value |
+ | packet_reg3 | 1 |
+
+ Scenario: new(packet_reg3: 1, packet_reg3_mask: 1)
+ When I create an OpenFlow message with:
+ """
+ Pio::Match.new(packet_reg3: 1, packet_reg3_mask: 1)
+ """
+ Then the message has the following fields and values:
+ | field | value |
+ | packet_reg3 | 1 |
+ | packet_reg3_mask | 1 |
+
Scenario: read (file: open_flow13/oxm_no_fields.raw)
- When I try to parse a file named "open_flow13/oxm_no_fields.raw" with "Pio::Match" class
- Then it should finish successfully
- And the message has the following fields and values:
+ When I parse a file named "open_flow13/oxm_no_fields.raw" with "Pio::Match" class
+ Then the message has the following fields and values:
| field | value |
| match_fields | [] |
Scenario: read (file: open_flow13/oxm_in_port_field.raw)
- When I try to parse a file named "open_flow13/oxm_in_port_field.raw" with "Pio::Match" class
- Then it should finish successfully
- And the message has the following fields and values:
+ When I parse a file named "open_flow13/oxm_in_port_field.raw" with "Pio::Match" class
+ Then the message has the following fields and values:
| field | value |
| in_port | 1 |
Scenario: read (file: open_flow13/oxm_metadata_field.raw)
- When I try to parse a file named "open_flow13/oxm_metadata_field.raw" with "Pio::Match" class
- Then it should finish successfully
- And the message has the following fields and values:
+ When I parse a file named "open_flow13/oxm_metadata_field.raw" with "Pio::Match" class
+ Then the message has the following fields and values:
| field | value |
| metadata | 1 |
Scenario: read (file: open_flow13/oxm_metadata_masked_field.raw)
- When I try to parse a file named "open_flow13/oxm_metadata_masked_field.raw" with "Pio::Match" class
- Then it should finish successfully
- And the message has the following fields and values:
+ When I parse a file named "open_flow13/oxm_metadata_masked_field.raw" with "Pio::Match" class
+ Then the message has the following fields and values:
| field | value |
| metadata | 1 |
| metadata_mask | 18446744069414584320 |
Scenario: read (file: open_flow13/oxm_ether_destination_field.raw)
- When I try to parse a file named "open_flow13/oxm_ether_destination_field.raw" with "Pio::Match" class
- Then it should finish successfully
- And the message has the following fields and values:
+ When I parse a file named "open_flow13/oxm_ether_destination_field.raw" with "Pio::Match" class
+ Then the message has the following fields and values:
| field | value |
| destination_mac_address | ff:ff:ff:ff:ff:ff |
Scenario: read (file: open_flow13/oxm_ether_source_field.raw)
- When I try to parse a file named "open_flow13/oxm_ether_source_field.raw" with "Pio::Match" class
- Then it should finish successfully
- And the message has the following fields and values:
+ When I parse a file named "open_flow13/oxm_ether_source_field.raw" with "Pio::Match" class
+ Then the message has the following fields and values:
| field | value |
| source_mac_address | 01:02:03:04:05:06 |
Scenario: read (file: open_flow13/oxm_masked_ether_destination_field.raw)
- When I try to parse a file named "open_flow13/oxm_masked_ether_destination_field.raw" with "Pio::Match" class
- Then it should finish successfully
- And the message has the following fields and values:
+ When I parse a file named "open_flow13/oxm_masked_ether_destination_field.raw" with "Pio::Match" class
+ Then the message has the following fields and values:
| field | value |
| destination_mac_address | ff:ff:ff:ff:ff:ff |
| destination_mac_address_mask | ff:ff:ff:00:00:00 |
Scenario: read (file: open_flow13/oxm_masked_ether_source_field.raw)
- When I try to parse a file named "open_flow13/oxm_masked_ether_source_field.raw" with "Pio::Match" class
- Then it should finish successfully
- And the message has the following fields and values:
+ When I parse a file named "open_flow13/oxm_masked_ether_source_field.raw" with "Pio::Match" class
+ Then the message has the following fields and values:
| field | value |
| source_mac_address | 01:02:03:04:05:06 |
| source_mac_address_mask | ff:ff:ff:00:00:00 |
Scenario: read (file: open_flow13/oxm_ether_type_field.raw)
- When I try to parse a file named "open_flow13/oxm_ether_type_field.raw" with "Pio::Match" class
- Then it should finish successfully
- And the message has the following fields and values:
+ When I parse a file named "open_flow13/oxm_ether_type_field.raw" with "Pio::Match" class
+ Then the message has the following fields and values:
| field | value |
| ether_type | 0 |
Scenario: read (file: open_flow13/oxm_vlan_vid_field.raw)
- When I try to parse a file named "open_flow13/oxm_vlan_vid_field.raw" with "Pio::Match" class
- Then it should finish successfully
- And the message has the following fields and values:
+ When I parse a file named "open_flow13/oxm_vlan_vid_field.raw" with "Pio::Match" class
+ Then the message has the following fields and values:
| field | value |
| vlan_vid | 10 |
Scenario: read (file: open_flow13/oxm_vlan_pcp_field.raw)
- When I try to parse a file named "open_flow13/oxm_vlan_pcp_field.raw" with "Pio::Match" class
- Then it should finish successfully
- And the message has the following fields and values:
+ When I parse a file named "open_flow13/oxm_vlan_pcp_field.raw" with "Pio::Match" class
+ Then the message has the following fields and values:
| field | value |
| vlan_vid | 10 |
| vlan_pcp | 5 |
Scenario: read (file: open_flow13/oxm_ip_dscp_field.raw)
- When I try to parse a file named "open_flow13/oxm_ip_dscp_field.raw" with "Pio::Match" class
- Then it should finish successfully
- And the message has the following fields and values:
+ When I parse a file named "open_flow13/oxm_ip_dscp_field.raw" with "Pio::Match" class
+ Then the message has the following fields and values:
| field | value |
| ether_type | 2048 |
| ip_dscp | 46 |
Scenario: read (file: open_flow13/oxm_ip_ecn_field.raw)
- When I try to parse a file named "open_flow13/oxm_ip_ecn_field.raw" with "Pio::Match" class
- Then it should finish successfully
- And the message has the following fields and values:
+ When I parse a file named "open_flow13/oxm_ip_ecn_field.raw" with "Pio::Match" class
+ Then the message has the following fields and values:
| field | value |
| ether_type | 2048 |
| ip_ecn | 3 |
Scenario: read (file: open_flow13/oxm_ipv4_source_field.raw)
- When I try to parse a file named "open_flow13/oxm_ipv4_source_field.raw" with "Pio::Match" class
- Then it should finish successfully
- And the message has the following fields and values:
+ When I parse a file named "open_flow13/oxm_ipv4_source_field.raw" with "Pio::Match" class
+ Then the message has the following fields and values:
| field | value |
| ether_type | 2048 |
| ipv4_source_address | 1.2.3.4 |
Scenario: read (file: open_flow13/oxm_ipv4_destination_field.raw)
- When I try to parse a file named "open_flow13/oxm_ipv4_destination_field.raw" with "Pio::Match" class
- Then it should finish successfully
- And the message has the following fields and values:
+ When I parse a file named "open_flow13/oxm_ipv4_destination_field.raw" with "Pio::Match" class
+ Then the message has the following fields and values:
| field | value |
| ether_type | 2048 |
| ipv4_destination_address | 11.22.33.44 |
Scenario: read (file: open_flow13/oxm_masked_ipv4_source_field.raw)
- When I try to parse a file named "open_flow13/oxm_masked_ipv4_source_field.raw" with "Pio::Match" class
- Then it should finish successfully
- And the message has the following fields and values:
+ When I parse a file named "open_flow13/oxm_masked_ipv4_source_field.raw" with "Pio::Match" class
+ Then the message has the following fields and values:
| field | value |
| ether_type | 2048 |
| ipv4_source_address | 1.2.3.4 |
| ipv4_source_address_mask | 255.255.0.0 |
Scenario: read (file: open_flow13/oxm_masked_ipv4_destination_field.raw)
- When I try to parse a file named "open_flow13/oxm_masked_ipv4_destination_field.raw" with "Pio::Match" class
- Then it should finish successfully
- And the message has the following fields and values:
+ When I parse a file named "open_flow13/oxm_masked_ipv4_destination_field.raw" with "Pio::Match" class
+ Then the message has the following fields and values:
| field | value |
| ether_type | 2048 |
| ipv4_destination_address | 11.22.33.44 |
| ipv4_destination_address_mask | 255.255.255.0 |
Scenario: read (file: open_flow13/oxm_tcp_source_field.raw)
- When I try to parse a file named "open_flow13/oxm_tcp_source_field.raw" with "Pio::Match" class
- Then it should finish successfully
- And the message has the following fields and values:
+ When I parse a file named "open_flow13/oxm_tcp_source_field.raw" with "Pio::Match" class
+ Then the message has the following fields and values:
| field | value |
| ether_type | 2048 |
| ip_protocol | 6 |
| tcp_source_port | 1111 |
Scenario: read (file: open_flow13/oxm_tcp_destination_field.raw)
- When I try to parse a file named "open_flow13/oxm_tcp_destination_field.raw" with "Pio::Match" class
- Then it should finish successfully
- And the message has the following fields and values:
+ When I parse a file named "open_flow13/oxm_tcp_destination_field.raw" with "Pio::Match" class
+ Then the message has the following fields and values:
| field | value |
| ether_type | 2048 |
| ip_protocol | 6 |
| tcp_destination_port | 80 |
Scenario: read (file: open_flow13/oxm_udp_source_field.raw)
- When I try to parse a file named "open_flow13/oxm_udp_source_field.raw" with "Pio::Match" class
- Then it should finish successfully
- And the message has the following fields and values:
+ When I parse a file named "open_flow13/oxm_udp_source_field.raw" with "Pio::Match" class
+ Then the message has the following fields and values:
| field | value |
| ether_type | 2048 |
| ip_protocol | 17 |
| udp_source_port | 2222 |
Scenario: read (file: open_flow13/oxm_udp_destination_field.raw)
- When I try to parse a file named "open_flow13/oxm_udp_destination_field.raw" with "Pio::Match" class
- Then it should finish successfully
- And the message has the following fields and values:
+ When I parse a file named "open_flow13/oxm_udp_destination_field.raw" with "Pio::Match" class
+ Then the message has the following fields and values:
| field | value |
| ether_type | 2048 |
| ip_protocol | 17 |
| udp_destination_port | 3333 |
Scenario: read (file: open_flow13/oxm_sctp_source_field.raw)
- When I try to parse a file named "open_flow13/oxm_sctp_source_field.raw" with "Pio::Match" class
- Then it should finish successfully
- And the message has the following fields and values:
+ When I parse a file named "open_flow13/oxm_sctp_source_field.raw" with "Pio::Match" class
+ Then the message has the following fields and values:
| field | value |
| ether_type | 2048 |
| ip_protocol | 132 |
| sctp_source_port | 22 |
Scenario: read (file: open_flow13/oxm_sctp_destination_field.raw)
- When I try to parse a file named "open_flow13/oxm_sctp_destination_field.raw" with "Pio::Match" class
- Then it should finish successfully
- And the message has the following fields and values:
+ When I parse a file named "open_flow13/oxm_sctp_destination_field.raw" with "Pio::Match" class
+ Then the message has the following fields and values:
| field | value |
| ether_type | 2048 |
| ip_protocol | 132 |
| sctp_destination_port | 22 |
Scenario: read (file: open_flow13/oxm_icmpv4_type_field.raw)
- When I try to parse a file named "open_flow13/oxm_icmpv4_type_field.raw" with "Pio::Match" class
- Then it should finish successfully
- And the message has the following fields and values:
+ When I parse a file named "open_flow13/oxm_icmpv4_type_field.raw" with "Pio::Match" class
+ Then the message has the following fields and values:
| field | value |
| ether_type | 2048 |
| ip_protocol | 1 |
| icmpv4_type | 8 |
Scenario: read (file: open_flow13/oxm_icmpv4_code_field.raw)
- When I try to parse a file named "open_flow13/oxm_icmpv4_code_field.raw" with "Pio::Match" class
- Then it should finish successfully
- And the message has the following fields and values:
+ When I parse a file named "open_flow13/oxm_icmpv4_code_field.raw" with "Pio::Match" class
+ Then the message has the following fields and values:
| field | value |
| ether_type | 2048 |
| ip_protocol | 1 |
| icmpv4_code | 0 |
Scenario: read (file: open_flow13/oxm_arp_op_field.raw)
- When I try to parse a file named "open_flow13/oxm_arp_op_field.raw" with "Pio::Match" class
- Then it should finish successfully
- And the message has the following fields and values:
+ When I parse a file named "open_flow13/oxm_arp_op_field.raw" with "Pio::Match" class
+ Then the message has the following fields and values:
| field | value |
| ether_type | 2054 |
| arp_operation | 1 |
Scenario: read (file: open_flow13/oxm_arp_spa_field.raw)
- When I try to parse a file named "open_flow13/oxm_arp_spa_field.raw" with "Pio::Match" class
- Then it should finish successfully
- And the message has the following fields and values:
+ When I parse a file named "open_flow13/oxm_arp_spa_field.raw" with "Pio::Match" class
+ Then the message has the following fields and values:
| field | value |
| ether_type | 2054 |
| arp_sender_protocol_address | 1.2.3.4 |
Scenario: read (file: open_flow13/oxm_masked_arp_spa_field.raw)
- When I try to parse a file named "open_flow13/oxm_masked_arp_spa_field.raw" with "Pio::Match" class
- Then it should finish successfully
- And the message has the following fields and values:
+ When I parse a file named "open_flow13/oxm_masked_arp_spa_field.raw" with "Pio::Match" class
+ Then the message has the following fields and values:
| field | value |
| ether_type | 2054 |
| arp_sender_protocol_address | 1.2.3.4 |
| arp_sender_protocol_address_mask | 255.255.0.0 |
Scenario: read (file: open_flow13/oxm_arp_tpa_field.raw)
- When I try to parse a file named "open_flow13/oxm_arp_tpa_field.raw" with "Pio::Match" class
- Then it should finish successfully
- And the message has the following fields and values:
+ When I parse a file named "open_flow13/oxm_arp_tpa_field.raw" with "Pio::Match" class
+ Then the message has the following fields and values:
| field | value |
| ether_type | 2054 |
| arp_target_protocol_address | 1.2.3.4 |
Scenario: read (file: open_flow13/oxm_masked_arp_tpa_field.raw)
- When I try to parse a file named "open_flow13/oxm_masked_arp_tpa_field.raw" with "Pio::Match" class
- Then it should finish successfully
- And the message has the following fields and values:
+ When I parse a file named "open_flow13/oxm_masked_arp_tpa_field.raw" with "Pio::Match" class
+ Then the message has the following fields and values:
| field | value |
| ether_type | 2054 |
| arp_target_protocol_address | 1.2.3.4 |
| arp_target_protocol_address_mask | 255.255.0.0 |
Scenario: read (file: open_flow13/oxm_arp_sha_field.raw)
- When I try to parse a file named "open_flow13/oxm_arp_sha_field.raw" with "Pio::Match" class
- Then it should finish successfully
- And the message has the following fields and values:
+ When I parse a file named "open_flow13/oxm_arp_sha_field.raw" with "Pio::Match" class
+ Then the message has the following fields and values:
| field | value |
| ether_type | 2054 |
| arp_sender_hardware_address | 11:22:33:44:55:66 |
Scenario: read (file: open_flow13/oxm_masked_arp_sha_field.raw)
- When I try to parse a file named "open_flow13/oxm_masked_arp_sha_field.raw" with "Pio::Match" class
- Then it should finish successfully
- And the message has the following fields and values:
+ When I parse a file named "open_flow13/oxm_masked_arp_sha_field.raw" with "Pio::Match" class
+ Then the message has the following fields and values:
| field | value |
| ether_type | 2054 |
| arp_sender_hardware_address | 11:22:33:44:55:66 |
| arp_sender_hardware_address_mask | ff:ff:ff:ff:ff:ff |
Scenario: read (file: open_flow13/oxm_arp_tha_field.raw)
- When I try to parse a file named "open_flow13/oxm_arp_tha_field.raw" with "Pio::Match" class
- Then it should finish successfully
- And the message has the following fields and values:
+ When I parse a file named "open_flow13/oxm_arp_tha_field.raw" with "Pio::Match" class
+ Then the message has the following fields and values:
| field | value |
| ether_type | 2054 |
| arp_target_hardware_address | 11:22:33:44:55:66 |
Scenario: read (file: open_flow13/oxm_masked_arp_tha_field.raw)
- When I try to parse a file named "open_flow13/oxm_masked_arp_tha_field.raw" with "Pio::Match" class
- Then it should finish successfully
- And the message has the following fields and values:
+ When I parse a file named "open_flow13/oxm_masked_arp_tha_field.raw" with "Pio::Match" class
+ Then the message has the following fields and values:
| field | value |
| ether_type | 2054 |
| arp_target_hardware_address | 11:22:33:44:55:66 |
| arp_target_hardware_address_mask | ff:ff:ff:ff:ff:ff |
Scenario: read (file: open_flow13/oxm_ipv6_source_field.raw)
- When I try to parse a file named "open_flow13/oxm_ipv6_source_field.raw" with "Pio::Match" class
- Then it should finish successfully
- And the message has the following fields and values:
+ When I parse a file named "open_flow13/oxm_ipv6_source_field.raw" with "Pio::Match" class
+ Then the message has the following fields and values:
| field | value |
| ether_type | 34525 |
| ipv6_source_address | 2001:db8:bd05:1d2:288a:1fc0:1:10ee |
Scenario: read (file: open_flow13/oxm_masked_ipv6_source_field.raw)
- When I try to parse a file named "open_flow13/oxm_masked_ipv6_source_field.raw" with "Pio::Match" class
- Then it should finish successfully
- And the message has the following fields and values:
+ When I parse a file named "open_flow13/oxm_masked_ipv6_source_field.raw" with "Pio::Match" class
+ Then the message has the following fields and values:
| field | value |
| ether_type | 34525 |
| ipv6_source_address | 2001:db8:bd05:1d2:288a:1fc0:1:10ee |
| ipv6_source_address_mask | ffff:ffff:ffff:ffff:: |
Scenario: read (file: open_flow13/oxm_ipv6_destination_field.raw)
- When I try to parse a file named "open_flow13/oxm_ipv6_destination_field.raw" with "Pio::Match" class
- Then it should finish successfully
- And the message has the following fields and values:
+ When I parse a file named "open_flow13/oxm_ipv6_destination_field.raw" with "Pio::Match" class
+ Then the message has the following fields and values:
| field | value |
| ether_type | 34525 |
| ipv6_destination_address | 2001:db8:bd05:1d2:288a:1fc0:1:10ee |
Scenario: read (file: open_flow13/oxm_masked_ipv6_destination_field.raw)
- When I try to parse a file named "open_flow13/oxm_masked_ipv6_destination_field.raw" with "Pio::Match" class
- Then it should finish successfully
- And the message has the following fields and values:
+ When I parse a file named "open_flow13/oxm_masked_ipv6_destination_field.raw" with "Pio::Match" class
+ Then the message has the following fields and values:
| field | value |
| ether_type | 34525 |
| ipv6_destination_address | 2001:db8:bd05:1d2:288a:1fc0:1:10ee |
| ipv6_destination_address_mask | ffff:ffff:ffff:ffff:: |
Scenario: read (file: open_flow13/oxm_tunnel_id_field.raw)
- When I try to parse a file named "open_flow13/oxm_tunnel_id_field.raw" with "Pio::Match" class
- Then it should finish successfully
- And the message has the following fields and values:
+ When I parse a file named "open_flow13/oxm_tunnel_id_field.raw" with "Pio::Match" class
+ Then the message has the following fields and values:
| field | value |
| tunnel_id | 1 |
Scenario: read (file: open_flow13/oxm_masked_tunnel_id_field.raw)
- When I try to parse a file named "open_flow13/oxm_masked_tunnel_id_field.raw" with "Pio::Match" class
- Then it should finish successfully
- And the message has the following fields and values:
+ When I parse a file named "open_flow13/oxm_masked_tunnel_id_field.raw" with "Pio::Match" class
+ Then the message has the following fields and values:
| field | value |
| tunnel_id | 1 |
| tunnel_id_mask | 9223372036854775808 |
- Scenario: read (file: open_flow13/oxm_invalid_field.raw)
- When I try to parse a file named "open_flow13/oxm_invalid_field.raw" with "Pio::Match" class
- Then it should fail with "RuntimeError", "Unknown OXM field value: 40"
-
Scenario: read (file: open_flow13/oxm_experimenter_stratos_basic_dot11.raw)
- When I try to parse a file named "open_flow13/oxm_experimenter_stratos_basic_dot11.raw" with "Pio::Match" class
- Then it should finish successfully
- And the message has the following fields and values:
+ When I parse a file named "open_flow13/oxm_experimenter_stratos_basic_dot11.raw" with "Pio::Match" class
+ Then the message has the following fields and values:
| field | value |
| match_fields.at(0).oxm_field | 0 |
| match_fields.at(0).experimenter | 4278247501 |
diff --git a/features/open_flow13/meter.feature b/features/open_flow13/meter.feature
index 870d7066..2f6dc15c 100644
--- a/features/open_flow13/meter.feature
+++ b/features/open_flow13/meter.feature
@@ -1,26 +1,24 @@
@open_flow13
-Feature: Pio::Meter
+Feature: Meter
Scenario: new(1)
- When I try to create an OpenFlow instruction with:
+ When I create an OpenFlow instruction with:
"""
- Pio::Meter.new(1)
+ Pio::OpenFlow13::Meter.new(1)
"""
- Then it should finish successfully
- And the message has the following fields and values:
- | field | value |
- | class | Pio::Meter |
- | instruction_type | 6 |
- | instruction_length | 8 |
- | to_binary_s.length | 8 |
- | meter_id | 1 |
+ Then the message has the following fields and values:
+ | field | value |
+ | class | Pio::OpenFlow13::Meter |
+ | instruction_type | 6 |
+ | instruction_length | 8 |
+ | to_binary_s.length | 8 |
+ | meter_id | 1 |
Scenario: read
- When I try to parse a file named "open_flow13/instruction_meter.raw" with "Pio::Meter" class
- Then it should finish successfully
- And the message has the following fields and values:
- | field | value |
- | class | Pio::Meter |
- | instruction_type | 6 |
- | instruction_length | 8 |
- | to_binary_s.length | 8 |
- | meter_id | 1 |
+ When I parse a file named "open_flow13/instruction_meter.raw" with "Pio::OpenFlow13::Meter" class
+ Then the message has the following fields and values:
+ | field | value |
+ | class | Pio::OpenFlow13::Meter |
+ | instruction_type | 6 |
+ | instruction_length | 8 |
+ | to_binary_s.length | 8 |
+ | meter_id | 1 |
diff --git a/features/open_flow13/nicira_conjunction.feature b/features/open_flow13/nicira_conjunction.feature
new file mode 100644
index 00000000..4faacee7
--- /dev/null
+++ b/features/open_flow13/nicira_conjunction.feature
@@ -0,0 +1,15 @@
+@open_flow13
+Feature: NiciraConjunction
+
+ Provides conjunctive flow match.
+
+ Scenario: new(clause: 1, n_clauses: 2, conjunction_id: 1)
+ When I create an OpenFlow action with:
+ """
+ Pio::NiciraConjunction.new(clause: 1, n_clauses: 2, conjunction_id: 1)
+ """
+ Then the action has the following fields and values:
+ | field | value |
+ | clause | 1 |
+ | n_clauses | 2 |
+ | conjunction_id | 1 |
diff --git a/features/open_flow13/nicira_reg_load.feature b/features/open_flow13/nicira_reg_load.feature
index 8c3629fc..d168e5c9 100644
--- a/features/open_flow13/nicira_reg_load.feature
+++ b/features/open_flow13/nicira_reg_load.feature
@@ -1,56 +1,41 @@
@open_flow13
-Feature: Pio::NiciraRegLoad
+Feature: NiciraRegLoad
+
+ Copies value[0:n_bits] to destination[ofs:ofs+n_bits], where a[b:c]
+ denotes the bits within 'a' numbered 'b' through 'c' (not including bit 'c').
Scenario: new(0xdeadbeef, :reg0)
- When I try to create an OpenFlow action with:
+ When I create an OpenFlow action with:
"""
Pio::NiciraRegLoad.new(0xdeadbeef, :reg0)
"""
- Then it should finish successfully
- And the action has the following fields and values:
- | field | value |
- | action_type.to_hex | 0xffff |
- | action_length | 24 |
- | experimenter_id.to_hex | 0x2320 |
- | experimenter_type | 7 |
- | offset | 0 |
- | n_bits | 32 |
- | destination | :reg0 |
- | destination_internal | 65540 |
- | value.to_hex | 0xdeadbeef |
+ Then the action has the following fields and values:
+ | field | value |
+ | offset | 0 |
+ | n_bits | 32 |
+ | destination | :reg0 |
+ | value.to_hex | 0xdeadbeef |
Scenario: new(0xdeadbeef, :metadata)
- When I try to create an OpenFlow action with:
+ When I create an OpenFlow action with:
"""
Pio::NiciraRegLoad.new(0xdeadbeef, :metadata)
"""
- Then it should finish successfully
- And the action has the following fields and values:
- | field | value |
- | action_type.to_hex | 0xffff |
- | action_length | 24 |
- | experimenter_id.to_hex | 0x2320 |
- | experimenter_type | 7 |
- | offset | 0 |
- | n_bits | 64 |
- | destination | :metadata |
- | destination_internal | 2147484680 |
- | value.to_hex | 0xdeadbeef |
+ Then the action has the following fields and values:
+ | field | value |
+ | offset | 0 |
+ | n_bits | 64 |
+ | destination | :metadata |
+ | value.to_hex | 0xdeadbeef |
- Scenario: new(0xdeadbeef, :metadata, offset: 32, n_bits: 32)
- When I try to create an OpenFlow action with:
+ Scenario: new(0xdeadbeef, :reg0, offset: 16, n_bits: 16)
+ When I create an OpenFlow action with:
"""
- Pio::NiciraRegLoad.new(0xdeadbeef, :metadata, offset: 32, n_bits: 32)
+ Pio::NiciraRegLoad.new(0xdeadbeef, :reg0, offset: 16, n_bits: 16)
"""
- Then it should finish successfully
- And the action has the following fields and values:
- | field | value |
- | action_type.to_hex | 0xffff |
- | action_length | 24 |
- | experimenter_id.to_hex | 0x2320 |
- | experimenter_type | 7 |
- | offset | 32 |
- | n_bits | 32 |
- | destination | :metadata |
- | destination_internal | 2147484680 |
- | value.to_hex | 0xdeadbeef |
+ Then the action has the following fields and values:
+ | field | value |
+ | offset | 16 |
+ | n_bits | 16 |
+ | destination | :reg0 |
+ | value.to_hex | 0xdeadbeef |
diff --git a/features/open_flow13/nicira_reg_move.feature b/features/open_flow13/nicira_reg_move.feature
index 490e846d..6f31a447 100644
--- a/features/open_flow13/nicira_reg_move.feature
+++ b/features/open_flow13/nicira_reg_move.feature
@@ -1,42 +1,48 @@
@open_flow13
-Feature: Pio::NiciraRegMove
+Feature: NiciraRegMove
- Scenario: new(from: :arp_sender_hardware_address, to: :arp_target_hardware_address)
- When I try to create an OpenFlow action with:
+ Copies source[source_offset:sourcce_offset+n_bits] to
+ destination[destination_offset:dst_ofs+n_bits], where a[b:c] denotes
+ the bits within 'a' numbered 'b' through 'c' (not including bit 'c').
+
+ Scenario: new(source: :arp_sender_hardware_address, destination: :arp_target_hardware_address)
+ When I create an OpenFlow action with:
"""
- Pio::NiciraRegMove.new(from: :arp_sender_hardware_address, to: :arp_target_hardware_address)
+ Pio::NiciraRegMove.new(source: :arp_sender_hardware_address,
+ destination: :arp_target_hardware_address)
"""
- Then it should finish successfully
- And the action has the following fields and values:
- | field | value |
- | action_type.to_hex | 0xffff |
- | action_length | 24 |
- | experimenter_id.to_hex | 0x2320 |
- | experimenter_type | 6 |
- | from | :arp_sender_hardware_address |
- | source_oxm_field | 24 |
- | source_oxm_length | 6 |
- | to | :arp_target_hardware_address |
- | destination_oxm_field | 25 |
- | destination_oxm_length | 6 |
+ Then the action has the following fields and values:
+ | field | value |
+ | n_bits | 48 |
+ | source | :arp_sender_hardware_address |
+ | source_offset | 0 |
+ | destination | :arp_target_hardware_address |
+ | destination_offset | 0 |
- Scenario: new(from: :reg0, to: :reg7)
- When I try to create an OpenFlow action with:
+ Scenario: new(source: :reg0, destination: :reg7)
+ When I create an OpenFlow action with:
+ """
+ Pio::NiciraRegMove.new(source: :reg0, destination: :reg7)
+ """
+ Then the action has the following fields and values:
+ | field | value |
+ | n_bits | 32 |
+ | source | :reg0 |
+ | source_offset | 0 |
+ | destination | :reg7 |
+ | destination_offset | 0 |
+
+ Scenario: new(source: :reg0, source_offset: 16, destination: :reg7, destination_offset: 16, n_bits: 16)
+ When I create an OpenFlow action with:
"""
- Pio::NiciraRegMove.new(from: :reg0, to: :reg7)
+ Pio::NiciraRegMove.new(source: :reg0, source_offset: 16,
+ destination: :reg7, destination_offset: 16,
+ n_bits: 16)
"""
- Then it should finish successfully
- And the action has the following fields and values:
- | field | value |
- | action_type.to_hex | 0xffff |
- | action_length | 24 |
- | experimenter_id.to_hex | 0x2320 |
- | experimenter_type | 6 |
- | from | :reg0 |
- | source_oxm_class | 1 |
- | source_oxm_field | 0 |
- | source_oxm_length | 4 |
- | to | :reg7 |
- | destination_oxm_class | 1 |
- | destination_oxm_field | 7 |
- | destination_oxm_length | 4 |
+ Then the action has the following fields and values:
+ | field | value |
+ | n_bits | 16 |
+ | source | :reg0 |
+ | source_offset | 16 |
+ | destination | :reg7 |
+ | destination_offset | 16 |
diff --git a/features/open_flow13/nicira_send_out_port.feature b/features/open_flow13/nicira_send_out_port.feature
index 8ddf50c8..0e84c38a 100644
--- a/features/open_flow13/nicira_send_out_port.feature
+++ b/features/open_flow13/nicira_send_out_port.feature
@@ -1,20 +1,28 @@
@open_flow13
-Feature: Pio::NiciraSendOutPort
+Feature: NiciraSendOutPort
+
+ Outputs to the OpenFlow port number written to source[offset:offset+n_bits]
Scenario: new(:reg0)
- When I try to create an OpenFlow action with:
+ When I create an OpenFlow action with:
"""
Pio::NiciraSendOutPort.new(:reg0)
"""
- Then it should finish successfully
- And the action has the following fields and values:
- | field | value |
- | action_type.to_hex | 0xffff |
- | action_length | 24 |
- | experimenter_id.to_hex | 0x2320 |
- | experimenter_type | 15 |
- | offset | 0 |
- | source | :reg0 |
- | max_length | 0 |
+ Then the action has the following fields and values:
+ | field | value |
+ | offset | 0 |
+ | n_bits | 32 |
+ | source | :reg0 |
+ | max_length.to_hex | 0xffff |
-
+ Scenario: new(:reg0, offset: 16, n_bits: 16, max_length: 256)
+ When I create an OpenFlow action with:
+ """
+ Pio::NiciraSendOutPort.new(:reg0, offset: 16, n_bits: 16, max_length: 256)
+ """
+ Then the action has the following fields and values:
+ | field | value |
+ | offset | 16 |
+ | n_bits | 16 |
+ | source | :reg0 |
+ | max_length | 256 |
diff --git a/features/open_flow13/nicira_stack_pop.feature b/features/open_flow13/nicira_stack_pop.feature
new file mode 100644
index 00000000..8c86c79c
--- /dev/null
+++ b/features/open_flow13/nicira_stack_pop.feature
@@ -0,0 +1,26 @@
+@open_flow13
+Feature: NiciraStackPop
+
+ Pops field[offset: offset + n_bits] from top of the stack.
+
+ Scenario: new(:reg0)
+ When I create an OpenFlow action with:
+ """
+ Pio::NiciraStackPop.new(:reg0)
+ """
+ Then the action has the following fields and values:
+ | field | value |
+ | offset | 0 |
+ | n_bits | 32 |
+ | field | :reg0 |
+
+ Scenario: new(:reg0, n_bits: 16, offset: 16)
+ When I create an OpenFlow action with:
+ """
+ Pio::NiciraStackPop.new(:reg0, n_bits: 16, offset: 16)
+ """
+ Then the action has the following fields and values:
+ | field | value |
+ | offset | 16 |
+ | n_bits | 16 |
+ | field | :reg0 |
diff --git a/features/open_flow13/nicira_stack_push.feature b/features/open_flow13/nicira_stack_push.feature
new file mode 100644
index 00000000..97888bfa
--- /dev/null
+++ b/features/open_flow13/nicira_stack_push.feature
@@ -0,0 +1,26 @@
+@open_flow13
+Feature: NiciraStackPush
+
+ Pushes field[offset: offset + n_bits] to top of the stack.
+
+ Scenario: new(:reg0)
+ When I create an OpenFlow action with:
+ """
+ Pio::NiciraStackPush.new(:reg0)
+ """
+ Then the action has the following fields and values:
+ | field | value |
+ | offset | 0 |
+ | n_bits | 32 |
+ | field | :reg0 |
+
+ Scenario: new(:reg0, n_bits: 16, offset: 16)
+ When I create an OpenFlow action with:
+ """
+ Pio::NiciraStackPush.new(:reg0, n_bits: 16, offset: 16)
+ """
+ Then the action has the following fields and values:
+ | field | value |
+ | offset | 16 |
+ | n_bits | 16 |
+ | field | :reg0 |
diff --git a/features/open_flow13/packet_in.feature b/features/open_flow13/packet_in.feature
index 74f00e07..0add0887 100644
--- a/features/open_flow13/packet_in.feature
+++ b/features/open_flow13/packet_in.feature
@@ -1,16 +1,13 @@
@open_flow13
-Feature: Pio::PacketIn
+Feature: PacketIn
Scenario: new
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::PacketIn.new
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
- | ofp_version | 4 |
- | message_type | 10 |
- | message_length | 34 |
+ | version | 4 |
| transaction_id | 0 |
| xid | 0 |
| buffer_id | 0 |
@@ -22,7 +19,7 @@ Feature: Pio::PacketIn
| raw_data.length | 0 |
Scenario: new (raw_data = ARP request)
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
data_dump = [
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xac, 0x5d, 0x10, 0x31, 0x37,
@@ -35,11 +32,9 @@ Feature: Pio::PacketIn
Pio::PacketIn.new(raw_data: data_dump)
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
- | ofp_version | 4 |
- | message_length | 94 |
+ | version | 4 |
| transaction_id | 0 |
| xid | 0 |
| buffer_id | 0 |
@@ -51,25 +46,3 @@ Feature: Pio::PacketIn
| raw_data.length | 60 |
| source_mac | ac:5d:10:31:37:79 |
| destination_mac | ff:ff:ff:ff:ff:ff |
-
- Scenario: read
- When I try to parse a file named "open_flow13/packet_in.raw" with "PacketIn" class
- Then it should finish successfully
- And the message has the following fields and values:
- | field | value |
- | ofp_version | 4 |
- | message_type | 10 |
- | message_length | 102 |
- | transaction_id | 123 |
- | xid | 123 |
- | buffer_id.to_hex | 0xcafebabe |
- | total_len | 60 |
- | in_port | 1 |
- | reason | :no_match |
- | table_id | 0 |
- | cookie | 0 |
- | match.match_fields.size | 1 |
- | match.match_fields[0].in_port | 1 |
- | raw_data.length | 60 |
- | source_mac | ac:5d:10:31:37:79 |
- | destination_mac | ff:ff:ff:ff:ff:ff |
diff --git a/features/open_flow13/packet_out.feature b/features/open_flow13/packet_out.feature
index 0aa520ae..f7caf636 100644
--- a/features/open_flow13/packet_out.feature
+++ b/features/open_flow13/packet_out.feature
@@ -1,16 +1,13 @@
@open_flow13
-Feature: Pio::PacketOut
+Feature: PacketOut
Scenario: new
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::PacketOut.new
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
- | ofp_version | 4 |
- | message_type | 13 |
- | message_length | 24 |
+ | version | 4 |
| to_binary.length | 24 |
| transaction_id | 0 |
| xid | 0 |
@@ -20,7 +17,7 @@ Feature: Pio::PacketOut
| raw_data.length | 0 |
Scenario: new (actions = SendOutPort(1), raw_data = ARP Request)
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
data_dump = [
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xac, 0x5d, 0x10, 0x31, 0x37,
@@ -33,12 +30,9 @@ Feature: Pio::PacketOut
Pio::PacketOut.new(raw_data: data_dump, actions: Pio::SendOutPort.new(1))
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
- | ofp_version | 4 |
- | message_type | 13 |
- | message_length | 100 |
+ | version | 4 |
| to_binary.length | 100 |
| transaction_id | 0 |
| xid | 0 |
@@ -59,35 +53,3 @@ Feature: Pio::PacketOut
| sender_protocol_address | 192.168.2.254 |
| target_hardware_address | ff:ff:ff:ff:ff:ff |
| target_protocol_address | 192.168.2.5 |
-
- Scenario: read
- When I try to parse a file named "open_flow13/packet_out.raw" with "PacketOut" class
- Then it should finish successfully
- And the message has the following fields and values:
- | field | value |
- | ofp_version | 4 |
- | message_type | 13 |
- | message_length | 100 |
- | transaction_id | 123 |
- | xid | 123 |
- | buffer_id | :no_buffer |
- | in_port | :controller |
- | actions_length | 16 |
- | actions.first.class | Pio::OpenFlow13::SendOutPort |
- | actions.first.port | 1 |
- | actions.first.max_length | :no_buffer |
- | raw_data.length | 60 |
- | data.class | Pio::Arp::Request |
- | destination_mac | ff:ff:ff:ff:ff:ff |
- | source_mac | ac:5d:10:31:37:79 |
- | ether_type | 2054 |
- | hardware_type | 1 |
- | protocol_type | 2048 |
- | hardware_length | 6 |
- | protocol_length | 4 |
- | operation | 1 |
- | sender_hardware_address | ac:5d:10:31:37:79 |
- | sender_protocol_address | 192.168.2.254 |
- | target_hardware_address | ff:ff:ff:ff:ff:ff |
- | target_protocol_address | 192.168.2.5 |
-
diff --git a/features/open_flow13/send_out_port.feature b/features/open_flow13/send_out_port.feature
index ad7f136e..987243c1 100644
--- a/features/open_flow13/send_out_port.feature
+++ b/features/open_flow13/send_out_port.feature
@@ -1,12 +1,11 @@
@open_flow13
-Feature: Pio::SendOutPort
+Feature: SendOutPort
Scenario: new(1)
- When I try to create an OpenFlow action with:
+ When I create an OpenFlow action with:
"""
Pio::SendOutPort.new(1)
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
| action_type | 0 |
| action_length | 16 |
@@ -14,12 +13,11 @@ Feature: Pio::SendOutPort
| max_length | :no_buffer |
Scenario: new(:all)
- When I try to create an OpenFlow action with:
+ When I create an OpenFlow action with:
"""
Pio::SendOutPort.new(:all)
"""
- Then it should finish successfully
- And the action has the following fields and values:
+ Then the action has the following fields and values:
| field | value |
| action_type | 0 |
| action_length | 16 |
@@ -27,12 +25,11 @@ Feature: Pio::SendOutPort
| max_length | :no_buffer |
Scenario: new(:controller)
- When I try to create an OpenFlow action with:
+ When I create an OpenFlow action with:
"""
Pio::SendOutPort.new(:controller)
"""
- Then it should finish successfully
- And the action has the following fields and values:
+ Then the action has the following fields and values:
| field | value |
| action_type | 0 |
| action_length | 16 |
@@ -40,12 +37,11 @@ Feature: Pio::SendOutPort
| max_length | :no_buffer |
Scenario: new(:local)
- When I try to create an OpenFlow action with:
+ When I create an OpenFlow action with:
"""
Pio::SendOutPort.new(:local)
"""
- Then it should finish successfully
- And the action has the following fields and values:
+ Then the action has the following fields and values:
| field | value |
| action_type | 0 |
| action_length | 16 |
@@ -53,12 +49,11 @@ Feature: Pio::SendOutPort
| max_length | :no_buffer |
Scenario: new(:table)
- When I try to create an OpenFlow action with:
+ When I create an OpenFlow action with:
"""
Pio::SendOutPort.new(:table)
"""
- Then it should finish successfully
- And the action has the following fields and values:
+ Then the action has the following fields and values:
| field | value |
| action_type | 0 |
| action_length | 16 |
@@ -66,12 +61,11 @@ Feature: Pio::SendOutPort
| max_length | :no_buffer |
Scenario: new(:in_port)
- When I try to create an OpenFlow action with:
+ When I create an OpenFlow action with:
"""
Pio::SendOutPort.new(:in_port)
"""
- Then it should finish successfully
- And the action has the following fields and values:
+ Then the action has the following fields and values:
| field | value |
| action_type | 0 |
| action_length | 16 |
@@ -79,12 +73,11 @@ Feature: Pio::SendOutPort
| max_length | :no_buffer |
Scenario: new(:normal)
- When I try to create an OpenFlow action with:
+ When I create an OpenFlow action with:
"""
Pio::SendOutPort.new(:normal)
"""
- Then it should finish successfully
- And the action has the following fields and values:
+ Then the action has the following fields and values:
| field | value |
| action_type | 0 |
| action_length | 16 |
@@ -92,12 +85,11 @@ Feature: Pio::SendOutPort
| max_length | :no_buffer |
Scenario: new(:flood)
- When I try to create an OpenFlow action with:
+ When I create an OpenFlow action with:
"""
Pio::SendOutPort.new(:flood)
"""
- Then it should finish successfully
- And the action has the following fields and values:
+ Then the action has the following fields and values:
| field | value |
| action_type | 0 |
| action_length | 16 |
@@ -105,9 +97,8 @@ Feature: Pio::SendOutPort
| max_length | :no_buffer |
Scenario: read
- When I try to parse a file named "open_flow13/send_out_port.raw" with "Pio::SendOutPort" class
- Then it should finish successfully
- And the message has the following fields and values:
+ When I parse a file named "open_flow13/send_out_port.raw" with "Pio::SendOutPort" class
+ Then the message has the following fields and values:
| field | value |
| action_type | 0 |
| action_length | 16 |
diff --git a/features/open_flow13/set_arp_operation.feature b/features/open_flow13/set_arp_operation.feature
index 9086b17e..876b9d8e 100644
--- a/features/open_flow13/set_arp_operation.feature
+++ b/features/open_flow13/set_arp_operation.feature
@@ -1,13 +1,12 @@
@open_flow13
-Feature: Pio::SetArpOperation
+Feature: SetArpOperation
Scenario: new(Pio::Arp::Reply::OPERATION)
- When I try to create an OpenFlow action with:
+ When I create an OpenFlow action with:
"""
- Pio::SetArpOperation.new(Pio::Arp::Reply::OPERATION)
+ Pio::SetArpOperation.new(Pio::Arp::Reply.operation)
"""
- Then it should finish successfully
- And the action has the following fields and values:
+ Then the action has the following fields and values:
| field | value |
| action_type | 25 |
| action_length | 16 |
diff --git a/features/open_flow13/set_arp_sender_hardware_address.feature b/features/open_flow13/set_arp_sender_hardware_address.feature
index ab6ac774..a8859fb6 100644
--- a/features/open_flow13/set_arp_sender_hardware_address.feature
+++ b/features/open_flow13/set_arp_sender_hardware_address.feature
@@ -1,13 +1,12 @@
@open_flow13
-Feature: Pio::SetArpSenderHardwareAddress
+Feature: SetArpSenderHardwareAddress
Scenario: new('00:00:de:ad:be:ef')
- When I try to create an OpenFlow action with:
+ When I create an OpenFlow action with:
"""
Pio::SetArpSenderHardwareAddress.new('00:00:de:ad:be:ef')
"""
- Then it should finish successfully
- And the action has the following fields and values:
+ Then the action has the following fields and values:
| field | value |
| action_type | 25 |
| action_length | 16 |
diff --git a/features/open_flow13/set_arp_sender_protocol_address.feature b/features/open_flow13/set_arp_sender_protocol_address.feature
index 2d603a4e..c0b59632 100644
--- a/features/open_flow13/set_arp_sender_protocol_address.feature
+++ b/features/open_flow13/set_arp_sender_protocol_address.feature
@@ -1,13 +1,12 @@
@open_flow13
-Feature: Pio::SetArpSenderProtocolAddress
+Feature: SetArpSenderProtocolAddress
Scenario: new('192.168.1.1')
- When I try to create an OpenFlow action with:
+ When I create an OpenFlow action with:
"""
Pio::SetArpSenderProtocolAddress.new('192.168.1.1')
"""
- Then it should finish successfully
- And the action has the following fields and values:
+ Then the action has the following fields and values:
| field | value |
| action_type | 25 |
| action_length | 16 |
diff --git a/features/open_flow13/set_destination_mac_address.feature b/features/open_flow13/set_destination_mac_address.feature
index 814d6a90..b013cd6f 100644
--- a/features/open_flow13/set_destination_mac_address.feature
+++ b/features/open_flow13/set_destination_mac_address.feature
@@ -1,13 +1,12 @@
@open_flow13
-Feature: Pio::SetDestinationMacAddress
+Feature: SetDestinationMacAddress
Scenario: new('11:22:33:44:55:66')
- When I try to create an OpenFlow action with:
+ When I create an OpenFlow action with:
"""
Pio::SetDestinationMacAddress.new('11:22:33:44:55:66')
"""
- Then it should finish successfully
- And the action has the following fields and values:
+ Then the action has the following fields and values:
| field | value |
| action_type | 25 |
| action_length | 16 |
diff --git a/features/open_flow13/set_ip_ttl.feature b/features/open_flow13/set_ip_ttl.feature
new file mode 100644
index 00000000..8fd75b49
--- /dev/null
+++ b/features/open_flow13/set_ip_ttl.feature
@@ -0,0 +1,12 @@
+@open_flow13
+Feature: SetIpTtl
+
+ Scenario: new(10)
+ When I create an OpenFlow action with:
+ """
+ Pio::SetIpTtl.new(10)
+ """
+ Then the action has the following fields and values:
+ | field | value |
+ | action_type | 23 |
+ | ttl | 10 |
diff --git a/features/open_flow13/set_metadata.feature b/features/open_flow13/set_metadata.feature
index f7f8cad3..556bdcd0 100644
--- a/features/open_flow13/set_metadata.feature
+++ b/features/open_flow13/set_metadata.feature
@@ -1,13 +1,12 @@
@open_flow13
-Feature: Pio::SetMetadata
+Feature: SetMetadata
Scenario: new(0x123)
- When I try to create an OpenFlow action with:
+ When I create an OpenFlow action with:
"""
Pio::SetMetadata.new(0x123)
"""
- Then it should finish successfully
- And the action has the following fields and values:
+ Then the action has the following fields and values:
| field | value |
| action_type | 25 |
| action_length | 16 |
diff --git a/features/open_flow13/set_source_mac_address.feature b/features/open_flow13/set_source_mac_address.feature
index 78c98ab2..84c74e20 100644
--- a/features/open_flow13/set_source_mac_address.feature
+++ b/features/open_flow13/set_source_mac_address.feature
@@ -1,13 +1,12 @@
@open_flow13
-Feature: Pio::SetSourceMacAddress
+Feature: SetSourceMacAddress
Scenario: new('11:22:33:44:55:66')
- When I try to create an OpenFlow action with:
+ When I create an OpenFlow action with:
"""
Pio::SetSourceMacAddress.new('11:22:33:44:55:66')
"""
- Then it should finish successfully
- And the action has the following fields and values:
+ Then the action has the following fields and values:
| field | value |
| action_type | 25 |
| action_length | 16 |
diff --git a/features/open_flow13/stats_request.feature b/features/open_flow13/stats_request.feature
index 24f739e0..5cbb57c8 100644
--- a/features/open_flow13/stats_request.feature
+++ b/features/open_flow13/stats_request.feature
@@ -1,19 +1,16 @@
-Feature: Pio::StatsRequest
+Feature: StatsRequest
Background:
Given I use OpenFlow 1.3
@wip
Scenario: new
- When I try to create an OpenFlow message with:
+ When I create an OpenFlow message with:
"""
Pio::StatsRequest.new(stats_type: :table)
"""
- Then it should finish successfully
- And the message has the following fields and values:
+ Then the message has the following fields and values:
| field | value |
- | ofp_version | 4 |
- | message_type | 16 |
- | message_length | 12 |
+ | version | 4 |
| transaction_id | 0 |
| xid | 0 |
| stats_type | :table |
diff --git a/features/open_flow13/write_metadata.feature b/features/open_flow13/write_metadata.feature
index fb46a56a..eff2e050 100644
--- a/features/open_flow13/write_metadata.feature
+++ b/features/open_flow13/write_metadata.feature
@@ -1,28 +1,26 @@
@open_flow13
-Feature: Pio::WriteMetadata
+Feature: WriteMetadata
Scenario: new(metadata: 1)
- When I try to create an OpenFlow instruction with:
+ When I create an OpenFlow instruction with:
"""
- Pio::WriteMetadata.new(metadata: 1)
+ Pio::OpenFlow13::WriteMetadata.new(metadata: 1)
"""
- Then it should finish successfully
- And the message has the following fields and values:
- | field | value |
- | class | Pio::WriteMetadata |
- | instruction_type | 2 |
- | instruction_length | 24 |
- | to_binary_s.length | 24 |
- | metadata | 1 |
- | metadata_mask | 0 |
+ Then the message has the following fields and values:
+ | field | value |
+ | class | Pio::OpenFlow13::WriteMetadata |
+ | instruction_type | 2 |
+ | instruction_length | 24 |
+ | to_binary_s.length | 24 |
+ | metadata | 1 |
+ | metadata_mask | 0 |
Scenario: read
- When I try to parse a file named "open_flow13/instruction_write_metadata.raw" with "Pio::WriteMetadata" class
- Then it should finish successfully
- And the message has the following fields and values:
- | field | value |
- | class | Pio::WriteMetadata |
- | instruction_type | 2 |
- | instruction_length | 24 |
- | to_binary_s.length | 24 |
- | metadata | 1 |
- | metadata_mask | 1 |
+ When I parse a file named "open_flow13/instruction_write_metadata.raw" with "Pio::OpenFlow13::WriteMetadata" class
+ Then the message has the following fields and values:
+ | field | value |
+ | class | Pio::OpenFlow13::WriteMetadata |
+ | instruction_type | 2 |
+ | instruction_length | 24 |
+ | to_binary_s.length | 24 |
+ | metadata | 1 |
+ | metadata_mask | 1 |
diff --git a/features/open_flow_read.feature b/features/open_flow_read.feature
index 5a24e237..efba0956 100644
--- a/features/open_flow_read.feature
+++ b/features/open_flow_read.feature
@@ -1,5 +1,5 @@
-Feature: Pio::OpenFlow.read
- Scenario: OpenFlow10
+Feature: OpenFlow.read
+ Scenario: OpenFlow 1.0
Given I switch the Pio::OpenFlow version to "OpenFlow10"
Then the following each raw file should be parsed into its corresponding object using OpenFlow.read
| raw file | result object |
@@ -19,8 +19,27 @@ Feature: Pio::OpenFlow.read
| open_flow10/flow_stats_request.raw | Pio::OpenFlow10::FlowStats::Request |
| open_flow10/hello.raw | Pio::OpenFlow10::Hello |
| open_flow10/hello_failed.raw | Pio::OpenFlow10::Error::HelloFailed |
- | open_flow10/packet_in.raw | Pio::OpenFlow10::PacketIn |
| open_flow10/packet_out.raw | Pio::OpenFlow10::PacketOut |
+ | open_flow10/port_stats_request.raw | Pio::OpenFlow10::PortStats::Request |
| open_flow10/port_status.raw | Pio::OpenFlow10::PortStatus |
- | open_flow13/bad_request.raw | Pio::OpenFlow13::Error::BadRequest |
- | open_flow13/hello_failed.raw | Pio::OpenFlow13::Error::HelloFailed |
+ | open_flow10/queue_stats_request.raw | Pio::OpenFlow10::QueueStats::Request |
+ | open_flow10/table_stats_request.raw | Pio::OpenFlow10::TableStats::Request |
+
+ Scenario: OpenFlow 1.3
+ Given I switch the Pio::OpenFlow version to "OpenFlow13"
+ Then the following each raw file should be parsed into its corresponding object using OpenFlow.read
+ | raw file | result object |
+ | open_flow13/bad_request.raw | Pio::OpenFlow13::Error::BadRequest |
+ | open_flow13/echo_reply_body.raw | Pio::OpenFlow13::Echo::Reply |
+ | open_flow13/echo_reply_no_body.raw | Pio::OpenFlow13::Echo::Reply |
+ | open_flow13/echo_request_body.raw | Pio::OpenFlow13::Echo::Request |
+ | open_flow13/echo_request_no_body.raw | Pio::OpenFlow13::Echo::Request |
+ | open_flow13/features_reply.raw | Pio::OpenFlow13::Features::Reply |
+ | open_flow13/features_request.raw | Pio::OpenFlow13::Features::Request |
+ | open_flow13/flow_mod_add_apply_no_match.raw | Pio::OpenFlow13::FlowMod |
+ | open_flow13/flow_mod_no_match_or_instructions.raw | Pio::OpenFlow13::FlowMod |
+ | open_flow13/hello_failed.raw | Pio::OpenFlow13::Error::HelloFailed |
+ | open_flow13/hello_no_version_bitmap.raw | Pio::OpenFlow13::Hello |
+ | open_flow13/hello_version_bitmap.raw | Pio::OpenFlow13::Hello |
+ | open_flow13/packet_in.raw | Pio::OpenFlow13::PacketIn |
+ | open_flow13/packet_out.raw | Pio::OpenFlow13::PacketOut |
diff --git a/features/open_flow_version.feature b/features/open_flow_version.feature
index 35882549..9c9052a3 100644
--- a/features/open_flow_version.feature
+++ b/features/open_flow_version.feature
@@ -1,4 +1,4 @@
-Feature: Pio::OpenFlow.version
+Feature: OpenFlow.version
Scenario: OpenFlow 1.0
Given I switch the Pio::OpenFlow version to "OpenFlow10"
When I get the OpenFlow version string
diff --git a/features/parser.feature b/features/parser.feature
index 6c4d2466..8e14dd93 100644
--- a/features/parser.feature
+++ b/features/parser.feature
@@ -1,10 +1,9 @@
-Feature: Pio::Parser
+Feature: Parser
Scenario: parse icmpv6.pcap
- When I try to parse a file named "icmpv6.pcap" with "Pio::Parser" class
- Then it should finish successfully
- And the message #1 have the following fields and values:
- | field | value |
- | class | Pio::Parser::EthernetFrame |
- | destination_mac | 00:60:97:07:69:ea |
- | source_mac | 00:00:86:05:80:da |
- | ether_type | 34525 |
+ When I parse a file named "icmpv6.pcap" with "Pio::Parser" class
+ Then the message #1 have the following fields and values:
+ | field | value |
+ | class | Pio::EthernetFrame |
+ | destination_mac | 00:60:97:07:69:ea |
+ | source_mac | 00:00:86:05:80:da |
+ | ether_type | 34525 |
diff --git a/features/step_definitions/LICENSE b/features/step_definitions/LICENSE
deleted file mode 100644
index 6b156fe1..00000000
--- a/features/step_definitions/LICENSE
+++ /dev/null
@@ -1,675 +0,0 @@
-GNU GENERAL PUBLIC LICENSE
- Version 3, 29 June 2007
-
- Copyright (C) 2007 Free Software Foundation, Inc.
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
- Preamble
-
- The GNU General Public License is a free, copyleft license for
-software and other kinds of works.
-
- The licenses for most software and other practical works are designed
-to take away your freedom to share and change the works. By contrast,
-the GNU General Public License is intended to guarantee your freedom to
-share and change all versions of a program--to make sure it remains free
-software for all its users. We, the Free Software Foundation, use the
-GNU General Public License for most of our software; it applies also to
-any other work released this way by its authors. You can apply it to
-your programs, too.
-
- When we speak of free software, we are referring to freedom, not
-price. Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-them if you wish), that you receive source code or can get it if you
-want it, that you can change the software or use pieces of it in new
-free programs, and that you know you can do these things.
-
- To protect your rights, we need to prevent others from denying you
-these rights or asking you to surrender the rights. Therefore, you have
-certain responsibilities if you distribute copies of the software, or if
-you modify it: responsibilities to respect the freedom of others.
-
- For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must pass on to the recipients the same
-freedoms that you received. You must make sure that they, too, receive
-or can get the source code. And you must show them these terms so they
-know their rights.
-
- Developers that use the GNU GPL protect your rights with two steps:
-(1) assert copyright on the software, and (2) offer you this License
-giving you legal permission to copy, distribute and/or modify it.
-
- For the developers' and authors' protection, the GPL clearly explains
-that there is no warranty for this free software. For both users' and
-authors' sake, the GPL requires that modified versions be marked as
-changed, so that their problems will not be attributed erroneously to
-authors of previous versions.
-
- Some devices are designed to deny users access to install or run
-modified versions of the software inside them, although the manufacturer
-can do so. This is fundamentally incompatible with the aim of
-protecting users' freedom to change the software. The systematic
-pattern of such abuse occurs in the area of products for individuals to
-use, which is precisely where it is most unacceptable. Therefore, we
-have designed this version of the GPL to prohibit the practice for those
-products. If such problems arise substantially in other domains, we
-stand ready to extend this provision to those domains in future versions
-of the GPL, as needed to protect the freedom of users.
-
- Finally, every program is threatened constantly by software patents.
-States should not allow patents to restrict development and use of
-software on general-purpose computers, but in those that do, we wish to
-avoid the special danger that patents applied to a free program could
-make it effectively proprietary. To prevent this, the GPL assures that
-patents cannot be used to render the program non-free.
-
- The precise terms and conditions for copying, distribution and
-modification follow.
-
- TERMS AND CONDITIONS
-
- 0. Definitions.
-
- "This License" refers to version 3 of the GNU General Public License.
-
- "Copyright" also means copyright-like laws that apply to other kinds of
-works, such as semiconductor masks.
-
- "The Program" refers to any copyrightable work licensed under this
-License. Each licensee is addressed as "you". "Licensees" and
-"recipients" may be individuals or organizations.
-
- To "modify" a work means to copy from or adapt all or part of the work
-in a fashion requiring copyright permission, other than the making of an
-exact copy. The resulting work is called a "modified version" of the
-earlier work or a work "based on" the earlier work.
-
- A "covered work" means either the unmodified Program or a work based
-on the Program.
-
- To "propagate" a work means to do anything with it that, without
-permission, would make you directly or secondarily liable for
-infringement under applicable copyright law, except executing it on a
-computer or modifying a private copy. Propagation includes copying,
-distribution (with or without modification), making available to the
-public, and in some countries other activities as well.
-
- To "convey" a work means any kind of propagation that enables other
-parties to make or receive copies. Mere interaction with a user through
-a computer network, with no transfer of a copy, is not conveying.
-
- An interactive user interface displays "Appropriate Legal Notices"
-to the extent that it includes a convenient and prominently visible
-feature that (1) displays an appropriate copyright notice, and (2)
-tells the user that there is no warranty for the work (except to the
-extent that warranties are provided), that licensees may convey the
-work under this License, and how to view a copy of this License. If
-the interface presents a list of user commands or options, such as a
-menu, a prominent item in the list meets this criterion.
-
- 1. Source Code.
-
- The "source code" for a work means the preferred form of the work
-for making modifications to it. "Object code" means any non-source
-form of a work.
-
- A "Standard Interface" means an interface that either is an official
-standard defined by a recognized standards body, or, in the case of
-interfaces specified for a particular programming language, one that
-is widely used among developers working in that language.
-
- The "System Libraries" of an executable work include anything, other
-than the work as a whole, that (a) is included in the normal form of
-packaging a Major Component, but which is not part of that Major
-Component, and (b) serves only to enable use of the work with that
-Major Component, or to implement a Standard Interface for which an
-implementation is available to the public in source code form. A
-"Major Component", in this context, means a major essential component
-(kernel, window system, and so on) of the specific operating system
-(if any) on which the executable work runs, or a compiler used to
-produce the work, or an object code interpreter used to run it.
-
- The "Corresponding Source" for a work in object code form means all
-the source code needed to generate, install, and (for an executable
-work) run the object code and to modify the work, including scripts to
-control those activities. However, it does not include the work's
-System Libraries, or general-purpose tools or generally available free
-programs which are used unmodified in performing those activities but
-which are not part of the work. For example, Corresponding Source
-includes interface definition files associated with source files for
-the work, and the source code for shared libraries and dynamically
-linked subprograms that the work is specifically designed to require,
-such as by intimate data communication or control flow between those
-subprograms and other parts of the work.
-
- The Corresponding Source need not include anything that users
-can regenerate automatically from other parts of the Corresponding
-Source.
-
- The Corresponding Source for a work in source code form is that
-same work.
-
- 2. Basic Permissions.
-
- All rights granted under this License are granted for the term of
-copyright on the Program, and are irrevocable provided the stated
-conditions are met. This License explicitly affirms your unlimited
-permission to run the unmodified Program. The output from running a
-covered work is covered by this License only if the output, given its
-content, constitutes a covered work. This License acknowledges your
-rights of fair use or other equivalent, as provided by copyright law.
-
- You may make, run and propagate covered works that you do not
-convey, without conditions so long as your license otherwise remains
-in force. You may convey covered works to others for the sole purpose
-of having them make modifications exclusively for you, or provide you
-with facilities for running those works, provided that you comply with
-the terms of this License in conveying all material for which you do
-not control copyright. Those thus making or running the covered works
-for you must do so exclusively on your behalf, under your direction
-and control, on terms that prohibit them from making any copies of
-your copyrighted material outside their relationship with you.
-
- Conveying under any other circumstances is permitted solely under
-the conditions stated below. Sublicensing is not allowed; section 10
-makes it unnecessary.
-
- 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
-
- No covered work shall be deemed part of an effective technological
-measure under any applicable law fulfilling obligations under article
-11 of the WIPO copyright treaty adopted on 20 December 1996, or
-similar laws prohibiting or restricting circumvention of such
-measures.
-
- When you convey a covered work, you waive any legal power to forbid
-circumvention of technological measures to the extent such circumvention
-is effected by exercising rights under this License with respect to
-the covered work, and you disclaim any intention to limit operation or
-modification of the work as a means of enforcing, against the work's
-users, your or third parties' legal rights to forbid circumvention of
-technological measures.
-
- 4. Conveying Verbatim Copies.
-
- You may convey verbatim copies of the Program's source code as you
-receive it, in any medium, provided that you conspicuously and
-appropriately publish on each copy an appropriate copyright notice;
-keep intact all notices stating that this License and any
-non-permissive terms added in accord with section 7 apply to the code;
-keep intact all notices of the absence of any warranty; and give all
-recipients a copy of this License along with the Program.
-
- You may charge any price or no price for each copy that you convey,
-and you may offer support or warranty protection for a fee.
-
- 5. Conveying Modified Source Versions.
-
- You may convey a work based on the Program, or the modifications to
-produce it from the Program, in the form of source code under the
-terms of section 4, provided that you also meet all of these conditions:
-
- a) The work must carry prominent notices stating that you modified
- it, and giving a relevant date.
-
- b) The work must carry prominent notices stating that it is
- released under this License and any conditions added under section
- 7. This requirement modifies the requirement in section 4 to
- "keep intact all notices".
-
- c) You must license the entire work, as a whole, under this
- License to anyone who comes into possession of a copy. This
- License will therefore apply, along with any applicable section 7
- additional terms, to the whole of the work, and all its parts,
- regardless of how they are packaged. This License gives no
- permission to license the work in any other way, but it does not
- invalidate such permission if you have separately received it.
-
- d) If the work has interactive user interfaces, each must display
- Appropriate Legal Notices; however, if the Program has interactive
- interfaces that do not display Appropriate Legal Notices, your
- work need not make them do so.
-
- A compilation of a covered work with other separate and independent
-works, which are not by their nature extensions of the covered work,
-and which are not combined with it such as to form a larger program,
-in or on a volume of a storage or distribution medium, is called an
-"aggregate" if the compilation and its resulting copyright are not
-used to limit the access or legal rights of the compilation's users
-beyond what the individual works permit. Inclusion of a covered work
-in an aggregate does not cause this License to apply to the other
-parts of the aggregate.
-
- 6. Conveying Non-Source Forms.
-
- You may convey a covered work in object code form under the terms
-of sections 4 and 5, provided that you also convey the
-machine-readable Corresponding Source under the terms of this License,
-in one of these ways:
-
- a) Convey the object code in, or embodied in, a physical product
- (including a physical distribution medium), accompanied by the
- Corresponding Source fixed on a durable physical medium
- customarily used for software interchange.
-
- b) Convey the object code in, or embodied in, a physical product
- (including a physical distribution medium), accompanied by a
- written offer, valid for at least three years and valid for as
- long as you offer spare parts or customer support for that product
- model, to give anyone who possesses the object code either (1) a
- copy of the Corresponding Source for all the software in the
- product that is covered by this License, on a durable physical
- medium customarily used for software interchange, for a price no
- more than your reasonable cost of physically performing this
- conveying of source, or (2) access to copy the
- Corresponding Source from a network server at no charge.
-
- c) Convey individual copies of the object code with a copy of the
- written offer to provide the Corresponding Source. This
- alternative is allowed only occasionally and noncommercially, and
- only if you received the object code with such an offer, in accord
- with subsection 6b.
-
- d) Convey the object code by offering access from a designated
- place (gratis or for a charge), and offer equivalent access to the
- Corresponding Source in the same way through the same place at no
- further charge. You need not require recipients to copy the
- Corresponding Source along with the object code. If the place to
- copy the object code is a network server, the Corresponding Source
- may be on a different server (operated by you or a third party)
- that supports equivalent copying facilities, provided you maintain
- clear directions next to the object code saying where to find the
- Corresponding Source. Regardless of what server hosts the
- Corresponding Source, you remain obligated to ensure that it is
- available for as long as needed to satisfy these requirements.
-
- e) Convey the object code using peer-to-peer transmission, provided
- you inform other peers where the object code and Corresponding
- Source of the work are being offered to the general public at no
- charge under subsection 6d.
-
- A separable portion of the object code, whose source code is excluded
-from the Corresponding Source as a System Library, need not be
-included in conveying the object code work.
-
- A "User Product" is either (1) a "consumer product", which means any
-tangible personal property which is normally used for personal, family,
-or household purposes, or (2) anything designed or sold for incorporation
-into a dwelling. In determining whether a product is a consumer product,
-doubtful cases shall be resolved in favor of coverage. For a particular
-product received by a particular user, "normally used" refers to a
-typical or common use of that class of product, regardless of the status
-of the particular user or of the way in which the particular user
-actually uses, or expects or is expected to use, the product. A product
-is a consumer product regardless of whether the product has substantial
-commercial, industrial or non-consumer uses, unless such uses represent
-the only significant mode of use of the product.
-
- "Installation Information" for a User Product means any methods,
-procedures, authorization keys, or other information required to install
-and execute modified versions of a covered work in that User Product from
-a modified version of its Corresponding Source. The information must
-suffice to ensure that the continued functioning of the modified object
-code is in no case prevented or interfered with solely because
-modification has been made.
-
- If you convey an object code work under this section in, or with, or
-specifically for use in, a User Product, and the conveying occurs as
-part of a transaction in which the right of possession and use of the
-User Product is transferred to the recipient in perpetuity or for a
-fixed term (regardless of how the transaction is characterized), the
-Corresponding Source conveyed under this section must be accompanied
-by the Installation Information. But this requirement does not apply
-if neither you nor any third party retains the ability to install
-modified object code on the User Product (for example, the work has
-been installed in ROM).
-
- The requirement to provide Installation Information does not include a
-requirement to continue to provide support service, warranty, or updates
-for a work that has been modified or installed by the recipient, or for
-the User Product in which it has been modified or installed. Access to a
-network may be denied when the modification itself materially and
-adversely affects the operation of the network or violates the rules and
-protocols for communication across the network.
-
- Corresponding Source conveyed, and Installation Information provided,
-in accord with this section must be in a format that is publicly
-documented (and with an implementation available to the public in
-source code form), and must require no special password or key for
-unpacking, reading or copying.
-
- 7. Additional Terms.
-
- "Additional permissions" are terms that supplement the terms of this
-License by making exceptions from one or more of its conditions.
-Additional permissions that are applicable to the entire Program shall
-be treated as though they were included in this License, to the extent
-that they are valid under applicable law. If additional permissions
-apply only to part of the Program, that part may be used separately
-under those permissions, but the entire Program remains governed by
-this License without regard to the additional permissions.
-
- When you convey a copy of a covered work, you may at your option
-remove any additional permissions from that copy, or from any part of
-it. (Additional permissions may be written to require their own
-removal in certain cases when you modify the work.) You may place
-additional permissions on material, added by you to a covered work,
-for which you have or can give appropriate copyright permission.
-
- Notwithstanding any other provision of this License, for material you
-add to a covered work, you may (if authorized by the copyright holders of
-that material) supplement the terms of this License with terms:
-
- a) Disclaiming warranty or limiting liability differently from the
- terms of sections 15 and 16 of this License; or
-
- b) Requiring preservation of specified reasonable legal notices or
- author attributions in that material or in the Appropriate Legal
- Notices displayed by works containing it; or
-
- c) Prohibiting misrepresentation of the origin of that material, or
- requiring that modified versions of such material be marked in
- reasonable ways as different from the original version; or
-
- d) Limiting the use for publicity purposes of names of licensors or
- authors of the material; or
-
- e) Declining to grant rights under trademark law for use of some
- trade names, trademarks, or service marks; or
-
- f) Requiring indemnification of licensors and authors of that
- material by anyone who conveys the material (or modified versions of
- it) with contractual assumptions of liability to the recipient, for
- any liability that these contractual assumptions directly impose on
- those licensors and authors.
-
- All other non-permissive additional terms are considered "further
-restrictions" within the meaning of section 10. If the Program as you
-received it, or any part of it, contains a notice stating that it is
-governed by this License along with a term that is a further
-restriction, you may remove that term. If a license document contains
-a further restriction but permits relicensing or conveying under this
-License, you may add to a covered work material governed by the terms
-of that license document, provided that the further restriction does
-not survive such relicensing or conveying.
-
- If you add terms to a covered work in accord with this section, you
-must place, in the relevant source files, a statement of the
-additional terms that apply to those files, or a notice indicating
-where to find the applicable terms.
-
- Additional terms, permissive or non-permissive, may be stated in the
-form of a separately written license, or stated as exceptions;
-the above requirements apply either way.
-
- 8. Termination.
-
- You may not propagate or modify a covered work except as expressly
-provided under this License. Any attempt otherwise to propagate or
-modify it is void, and will automatically terminate your rights under
-this License (including any patent licenses granted under the third
-paragraph of section 11).
-
- However, if you cease all violation of this License, then your
-license from a particular copyright holder is reinstated (a)
-provisionally, unless and until the copyright holder explicitly and
-finally terminates your license, and (b) permanently, if the copyright
-holder fails to notify you of the violation by some reasonable means
-prior to 60 days after the cessation.
-
- Moreover, your license from a particular copyright holder is
-reinstated permanently if the copyright holder notifies you of the
-violation by some reasonable means, this is the first time you have
-received notice of violation of this License (for any work) from that
-copyright holder, and you cure the violation prior to 30 days after
-your receipt of the notice.
-
- Termination of your rights under this section does not terminate the
-licenses of parties who have received copies or rights from you under
-this License. If your rights have been terminated and not permanently
-reinstated, you do not qualify to receive new licenses for the same
-material under section 10.
-
- 9. Acceptance Not Required for Having Copies.
-
- You are not required to accept this License in order to receive or
-run a copy of the Program. Ancillary propagation of a covered work
-occurring solely as a consequence of using peer-to-peer transmission
-to receive a copy likewise does not require acceptance. However,
-nothing other than this License grants you permission to propagate or
-modify any covered work. These actions infringe copyright if you do
-not accept this License. Therefore, by modifying or propagating a
-covered work, you indicate your acceptance of this License to do so.
-
- 10. Automatic Licensing of Downstream Recipients.
-
- Each time you convey a covered work, the recipient automatically
-receives a license from the original licensors, to run, modify and
-propagate that work, subject to this License. You are not responsible
-for enforcing compliance by third parties with this License.
-
- An "entity transaction" is a transaction transferring control of an
-organization, or substantially all assets of one, or subdividing an
-organization, or merging organizations. If propagation of a covered
-work results from an entity transaction, each party to that
-transaction who receives a copy of the work also receives whatever
-licenses to the work the party's predecessor in interest had or could
-give under the previous paragraph, plus a right to possession of the
-Corresponding Source of the work from the predecessor in interest, if
-the predecessor has it or can get it with reasonable efforts.
-
- You may not impose any further restrictions on the exercise of the
-rights granted or affirmed under this License. For example, you may
-not impose a license fee, royalty, or other charge for exercise of
-rights granted under this License, and you may not initiate litigation
-(including a cross-claim or counterclaim in a lawsuit) alleging that
-any patent claim is infringed by making, using, selling, offering for
-sale, or importing the Program or any portion of it.
-
- 11. Patents.
-
- A "contributor" is a copyright holder who authorizes use under this
-License of the Program or a work on which the Program is based. The
-work thus licensed is called the contributor's "contributor version".
-
- A contributor's "essential patent claims" are all patent claims
-owned or controlled by the contributor, whether already acquired or
-hereafter acquired, that would be infringed by some manner, permitted
-by this License, of making, using, or selling its contributor version,
-but do not include claims that would be infringed only as a
-consequence of further modification of the contributor version. For
-purposes of this definition, "control" includes the right to grant
-patent sublicenses in a manner consistent with the requirements of
-this License.
-
- Each contributor grants you a non-exclusive, worldwide, royalty-free
-patent license under the contributor's essential patent claims, to
-make, use, sell, offer for sale, import and otherwise run, modify and
-propagate the contents of its contributor version.
-
- In the following three paragraphs, a "patent license" is any express
-agreement or commitment, however denominated, not to enforce a patent
-(such as an express permission to practice a patent or covenant not to
-sue for patent infringement). To "grant" such a patent license to a
-party means to make such an agreement or commitment not to enforce a
-patent against the party.
-
- If you convey a covered work, knowingly relying on a patent license,
-and the Corresponding Source of the work is not available for anyone
-to copy, free of charge and under the terms of this License, through a
-publicly available network server or other readily accessible means,
-then you must either (1) cause the Corresponding Source to be so
-available, or (2) arrange to deprive yourself of the benefit of the
-patent license for this particular work, or (3) arrange, in a manner
-consistent with the requirements of this License, to extend the patent
-license to downstream recipients. "Knowingly relying" means you have
-actual knowledge that, but for the patent license, your conveying the
-covered work in a country, or your recipient's use of the covered work
-in a country, would infringe one or more identifiable patents in that
-country that you have reason to believe are valid.
-
- If, pursuant to or in connection with a single transaction or
-arrangement, you convey, or propagate by procuring conveyance of, a
-covered work, and grant a patent license to some of the parties
-receiving the covered work authorizing them to use, propagate, modify
-or convey a specific copy of the covered work, then the patent license
-you grant is automatically extended to all recipients of the covered
-work and works based on it.
-
- A patent license is "discriminatory" if it does not include within
-the scope of its coverage, prohibits the exercise of, or is
-conditioned on the non-exercise of one or more of the rights that are
-specifically granted under this License. You may not convey a covered
-work if you are a party to an arrangement with a third party that is
-in the business of distributing software, under which you make payment
-to the third party based on the extent of your activity of conveying
-the work, and under which the third party grants, to any of the
-parties who would receive the covered work from you, a discriminatory
-patent license (a) in connection with copies of the covered work
-conveyed by you (or copies made from those copies), or (b) primarily
-for and in connection with specific products or compilations that
-contain the covered work, unless you entered into that arrangement,
-or that patent license was granted, prior to 28 March 2007.
-
- Nothing in this License shall be construed as excluding or limiting
-any implied license or other defenses to infringement that may
-otherwise be available to you under applicable patent law.
-
- 12. No Surrender of Others' Freedom.
-
- If conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License. If you cannot convey a
-covered work so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you may
-not convey it at all. For example, if you agree to terms that obligate you
-to collect a royalty for further conveying from those to whom you convey
-the Program, the only way you could satisfy both those terms and this
-License would be to refrain entirely from conveying the Program.
-
- 13. Use with the GNU Affero General Public License.
-
- Notwithstanding any other provision of this License, you have
-permission to link or combine any covered work with a work licensed
-under version 3 of the GNU Affero General Public License into a single
-combined work, and to convey the resulting work. The terms of this
-License will continue to apply to the part which is the covered work,
-but the special requirements of the GNU Affero General Public License,
-section 13, concerning interaction through a network will apply to the
-combination as such.
-
- 14. Revised Versions of this License.
-
- The Free Software Foundation may publish revised and/or new versions of
-the GNU General Public License from time to time. Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
- Each version is given a distinguishing version number. If the
-Program specifies that a certain numbered version of the GNU General
-Public License "or any later version" applies to it, you have the
-option of following the terms and conditions either of that numbered
-version or of any later version published by the Free Software
-Foundation. If the Program does not specify a version number of the
-GNU General Public License, you may choose any version ever published
-by the Free Software Foundation.
-
- If the Program specifies that a proxy can decide which future
-versions of the GNU General Public License can be used, that proxy's
-public statement of acceptance of a version permanently authorizes you
-to choose that version for the Program.
-
- Later license versions may give you additional or different
-permissions. However, no additional obligations are imposed on any
-author or copyright holder as a result of your choosing to follow a
-later version.
-
- 15. Disclaimer of Warranty.
-
- THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
-APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
-HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
-OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
-THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
-IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
-ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
-
- 16. Limitation of Liability.
-
- IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
-THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
-GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
-USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
-DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
-PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
-EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGES.
-
- 17. Interpretation of Sections 15 and 16.
-
- If the disclaimer of warranty and limitation of liability provided
-above cannot be given local legal effect according to their terms,
-reviewing courts shall apply local law that most closely approximates
-an absolute waiver of all civil liability in connection with the
-Program, unless a warranty or assumption of liability accompanies a
-copy of the Program in return for a fee.
-
- END OF TERMS AND CONDITIONS
-
- How to Apply These Terms to Your New Programs
-
- If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
- To do so, attach the following notices to the program. It is safest
-to attach them to the start of each source file to most effectively
-state the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
- {one line to give the program's name and a brief idea of what it does.}
- Copyright (C) {year} {name of author}
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see .
-
-Also add information on how to contact you by electronic and paper mail.
-
- If the program does terminal interaction, make it output a short
-notice like this when it starts in an interactive mode:
-
- {project} Copyright (C) {year} {fullname}
- This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
- This is free software, and you are welcome to redistribute it
- under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License. Of course, your program's commands
-might be different; for a GUI interface, you would use an "about box".
-
- You should also get your employer (if you work as a programmer) or school,
-if any, to sign a "copyright disclaimer" for the program, if necessary.
-For more information on this, and how to apply and follow the GNU GPL, see
-.
-
- The GNU General Public License does not permit incorporating your program
-into proprietary programs. If your program is a subroutine library, you
-may consider it more useful to permit linking proprietary applications with
-the library. If this is what you want to do, use the GNU Lesser General
-Public License instead of this License. But first, please read
-.
-
diff --git a/features/step_definitions/open_flow_steps.rb b/features/step_definitions/open_flow_steps.rb
index 429c5fe9..78801ac4 100644
--- a/features/step_definitions/open_flow_steps.rb
+++ b/features/step_definitions/open_flow_steps.rb
@@ -1,40 +1,35 @@
Given(/^I switch the Pio::OpenFlow version to "([^"]*)"$/) do |version|
- Pio::OpenFlow.switch_version version.to_sym
+ Pio::OpenFlow.version = version
end
When(/^I get the OpenFlow version string$/) do
- @version = Pio::OpenFlow.version
+ @version = Pio::OpenFlow.version.to_s
end
Then(/^the version string should be "([^"]*)"$/) do |expected_version_string|
expect(@version).to eq(expected_version_string)
end
-When(/^I try to create a packet with:$/) do |ruby_code|
- begin
- @result = Pio.module_eval(ruby_code)
- rescue
- @last_error = $ERROR_INFO
- end
+When(/^I create a packet with:$/) do |ruby_code|
+ cd('.') { @result = Pio.module_eval(ruby_code) }
end
-When(/^I try to create an OpenFlow message with:$/) do |ruby_code|
- step 'I try to create a packet with:', ruby_code
+When(/^I create an OpenFlow message with:$/) do |ruby_code|
+ step 'I create a packet with:', ruby_code
end
-When(/^I try to create an OpenFlow action with:$/) do |ruby_code|
- step 'I try to create a packet with:', ruby_code
+When(/^I create an OpenFlow action with:$/) do |ruby_code|
+ step 'I create a packet with:', ruby_code
end
-When(/^I try to create an OpenFlow instruction with:$/) do |ruby_code|
- step 'I try to create a packet with:', ruby_code
+When(/^I create an OpenFlow instruction with:$/) do |ruby_code|
+ step 'I create a packet with:', ruby_code
end
# rubocop:disable LineLength
Then(/^the following each raw file should be parsed into its corresponding object using OpenFlow\.read$/) do |table|
table.hashes.each do |each|
- step %(I try to parse a file named "#{each['raw file']}" with "OpenFlow" class)
- step 'it should finish successfully'
+ step %(I parse a file named "#{each['raw file']}" with "Pio::OpenFlow" class)
step %(the message should be a "#{each['result object']}")
end
end
diff --git a/features/step_definitions/packet_data_steps.rb b/features/step_definitions/packet_data_steps.rb
index fcbaebc0..8858d96c 100644
--- a/features/step_definitions/packet_data_steps.rb
+++ b/features/step_definitions/packet_data_steps.rb
@@ -1,43 +1,27 @@
-# rubocop:disable LineLength
-When(/^I try to parse a file named "(.*?\.raw)" with "(.*?)" class$/) do |path, klass|
- full_path = File.expand_path(File.join(__dir__, '..', path))
- raw_data = IO.read(full_path)
+When(/^I parse a file named "(.*?\.raw)" with "(.*?)" class$/) do |path, klass|
+ raw_data = IO.read(expand_path("%/#{path}"))
parser_klass = Pio.const_get(klass)
- begin
- @result = parser_klass.read(raw_data)
- rescue
- @last_error = $ERROR_INFO
- end
+ @result = parser_klass.read(raw_data)
end
-# rubocop:enable LineLength
-# rubocop:disable LineLength
-When(/^I try to parse a file named "(.*?\.pcap)" with "(.*?)" class$/) do |path, klass|
- full_path = File.expand_path(File.join(__dir__, '..', path))
- pcap = Pio::Pcap::Frame.read(IO.read(full_path))
+When(/^I parse a file named "(.*?\.pcap)" with "(.*?)" class$/) do |path, klass|
+ pcap = Pio::Pcap::Frame.read(IO.read(expand_path("%/#{path}")))
parser_klass = Pio.const_get(klass)
- begin
- @result = pcap.records.each_with_object([]) do |each, result|
- result << parser_klass.read(each.data)
- end
- rescue
- @last_error = $ERROR_INFO
+ @result = pcap.records.each_with_object([]) do |each, result|
+ result << parser_klass.read(each.data)
end
end
-# rubocop:enable LineLength
-
-Then(/^it should finish successfully$/) do
- expect(@last_error).to be_nil
-end
-
-Then(/^it should fail with "([^"]*)", "([^"]*)"$/) do |error, message|
- expect(@last_error.class.to_s).to eq(error)
- expect(@last_error.message).to eq(message)
-end
When(/^I create an exact match from "(.*?)"$/) do |path|
- full_path = File.expand_path(File.join(__dir__, '..', path))
- @result = Pio::ExactMatch.new(Pio::PacketIn.read(IO.read(full_path)))
+ raw_data = case File.extname(path)
+ when '.raw'
+ IO.read(expand_path("%/#{path}"))
+ when '.rb'
+ Pio.module_eval(IO.read(expand_path("%/#{path}")))
+ else
+ raise
+ end
+ @result = Pio::ExactMatch.new(Pio::PacketIn.read(raw_data))
end
Then(/^the message should be a "([^"]*)"$/) do |expected_klass|
diff --git a/features/step_definitions/ruby_steps.rb b/features/step_definitions/ruby_steps.rb
new file mode 100644
index 00000000..08c98437
--- /dev/null
+++ b/features/step_definitions/ruby_steps.rb
@@ -0,0 +1,7 @@
+When(/^I eval the following Ruby code:$/) do |ruby_code|
+ @result = Pio.module_eval(ruby_code)
+end
+
+Then(/^the result of eval should be:$/) do |expected|
+ expect(@result.to_s).to eq expected
+end
diff --git a/features/step_definitions/show_stats_steps.rb b/features/step_definitions/show_stats_steps.rb
index 6e1f1b2c..00a42e3d 100644
--- a/features/step_definitions/show_stats_steps.rb
+++ b/features/step_definitions/show_stats_steps.rb
@@ -15,7 +15,7 @@
when /-> (\S+) = (\d+) packet/
result[Regexp.last_match(1)] = Regexp.last_match(2).to_i
else
- fail "Failed to parse line '#{each}'"
+ raise "Failed to parse line '#{each}'"
end
end
end
@@ -43,7 +43,7 @@
next unless received
result[Regexp.last_match(1)] = Regexp.last_match(3).to_i
else
- fail "Failed to parse line '#{each}'"
+ raise "Failed to parse line '#{each}'"
end
end
end
@@ -72,7 +72,7 @@
next unless received
result += Regexp.last_match(3).to_i
else
- fail "Failed to parse line '#{each}'"
+ raise "Failed to parse line '#{each}'"
end
end
end
diff --git a/features/support/env.rb b/features/support/env.rb
index 618b0bbc..c4dc53e5 100644
--- a/features/support/env.rb
+++ b/features/support/env.rb
@@ -1,5 +1,6 @@
require 'coveralls'
Coveralls.wear!
+require 'aruba/cucumber'
require 'pio'
require 'pio/pcap'
diff --git a/features/support/hooks.rb b/features/support/hooks.rb
index aea8c2bb..99208b24 100644
--- a/features/support/hooks.rb
+++ b/features/support/hooks.rb
@@ -1,7 +1,7 @@
Before('@open_flow10') do
- Pio::OpenFlow.switch_version :OpenFlow10
+ Pio::OpenFlow.version = :OpenFlow10
end
Before('@open_flow13') do
- Pio::OpenFlow.switch_version :OpenFlow13
+ Pio::OpenFlow.version = :OpenFlow13
end
diff --git a/features/udp.feature b/features/udp.feature
index e8393a34..ff302f86 100644
--- a/features/udp.feature
+++ b/features/udp.feature
@@ -1,28 +1,27 @@
-Feature: Pio::Udp
+Feature: Udp
Scenario: parse dhcp.pcap
- When I try to parse a file named "dhcp.pcap" with "Pio::Udp" class
- Then it should finish successfully
- And the message #1 have the following fields and values:
- | field | value |
- | class | Pio::Udp |
- | destination_mac | ff:ff:ff:ff:ff:ff |
- | source_mac | 00:0b:82:01:fc:42 |
- | ether_type | 2048 |
- | ip_version | 4 |
- | ip_header_length | 5 |
- | ip_type_of_service | 0 |
- | ip_total_length | 300 |
- | ip_identifier | 43062 |
- | ip_flag | 0 |
- | ip_fragment | 0 |
- | ip_ttl | 250 |
- | ip_protocol | 17 |
- | ip_header_checksum | 6027 |
- | source_ip_address | 0.0.0.0 |
- | destination_ip_address | 255.255.255.255 |
- | ip_option | |
- | udp_source_port | 68 |
- | udp_destination_port | 67 |
- | udp_length | 280 |
- | udp_checksum | 22815 |
- | udp_payload.length | 272 |
+ When I parse a file named "dhcp.pcap" with "Pio::Udp" class
+ Then the message #1 have the following fields and values:
+ | field | value |
+ | class | Pio::Udp |
+ | destination_mac | ff:ff:ff:ff:ff:ff |
+ | source_mac | 00:0b:82:01:fc:42 |
+ | ether_type.to_hex | 0x800 |
+ | ip_version | 4 |
+ | ip_header_length | 5 |
+ | ip_type_of_service | 0 |
+ | ip_total_length | 300 |
+ | ip_identifier | 43062 |
+ | ip_flag | 0 |
+ | ip_fragment | 0 |
+ | ip_ttl | 250 |
+ | ip_protocol | 17 |
+ | ip_header_checksum | 6027 |
+ | source_ip_address | 0.0.0.0 |
+ | destination_ip_address | 255.255.255.255 |
+ | ip_option | |
+ | udp_source_port | 68 |
+ | udp_destination_port | 67 |
+ | udp_length | 280 |
+ | udp_checksum | 22815 |
+ | udp_payload.length | 272 |
diff --git a/fixtures/arp/arp_reply.rb b/fixtures/arp/arp_reply.rb
new file mode 100644
index 00000000..7d3b48d9
--- /dev/null
+++ b/fixtures/arp/arp_reply.rb
@@ -0,0 +1,14 @@
+[
+ 0x00, 0x26, 0x82, 0xeb, 0xea, 0xd1, # destination_mac
+ 0x00, 0x16, 0x9d, 0x1d, 0x9c, 0xc4, # source_mac
+ 0x08, 0x06, # ether_type
+ 0x00, 0x01, # hardware_type
+ 0x08, 0x00, # protocol_type
+ 0x06, # hardware_length
+ 0x04, # protocol_length
+ 0x00, 0x02, # operation
+ 0x00, 0x16, 0x9d, 0x1d, 0x9c, 0xc4, # sender_hardware_address
+ 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*')
diff --git a/fixtures/arp/arp_request.rb b/fixtures/arp/arp_request.rb
new file mode 100644
index 00000000..bf5d4d6b
--- /dev/null
+++ b/fixtures/arp/arp_request.rb
@@ -0,0 +1,14 @@
+[
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, # destination_mac
+ 0x00, 0x26, 0x82, 0xeb, 0xea, 0xd1, # source_mac
+ 0x08, 0x06, # ether_type
+ 0x00, 0x01, # hardware_type
+ 0x08, 0x00, # protocol_type
+ 0x06, # hardware_length
+ 0x04, # protocol_length
+ 0x00, 0x01, # operation
+ 0x00, 0x26, 0x82, 0xeb, 0xea, 0xd1, # sender_hardware_address
+ 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*')
diff --git a/features/dhcp.pcap b/fixtures/dhcp.pcap
similarity index 100%
rename from features/dhcp.pcap
rename to fixtures/dhcp.pcap
diff --git a/fixtures/ethernet_header/ethernet_header.rb b/fixtures/ethernet_header/ethernet_header.rb
new file mode 100644
index 00000000..739268c9
--- /dev/null
+++ b/fixtures/ethernet_header/ethernet_header.rb
@@ -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*')
diff --git a/fixtures/ethernet_header/vlan_ethernet_header.rb b/fixtures/ethernet_header/vlan_ethernet_header.rb
new file mode 100644
index 00000000..0de1b379
--- /dev/null
+++ b/fixtures/ethernet_header/vlan_ethernet_header.rb
@@ -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')
diff --git a/fixtures/icmp/icmp_reply.rb b/fixtures/icmp/icmp_reply.rb
new file mode 100644
index 00000000..f811d54c
--- /dev/null
+++ b/fixtures/icmp/icmp_reply.rb
@@ -0,0 +1,21 @@
+[
+ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, # destination_mac
+ 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, # source_mac
+ 0x08, 0x00, # ether_type
+ 0b0100_0101, # ip_version, ip_header_length
+ 0x00, # ip_type_of_service
+ 0x00, 0x36, # ip_total_length
+ 0x00, 0x00, # ip_identifier
+ 0b000_0000000000000, # ip_flag, ip_fragment
+ 0x80, # ip_ttl
+ 0x01, # ip_protocol
+ 0x12, 0x75, # ip_header_checksum
+ 0xc0, 0xa8, 0x53, 0xfe, # source_ip_address
+ 0xc0, 0xa8, 0x53, 0x03, # destination_ip_address
+ 0x00, # icmp_type
+ 0x00, # icmp_code
+ 0x6f, 0xf5, # icmp_checksum
+ 0x01, 0x00, # icmp_identifier
+ 0x00, 0x6f, # icmp_sequence_number
+ 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, # echo_data
+].pack('C20nC46')
diff --git a/fixtures/icmp/icmp_request.rb b/fixtures/icmp/icmp_request.rb
new file mode 100644
index 00000000..2a6e826c
--- /dev/null
+++ b/fixtures/icmp/icmp_request.rb
@@ -0,0 +1,21 @@
+[
+ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, # destination_mac
+ 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, # source_mac
+ 0x08, 0x00, # ether_type
+ 0b0100_0101, # ip_version, ip_header_length
+ 0x00, # ip_type_of_service
+ 0x00, 0x36, # ip_total_length
+ 0x00, 0x00, # ip_identifier
+ 0b000_0000000000000, # ip_flag, ip_fragment
+ 0x80, # ip_ttl
+ 0x01, # ip_protocol
+ 0x12, 0x75, # ip_header_checksum
+ 0xc0, 0xa8, 0x53, 0x03, # source_ip_address
+ 0xc0, 0xa8, 0x53, 0xfe, # destination_ip_address
+ 0x08, # icmp_type
+ 0x00, # icmp_code
+ 0x67, 0xf5, # icmp_checksum
+ 0x01, 0x00, # icmp_identifier
+ 0x00, 0x6f, # icmp_sequence_number
+ 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, # echo_data
+].pack('C20nC46')
diff --git a/features/icmpv6.pcap b/fixtures/icmpv6.pcap
similarity index 100%
rename from features/icmpv6.pcap
rename to fixtures/icmpv6.pcap
diff --git a/fixtures/ipv4_header/ipv4_header.rb b/fixtures/ipv4_header/ipv4_header.rb
new file mode 100644
index 00000000..c8e19d93
--- /dev/null
+++ b/fixtures/ipv4_header/ipv4_header.rb
@@ -0,0 +1,12 @@
+[
+ 0b0100_0101, # ip_version, ip_header_length
+ 0x00, # ip_type_of_service
+ 0x00, 0x14, # ip_total_length
+ 0x00, 0x00, # ip_identifier
+ 0b000_0000000000000, # ip_flag, ip_fragment
+ 0x80, # ip_ttl
+ 0x00, # ip_protocol
+ 0x30, 0xe1, # ip_header_checksum
+ 0x01, 0x02, 0x03, 0x04, # source_ip_address
+ 0x04, 0x03, 0x02, 0x01, # destination_ip_address
+].pack('C6nC12')
diff --git a/features/lldp.detailed.pcap b/fixtures/lldp.detailed.pcap
similarity index 100%
rename from features/lldp.detailed.pcap
rename to fixtures/lldp.detailed.pcap
diff --git a/features/lldp.minimal.pcap b/fixtures/lldp.minimal.pcap
similarity index 100%
rename from features/lldp.minimal.pcap
rename to fixtures/lldp.minimal.pcap
diff --git a/features/open_flow10/aggregate_stats_reply.raw b/fixtures/open_flow10/aggregate_stats_reply.raw
similarity index 100%
rename from features/open_flow10/aggregate_stats_reply.raw
rename to fixtures/open_flow10/aggregate_stats_reply.raw
diff --git a/features/open_flow10/aggregate_stats_request.raw b/fixtures/open_flow10/aggregate_stats_request.raw
similarity index 100%
rename from features/open_flow10/aggregate_stats_request.raw
rename to fixtures/open_flow10/aggregate_stats_request.raw
diff --git a/fixtures/open_flow10/bad_action_bad_argument.raw b/fixtures/open_flow10/bad_action_bad_argument.raw
new file mode 100644
index 00000000..caf8d5f9
Binary files /dev/null and b/fixtures/open_flow10/bad_action_bad_argument.raw differ
diff --git a/fixtures/open_flow10/bad_action_bad_length.raw b/fixtures/open_flow10/bad_action_bad_length.raw
new file mode 100644
index 00000000..b0aeae1c
Binary files /dev/null and b/fixtures/open_flow10/bad_action_bad_length.raw differ
diff --git a/fixtures/open_flow10/bad_action_bad_out_port.raw b/fixtures/open_flow10/bad_action_bad_out_port.raw
new file mode 100644
index 00000000..38d0ad6d
Binary files /dev/null and b/fixtures/open_flow10/bad_action_bad_out_port.raw differ
diff --git a/fixtures/open_flow10/bad_action_bad_queue.raw b/fixtures/open_flow10/bad_action_bad_queue.raw
new file mode 100644
index 00000000..f38f42f1
Binary files /dev/null and b/fixtures/open_flow10/bad_action_bad_queue.raw differ
diff --git a/fixtures/open_flow10/bad_action_bad_type.raw b/fixtures/open_flow10/bad_action_bad_type.raw
new file mode 100644
index 00000000..9e077d57
Binary files /dev/null and b/fixtures/open_flow10/bad_action_bad_type.raw differ
diff --git a/fixtures/open_flow10/bad_action_bad_vendor.raw b/fixtures/open_flow10/bad_action_bad_vendor.raw
new file mode 100644
index 00000000..099de538
Binary files /dev/null and b/fixtures/open_flow10/bad_action_bad_vendor.raw differ
diff --git a/fixtures/open_flow10/bad_action_bad_vendor_type.raw b/fixtures/open_flow10/bad_action_bad_vendor_type.raw
new file mode 100644
index 00000000..3d550f1c
Binary files /dev/null and b/fixtures/open_flow10/bad_action_bad_vendor_type.raw differ
diff --git a/fixtures/open_flow10/bad_action_eperm.raw b/fixtures/open_flow10/bad_action_eperm.raw
new file mode 100644
index 00000000..27e1179f
Binary files /dev/null and b/fixtures/open_flow10/bad_action_eperm.raw differ
diff --git a/fixtures/open_flow10/bad_action_too_many.raw b/fixtures/open_flow10/bad_action_too_many.raw
new file mode 100644
index 00000000..5a9aad1c
Binary files /dev/null and b/fixtures/open_flow10/bad_action_too_many.raw differ
diff --git a/features/open_flow10/bad_request.raw b/fixtures/open_flow10/bad_request.raw
similarity index 100%
rename from features/open_flow10/bad_request.raw
rename to fixtures/open_flow10/bad_request.raw
diff --git a/features/open_flow10/barrier_reply.raw b/fixtures/open_flow10/barrier_reply.raw
similarity index 100%
rename from features/open_flow10/barrier_reply.raw
rename to fixtures/open_flow10/barrier_reply.raw
diff --git a/features/open_flow10/barrier_request.raw b/fixtures/open_flow10/barrier_request.raw
similarity index 100%
rename from features/open_flow10/barrier_request.raw
rename to fixtures/open_flow10/barrier_request.raw
diff --git a/features/open_flow10/description_stats_reply.raw b/fixtures/open_flow10/description_stats_reply.raw
similarity index 100%
rename from features/open_flow10/description_stats_reply.raw
rename to fixtures/open_flow10/description_stats_reply.raw
diff --git a/features/open_flow10/description_stats_request.raw b/fixtures/open_flow10/description_stats_request.raw
similarity index 100%
rename from features/open_flow10/description_stats_request.raw
rename to fixtures/open_flow10/description_stats_request.raw
diff --git a/features/open_flow10/echo_reply.raw b/fixtures/open_flow10/echo_reply.raw
similarity index 100%
rename from features/open_flow10/echo_reply.raw
rename to fixtures/open_flow10/echo_reply.raw
diff --git a/features/open_flow10/echo_request.raw b/fixtures/open_flow10/echo_request.raw
similarity index 100%
rename from features/open_flow10/echo_request.raw
rename to fixtures/open_flow10/echo_request.raw
diff --git a/features/open_flow10/error.raw b/fixtures/open_flow10/error.raw
similarity index 100%
rename from features/open_flow10/error.raw
rename to fixtures/open_flow10/error.raw
diff --git a/features/open_flow10/features_reply.raw b/fixtures/open_flow10/features_reply.raw
similarity index 100%
rename from features/open_flow10/features_reply.raw
rename to fixtures/open_flow10/features_reply.raw
diff --git a/features/open_flow10/features_request.raw b/fixtures/open_flow10/features_request.raw
similarity index 100%
rename from features/open_flow10/features_request.raw
rename to fixtures/open_flow10/features_request.raw
diff --git a/features/open_flow10/flow_mod_add.raw b/fixtures/open_flow10/flow_mod_add.raw
similarity index 100%
rename from features/open_flow10/flow_mod_add.raw
rename to fixtures/open_flow10/flow_mod_add.raw
diff --git a/features/open_flow10/flow_mod_delete.raw b/fixtures/open_flow10/flow_mod_delete.raw
similarity index 100%
rename from features/open_flow10/flow_mod_delete.raw
rename to fixtures/open_flow10/flow_mod_delete.raw
diff --git a/features/open_flow10/flow_mod_delete_strict.raw b/fixtures/open_flow10/flow_mod_delete_strict.raw
similarity index 100%
rename from features/open_flow10/flow_mod_delete_strict.raw
rename to fixtures/open_flow10/flow_mod_delete_strict.raw
diff --git a/fixtures/open_flow10/flow_mod_failed_all_tables_full.raw b/fixtures/open_flow10/flow_mod_failed_all_tables_full.raw
new file mode 100644
index 00000000..8b3732c5
Binary files /dev/null and b/fixtures/open_flow10/flow_mod_failed_all_tables_full.raw differ
diff --git a/fixtures/open_flow10/flow_mod_failed_bad_command.raw b/fixtures/open_flow10/flow_mod_failed_bad_command.raw
new file mode 100644
index 00000000..5420f872
Binary files /dev/null and b/fixtures/open_flow10/flow_mod_failed_bad_command.raw differ
diff --git a/fixtures/open_flow10/flow_mod_failed_bad_emerg_timeout.raw b/fixtures/open_flow10/flow_mod_failed_bad_emerg_timeout.raw
new file mode 100644
index 00000000..bc3a0620
Binary files /dev/null and b/fixtures/open_flow10/flow_mod_failed_bad_emerg_timeout.raw differ
diff --git a/fixtures/open_flow10/flow_mod_failed_eperm.raw b/fixtures/open_flow10/flow_mod_failed_eperm.raw
new file mode 100644
index 00000000..83279e53
Binary files /dev/null and b/fixtures/open_flow10/flow_mod_failed_eperm.raw differ
diff --git a/fixtures/open_flow10/flow_mod_failed_overlap.raw b/fixtures/open_flow10/flow_mod_failed_overlap.raw
new file mode 100644
index 00000000..afd7e067
Binary files /dev/null and b/fixtures/open_flow10/flow_mod_failed_overlap.raw differ
diff --git a/fixtures/open_flow10/flow_mod_failed_unsupported.raw b/fixtures/open_flow10/flow_mod_failed_unsupported.raw
new file mode 100644
index 00000000..074a2998
Binary files /dev/null and b/fixtures/open_flow10/flow_mod_failed_unsupported.raw differ
diff --git a/features/open_flow10/flow_mod_modify.raw b/fixtures/open_flow10/flow_mod_modify.raw
similarity index 100%
rename from features/open_flow10/flow_mod_modify.raw
rename to fixtures/open_flow10/flow_mod_modify.raw
diff --git a/features/open_flow10/flow_mod_modify_strict.raw b/fixtures/open_flow10/flow_mod_modify_strict.raw
similarity index 100%
rename from features/open_flow10/flow_mod_modify_strict.raw
rename to fixtures/open_flow10/flow_mod_modify_strict.raw
diff --git a/features/open_flow10/flow_removed.raw b/fixtures/open_flow10/flow_removed.raw
similarity index 100%
rename from features/open_flow10/flow_removed.raw
rename to fixtures/open_flow10/flow_removed.raw
diff --git a/features/open_flow10/flow_stats_reply.raw b/fixtures/open_flow10/flow_stats_reply.raw
similarity index 100%
rename from features/open_flow10/flow_stats_reply.raw
rename to fixtures/open_flow10/flow_stats_reply.raw
diff --git a/features/open_flow10/flow_stats_request.raw b/fixtures/open_flow10/flow_stats_request.raw
similarity index 100%
rename from features/open_flow10/flow_stats_request.raw
rename to fixtures/open_flow10/flow_stats_request.raw
diff --git a/features/open_flow10/get_config_reply.raw b/fixtures/open_flow10/get_config_reply.raw
similarity index 100%
rename from features/open_flow10/get_config_reply.raw
rename to fixtures/open_flow10/get_config_reply.raw
diff --git a/features/open_flow10/get_config_request.raw b/fixtures/open_flow10/get_config_request.raw
similarity index 100%
rename from features/open_flow10/get_config_request.raw
rename to fixtures/open_flow10/get_config_request.raw
diff --git a/features/open_flow10/hello.raw b/fixtures/open_flow10/hello.raw
similarity index 100%
rename from features/open_flow10/hello.raw
rename to fixtures/open_flow10/hello.raw
diff --git a/features/open_flow10/hello_failed.raw b/fixtures/open_flow10/hello_failed.raw
similarity index 100%
rename from features/open_flow10/hello_failed.raw
rename to fixtures/open_flow10/hello_failed.raw
diff --git a/features/open_flow10/nx_flow_mod_add.raw b/fixtures/open_flow10/nx_flow_mod_add.raw
similarity index 100%
rename from features/open_flow10/nx_flow_mod_add.raw
rename to fixtures/open_flow10/nx_flow_mod_add.raw
diff --git a/features/open_flow10/nx_flow_mod_delete.raw b/fixtures/open_flow10/nx_flow_mod_delete.raw
similarity index 100%
rename from features/open_flow10/nx_flow_mod_delete.raw
rename to fixtures/open_flow10/nx_flow_mod_delete.raw
diff --git a/features/open_flow10/nx_flow_mod_delete_strict.raw b/fixtures/open_flow10/nx_flow_mod_delete_strict.raw
similarity index 100%
rename from features/open_flow10/nx_flow_mod_delete_strict.raw
rename to fixtures/open_flow10/nx_flow_mod_delete_strict.raw
diff --git a/features/open_flow10/nx_flow_mod_modify.raw b/fixtures/open_flow10/nx_flow_mod_modify.raw
similarity index 100%
rename from features/open_flow10/nx_flow_mod_modify.raw
rename to fixtures/open_flow10/nx_flow_mod_modify.raw
diff --git a/features/open_flow10/nx_flow_mod_modify_strict.raw b/fixtures/open_flow10/nx_flow_mod_modify_strict.raw
similarity index 100%
rename from features/open_flow10/nx_flow_mod_modify_strict.raw
rename to fixtures/open_flow10/nx_flow_mod_modify_strict.raw
diff --git a/fixtures/open_flow10/nx_packet_in.raw b/fixtures/open_flow10/nx_packet_in.raw
new file mode 100644
index 00000000..f94c29f8
Binary files /dev/null and b/fixtures/open_flow10/nx_packet_in.raw differ
diff --git a/features/open_flow10/nxast_learn.raw b/fixtures/open_flow10/nxast_learn.raw
similarity index 100%
rename from features/open_flow10/nxast_learn.raw
rename to fixtures/open_flow10/nxast_learn.raw
diff --git a/fixtures/open_flow10/packet_in.rb b/fixtures/open_flow10/packet_in.rb
new file mode 100644
index 00000000..84ddb816
--- /dev/null
+++ b/fixtures/open_flow10/packet_in.rb
@@ -0,0 +1,11 @@
+[
+ 0x01, # version
+ 0x0a, # type
+ 0x00, 0x12, # _length
+ 0x00, 0x00, 0x00, 0x00, # transaction_id
+ 0xff, 0xff, 0xff, 0x00, # buffer_id
+ 0x00, 0x00, # total_length
+ 0x00, 0x01, # in_port
+ 0x00, # reason
+ 0x00, # padding
+].pack('C18')
diff --git a/features/open_flow10/packet_in_arp_reply.raw b/fixtures/open_flow10/packet_in_arp_reply.raw
similarity index 100%
rename from features/open_flow10/packet_in_arp_reply.raw
rename to fixtures/open_flow10/packet_in_arp_reply.raw
diff --git a/fixtures/open_flow10/packet_in_arp_request.rb b/fixtures/open_flow10/packet_in_arp_request.rb
new file mode 100644
index 00000000..4921e3bc
--- /dev/null
+++ b/fixtures/open_flow10/packet_in_arp_request.rb
@@ -0,0 +1,12 @@
+[
+ 0x01, # version
+ 0x0a, # type
+ 0x00, 0x52, # _length
+ 0x00, 0x00, 0x00, 0x00, # transaction_id
+ 0xff, 0xff, 0xff, 0x00, # buffer_id
+ 0x00, 0x40, # total_length
+ 0x00, 0x01, # in_port
+ 0x00, # reason
+ 0x00, # padding
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfa, 0xce, 0xb0, 0x00, 0x00, 0xcc, 0x08, 0x06, 0x00, 0x01, 0x08, 0x00, 0x06, 0x04, 0x00, 0x01, 0xfa, 0xce, 0xb0, 0x00, 0x00, 0xcc, 0xc0, 0xa8, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xa8, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, # raw_data
+].pack('C82')
diff --git a/features/open_flow10/packet_in_cbench.raw b/fixtures/open_flow10/packet_in_cbench.raw
similarity index 100%
rename from features/open_flow10/packet_in_cbench.raw
rename to fixtures/open_flow10/packet_in_cbench.raw
diff --git a/features/open_flow10/packet_out.raw b/fixtures/open_flow10/packet_out.raw
similarity index 100%
rename from features/open_flow10/packet_out.raw
rename to fixtures/open_flow10/packet_out.raw
diff --git a/features/open_flow10/port_mod.raw b/fixtures/open_flow10/port_mod.raw
similarity index 100%
rename from features/open_flow10/port_mod.raw
rename to fixtures/open_flow10/port_mod.raw
diff --git a/features/open_flow10/port_stats_reply.raw b/fixtures/open_flow10/port_stats_reply.raw
similarity index 100%
rename from features/open_flow10/port_stats_reply.raw
rename to fixtures/open_flow10/port_stats_reply.raw
diff --git a/features/open_flow10/port_stats_request.raw b/fixtures/open_flow10/port_stats_request.raw
similarity index 100%
rename from features/open_flow10/port_stats_request.raw
rename to fixtures/open_flow10/port_stats_request.raw
diff --git a/features/open_flow10/port_status.raw b/fixtures/open_flow10/port_status.raw
similarity index 100%
rename from features/open_flow10/port_status.raw
rename to fixtures/open_flow10/port_status.raw
diff --git a/features/open_flow10/queue_get_config_reply.raw b/fixtures/open_flow10/queue_get_config_reply.raw
similarity index 100%
rename from features/open_flow10/queue_get_config_reply.raw
rename to fixtures/open_flow10/queue_get_config_reply.raw
diff --git a/features/open_flow10/queue_get_config_request.raw b/fixtures/open_flow10/queue_get_config_request.raw
similarity index 100%
rename from features/open_flow10/queue_get_config_request.raw
rename to fixtures/open_flow10/queue_get_config_request.raw
diff --git a/features/open_flow10/queue_stats_request.raw b/fixtures/open_flow10/queue_stats_request.raw
similarity index 100%
rename from features/open_flow10/queue_stats_request.raw
rename to fixtures/open_flow10/queue_stats_request.raw
diff --git a/features/open_flow10/set_config.raw b/fixtures/open_flow10/set_config.raw
similarity index 100%
rename from features/open_flow10/set_config.raw
rename to fixtures/open_flow10/set_config.raw
diff --git a/features/open_flow10/table_stats_reply.raw b/fixtures/open_flow10/table_stats_reply.raw
similarity index 100%
rename from features/open_flow10/table_stats_reply.raw
rename to fixtures/open_flow10/table_stats_reply.raw
diff --git a/features/open_flow10/table_stats_request.raw b/fixtures/open_flow10/table_stats_request.raw
similarity index 100%
rename from features/open_flow10/table_stats_request.raw
rename to fixtures/open_flow10/table_stats_request.raw
diff --git a/features/open_flow10/vendor.raw b/fixtures/open_flow10/vendor.raw
similarity index 100%
rename from features/open_flow10/vendor.raw
rename to fixtures/open_flow10/vendor.raw
diff --git a/features/open_flow10/vendor_stats_request.raw b/fixtures/open_flow10/vendor_stats_request.raw
similarity index 100%
rename from features/open_flow10/vendor_stats_request.raw
rename to fixtures/open_flow10/vendor_stats_request.raw
diff --git a/features/open_flow13/action_copy_ttl_in.raw b/fixtures/open_flow13/action_copy_ttl_in.raw
similarity index 100%
rename from features/open_flow13/action_copy_ttl_in.raw
rename to fixtures/open_flow13/action_copy_ttl_in.raw
diff --git a/features/open_flow13/action_copy_ttl_out.raw b/fixtures/open_flow13/action_copy_ttl_out.raw
similarity index 100%
rename from features/open_flow13/action_copy_ttl_out.raw
rename to fixtures/open_flow13/action_copy_ttl_out.raw
diff --git a/features/open_flow13/action_dec_mpls_ttl.raw b/fixtures/open_flow13/action_dec_mpls_ttl.raw
similarity index 100%
rename from features/open_flow13/action_dec_mpls_ttl.raw
rename to fixtures/open_flow13/action_dec_mpls_ttl.raw
diff --git a/features/open_flow13/action_dec_nw_ttl.raw b/fixtures/open_flow13/action_dec_nw_ttl.raw
similarity index 100%
rename from features/open_flow13/action_dec_nw_ttl.raw
rename to fixtures/open_flow13/action_dec_nw_ttl.raw
diff --git a/features/open_flow13/action_group.raw b/fixtures/open_flow13/action_group.raw
similarity index 100%
rename from features/open_flow13/action_group.raw
rename to fixtures/open_flow13/action_group.raw
diff --git a/features/open_flow13/action_pop_mpls.raw b/fixtures/open_flow13/action_pop_mpls.raw
similarity index 100%
rename from features/open_flow13/action_pop_mpls.raw
rename to fixtures/open_flow13/action_pop_mpls.raw
diff --git a/features/open_flow13/action_pop_pbb.raw b/fixtures/open_flow13/action_pop_pbb.raw
similarity index 100%
rename from features/open_flow13/action_pop_pbb.raw
rename to fixtures/open_flow13/action_pop_pbb.raw
diff --git a/features/open_flow13/action_pop_vlan.raw b/fixtures/open_flow13/action_pop_vlan.raw
similarity index 100%
rename from features/open_flow13/action_pop_vlan.raw
rename to fixtures/open_flow13/action_pop_vlan.raw
diff --git a/features/open_flow13/action_push_mpls.raw b/fixtures/open_flow13/action_push_mpls.raw
similarity index 100%
rename from features/open_flow13/action_push_mpls.raw
rename to fixtures/open_flow13/action_push_mpls.raw
diff --git a/features/open_flow13/action_push_pbb.raw b/fixtures/open_flow13/action_push_pbb.raw
similarity index 100%
rename from features/open_flow13/action_push_pbb.raw
rename to fixtures/open_flow13/action_push_pbb.raw
diff --git a/features/open_flow13/action_push_vlan.raw b/fixtures/open_flow13/action_push_vlan.raw
similarity index 100%
rename from features/open_flow13/action_push_vlan.raw
rename to fixtures/open_flow13/action_push_vlan.raw
diff --git a/features/open_flow13/action_set_field.raw b/fixtures/open_flow13/action_set_field.raw
similarity index 100%
rename from features/open_flow13/action_set_field.raw
rename to fixtures/open_flow13/action_set_field.raw
diff --git a/features/open_flow13/action_set_mpls_ttl.raw b/fixtures/open_flow13/action_set_mpls_ttl.raw
similarity index 100%
rename from features/open_flow13/action_set_mpls_ttl.raw
rename to fixtures/open_flow13/action_set_mpls_ttl.raw
diff --git a/features/open_flow13/action_set_nw_ttl.raw b/fixtures/open_flow13/action_set_nw_ttl.raw
similarity index 100%
rename from features/open_flow13/action_set_nw_ttl.raw
rename to fixtures/open_flow13/action_set_nw_ttl.raw
diff --git a/features/open_flow13/action_set_queue.raw b/fixtures/open_flow13/action_set_queue.raw
similarity index 100%
rename from features/open_flow13/action_set_queue.raw
rename to fixtures/open_flow13/action_set_queue.raw
diff --git a/features/open_flow13/apply_actions.raw b/fixtures/open_flow13/apply_actions.raw
similarity index 100%
rename from features/open_flow13/apply_actions.raw
rename to fixtures/open_flow13/apply_actions.raw
diff --git a/features/open_flow13/bad_request.raw b/fixtures/open_flow13/bad_request.raw
similarity index 100%
rename from features/open_flow13/bad_request.raw
rename to fixtures/open_flow13/bad_request.raw
diff --git a/features/open_flow13/echo_reply_body.raw b/fixtures/open_flow13/echo_reply_body.raw
similarity index 100%
rename from features/open_flow13/echo_reply_body.raw
rename to fixtures/open_flow13/echo_reply_body.raw
diff --git a/features/open_flow13/echo_reply_no_body.raw b/fixtures/open_flow13/echo_reply_no_body.raw
similarity index 100%
rename from features/open_flow13/echo_reply_no_body.raw
rename to fixtures/open_flow13/echo_reply_no_body.raw
diff --git a/features/open_flow13/echo_request_body.raw b/fixtures/open_flow13/echo_request_body.raw
similarity index 100%
rename from features/open_flow13/echo_request_body.raw
rename to fixtures/open_flow13/echo_request_body.raw
diff --git a/features/open_flow13/echo_request_no_body.raw b/fixtures/open_flow13/echo_request_no_body.raw
similarity index 100%
rename from features/open_flow13/echo_request_no_body.raw
rename to fixtures/open_flow13/echo_request_no_body.raw
diff --git a/features/open_flow13/features_reply.raw b/fixtures/open_flow13/features_reply.raw
similarity index 100%
rename from features/open_flow13/features_reply.raw
rename to fixtures/open_flow13/features_reply.raw
diff --git a/features/open_flow13/features_request.raw b/fixtures/open_flow13/features_request.raw
similarity index 100%
rename from features/open_flow13/features_request.raw
rename to fixtures/open_flow13/features_request.raw
diff --git a/features/open_flow13/flow_add_apply_no_match.raw b/fixtures/open_flow13/flow_add_apply_no_match.raw
similarity index 100%
rename from features/open_flow13/flow_add_apply_no_match.raw
rename to fixtures/open_flow13/flow_add_apply_no_match.raw
diff --git a/features/open_flow13/flow_mod_add_apply_no_match.raw b/fixtures/open_flow13/flow_mod_add_apply_no_match.raw
similarity index 100%
rename from features/open_flow13/flow_mod_add_apply_no_match.raw
rename to fixtures/open_flow13/flow_mod_add_apply_no_match.raw
diff --git a/features/open_flow13/flow_mod_no_match_or_instructions.raw b/fixtures/open_flow13/flow_mod_no_match_or_instructions.raw
similarity index 100%
rename from features/open_flow13/flow_mod_no_match_or_instructions.raw
rename to fixtures/open_flow13/flow_mod_no_match_or_instructions.raw
diff --git a/features/open_flow13/hello_failed.raw b/fixtures/open_flow13/hello_failed.raw
similarity index 100%
rename from features/open_flow13/hello_failed.raw
rename to fixtures/open_flow13/hello_failed.raw
diff --git a/features/open_flow13/hello_no_version_bitmap.raw b/fixtures/open_flow13/hello_no_version_bitmap.raw
similarity index 100%
rename from features/open_flow13/hello_no_version_bitmap.raw
rename to fixtures/open_flow13/hello_no_version_bitmap.raw
diff --git a/features/open_flow13/hello_version_bitmap.raw b/fixtures/open_flow13/hello_version_bitmap.raw
similarity index 100%
rename from features/open_flow13/hello_version_bitmap.raw
rename to fixtures/open_flow13/hello_version_bitmap.raw
diff --git a/features/open_flow13/instruction_clear_actions.raw b/fixtures/open_flow13/instruction_clear_actions.raw
similarity index 100%
rename from features/open_flow13/instruction_clear_actions.raw
rename to fixtures/open_flow13/instruction_clear_actions.raw
diff --git a/features/open_flow13/instruction_goto_table.raw b/fixtures/open_flow13/instruction_goto_table.raw
similarity index 100%
rename from features/open_flow13/instruction_goto_table.raw
rename to fixtures/open_flow13/instruction_goto_table.raw
diff --git a/features/open_flow13/instruction_meter.raw b/fixtures/open_flow13/instruction_meter.raw
similarity index 100%
rename from features/open_flow13/instruction_meter.raw
rename to fixtures/open_flow13/instruction_meter.raw
diff --git a/features/open_flow13/instruction_write_actions.raw b/fixtures/open_flow13/instruction_write_actions.raw
similarity index 100%
rename from features/open_flow13/instruction_write_actions.raw
rename to fixtures/open_flow13/instruction_write_actions.raw
diff --git a/features/open_flow13/instruction_write_metadata.raw b/fixtures/open_flow13/instruction_write_metadata.raw
similarity index 100%
rename from features/open_flow13/instruction_write_metadata.raw
rename to fixtures/open_flow13/instruction_write_metadata.raw
diff --git a/features/open_flow13/oxm_arp_op_field.raw b/fixtures/open_flow13/oxm_arp_op_field.raw
similarity index 100%
rename from features/open_flow13/oxm_arp_op_field.raw
rename to fixtures/open_flow13/oxm_arp_op_field.raw
diff --git a/features/open_flow13/oxm_arp_sha_field.raw b/fixtures/open_flow13/oxm_arp_sha_field.raw
similarity index 100%
rename from features/open_flow13/oxm_arp_sha_field.raw
rename to fixtures/open_flow13/oxm_arp_sha_field.raw
diff --git a/features/open_flow13/oxm_arp_spa_field.raw b/fixtures/open_flow13/oxm_arp_spa_field.raw
similarity index 100%
rename from features/open_flow13/oxm_arp_spa_field.raw
rename to fixtures/open_flow13/oxm_arp_spa_field.raw
diff --git a/features/open_flow13/oxm_arp_tha_field.raw b/fixtures/open_flow13/oxm_arp_tha_field.raw
similarity index 100%
rename from features/open_flow13/oxm_arp_tha_field.raw
rename to fixtures/open_flow13/oxm_arp_tha_field.raw
diff --git a/features/open_flow13/oxm_arp_tpa_field.raw b/fixtures/open_flow13/oxm_arp_tpa_field.raw
similarity index 100%
rename from features/open_flow13/oxm_arp_tpa_field.raw
rename to fixtures/open_flow13/oxm_arp_tpa_field.raw
diff --git a/features/open_flow13/oxm_ether_destination_field.raw b/fixtures/open_flow13/oxm_ether_destination_field.raw
similarity index 100%
rename from features/open_flow13/oxm_ether_destination_field.raw
rename to fixtures/open_flow13/oxm_ether_destination_field.raw
diff --git a/features/open_flow13/oxm_ether_source_field.raw b/fixtures/open_flow13/oxm_ether_source_field.raw
similarity index 100%
rename from features/open_flow13/oxm_ether_source_field.raw
rename to fixtures/open_flow13/oxm_ether_source_field.raw
diff --git a/features/open_flow13/oxm_ether_type_field.raw b/fixtures/open_flow13/oxm_ether_type_field.raw
similarity index 100%
rename from features/open_flow13/oxm_ether_type_field.raw
rename to fixtures/open_flow13/oxm_ether_type_field.raw
diff --git a/features/open_flow13/oxm_experimenter_stratos_basic_dot11.raw b/fixtures/open_flow13/oxm_experimenter_stratos_basic_dot11.raw
similarity index 100%
rename from features/open_flow13/oxm_experimenter_stratos_basic_dot11.raw
rename to fixtures/open_flow13/oxm_experimenter_stratos_basic_dot11.raw
diff --git a/features/open_flow13/oxm_icmpv4_code_field.raw b/fixtures/open_flow13/oxm_icmpv4_code_field.raw
similarity index 100%
rename from features/open_flow13/oxm_icmpv4_code_field.raw
rename to fixtures/open_flow13/oxm_icmpv4_code_field.raw
diff --git a/features/open_flow13/oxm_icmpv4_type_field.raw b/fixtures/open_flow13/oxm_icmpv4_type_field.raw
similarity index 100%
rename from features/open_flow13/oxm_icmpv4_type_field.raw
rename to fixtures/open_flow13/oxm_icmpv4_type_field.raw
diff --git a/features/open_flow13/oxm_in_phy_port_field.raw b/fixtures/open_flow13/oxm_in_phy_port_field.raw
similarity index 100%
rename from features/open_flow13/oxm_in_phy_port_field.raw
rename to fixtures/open_flow13/oxm_in_phy_port_field.raw
diff --git a/features/open_flow13/oxm_in_port_field.raw b/fixtures/open_flow13/oxm_in_port_field.raw
similarity index 100%
rename from features/open_flow13/oxm_in_port_field.raw
rename to fixtures/open_flow13/oxm_in_port_field.raw
diff --git a/features/open_flow13/oxm_invalid_field.raw b/fixtures/open_flow13/oxm_invalid_field.raw
similarity index 100%
rename from features/open_flow13/oxm_invalid_field.raw
rename to fixtures/open_flow13/oxm_invalid_field.raw
diff --git a/features/open_flow13/oxm_ip_dscp_field.raw b/fixtures/open_flow13/oxm_ip_dscp_field.raw
similarity index 100%
rename from features/open_flow13/oxm_ip_dscp_field.raw
rename to fixtures/open_flow13/oxm_ip_dscp_field.raw
diff --git a/features/open_flow13/oxm_ip_ecn_field.raw b/fixtures/open_flow13/oxm_ip_ecn_field.raw
similarity index 100%
rename from features/open_flow13/oxm_ip_ecn_field.raw
rename to fixtures/open_flow13/oxm_ip_ecn_field.raw
diff --git a/features/open_flow13/oxm_ipv4_destination_field.raw b/fixtures/open_flow13/oxm_ipv4_destination_field.raw
similarity index 100%
rename from features/open_flow13/oxm_ipv4_destination_field.raw
rename to fixtures/open_flow13/oxm_ipv4_destination_field.raw
diff --git a/features/open_flow13/oxm_ipv4_source_field.raw b/fixtures/open_flow13/oxm_ipv4_source_field.raw
similarity index 100%
rename from features/open_flow13/oxm_ipv4_source_field.raw
rename to fixtures/open_flow13/oxm_ipv4_source_field.raw
diff --git a/features/open_flow13/oxm_ipv6_destination_field.raw b/fixtures/open_flow13/oxm_ipv6_destination_field.raw
similarity index 100%
rename from features/open_flow13/oxm_ipv6_destination_field.raw
rename to fixtures/open_flow13/oxm_ipv6_destination_field.raw
diff --git a/features/open_flow13/oxm_ipv6_source_field.raw b/fixtures/open_flow13/oxm_ipv6_source_field.raw
similarity index 100%
rename from features/open_flow13/oxm_ipv6_source_field.raw
rename to fixtures/open_flow13/oxm_ipv6_source_field.raw
diff --git a/features/open_flow13/oxm_masked_arp_sha_field.raw b/fixtures/open_flow13/oxm_masked_arp_sha_field.raw
similarity index 100%
rename from features/open_flow13/oxm_masked_arp_sha_field.raw
rename to fixtures/open_flow13/oxm_masked_arp_sha_field.raw
diff --git a/features/open_flow13/oxm_masked_arp_spa_field.raw b/fixtures/open_flow13/oxm_masked_arp_spa_field.raw
similarity index 100%
rename from features/open_flow13/oxm_masked_arp_spa_field.raw
rename to fixtures/open_flow13/oxm_masked_arp_spa_field.raw
diff --git a/features/open_flow13/oxm_masked_arp_tha_field.raw b/fixtures/open_flow13/oxm_masked_arp_tha_field.raw
similarity index 100%
rename from features/open_flow13/oxm_masked_arp_tha_field.raw
rename to fixtures/open_flow13/oxm_masked_arp_tha_field.raw
diff --git a/features/open_flow13/oxm_masked_arp_tpa_field.raw b/fixtures/open_flow13/oxm_masked_arp_tpa_field.raw
similarity index 100%
rename from features/open_flow13/oxm_masked_arp_tpa_field.raw
rename to fixtures/open_flow13/oxm_masked_arp_tpa_field.raw
diff --git a/features/open_flow13/oxm_masked_ether_destination_field.raw b/fixtures/open_flow13/oxm_masked_ether_destination_field.raw
similarity index 100%
rename from features/open_flow13/oxm_masked_ether_destination_field.raw
rename to fixtures/open_flow13/oxm_masked_ether_destination_field.raw
diff --git a/features/open_flow13/oxm_masked_ether_source_field.raw b/fixtures/open_flow13/oxm_masked_ether_source_field.raw
similarity index 100%
rename from features/open_flow13/oxm_masked_ether_source_field.raw
rename to fixtures/open_flow13/oxm_masked_ether_source_field.raw
diff --git a/features/open_flow13/oxm_masked_ipv4_destination_field.raw b/fixtures/open_flow13/oxm_masked_ipv4_destination_field.raw
similarity index 100%
rename from features/open_flow13/oxm_masked_ipv4_destination_field.raw
rename to fixtures/open_flow13/oxm_masked_ipv4_destination_field.raw
diff --git a/features/open_flow13/oxm_masked_ipv4_source_field.raw b/fixtures/open_flow13/oxm_masked_ipv4_source_field.raw
similarity index 100%
rename from features/open_flow13/oxm_masked_ipv4_source_field.raw
rename to fixtures/open_flow13/oxm_masked_ipv4_source_field.raw
diff --git a/features/open_flow13/oxm_masked_ipv6_destination_field.raw b/fixtures/open_flow13/oxm_masked_ipv6_destination_field.raw
similarity index 100%
rename from features/open_flow13/oxm_masked_ipv6_destination_field.raw
rename to fixtures/open_flow13/oxm_masked_ipv6_destination_field.raw
diff --git a/features/open_flow13/oxm_masked_ipv6_source_field.raw b/fixtures/open_flow13/oxm_masked_ipv6_source_field.raw
similarity index 100%
rename from features/open_flow13/oxm_masked_ipv6_source_field.raw
rename to fixtures/open_flow13/oxm_masked_ipv6_source_field.raw
diff --git a/features/open_flow13/oxm_masked_tunnel_id_field.raw b/fixtures/open_flow13/oxm_masked_tunnel_id_field.raw
similarity index 100%
rename from features/open_flow13/oxm_masked_tunnel_id_field.raw
rename to fixtures/open_flow13/oxm_masked_tunnel_id_field.raw
diff --git a/features/open_flow13/oxm_metadata_field.raw b/fixtures/open_flow13/oxm_metadata_field.raw
similarity index 100%
rename from features/open_flow13/oxm_metadata_field.raw
rename to fixtures/open_flow13/oxm_metadata_field.raw
diff --git a/features/open_flow13/oxm_metadata_masked_field.raw b/fixtures/open_flow13/oxm_metadata_masked_field.raw
similarity index 100%
rename from features/open_flow13/oxm_metadata_masked_field.raw
rename to fixtures/open_flow13/oxm_metadata_masked_field.raw
diff --git a/features/open_flow13/oxm_no_fields.raw b/fixtures/open_flow13/oxm_no_fields.raw
similarity index 100%
rename from features/open_flow13/oxm_no_fields.raw
rename to fixtures/open_flow13/oxm_no_fields.raw
diff --git a/features/open_flow13/oxm_sctp_destination_field.raw b/fixtures/open_flow13/oxm_sctp_destination_field.raw
similarity index 100%
rename from features/open_flow13/oxm_sctp_destination_field.raw
rename to fixtures/open_flow13/oxm_sctp_destination_field.raw
diff --git a/features/open_flow13/oxm_sctp_source_field.raw b/fixtures/open_flow13/oxm_sctp_source_field.raw
similarity index 100%
rename from features/open_flow13/oxm_sctp_source_field.raw
rename to fixtures/open_flow13/oxm_sctp_source_field.raw
diff --git a/features/open_flow13/oxm_tcp_destination_field.raw b/fixtures/open_flow13/oxm_tcp_destination_field.raw
similarity index 100%
rename from features/open_flow13/oxm_tcp_destination_field.raw
rename to fixtures/open_flow13/oxm_tcp_destination_field.raw
diff --git a/features/open_flow13/oxm_tcp_field.raw b/fixtures/open_flow13/oxm_tcp_field.raw
similarity index 100%
rename from features/open_flow13/oxm_tcp_field.raw
rename to fixtures/open_flow13/oxm_tcp_field.raw
diff --git a/features/open_flow13/oxm_tcp_source_field.raw b/fixtures/open_flow13/oxm_tcp_source_field.raw
similarity index 100%
rename from features/open_flow13/oxm_tcp_source_field.raw
rename to fixtures/open_flow13/oxm_tcp_source_field.raw
diff --git a/features/open_flow13/oxm_tunnel_id_field.raw b/fixtures/open_flow13/oxm_tunnel_id_field.raw
similarity index 100%
rename from features/open_flow13/oxm_tunnel_id_field.raw
rename to fixtures/open_flow13/oxm_tunnel_id_field.raw
diff --git a/features/open_flow13/oxm_udp_destination_field.raw b/fixtures/open_flow13/oxm_udp_destination_field.raw
similarity index 100%
rename from features/open_flow13/oxm_udp_destination_field.raw
rename to fixtures/open_flow13/oxm_udp_destination_field.raw
diff --git a/features/open_flow13/oxm_udp_field.raw b/fixtures/open_flow13/oxm_udp_field.raw
similarity index 100%
rename from features/open_flow13/oxm_udp_field.raw
rename to fixtures/open_flow13/oxm_udp_field.raw
diff --git a/features/open_flow13/oxm_udp_source_field.raw b/fixtures/open_flow13/oxm_udp_source_field.raw
similarity index 100%
rename from features/open_flow13/oxm_udp_source_field.raw
rename to fixtures/open_flow13/oxm_udp_source_field.raw
diff --git a/features/open_flow13/oxm_vlan_pcp_field.raw b/fixtures/open_flow13/oxm_vlan_pcp_field.raw
similarity index 100%
rename from features/open_flow13/oxm_vlan_pcp_field.raw
rename to fixtures/open_flow13/oxm_vlan_pcp_field.raw
diff --git a/features/open_flow13/oxm_vlan_vid_field.raw b/fixtures/open_flow13/oxm_vlan_vid_field.raw
similarity index 100%
rename from features/open_flow13/oxm_vlan_vid_field.raw
rename to fixtures/open_flow13/oxm_vlan_vid_field.raw
diff --git a/features/open_flow13/packet_in.raw b/fixtures/open_flow13/packet_in.raw
similarity index 100%
rename from features/open_flow13/packet_in.raw
rename to fixtures/open_flow13/packet_in.raw
diff --git a/features/open_flow13/packet_out.raw b/fixtures/open_flow13/packet_out.raw
similarity index 100%
rename from features/open_flow13/packet_out.raw
rename to fixtures/open_flow13/packet_out.raw
diff --git a/features/open_flow13/send_out_port.raw b/fixtures/open_flow13/send_out_port.raw
similarity index 100%
rename from features/open_flow13/send_out_port.raw
rename to fixtures/open_flow13/send_out_port.raw
diff --git a/features/open_flow13/table_stats_reply.raw b/fixtures/open_flow13/table_stats_reply.raw
similarity index 100%
rename from features/open_flow13/table_stats_reply.raw
rename to fixtures/open_flow13/table_stats_reply.raw
diff --git a/features/open_flow13/table_stats_request.raw b/fixtures/open_flow13/table_stats_request.raw
similarity index 100%
rename from features/open_flow13/table_stats_request.raw
rename to fixtures/open_flow13/table_stats_request.raw
diff --git a/features/udp_no_payload.raw b/fixtures/udp_no_payload.raw
similarity index 100%
rename from features/udp_no_payload.raw
rename to fixtures/udp_no_payload.raw
diff --git a/features/udp_with_payload.raw b/fixtures/udp_with_payload.raw
similarity index 100%
rename from features/udp_with_payload.raw
rename to fixtures/udp_with_payload.raw
diff --git a/lib/pio.rb b/lib/pio.rb
index 951f7078..1cc0091c 100644
--- a/lib/pio.rb
+++ b/lib/pio.rb
@@ -5,5 +5,5 @@
require 'pio/icmp'
require 'pio/lldp'
require 'pio/mac'
-require 'pio/open_flow'
require 'pio/udp'
+require 'pio/open_flow'
diff --git a/lib/pio/arp.rb b/lib/pio/arp.rb
index 312ef4a3..f4f41046 100644
--- a/lib/pio/arp.rb
+++ b/lib/pio/arp.rb
@@ -1,14 +1,17 @@
require 'pio/arp/format'
-require 'pio/arp/request'
require 'pio/arp/reply'
-require 'pio/message_type_selector'
+require 'pio/arp/request'
+require 'pio/parse_error'
-# Packet parser and generator library.
module Pio
# ARP parser and generator.
class Arp
- extend MessageTypeSelector
- message_type Request::OPERATION => Request, Reply::OPERATION => Reply
+ def self.read(raw_data)
+ format = Format.read(raw_data)
+ { Request.operation => Request,
+ Reply.operation => Reply }.fetch(format.operation).create(format)
+ rescue
+ raise Pio::ParseError, $ERROR_INFO.message
+ end
end
- ARP = Arp
end
diff --git a/lib/pio/arp/format.rb b/lib/pio/arp/format.rb
index 0911231f..46e211c2 100644
--- a/lib/pio/arp/format.rb
+++ b/lib/pio/arp/format.rb
@@ -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
@@ -23,17 +23,13 @@ class Format < BinData::Record
mac_address :target_hardware_address
ip_address :target_protocol_address
- def message_type
- operation
- end
-
# rubocop:disable MethodLength
def to_exact_match(in_port)
match_options = {
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,
diff --git a/lib/pio/arp/message.rb b/lib/pio/arp/message.rb
index 74a5daab..2070a0b1 100644
--- a/lib/pio/arp/message.rb
+++ b/lib/pio/arp/message.rb
@@ -1,14 +1,18 @@
require 'pio/arp/format'
+require 'pio/message'
module Pio
class Arp
# Base class of ARP Request and Reply
- class Message
- private_class_method :new
+ class Message < Pio::Message
+ def self.create(format)
+ allocate.tap do |message|
+ message.instance_variable_set :@format, format
+ end
+ end
def initialize(user_options)
- options = self.class.const_get(:Options).new(user_options.dup.freeze)
- @format = Arp::Format.new(options.to_hash)
+ @format = Arp::Format.new(parse_options(user_options))
end
def method_missing(method, *args)
diff --git a/lib/pio/arp/reply.rb b/lib/pio/arp/reply.rb
index 30b4c43e..5650b820 100644
--- a/lib/pio/arp/reply.rb
+++ b/lib/pio/arp/reply.rb
@@ -1,43 +1,20 @@
require 'pio/arp/message'
+require 'pio/instance_inspector'
require 'pio/mac'
-require 'pio/options'
module Pio
class Arp
# ARP Reply packet generator
class Reply < Message
- OPERATION = 2
- public_class_method :new
+ include InstanceInspector
- # User options for creating an Arp Reply.
- class Options < Pio::Options
- mandatory_option :source_mac
- mandatory_option :destination_mac
- mandatory_option :sender_protocol_address
- mandatory_option :target_protocol_address
-
- def initialize(options)
- validate options
- @source_mac = Mac.new(options[:source_mac]).freeze
- @destination_mac = Mac.new(options[:destination_mac]).freeze
- @sender_protocol_address =
- IPv4Address.new(options[:sender_protocol_address]).freeze
- @target_protocol_address =
- IPv4Address.new(options[:target_protocol_address]).freeze
- end
-
- def to_hash
- {
- operation: OPERATION,
- source_mac: @source_mac,
- destination_mac: @destination_mac,
- sender_hardware_address: @source_mac,
- target_hardware_address: @destination_mac,
- sender_protocol_address: @sender_protocol_address,
- target_protocol_address: @target_protocol_address
- }.freeze
- end
- end
+ option :operation, value: 2
+ option :source_mac
+ option :destination_mac
+ option :sender_hardware_address, value: :source_mac
+ option :target_hardware_address, value: :destination_mac
+ option :sender_protocol_address
+ option :target_protocol_address
end
end
end
diff --git a/lib/pio/arp/request.rb b/lib/pio/arp/request.rb
index 0d240867..d1ae4dae 100644
--- a/lib/pio/arp/request.rb
+++ b/lib/pio/arp/request.rb
@@ -1,44 +1,20 @@
require 'pio/arp/message'
+require 'pio/instance_inspector'
require 'pio/mac'
-require 'pio/options'
module Pio
class Arp
# ARP Request packet generator
class Request < Message
- OPERATION = 1
- public_class_method :new
+ include InstanceInspector
- # User options for creating an Arp Request.
- class Options < Pio::Options
- mandatory_option :source_mac
- mandatory_option :sender_protocol_address
- mandatory_option :target_protocol_address
-
- BROADCAST_MAC = Mac.new(0xffffffffffff).freeze
- ALL_ZERO_MAC = Mac.new(0).freeze
-
- def initialize(options)
- validate options
- @source_mac = Mac.new(options[:source_mac]).freeze
- @sender_protocol_address =
- IPv4Address.new(options[:sender_protocol_address]).freeze
- @target_protocol_address =
- IPv4Address.new(options[:target_protocol_address]).freeze
- end
-
- def to_hash
- {
- operation: OPERATION,
- source_mac: @source_mac,
- destination_mac: BROADCAST_MAC,
- sender_hardware_address: @source_mac,
- target_hardware_address: ALL_ZERO_MAC,
- sender_protocol_address: @sender_protocol_address,
- target_protocol_address: @target_protocol_address
- }.freeze
- end
- end
+ option :operation, value: 1
+ option :source_mac
+ option :destination_mac, default: 'ff:ff:ff:ff:ff:ff'.freeze
+ option :sender_hardware_address, value: :source_mac
+ option :target_hardware_address, default: '00:00:00:00:00:00'.freeze
+ option :sender_protocol_address
+ option :target_protocol_address
end
end
end
diff --git a/lib/pio/class_inspector.rb b/lib/pio/class_inspector.rb
new file mode 100644
index 00000000..994e4265
--- /dev/null
+++ b/lib/pio/class_inspector.rb
@@ -0,0 +1,18 @@
+require 'active_support/core_ext/string/inflections'
+
+module Pio
+ # Introduces Class.inspect method
+ module ClassInspector
+ # rubocop:disable LineLength
+ def inspect
+ field_and_type = fields.each_with_object([]) do |each, result|
+ next if each.name == :padding
+ result << [each.name,
+ each.prototype.instance_variable_get(:@obj_class).name.demodulize.sub(/be$/, '').underscore]
+ end
+ signature = field_and_type.map { |field, type| "#{field}: #{type}" }.join(', ')
+ "#{self}(#{signature})"
+ end
+ # rubocop:enable LineLength
+ end
+end
diff --git a/lib/pio/dhcp.rb b/lib/pio/dhcp.rb
index 48761e44..7b5dcc08 100644
--- a/lib/pio/dhcp.rb
+++ b/lib/pio/dhcp.rb
@@ -22,7 +22,7 @@ class Dhcp
ROUTER_TLV,
DNS_TLV,
NTP_SERVERS_TLV
- ]
+ ].freeze
end
DHCP = Dhcp
end
@@ -41,7 +41,7 @@ class Dhcp
Offer::TYPE => Offer,
Request::TYPE => Request,
Ack::TYPE => Ack
- }
+ }.freeze
def self.read(raw_data)
begin
diff --git a/lib/pio/dhcp/frame.rb b/lib/pio/dhcp/frame.rb
index 1241179c..b8b451c2 100644
--- a/lib/pio/dhcp/frame.rb
+++ b/lib/pio/dhcp/frame.rb
@@ -12,12 +12,12 @@ class Frame < BinData::Record
OPTION_FIELD_LENGTH = 60
- include EthernetHeader
- include IPv4Header
+ include Ethernet
+ include IPv4
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
@@ -31,7 +31,7 @@ def to_binary
def ff_and_padding
padding_length = OPTION_FIELD_LENGTH - dhcp.optional_tlvs.num_bytes - 1
- "\xFF" + (padding_length > 0 ? "\x00" * padding_length : '')
+ [0xFF].pack('C') + (padding_length > 0 ? "\x00" * padding_length : '')
end
end
end
diff --git a/lib/pio/dhcp/optional_tlv.rb b/lib/pio/dhcp/optional_tlv.rb
index 0fe443cb..90a4491f 100644
--- a/lib/pio/dhcp/optional_tlv.rb
+++ b/lib/pio/dhcp/optional_tlv.rb
@@ -7,15 +7,15 @@ module Pio
class Dhcp
# DHCP Optional TLV
class OptionalTlv < BinData::Record
- DEFAULT = 'default'
+ DEFAULT = 'default'.freeze
endian :big
bit8 :tlv_type
bit8 :tlv_info_length,
- onlyif: -> { !(end_of_dhcpdu?) }
+ onlyif: -> { !end_of_dhcpdu? }
choice :tlv_value,
- onlyif: -> { !(end_of_dhcpdu?) },
+ onlyif: -> { !end_of_dhcpdu? },
selection: :chooser do
uint8 Dhcp::MESSAGE_TYPE_TLV
ip_address Dhcp::SERVER_IDENTIFIER_TLV
diff --git a/lib/pio/ethernet_frame.rb b/lib/pio/ethernet_frame.rb
new file mode 100644
index 00000000..cef4fde3
--- /dev/null
+++ b/lib/pio/ethernet_frame.rb
@@ -0,0 +1,20 @@
+require 'bindata'
+require 'pio/ethernet_header'
+require 'pio/class_inspector'
+require 'pio/instance_inspector'
+require 'pio/ruby_dumper'
+
+module Pio
+ # Ethernet frame parser
+ class EthernetFrame < BinData::Record
+ extend ClassInspector
+ include Ethernet
+ include InstanceInspector
+ include RubyDumper
+
+ endian :big
+
+ ethernet_header
+ rest :rest
+ end
+end
diff --git a/lib/pio/ethernet_header.rb b/lib/pio/ethernet_header.rb
index a580e1a8..e66d6ca7 100644
--- a/lib/pio/ethernet_header.rb
+++ b/lib/pio/ethernet_header.rb
@@ -1,10 +1,16 @@
+require 'pio/class_inspector'
+require 'pio/instance_inspector'
+require 'pio/ruby_dumper'
+require 'pio/type/ether_type'
require 'pio/type/mac_address'
module Pio
# Adds ethernet_header macro.
- module EthernetHeader
- # EtherType constants for ethernet_header.ether_type.
- module EtherType
+ module Ethernet
+ MINIMUM_FRAME_SIZE = 64
+
+ # EtherType constants
+ module Type
ARP = 0x0806
IPV4 = 0x0800
VLAN = 0x8100
@@ -12,28 +18,44 @@ module EtherType
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
+ ether_type :ether_type, value: options.fetch(:ether_type)
+ else
+ ether_type :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 vlan_vid
- vlan? ? vlan_vid_internal : 0xffff
+ def ethernet_header_length
+ vlan? ? 18 : 14
end
- def vlan_pcp
- vlan? ? vlan_pcp_internal : 0
- end
+ private
def vlan?
- ether_type == EtherType::VLAN
+ ether_type == Type::VLAN
end
end
+
+ # Ethernet header generator/parser
+ class EthernetHeader < BinData::Record
+ extend ClassInspector
+ include Ethernet
+ include InstanceInspector
+ include RubyDumper
+
+ endian :big
+
+ ethernet_header
+ end
end
diff --git a/lib/pio/icmp.rb b/lib/pio/icmp.rb
index 01278460..ed4b7b21 100644
--- a/lib/pio/icmp.rb
+++ b/lib/pio/icmp.rb
@@ -1,14 +1,17 @@
require 'pio/icmp/format'
require 'pio/icmp/reply'
require 'pio/icmp/request'
-require 'pio/message_type_selector'
+require 'pio/parse_error'
-# Packet parser and generator library.
module Pio
# Icmp parser and generator.
class Icmp
- extend MessageTypeSelector
- message_type Request::TYPE => Request, Reply::TYPE => Reply
+ def self.read(raw_data)
+ format = Format.read(raw_data)
+ { Request.icmp_type => Request,
+ Reply.icmp_type => Reply }.fetch(format.icmp_type).create(format)
+ rescue
+ raise Pio::ParseError, $ERROR_INFO.message
+ end
end
- ICMP = Icmp
end
diff --git a/lib/pio/icmp/format.rb b/lib/pio/icmp/format.rb
index 11a8d342..139140fa 100644
--- a/lib/pio/icmp/format.rb
+++ b/lib/pio/icmp/format.rb
@@ -1,37 +1,33 @@
-require 'bindata'
require 'pio/ethernet_header'
require 'pio/ipv4_header'
+require 'pio/monkey_patch/bindata_record'
module Pio
- # Icmp parser and generator.
+ # ICMP parser and generator
class Icmp
- # Icmp parser.
+ # ICMP format
class Format < BinData::Record
- MINIMUM_IP_PACKET_LENGTH = 50
-
- include EthernetHeader
- include IPv4Header
+ include Ethernet
+ include IPv4
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
uint16 :icmp_checksum, value: :calculate_icmp_checksum
uint16 :icmp_identifier
uint16 :icmp_sequence_number
- string :echo_data, read_length: :echo_data_read_length
- string :padding, read_length: 0, initial_value: :icmp_padding_length
-
- def message_type
- icmp_type
- end
-
- alias_method :to_binary, :to_binary_s
+ string :echo_data, read_length: :echo_data_length
+ string :padding, length: :padding_length
private
+ def icmp_header_length
+ 8
+ end
+
def calculate_icmp_checksum
sum = [icmp_type * 0x100 + icmp_code,
icmp_identifier,
@@ -40,14 +36,15 @@ def calculate_icmp_checksum
~((sum & 0xffff) + (sum >> 16)) & 0xffff
end
- def echo_data_read_length
- ip_total_length - (ip_header_length * 4) - 8
+ def echo_data_length
+ ip_total_length - ip_header_length_in_bytes - icmp_header_length
end
- def icmp_padding_length
- length = MINIMUM_IP_PACKET_LENGTH -
- (ip_header_length * 4) - 8 - echo_data.length
- length > 0 ? "\x00" * length : ''
+ def padding_length
+ tmp = Ethernet::MINIMUM_FRAME_SIZE -
+ ethernet_header_length - ip_header_length_in_bytes -
+ icmp_header_length - echo_data.length
+ tmp > 0 ? tmp : 0
end
end
end
diff --git a/lib/pio/icmp/message.rb b/lib/pio/icmp/message.rb
index 6cc94f03..fe05e227 100644
--- a/lib/pio/icmp/message.rb
+++ b/lib/pio/icmp/message.rb
@@ -1,14 +1,28 @@
+require 'pio/class_inspector'
require 'pio/icmp/format'
+require 'pio/instance_inspector'
+require 'pio/message'
+require 'pio/ruby_dumper'
module Pio
class Icmp
# Base class of Icmp::Request and Icmp::Reply.
- class Message
- private_class_method :new
+ class Message < Pio::Message
+ extend ClassInspector
+ include InstanceInspector
+
+ def self.fields
+ Icmp::Format.fields
+ end
+
+ def self.create(format)
+ allocate.tap do |message|
+ message.instance_variable_set :@format, format
+ end
+ end
def initialize(user_options)
- options = self.class.const_get(:Options).new(user_options)
- @format = Icmp::Format.new(options.to_hash)
+ @format = Icmp::Format.new(parse_options(user_options))
end
def method_missing(method, *args)
diff --git a/lib/pio/icmp/options.rb b/lib/pio/icmp/options.rb
deleted file mode 100644
index 8ae9632d..00000000
--- a/lib/pio/icmp/options.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-require 'pio/options'
-
-module Pio
- class Icmp
- # User options for creating an ICMP messages.
- class Options < Pio::Options
- def to_hash
- {
- icmp_type: @type,
- source_mac: @source_mac,
- destination_mac: @destination_mac,
- source_ip_address: @source_ip_address,
- destination_ip_address: @destination_ip_address,
- icmp_identifier: @identifier,
- icmp_sequence_number: @sequence_number,
- echo_data: @echo_data
- }.freeze
- end
- end
- end
-end
diff --git a/lib/pio/icmp/reply.rb b/lib/pio/icmp/reply.rb
index 79954e6c..71693c90 100644
--- a/lib/pio/icmp/reply.rb
+++ b/lib/pio/icmp/reply.rb
@@ -1,43 +1,17 @@
require 'pio/icmp/message'
-require 'pio/icmp/options'
-require 'pio/mac'
module Pio
class Icmp
# ICMP Reply packet generator
class Reply < Message
- TYPE = 0
- public_class_method :new
-
- # User options for creating an ICMP Reply.
- class Options < Pio::Icmp::Options
- mandatory_option :source_mac
- mandatory_option :destination_mac
- mandatory_option :source_ip_address
- mandatory_option :destination_ip_address
- mandatory_option :identifier
- mandatory_option :sequence_number
- option :echo_data
-
- # rubocop:disable MethodLength
- # rubocop:disable AbcSize
- def initialize(options)
- validate options
- @type = TYPE
-
- @source_mac = Mac.new(options[:source_mac]).freeze
- @destination_mac = Mac.new(options[:destination_mac]).freeze
- @source_ip_address =
- IPv4Address.new(options[:source_ip_address]).freeze
- @destination_ip_address =
- IPv4Address.new(options[:destination_ip_address]).freeze
- @identifier = options[:identifier]
- @sequence_number = options[:sequence_number]
- @echo_data = options[:echo_data] || ''
- end
- # rubocop:enable AbcSize
- # rubocop:enable MethodLength
- end
+ option :icmp_type, value: 0
+ option :source_mac
+ option :destination_mac
+ option :source_ip_address
+ option :destination_ip_address
+ option :icmp_identifier
+ option :icmp_sequence_number
+ option :echo_data, default: ''
end
end
end
diff --git a/lib/pio/icmp/request.rb b/lib/pio/icmp/request.rb
index cf8fa8dc..2298165e 100644
--- a/lib/pio/icmp/request.rb
+++ b/lib/pio/icmp/request.rb
@@ -1,52 +1,17 @@
require 'pio/icmp/message'
-require 'pio/icmp/options'
-require 'pio/mac'
module Pio
class Icmp
# ICMP Request packet generator
class Request < Message
- TYPE = 8
- public_class_method :new
-
- # User options for creating an ICMP Request.
- class Options < Pio::Icmp::Options
- DEFAULT_IDENTIFIER = 0x0100
- DEFAULT_SEQUENCE_NUMBER = 0
-
- mandatory_option :source_mac
- mandatory_option :destination_mac
- mandatory_option :source_ip_address
- mandatory_option :destination_ip_address
- option :identifier
- option :sequence_number
- option :echo_data
-
- # rubocop:disable MethodLength
- # rubocop:disable AbcSize
- def initialize(options)
- validate options
- @type = TYPE
-
- @source_mac = Mac.new(options[:source_mac]).freeze
- @destination_mac = Mac.new(options[:destination_mac]).freeze
- @source_ip_address =
- IPv4Address.new(options[:source_ip_address]).freeze
- @destination_ip_address =
- IPv4Address.new(options[:destination_ip_address]).freeze
- @identifier =
- options[:icmp_identifier] ||
- options[:identifier] ||
- DEFAULT_IDENTIFIER
- @sequence_number =
- options[:icmp_sequence_number] ||
- options[:sequence_number] ||
- DEFAULT_SEQUENCE_NUMBER
- @echo_data = options[:echo_data] || ''
- end
- # rubocop:enable AbcSize
- # rubocop:enable MethodLength
- end
+ option :icmp_type, value: 8
+ option :source_mac
+ option :destination_mac
+ option :source_ip_address
+ option :destination_ip_address
+ option :icmp_identifier, default: 0
+ option :icmp_sequence_number, default: 0
+ option :echo_data, default: ''
end
end
end
diff --git a/lib/pio/instance_inspector.rb b/lib/pio/instance_inspector.rb
new file mode 100644
index 00000000..48d9f0b0
--- /dev/null
+++ b/lib/pio/instance_inspector.rb
@@ -0,0 +1,14 @@
+module Pio
+ # Introduces #inspect method
+ module InstanceInspector
+ def inspect
+ "#<#{self.class.name} " +
+ field_names.map do |each|
+ next if each == :padding
+ next unless __send__("#{each}?")
+ "#{each}: #{__send__(each).inspect}"
+ end.compact.join(', ') +
+ '>'
+ end
+ end
+end
diff --git a/lib/pio/ipv4_address.rb b/lib/pio/ipv4_address.rb
index 74490050..ba16bd88 100644
--- a/lib/pio/ipv4_address.rb
+++ b/lib/pio/ipv4_address.rb
@@ -21,14 +21,14 @@ class IPv4Address
# @return [IPv4Address] self
# a proxy to IPAddr.
def initialize(addr)
- case addr
- when Integer
- @value = IPAddr.new(addr, Socket::AF_INET)
- when String
- @value = IPAddr.new(addr)
- else
- @value = addr.value
- end
+ @value = case addr
+ when Integer
+ IPAddr.new(addr, Socket::AF_INET)
+ when String
+ IPAddr.new(addr)
+ else
+ addr.value
+ end
end
# @return [String] the IPv4 address in its text representation.
@@ -78,14 +78,14 @@ def mask!(masklen)
@value = @value.mask(masklen)
self
end
- alias_method :prefix!, :mask!
+ alias prefix! mask!
# Returns the IPv4 address masked with +masklen+.
# @return [IPv4Address]
def mask(masklen)
clone.mask!(masklen)
end
- alias_method :prefix, :mask
+ alias prefix mask
# @return [bool]
# Returns true if the address belongs to class A.
@@ -110,7 +110,7 @@ def class_c?
def class_d?
mask(4).to_s == '224.0.0.0'
end
- alias_method :multicast?, :class_d?
+ alias multicast? class_d?
# @return [bool]
# Returns true if the address belongs to class E.
diff --git a/lib/pio/ipv4_header.rb b/lib/pio/ipv4_header.rb
index 2e9e786e..4d44c009 100644
--- a/lib/pio/ipv4_header.rb
+++ b/lib/pio/ipv4_header.rb
@@ -1,9 +1,12 @@
+require 'pio/instance_inspector'
+require 'pio/monkey_patch/bindata_string'
require 'pio/payload'
+require 'pio/ruby_dumper'
require 'pio/type/ip_address'
module Pio
# IP Version 4 Header Format
- module IPv4Header
+ module IPv4
# Internet protocol numbers for ipv4_header.ip_protocol
module ProtocolNumber
ICMP = 1
@@ -39,7 +42,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,
@@ -53,6 +56,10 @@ def to_exact_match(in_port)
end
# rubocop:enable MethodLength
+ def ip_header_length_in_bytes
+ ip_header_length * 4
+ end
+
private
def calculate_ip_length
@@ -82,4 +89,19 @@ def ip_option_length
20 - ip_header_length * 4
end
end
+
+ # IPv4 header generator/parser
+ class IPv4Header < BinData::Record
+ include IPv4
+ include InstanceInspector
+ include RubyDumper
+
+ ipv4_header
+
+ # rubocop:disable LineLength
+ def self.inspect
+ 'IPv4Header(ip_version: bit4, ip_header_length: bit4, ip_type_of_service: uint8, ip_total_length: uint16, ip_identifier: uint16, ip_flag: bit3, ip_fragment: bit13, ip_ttl: uint8, ip_protocol: uint8, ip_header_checksum: uint16, source_ip_address: ip_address, destination_ip_address: ip_address, ip_option: string)'
+ end
+ # rubocop:enable LineLength
+ end
end
diff --git a/lib/pio/lldp/frame.rb b/lib/pio/lldp/frame.rb
index 8038a916..7909c8c7 100644
--- a/lib/pio/lldp/frame.rb
+++ b/lib/pio/lldp/frame.rb
@@ -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
diff --git a/lib/pio/lldp/optional_tlv.rb b/lib/pio/lldp/optional_tlv.rb
index 842f3041..8526db9d 100644
--- a/lib/pio/lldp/optional_tlv.rb
+++ b/lib/pio/lldp/optional_tlv.rb
@@ -29,7 +29,7 @@ class OptionalTlv < BinData::Record
end
def end_of_lldpdu?
- tlv_type == 0
+ tlv_type.zero?
end
def chooser
@@ -52,7 +52,7 @@ def optional_tlv?
end
def end_of_lldpdu_tlv?
- tlv_type == 0
+ tlv_type.zero?
end
end
end
diff --git a/lib/pio/mac.rb b/lib/pio/mac.rb
index b4a2fe5a..430ccdcc 100644
--- a/lib/pio/mac.rb
+++ b/lib/pio/mac.rb
@@ -29,7 +29,7 @@ def initialize(value)
@value = value.to_int
validate_value_range
else
- fail TypeError
+ raise TypeError
end
rescue ArgumentError, TypeError
raise InvalidValueError, "Invalid MAC address: #{value.inspect}"
@@ -91,6 +91,10 @@ def to_a
to_s.split(':').map(&:hex)
end
+ def to_hex
+ to_a.map(&:to_hex).join(', ')
+ end
+
# @!endgroup
# @!group Predicates
@@ -202,12 +206,12 @@ def parse_mac_string(mac)
when /^(?:#{octet}(:)){5}#{octet}$/, /^(?:#{doctet}(\.)){2}#{doctet}$/
mac.gsub(Regexp.last_match[1], '').hex
else
- fail ArgumentError
+ raise ArgumentError
end
end
def validate_value_range
- fail ArgumentError unless @value >= 0 && @value <= 0xffffffffffff
+ raise ArgumentError unless @value >= 0 && @value <= 0xffffffffffff
end
end
end
diff --git a/lib/pio/message.rb b/lib/pio/message.rb
new file mode 100644
index 00000000..c9b596a5
--- /dev/null
+++ b/lib/pio/message.rb
@@ -0,0 +1,50 @@
+require 'active_support/core_ext/class/attribute'
+require 'pio/ruby_dumper'
+
+module Pio
+ # Base message class
+ class Message
+ include RubyDumper
+
+ class_attribute :options
+
+ def self.option(name, value: nil, default: nil)
+ self.options ||= {}
+ if value && !value.is_a?(Symbol)
+ class_eval { class_attribute name }
+ class_eval { __send__("#{name}=", value) }
+ end
+ self.options.merge! name => { value: value, default: default }
+ end
+
+ private
+
+ # rubocop:disable MethodLength
+ # rubocop:disable AbcSize
+ # rubocop:disable PerceivedComplexity
+ # rubocop:disable CyclomaticComplexity
+ def parse_options(user_options)
+ options = {}
+ self.class.options.each_pair do |key, attrs|
+ if !attrs[:value] && !attrs[:default]
+ begin
+ options[key] = user_options.fetch(key)
+ rescue
+ raise "#{key} option is a mandatory"
+ end
+ elsif attrs[:value] && attrs[:value].is_a?(Symbol)
+ options[key] = user_options.fetch(attrs[:value])
+ elsif attrs[:value]
+ options[key] = user_options[key] || attrs[:value]
+ else
+ options[key] = user_options[key] || attrs[:default]
+ end
+ end
+ options
+ end
+ # rubocop:enable MethodLength
+ # rubocop:enable AbcSize
+ # rubocop:enable PerceivedComplexity
+ # rubocop:enable CyclomaticComplexity
+ end
+end
diff --git a/lib/pio/monkey_patch/bindata_record.rb b/lib/pio/monkey_patch/bindata_record.rb
new file mode 100644
index 00000000..44fe52d6
--- /dev/null
+++ b/lib/pio/monkey_patch/bindata_record.rb
@@ -0,0 +1,6 @@
+module BinData
+ # Add BinData::Record#to_binary
+ class Record
+ alias to_binary to_binary_s
+ end
+end
diff --git a/lib/pio/monkey_patch/bindata_string.rb b/lib/pio/monkey_patch/bindata_string.rb
new file mode 100644
index 00000000..2ee54820
--- /dev/null
+++ b/lib/pio/monkey_patch/bindata_string.rb
@@ -0,0 +1,10 @@
+module BinData
+ # Add BinData::String#to_bytes
+ class String
+ def to_bytes
+ to_s.unpack('H*').pop.scan(/[0-9a-f]{2}/).map do |each|
+ "0x#{each}"
+ end.join(', ')
+ end
+ end
+end
diff --git a/lib/pio/monkey_patch/integer/base_conversions.rb b/lib/pio/monkey_patch/integer/base_conversions.rb
index 6782bd52..8d60f5cc 100644
--- a/lib/pio/monkey_patch/integer/base_conversions.rb
+++ b/lib/pio/monkey_patch/integer/base_conversions.rb
@@ -3,7 +3,7 @@ module Integer
# to_hex etc.
module BaseConversions
def to_hex
- format '%#x', self
+ format '0x%02x', self
end
end
end
diff --git a/lib/pio/monkey_patch/uint.rb b/lib/pio/monkey_patch/uint.rb
new file mode 100644
index 00000000..9673cfb9
--- /dev/null
+++ b/lib/pio/monkey_patch/uint.rb
@@ -0,0 +1,9 @@
+require 'bindata'
+require 'pio/monkey_patch/uint/base_conversions'
+
+# BinData's base module
+module BinData
+ Uint8.__send__ :include, MonkeyPatch::Uint::BaseConversions
+ Uint16be.__send__ :include, MonkeyPatch::Uint::BaseConversions
+ Uint32be.__send__ :include, MonkeyPatch::Uint::BaseConversions
+end
diff --git a/lib/pio/monkey_patch/uint/base_conversions.rb b/lib/pio/monkey_patch/uint/base_conversions.rb
new file mode 100644
index 00000000..e28132ed
--- /dev/null
+++ b/lib/pio/monkey_patch/uint/base_conversions.rb
@@ -0,0 +1,14 @@
+module MonkeyPatch
+ module Uint
+ # Uint8#to_bytes, Uintxxbe#to_bytes
+ module BaseConversions
+ def to_bytes
+ /Uint(8|\d+be)$/=~ self.class.name
+ nbyte = Regexp.last_match(1).to_i / 4
+ format("%0#{nbyte}x", to_i).scan(/.{1,2}/).map do |each|
+ '0x' + each
+ end.join(', ')
+ end
+ end
+ end
+end
diff --git a/lib/pio/open_flow.rb b/lib/pio/open_flow.rb
index 65b62649..841cc0d4 100644
--- a/lib/pio/open_flow.rb
+++ b/lib/pio/open_flow.rb
@@ -1,67 +1,53 @@
-require 'pio/open_flow/datapath_id'
-require 'pio/open_flow/error'
-require 'pio/open_flow/flags'
-require 'pio/open_flow/message'
-require 'pio/open_flow/open_flow_header'
-require 'pio/open_flow10'
-require 'pio/open_flow13'
+require 'active_support/core_ext/array/access'
+require 'active_support/core_ext/module/attribute_accessors'
+require 'active_support/core_ext/module/introspection'
+require 'pio/open_flow/header'
+require 'pio/open_flow/parser'
module Pio
# Common OpenFlow modules/classes.
module OpenFlow
- def self.version
- fail unless @version
- @version
- end
+ mattr_reader :version, instance_reader: false
- def self.switch_version(version)
- [:Barrier, :Echo, :Features, :FlowMod, :Hello, :Match,
- :PacketIn, :FlowRemoved, :PacketOut, :SendOutPort,
- :SetSourceMacAddress, :SetDestinationMacAddress, :PortStatus, :Stats,
- :FlowStats, :DescriptionStats, :AggregateStats, :TableStats, :PortStats,
- :QueueStats, :Error, :SetArpOperation, :SetArpSenderProtocolAddress,
- :SetArpSenderHardwareAddress, :NiciraRegMove, :SetMetadata,
- :NiciraRegLoad, :NiciraSendOutPort].each do |each|
- set_message_class_name each, version
- @version = version.to_s
+ def self.version=(version)
+ return if OpenFlow.version == version.to_sym
+ find_all_class_by_version(version).each do |each|
+ alias_open_flow_class each
end
+ @@version = version.to_sym # rubocop:disable ClassVars
+ rescue NameError
+ raise "#{version} is not supported yet."
end
- # rubocop:disable MethodLength
def self.read(binary)
- parser = {
- 0 => Pio::Hello,
- 1 => Pio::OpenFlow::Error,
- 2 => Pio::Echo::Request,
- 3 => Pio::Echo::Reply,
- 5 => Pio::Features::Request,
- 6 => Pio::Features::Reply,
- 10 => Pio::PacketIn,
- 11 => Pio::FlowRemoved,
- 12 => Pio::PortStatus,
- 13 => Pio::PacketOut,
- 14 => Pio::FlowMod,
- 16 => Pio::Stats::Request,
- 17 => Pio::Stats::Reply,
- 18 => Pio::Barrier::Request,
- 19 => Pio::Barrier::Reply
- }
- header = OpenFlowHeaderParser.read(binary)
- parser.fetch(header.message_type).read(binary)
+ header = OpenFlow::Header.read(binary)
+ self.version = header.version
+ Parser.find_by_type!(header.type).read(binary)
end
- # rubocop:enable MethodLength
- def self.set_message_class_name(klass_name, version)
- open_flow_module = Pio.const_get(version)
- return unless open_flow_module.const_defined?(klass_name)
- Pio.__send__ :remove_const, klass_name if Pio.const_defined?(klass_name)
- Pio.const_set(klass_name, open_flow_module.const_get(klass_name))
- rescue NameError
- raise "#{version} is not supported yet."
+ def self.find_all_class_by_version(version)
+ all_class = [Message, Instruction, Action, FlowMatch].
+ inject([]) do |result, each|
+ result + each.descendants
+ end
+ all_class.select do |each|
+ each.parents.include?(Class.const_get("Pio::#{version}"))
+ end
end
- private_class_method :set_message_class_name
+ private_class_method :find_all_class_by_version
- # The default OpenFlow version is 1.0
- switch_version 'OpenFlow10'
+ # Pio::OpenFlow10::Hello -> Pio::Hello
+ def self.alias_open_flow_class(klass)
+ version = klass.name.split('::').second
+ class_name = klass.name.split('::').third
+ if Pio.const_defined?(class_name)
+ Pio.module_eval { remove_const class_name }
+ end
+ Pio.const_set(class_name, "Pio::#{version}::#{class_name}".constantize)
+ end
+ private_class_method :alias_open_flow_class
end
end
+
+# The default OpenFlow version is 1.0
+Pio::OpenFlow.version = :OpenFlow10
diff --git a/lib/pio/open_flow/action.rb b/lib/pio/open_flow/action.rb
index 1b8db9a1..4f4f8d76 100644
--- a/lib/pio/open_flow/action.rb
+++ b/lib/pio/open_flow/action.rb
@@ -1,12 +1,11 @@
+require 'active_support/descendants_tracker'
require 'bindata'
module Pio
module OpenFlow
# OpenFlow actions.
class Action
- def self.inherited(child_klass)
- child_klass.const_set :Format, Class.new(BinData::Record)
- end
+ extend ActiveSupport::DescendantsTracker
def self.action_header(options)
module_eval do
@@ -24,7 +23,12 @@ def self.read(raw_data)
end
def self.method_missing(method, *args, &block)
- const_get(:Format).__send__ method, *args, &block
+ begin
+ const_get(:Format).__send__ method, *args, &block
+ rescue NameError
+ const_set :Format, Class.new(BinData::Record)
+ retry
+ end
return if method == :endian || method == :virtual
define_method(args.first) { @format.__send__ args.first }
end
diff --git a/lib/pio/open_flow/buffer_id.rb b/lib/pio/open_flow/buffer_id.rb
new file mode 100644
index 00000000..d74a7083
--- /dev/null
+++ b/lib/pio/open_flow/buffer_id.rb
@@ -0,0 +1,19 @@
+module Pio
+ module OpenFlow
+ # Buffered packet to apply to, or :no_buffer.
+ class BufferId < BinData::Primitive
+ NO_BUFFER = 0xffffffff
+
+ endian :big
+ uint32 :buffer_id, initial_value: NO_BUFFER
+
+ def get
+ buffer_id == NO_BUFFER ? :no_buffer : buffer_id
+ end
+
+ def set(value)
+ self.buffer_id = (value == :no_buffer ? NO_BUFFER : value)
+ end
+ end
+ end
+end
diff --git a/lib/pio/open_flow/datapath_id.rb b/lib/pio/open_flow/datapath_id.rb
index a9576ee2..b18e0949 100644
--- a/lib/pio/open_flow/datapath_id.rb
+++ b/lib/pio/open_flow/datapath_id.rb
@@ -12,14 +12,14 @@ class DatapathId < BinData::Primitive
def set(value)
unless value.unsigned_64bit?
- fail(ArgumentError,
- 'Datapath ID should be an unsigned 64-bit integer.')
+ raise(ArgumentError,
+ 'Datapath ID should be an unsigned 64-bit integer.')
end
self.datapath_id = value
end
def get
- datapath_id
+ datapath_id.to_i
end
end
end
diff --git a/lib/pio/open_flow/error.rb b/lib/pio/open_flow/error.rb
deleted file mode 100644
index d2937601..00000000
--- a/lib/pio/open_flow/error.rb
+++ /dev/null
@@ -1,19 +0,0 @@
-module Pio
- module OpenFlow
- # Error message parser
- class Error
- def self.read(binary)
- version = OpenFlowHeaderParser.read(binary).ofp_version
- error_parser = case version
- when 1
- Pio::OpenFlow10::Error
- when 4
- Pio::OpenFlow13::Error
- else
- fail "Unsupported OpenFlow version: #{version}"
- end
- error_parser.read binary
- end
- end
- end
-end
diff --git a/lib/pio/open_flow/error_message.rb b/lib/pio/open_flow/error_message.rb
new file mode 100644
index 00000000..f350c43c
--- /dev/null
+++ b/lib/pio/open_flow/error_message.rb
@@ -0,0 +1,32 @@
+require 'active_support/core_ext/module/attribute_accessors'
+require 'active_support/core_ext/string/inflections'
+
+module Pio
+ module OpenFlow
+ # Error message parser
+ module ErrorMessage
+ mattr_reader(:type) { 1 }
+
+ # rubocop:disable AbcSize
+ def read(binary)
+ body = OpenFlow::Header.read(binary).body
+ error = const_get(:BodyParser).read(body)
+ klass = error_classes.find do |each|
+ each.name.split('::').last.underscore == error.error_type.to_s
+ end
+ unless klass
+ raise 'Unknown error message '\
+ "(type=#{error.error_type}, code=#{error.error_code})"
+ end
+ klass.read binary
+ end
+ # rubocop:enable AbcSize
+
+ def error_classes
+ OpenFlow::Message.descendants.select do |each|
+ each.parents.include? parent.const_get(:Error)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/pio/open_flow/flags.rb b/lib/pio/open_flow/flags.rb
index cc797e5f..cd756dfd 100644
--- a/lib/pio/open_flow/flags.rb
+++ b/lib/pio/open_flow/flags.rb
@@ -3,17 +3,21 @@ module OpenFlow
# bitmap functions.
# This class smells of :reek:DataClump
module Flags
+ def define_flags_32bit(name, flags)
+ _define_flags name, 32, flags
+ end
+
def flags_32bit(name, flags)
- _def_flags name, 32, flags
+ _flags name, 32, flags
end
def flags_16bit(name, flags)
- _def_flags name, 16, flags
+ _flags name, 16, flags
end
# rubocop:disable MethodLength
# This method smells of :reek:TooManyStatements
- def _def_flags(name, size, flags)
+ def _define_flags(name, size, flags)
flag_value = case flags
when Array
shift = 0
@@ -29,7 +33,7 @@ def _def_flags(name, size, flags)
klass_name = name.to_s.split('_').map(&:capitalize).join
flags_hash = flag_value.inspect
- code = %{
+ module_eval <<-end_eval, __FILE__, __LINE__
class #{klass_name} < BinData::Primitive
endian :big
@@ -53,8 +57,13 @@ def set(v)
v.map { |each| list[each] }.inject(:|)
end
end
- }
- module_eval code
+ end_eval
+ end
+ # rubocop:enable MethodLength
+
+ def _flags(name, size, flags_)
+ _define_flags name, size, flags_
+ module_eval "#{name} :#{name}", __FILE__, __LINE__
end
end
end
diff --git a/lib/pio/open_flow/flow_match.rb b/lib/pio/open_flow/flow_match.rb
new file mode 100644
index 00000000..3c825c16
--- /dev/null
+++ b/lib/pio/open_flow/flow_match.rb
@@ -0,0 +1,10 @@
+require 'active_support/descendants_tracker'
+
+module Pio
+ module OpenFlow
+ # Flow match
+ class FlowMatch
+ extend ActiveSupport::DescendantsTracker
+ end
+ end
+end
diff --git a/lib/pio/open_flow/header.rb b/lib/pio/open_flow/header.rb
new file mode 100644
index 00000000..a5cfd440
--- /dev/null
+++ b/lib/pio/open_flow/header.rb
@@ -0,0 +1,26 @@
+require 'bindata'
+require 'pio/monkey_patch/uint'
+require 'pio/open_flow/transaction_id'
+require 'pio/open_flow/version'
+
+module Pio
+ module OpenFlow
+ # OpenFlow message header parser
+ class Header < BinData::Record
+ endian :big
+
+ version :version
+ uint8 :type
+ uint16 :message_length
+ transaction_id :transaction_id
+ rest :body
+
+ def to_bytes
+ [version,
+ type,
+ message_length,
+ transaction_id].map(&:to_bytes).join(', ')
+ end
+ end
+ end
+end
diff --git a/lib/pio/open_flow/hello_failed_code.rb b/lib/pio/open_flow/hello_failed_code.rb
index 76f30c75..25faa437 100644
--- a/lib/pio/open_flow/hello_failed_code.rb
+++ b/lib/pio/open_flow/hello_failed_code.rb
@@ -4,7 +4,7 @@ module Pio
module OpenFlow
# enum ofp_hello_failed_code
class HelloFailedCode < BinData::Primitive
- ERROR_CODES = { incompatible: 0, permissions_error: 1 }
+ ERROR_CODES = { incompatible: 0, permissions_error: 1 }.freeze
endian :big
uint16 :error_code
diff --git a/lib/pio/open_flow/instruction.rb b/lib/pio/open_flow/instruction.rb
new file mode 100644
index 00000000..1901fb90
--- /dev/null
+++ b/lib/pio/open_flow/instruction.rb
@@ -0,0 +1,10 @@
+require 'active_support/descendants_tracker'
+
+module Pio
+ module OpenFlow
+ # Flow instruction
+ class Instruction
+ extend ActiveSupport::DescendantsTracker
+ end
+ end
+end
diff --git a/lib/pio/open_flow/message.rb b/lib/pio/open_flow/message.rb
index f0a09212..49eebbca 100644
--- a/lib/pio/open_flow/message.rb
+++ b/lib/pio/open_flow/message.rb
@@ -1,15 +1,19 @@
+require 'active_support/core_ext/module/attribute_accessors'
+require 'active_support/descendants_tracker'
require 'bindata'
+require 'pio/open_flow/flags'
+require 'pio/open_flow/header'
require 'pio/parse_error'
-require 'pio/open_flow/open_flow_header'
+require 'pio/ruby_dumper'
module Pio
module OpenFlow
# OpenFlow messages.
class Message
- def self.inherited(child_klass)
- child_klass.const_set :Format, Class.new(BinData::Record)
- child_klass.class_variable_set(:@@valid_options, [])
- end
+ attr_reader :format
+
+ extend ActiveSupport::DescendantsTracker
+ extend OpenFlow::Flags
def self.read(raw_data)
allocate.tap do |message|
@@ -21,31 +25,62 @@ def self.read(raw_data)
raise Pio::ParseError, "Invalid #{message_name} message."
end
+ # rubocop:disable MethodLength
+ # rubocop:disable AbcSize
def self.method_missing(method, *args, &block)
- const_get(:Format).__send__ method, *args, &block
+ begin
+ const_get(:Format).__send__ method, *args, &block
+ rescue NameError
+ klass = Class.new(BinData::Record)
+ const_set :Format, klass
+ klass.class_eval do
+ include RubyDumper
+ define_method(:header_length) { 8 }
+ define_method(:length) { _length }
+ end
+ class_variable_set(:@@valid_options, [])
+ retry
+ end
return if method == :endian || method == :virtual
+
define_method(args.first) do
- @format.__send__ args.first
+ snapshot = @format.snapshot.__send__(args.first)
+ if snapshot.class == BinData::Struct::Snapshot
+ @format.__send__(args.first)
+ else
+ snapshot
+ end
end
class_variable_set(:@@valid_options,
class_variable_get(:@@valid_options) + [args.first])
end
+ # rubocop:enable MethodLength
+ # rubocop:enable AbcSize
# rubocop:disable AbcSize
# rubocop:disable MethodLength
def self.open_flow_header(opts)
module_eval do
+ cattr_reader(:type) { opts.fetch(:type) }
+
endian :big
- uint8 :ofp_version, value: opts.fetch(:version)
- virtual assert: -> { ofp_version == opts.fetch(:version) }
- uint8 :message_type, value: opts.fetch(:message_type)
- virtual assert: -> { message_type == opts.fetch(:message_type) }
- uint16 :message_length,
- initial_value: opts[:message_length] || -> { 8 + body.length }
+ uint8 :version, value: opts.fetch(:version)
+ uint8 :type, value: opts.fetch(:type)
+ uint16(:_length,
+ initial_value: opts[:length] || lambda do
+ begin
+ 8 + body.length
+ rescue
+ 8
+ end
+ end)
transaction_id :transaction_id, initial_value: 0
+ virtual assert: -> { version == opts.fetch(:version) }
+ virtual assert: -> { type == opts.fetch(:type) }
+
alias_method :xid, :transaction_id
end
end
@@ -71,7 +106,7 @@ def validate_user_options(user_options)
unknown_options =
user_options.keys - self.class.class_variable_get(:@@valid_options)
return if unknown_options.empty?
- fail "Unknown option: #{unknown_options.first}"
+ raise "Unknown option: #{unknown_options.first}"
end
def parse_options(user_options)
diff --git a/lib/pio/open_flow/nicira_resubmit.rb b/lib/pio/open_flow/nicira_resubmit.rb
index 61925cdc..216fe20f 100644
--- a/lib/pio/open_flow/nicira_resubmit.rb
+++ b/lib/pio/open_flow/nicira_resubmit.rb
@@ -1,18 +1,20 @@
require 'pio/open_flow/nicira_action'
-require 'pio/open_flow10/port16'
module Pio
- # NXAST_RESUBMIT action
- class NiciraResubmit < OpenFlow::NiciraAction
- nicira_action_header action_type: 0xffff,
- action_length: 16,
- subtype: 1
- port16 :in_port
- string :padding, length: 4
- hide :padding
+ module OpenFlow
+ # NXAST_RESUBMIT action
+ class NiciraResubmit < OpenFlow::NiciraAction
+ nicira_action_header action_type: 0xffff,
+ action_length: 16,
+ subtype: 1
+ uint16 :in_port
+ string :padding, length: 4
+ hide :padding
- def initialize(port_number)
- super(in_port: port_number)
+ def initialize(port_number)
+ super(in_port: port_number)
+ end
end
end
+ NiciraResubmit = OpenFlow::NiciraResubmit
end
diff --git a/lib/pio/open_flow/nicira_resubmit_table.rb b/lib/pio/open_flow/nicira_resubmit_table.rb
index 5ea9ab97..6d30d221 100644
--- a/lib/pio/open_flow/nicira_resubmit_table.rb
+++ b/lib/pio/open_flow/nicira_resubmit_table.rb
@@ -1,15 +1,22 @@
require 'pio/open_flow/nicira_action'
-require 'pio/open_flow10/port16'
module Pio
- # NXAST_RESUBMIT_TABLE action
- class NiciraResubmitTable < OpenFlow::NiciraAction
- nicira_action_header action_type: 0xffff,
- action_length: 16,
- subtype: 14
- port16 :in_port
- uint8 :table, initial_value: 0xff
- string :padding, length: 3
- hide :padding
+ module OpenFlow
+ # NXAST_RESUBMIT_TABLE action
+ class NiciraResubmitTable < OpenFlow::NiciraAction
+ nicira_action_header action_type: 0xffff,
+ action_length: 16,
+ subtype: 14
+ uint16 :in_port
+ uint8 :table, initial_value: 0xff
+ string :padding, length: 3
+ hide :padding
+
+ def initialize(options)
+ raise ':in_port option is a mandatory' unless options.key?(:in_port)
+ super options
+ end
+ end
end
+ NiciraResubmitTable = OpenFlow::NiciraResubmitTable
end
diff --git a/lib/pio/open_flow/open_flow_header.rb b/lib/pio/open_flow/open_flow_header.rb
deleted file mode 100644
index bc894866..00000000
--- a/lib/pio/open_flow/open_flow_header.rb
+++ /dev/null
@@ -1,31 +0,0 @@
-require 'bindata'
-require 'pio/open_flow/transaction_id'
-
-module Pio
- # OpenFlow message header parser
- class OpenFlowHeaderParser < BinData::Record
- endian :big
-
- uint8 :ofp_version
- uint8 :message_type
- uint16 :message_length
- transaction_id :transaction_id
- rest :body
- end
-
- # OpenFlow message header.
- class OfpHeader < BinData::Record
- endian :big
-
- uint8 :ofp_version, value: :version_value
- virtual assert: -> { ofp_version == version_value }
- uint8 :message_type, value: :message_type_value
- virtual assert: -> { message_type == message_type_value }
- uint16 :message_length, initial_value: -> { length + body_length }
- transaction_id :transaction_id, initial_value: 0
-
- def length
- 8
- end
- end
-end
diff --git a/lib/pio/open_flow/parser.rb b/lib/pio/open_flow/parser.rb
new file mode 100644
index 00000000..73594131
--- /dev/null
+++ b/lib/pio/open_flow/parser.rb
@@ -0,0 +1,19 @@
+require 'pio/open_flow10'
+require 'pio/open_flow13'
+
+module Pio
+ module OpenFlow
+ # Collection class of OpenFlow message parser class
+ class Parser
+ def self.find_by_type!(type)
+ message_class = [Hello, Error, Echo::Request, Echo::Reply,
+ Features::Request, Features::Reply, PacketIn,
+ PacketOut, FlowMod, PortStatus, Stats::Request,
+ Stats::Reply, Barrier::Request, Barrier::Reply]
+ message_class.each_with_object({}) do |each, hash|
+ hash[each.type] = each
+ end.fetch(type)
+ end
+ end
+ end
+end
diff --git a/lib/pio/open_flow/port.rb b/lib/pio/open_flow/port.rb
index 9b806a8b..34be270c 100644
--- a/lib/pio/open_flow/port.rb
+++ b/lib/pio/open_flow/port.rb
@@ -57,9 +57,9 @@ def set(port)
self.port = reserved_port_number(port)
else
port_num = port.to_i
- fail ArgumentError, 'The port should be > 0' if port_num < 1
+ raise ArgumentError, 'The port should be > 0' if port_num < 1
if port_num >= max
- fail ArgumentError, "The port should be < #{max.to_hex}"
+ raise ArgumentError, "The port should be < #{max.to_hex}"
end
self.port = port_num
end
diff --git a/lib/pio/open_flow/transaction_id.rb b/lib/pio/open_flow/transaction_id.rb
index bbbfdf39..d7e93672 100644
--- a/lib/pio/open_flow/transaction_id.rb
+++ b/lib/pio/open_flow/transaction_id.rb
@@ -11,8 +11,8 @@ class TransactionId < BinData::Primitive
def set(value)
unless value.unsigned_32bit?
- fail(ArgumentError,
- 'Transaction ID should be an unsigned 32-bit integer.')
+ raise(ArgumentError,
+ 'Transaction ID should be an unsigned 32-bit integer.')
end
self.xid = value
end
diff --git a/lib/pio/open_flow/version.rb b/lib/pio/open_flow/version.rb
new file mode 100644
index 00000000..eff1659d
--- /dev/null
+++ b/lib/pio/open_flow/version.rb
@@ -0,0 +1,22 @@
+module Pio
+ module OpenFlow
+ # OpenFlow version
+ class Version < BinData::Primitive
+ VERSIONS = { 1 => :OpenFlow10, 4 => :OpenFlow13 }.freeze
+
+ uint8 :version
+
+ def get
+ VERSIONS.fetch(version)
+ end
+
+ def set(value)
+ self.version = VERSIONS.invert.fetch(value)
+ end
+
+ def to_bytes
+ version.to_hex
+ end
+ end
+ end
+end
diff --git a/lib/pio/open_flow10/actions.rb b/lib/pio/open_flow10/actions.rb
index 950fa12d..a924ff03 100644
--- a/lib/pio/open_flow10/actions.rb
+++ b/lib/pio/open_flow10/actions.rb
@@ -15,22 +15,22 @@
module Pio
module OpenFlow
# Actions list.
- class Actions < BinData::Primitive
+ class Actions10 < BinData::Primitive
ACTION_CLASS = {
- 0 => Pio::OpenFlow10::SendOutPort,
- 1 => Pio::OpenFlow10::SetVlanVid,
- 2 => Pio::OpenFlow10::SetVlanPriority,
- 3 => Pio::OpenFlow10::StripVlanHeader,
- 4 => Pio::OpenFlow10::SetSourceMacAddress,
- 5 => Pio::OpenFlow10::SetDestinationMacAddress,
- 6 => Pio::OpenFlow10::SetSourceIpAddress,
- 7 => Pio::OpenFlow10::SetDestinationIpAddress,
- 8 => Pio::OpenFlow10::SetTos,
- 9 => Pio::OpenFlow10::SetTransportSourcePort,
- 10 => Pio::OpenFlow10::SetTransportDestinationPort,
- 11 => Pio::OpenFlow10::Enqueue,
- 0xffff => Pio::VendorAction
- }
+ 0 => OpenFlow10::SendOutPort,
+ 1 => OpenFlow10::SetVlanVid,
+ 2 => OpenFlow10::SetVlanPriority,
+ 3 => OpenFlow10::StripVlanHeader,
+ 4 => OpenFlow10::SetSourceMacAddress,
+ 5 => OpenFlow10::SetDestinationMacAddress,
+ 6 => OpenFlow10::SetSourceIpAddress,
+ 7 => OpenFlow10::SetDestinationIpAddress,
+ 8 => OpenFlow10::SetTos,
+ 9 => OpenFlow10::SetTransportSourcePort,
+ 10 => OpenFlow10::SetTransportDestinationPort,
+ 11 => OpenFlow10::Enqueue,
+ 0xffff => OpenFlow10::VendorAction
+ }.freeze
mandatory_parameter :length
@@ -47,7 +47,7 @@ def set(actions)
def get
actions = []
tmp = binary
- while tmp.length > 0
+ until tmp.empty?
type = BinData::Uint16be.read(tmp)
begin
action = ACTION_CLASS.fetch(type).read(tmp)
diff --git a/lib/pio/open_flow10/aggregate_stats/reply.rb b/lib/pio/open_flow10/aggregate_stats/reply.rb
index 3f82d971..7e2e6e32 100644
--- a/lib/pio/open_flow10/aggregate_stats/reply.rb
+++ b/lib/pio/open_flow10/aggregate_stats/reply.rb
@@ -1,3 +1,4 @@
+require 'pio/open_flow/message'
require 'pio/open_flow10/stats_type'
module Pio
@@ -6,7 +7,7 @@ module OpenFlow10
module AggregateStats
# Aggregate Stats Reply message
class Reply < OpenFlow::Message
- open_flow_header version: 1, message_type: 17, message_length: 32
+ open_flow_header version: 1, type: 17, length: 32
stats_type :stats_type, value: -> { :aggregate }
uint16 :flags
uint64 :packet_count
diff --git a/lib/pio/open_flow10/aggregate_stats/request.rb b/lib/pio/open_flow10/aggregate_stats/request.rb
index 0f411059..de5b4f76 100644
--- a/lib/pio/open_flow10/aggregate_stats/request.rb
+++ b/lib/pio/open_flow10/aggregate_stats/request.rb
@@ -9,9 +9,7 @@ module OpenFlow10
module AggregateStats
# OpenFlow 1.0 Aggregate Stats Request message
class Request < OpenFlow::Message
- open_flow_header version: 1,
- message_type: 16,
- message_length: 56
+ open_flow_header version: 1, type: 16, length: 56
stats_type :stats_type, value: -> { :aggregate }
uint16 :flags
match10 :match
diff --git a/lib/pio/open_flow10/barrier/reply.rb b/lib/pio/open_flow10/barrier/reply.rb
index 9a7e49ce..de9dd20d 100644
--- a/lib/pio/open_flow10/barrier/reply.rb
+++ b/lib/pio/open_flow10/barrier/reply.rb
@@ -6,7 +6,7 @@ module OpenFlow10
module Barrier
# OpenFlow 1.0 Barrier Request message
class Reply < OpenFlow::Message
- open_flow_header version: 1, message_type: 19
+ open_flow_header version: 1, type: 19
string :body, value: ''
end
end
diff --git a/lib/pio/open_flow10/barrier/request.rb b/lib/pio/open_flow10/barrier/request.rb
index 6c10ee8c..0ff9138e 100644
--- a/lib/pio/open_flow10/barrier/request.rb
+++ b/lib/pio/open_flow10/barrier/request.rb
@@ -6,7 +6,7 @@ module OpenFlow10
module Barrier
# OpenFlow 1.0 Barrier Request message
class Request < OpenFlow::Message
- open_flow_header version: 1, message_type: 18
+ open_flow_header version: 1, type: 18
string :body, value: ''
end
end
diff --git a/lib/pio/open_flow10/description_stats/reply.rb b/lib/pio/open_flow10/description_stats/reply.rb
index 8a09398f..1c387d66 100644
--- a/lib/pio/open_flow10/description_stats/reply.rb
+++ b/lib/pio/open_flow10/description_stats/reply.rb
@@ -6,7 +6,7 @@ module OpenFlow10
module DescriptionStats
# OpenFlow 1.0 Description Stats Reply message
class Reply < OpenFlow::Message
- open_flow_header version: 1, message_type: 17, message_length: 1068
+ open_flow_header version: 1, type: 17, length: 1068
stats_type :stats_type, value: -> { :description }
uint16 :flags
diff --git a/lib/pio/open_flow10/description_stats/request.rb b/lib/pio/open_flow10/description_stats/request.rb
index ec921c09..f60b724f 100644
--- a/lib/pio/open_flow10/description_stats/request.rb
+++ b/lib/pio/open_flow10/description_stats/request.rb
@@ -7,9 +7,7 @@ module OpenFlow10
module DescriptionStats
# OpenFlow 1.0 Description Stats Request message
class Request < OpenFlow::Message
- open_flow_header version: 1,
- message_type: 16,
- message_length: 12
+ open_flow_header version: 1, type: 16, length: 12
stats_type :stats_type, value: -> { :description }
uint16 :flags
string :body, value: ''
diff --git a/lib/pio/open_flow10/echo/reply.rb b/lib/pio/open_flow10/echo/reply.rb
index 1f8957e0..159c978a 100644
--- a/lib/pio/open_flow10/echo/reply.rb
+++ b/lib/pio/open_flow10/echo/reply.rb
@@ -6,10 +6,10 @@ module OpenFlow10
module Echo
# OpenFlow 1.0 Echo Reply message.
class Reply < OpenFlow::Message
- open_flow_header version: 1, message_type: 3
- string :body, read_length: -> { message_length - 8 }
+ open_flow_header version: 1, type: 3
+ string :body, read_length: -> { length - header_length }
- alias_method :user_data, :body
+ alias user_data body
end
end
end
diff --git a/lib/pio/open_flow10/echo/request.rb b/lib/pio/open_flow10/echo/request.rb
index 40b245ed..5fb38957 100644
--- a/lib/pio/open_flow10/echo/request.rb
+++ b/lib/pio/open_flow10/echo/request.rb
@@ -6,10 +6,10 @@ module OpenFlow10
module Echo
# OpenFlow 1.0 Echo Request message.
class Request < OpenFlow::Message
- open_flow_header version: 1, message_type: 2
- string :body, read_length: -> { message_length - 8 }
+ open_flow_header version: 1, type: 2
+ string :body, read_length: -> { length - header_length }
- alias_method :user_data, :body
+ alias user_data body
end
end
end
diff --git a/lib/pio/open_flow10/enqueue.rb b/lib/pio/open_flow10/enqueue.rb
index f17684b9..4ed3223b 100644
--- a/lib/pio/open_flow10/enqueue.rb
+++ b/lib/pio/open_flow10/enqueue.rb
@@ -24,8 +24,8 @@ def initialize(user_options)
def validate_port(user_options)
port = user_options.fetch(:port)
if port.is_a?(Symbol) && port != :in_port
- fail(ArgumentError,
- ':port must be a valid physical port or :in_port')
+ raise(ArgumentError,
+ ':port must be a valid physical port or :in_port')
end
rescue KeyError
raise ArgumentError, ':port is a mandatory option'
@@ -33,7 +33,7 @@ def validate_port(user_options)
def validate_queue_id(user_options)
unless user_options.fetch(:queue_id).unsigned_32bit?
- fail ArgumentError, ':queue_id must be an unsigned 32-bit integer'
+ raise ArgumentError, ':queue_id must be an unsigned 32-bit integer'
end
rescue KeyError
raise ArgumentError, ':queue_id is a mandatory option'
diff --git a/lib/pio/open_flow10/error.rb b/lib/pio/open_flow10/error.rb
index dc6d9dfb..f5ae21c4 100644
--- a/lib/pio/open_flow10/error.rb
+++ b/lib/pio/open_flow10/error.rb
@@ -1,28 +1,18 @@
+require 'pio/open_flow/error_message'
require 'pio/open_flow10/error/error_type10'
module Pio
module OpenFlow10
# Error message parser
module Error
+ extend OpenFlow::ErrorMessage
+
# Error message body parser.
class BodyParser < BinData::Record
endian :big
error_type10 :error_type
uint16 :error_code
end
-
- def self.read(binary)
- body = OpenFlowHeaderParser.read(binary).body
- klass = case BodyParser.read(body).snapshot.error_type
- when :hello_failed
- HelloFailed
- when :bad_request
- BadRequest
- else
- fail 'Unknown error message.'
- end
- klass.read binary
- end
end
end
end
diff --git a/lib/pio/open_flow10/error/bad_request.rb b/lib/pio/open_flow10/error/bad_request.rb
index 4757182d..8260b6f4 100644
--- a/lib/pio/open_flow10/error/bad_request.rb
+++ b/lib/pio/open_flow10/error/bad_request.rb
@@ -1,4 +1,6 @@
+require 'pio/open_flow/error_message'
require 'pio/open_flow/message'
+require 'pio/open_flow10/error/bad_request/bad_request_code'
require 'pio/open_flow10/error/error_type10'
module Pio
@@ -6,38 +8,12 @@ module OpenFlow10
module Error
# Bad request error.
class BadRequest < OpenFlow::Message
- # enum ofp_bad_request_code
- class BadRequestCode < BinData::Primitive
- ERROR_CODES = {
- bad_version: 0,
- bad_type: 1,
- bad_stats: 2,
- bad_vendor: 3,
- bad_subtype: 4,
- permissions_error: 5,
- bad_length: 6,
- buffer_empty: 7,
- buffer_unknown: 8
- }
-
- endian :big
- uint16 :error_code
-
- def get
- ERROR_CODES.invert.fetch(error_code)
- end
-
- def set(value)
- self.error_code = ERROR_CODES.fetch(value)
- end
- end
-
open_flow_header version: 1,
- message_type: 1,
- message_length: -> { 12 + raw_data.length }
+ type: OpenFlow::ErrorMessage.type,
+ length: -> { header_length + 4 + raw_data.length }
error_type10 :error_type, value: -> { :bad_request }
bad_request_code :error_code
- rest :raw_data
+ string :raw_data, read_length: -> { length - header_length - 4 }
end
end
end
diff --git a/lib/pio/open_flow10/error/bad_request/bad_request_code.rb b/lib/pio/open_flow10/error/bad_request/bad_request_code.rb
new file mode 100644
index 00000000..ca4fa01c
--- /dev/null
+++ b/lib/pio/open_flow10/error/bad_request/bad_request_code.rb
@@ -0,0 +1,35 @@
+require 'bindata'
+
+module Pio
+ module OpenFlow10
+ module Error
+ class BadRequest < OpenFlow::Message
+ # enum ofp_bad_request_code
+ class BadRequestCode < BinData::Primitive
+ ERROR_CODES = {
+ bad_version: 0,
+ bad_type: 1,
+ bad_stats: 2,
+ bad_vendor: 3,
+ bad_subtype: 4,
+ permissions_error: 5,
+ bad_length: 6,
+ buffer_empty: 7,
+ buffer_unknown: 8
+ }.freeze
+
+ endian :big
+ uint16 :error_code
+
+ def get
+ ERROR_CODES.invert.fetch(error_code)
+ end
+
+ def set(value)
+ self.error_code = ERROR_CODES.fetch(value)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/pio/open_flow10/error/error_type10.rb b/lib/pio/open_flow10/error/error_type10.rb
index b659e11d..fdffd6d8 100644
--- a/lib/pio/open_flow10/error/error_type10.rb
+++ b/lib/pio/open_flow10/error/error_type10.rb
@@ -10,7 +10,7 @@ class ErrorType10 < BinData::Primitive
flow_mod_failed: 3,
port_mod_failed: 4,
queue_operation_failed: 5
- }
+ }.freeze
endian :big
uint16 :error_type
diff --git a/lib/pio/open_flow10/error/hello_failed.rb b/lib/pio/open_flow10/error/hello_failed.rb
index b7f198e7..9778fa71 100644
--- a/lib/pio/open_flow10/error/hello_failed.rb
+++ b/lib/pio/open_flow10/error/hello_failed.rb
@@ -1,3 +1,4 @@
+require 'pio/open_flow/error_message'
require 'pio/open_flow/hello_failed_code'
require 'pio/open_flow/message'
require 'pio/open_flow10/error/error_type10'
@@ -8,11 +9,12 @@ module Error
# Hello failed error.
class HelloFailed < OpenFlow::Message
open_flow_header version: 1,
- message_type: 1,
- message_length: -> { 12 + description.length }
- error_type10 :error_type
+ type: OpenFlow::ErrorMessage.type,
+ length: -> { header_length + 4 + description.length }
+
+ error_type10 :error_type, value: -> { :hello_failed }
hello_failed_code :error_code
- rest :description
+ string :description, read_length: -> { length - header_length - 4 }
end
end
end
diff --git a/lib/pio/open_flow10/exact_match.rb b/lib/pio/open_flow10/exact_match.rb
index fd58249a..3112db24 100644
--- a/lib/pio/open_flow10/exact_match.rb
+++ b/lib/pio/open_flow10/exact_match.rb
@@ -1,17 +1,19 @@
require 'pio/open_flow10/match'
module Pio
- # OpenFlow 1.0 exact match
- class ExactMatch
- def initialize(packet_in)
- @match = packet_in.data.to_exact_match(packet_in.in_port)
- rescue NoMethodError
- raise NotImplementedError,
- "#{packet_in.data.class} is not yet supported by ExactMatch."
- end
+ module OpenFlow10
+ # OpenFlow 1.0 exact match
+ class ExactMatch < OpenFlow::FlowMatch
+ def initialize(packet_in)
+ @match = packet_in.data.to_exact_match(packet_in.in_port)
+ rescue NoMethodError
+ raise NotImplementedError,
+ "#{packet_in.data.class} is not yet supported by ExactMatch."
+ end
- def method_missing(method, *args, &block)
- @match.__send__ method, *args, &block
+ def method_missing(method, *args, &block)
+ @match.__send__ method, *args, &block
+ end
end
end
end
diff --git a/lib/pio/open_flow10/features/reply.rb b/lib/pio/open_flow10/features/reply.rb
index 99428e6c..504958b3 100644
--- a/lib/pio/open_flow10/features/reply.rb
+++ b/lib/pio/open_flow10/features/reply.rb
@@ -1,4 +1,5 @@
-require 'pio/open_flow'
+require 'pio/open_flow/datapath_id'
+require 'pio/open_flow/message'
require 'pio/open_flow10/phy_port16'
require 'pio/open_flow10/port16'
@@ -8,9 +9,18 @@ module OpenFlow10
class Features
# OpenFlow 1.0 Features Reply message.
class Reply < OpenFlow::Message
- extend OpenFlow::Flags
+ open_flow_header(version: 1,
+ type: 6,
+ length: lambda do
+ header_length + 24 + PhyPort16.length * ports.length
+ end)
- # enum ofp_capabilities
+ datapath_id :datapath_id
+ alias dpid datapath_id
+ uint32 :n_buffers
+ uint8 :n_tables
+ string :padding, length: 3
+ hide :padding
flags_32bit :capabilities,
[:flow_stats,
:table_stats,
@@ -20,9 +30,7 @@ class Reply < OpenFlow::Message
:ip_reasm,
:queue_stats,
:arp_match_ip]
-
- # enum ofp_action_type
- flags_32bit :actions_flag,
+ flags_32bit :actions,
[:output,
:set_vlan_vid,
:set_vlan_pcp,
@@ -35,37 +43,14 @@ class Reply < OpenFlow::Message
:set_transport_source_port,
:set_transport_destination_port,
:enqueue]
-
- open_flow_header version: 1,
- message_type: 6,
- message_length: -> { 32 + ports.to_binary_s.length }
-
- datapath_id :datapath_id
- uint32 :n_buffers
- uint8 :n_tables
- uint24 :padding
- hide :padding
- capabilities :capabilities
- actions_flag :actions
array :ports, type: :phy_port16, read_until: :eof
- def datapath_id
- @format.datapath_id.to_i
- end
- alias_method :dpid, :datapath_id
-
def ports
- snapshot.ports.map do |each|
- each.instance_variable_set :@datapath_id, datapath_id
+ super.map do |each|
+ each.datapath_id = datapath_id
each
end
end
-
- def physical_ports
- ports.select do |each|
- each.port_no <= OpenFlow10::Port16::MAX
- end
- end
end
end
end
diff --git a/lib/pio/open_flow10/features/request.rb b/lib/pio/open_flow10/features/request.rb
index 75d94b9b..41a56bc7 100644
--- a/lib/pio/open_flow10/features/request.rb
+++ b/lib/pio/open_flow10/features/request.rb
@@ -6,10 +6,7 @@ module OpenFlow10
class Features
# Features Request message.
class Request < OpenFlow::Message
- open_flow_header version: 1, message_type: 5
- string :body, length: 0
-
- alias_method :user_data, :body
+ open_flow_header version: 1, type: 5
end
end
end
diff --git a/lib/pio/open_flow10/flow_mod.rb b/lib/pio/open_flow10/flow_mod.rb
index 2e318bc2..ece0f94a 100644
--- a/lib/pio/open_flow10/flow_mod.rb
+++ b/lib/pio/open_flow10/flow_mod.rb
@@ -1,43 +1,15 @@
require 'pio/open_flow'
require 'pio/open_flow10/actions'
+require 'pio/open_flow10/flow_mod/command'
require 'pio/open_flow10/match10'
module Pio
module OpenFlow10
# OpenFlow 1.0 Flow Mod message.
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
- }
+ open_flow_header version: 1, type: 14,
+ length: -> { 72 + actions.binary.length }
- endian :big
- uint16 :command
-
- def get
- COMMANDS.invert.fetch(command)
- end
-
- def set(value)
- self.command = COMMANDS.fetch(value)
- end
- end
-
- extend OpenFlow::Flags
-
- flags_16bit :flags,
- [:send_flow_rem,
- :check_overwrap,
- :emerg]
-
- open_flow_header version: 1,
- message_type: 14,
- message_length: -> { 72 + actions.binary.length }
match10 :match
uint64 :cookie
command :command
@@ -46,8 +18,11 @@ def set(value)
uint16 :priority
uint32 :buffer_id
uint16 :out_port
- flags :flags
- actions :actions, length: -> { message_length - 72 }
+ flags_16bit :flags,
+ [:send_flow_rem,
+ :check_overwrap,
+ :emerg]
+ actions10 :actions, length: -> { length - 72 }
end
end
end
diff --git a/lib/pio/open_flow10/flow_mod/command.rb b/lib/pio/open_flow10/flow_mod/command.rb
new file mode 100644
index 00000000..8d4a1621
--- /dev/null
+++ b/lib/pio/open_flow10/flow_mod/command.rb
@@ -0,0 +1,28 @@
+module Pio
+ module OpenFlow10
+ # OpenFlow 1.0 Flow Mod message.
+ 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
+
+ endian :big
+ uint16 :command
+
+ def get
+ COMMANDS.invert.fetch(command)
+ end
+
+ def set(value)
+ self.command = COMMANDS.fetch(value)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/pio/open_flow10/flow_removed.rb b/lib/pio/open_flow10/flow_removed.rb
index 5273db69..c7de9c6c 100644
--- a/lib/pio/open_flow10/flow_removed.rb
+++ b/lib/pio/open_flow10/flow_removed.rb
@@ -1,27 +1,12 @@
require 'pio/open_flow/message'
require 'pio/open_flow10/match10'
+require 'pio/open_flow10/flow_removed/reason'
module Pio
module OpenFlow10
# Flow Removed message
class FlowRemoved < OpenFlow::Message
- # Why was this flow removed?
- # (enum ofp_flow_removed_reason)
- class Reason < BinData::Primitive
- REASONS = { idle_timeout: 0, hard_timeout: 1, delete: 2 }
-
- uint8 :reason
-
- def get
- REASONS.invert.fetch(reason)
- end
-
- def set(value)
- self.reason = REASONS.fetch(value)
- end
- end
-
- open_flow_header version: 1, message_type: 11, message_length: 88
+ open_flow_header version: 1, type: 11, length: 88
match10 :match
uint64 :cookie
uint16 :priority
diff --git a/lib/pio/open_flow10/flow_removed/reason.rb b/lib/pio/open_flow10/flow_removed/reason.rb
new file mode 100644
index 00000000..cf9001df
--- /dev/null
+++ b/lib/pio/open_flow10/flow_removed/reason.rb
@@ -0,0 +1,22 @@
+module Pio
+ module OpenFlow10
+ # Flow Removed message
+ class FlowRemoved < OpenFlow::Message
+ # Why was this flow removed?
+ # (enum ofp_flow_removed_reason)
+ class Reason < BinData::Primitive
+ REASONS = { idle_timeout: 0, hard_timeout: 1, delete: 2 }.freeze
+
+ uint8 :reason
+
+ def get
+ REASONS.invert.fetch(reason)
+ end
+
+ def set(value)
+ self.reason = REASONS.fetch(value)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/pio/open_flow10/flow_stats/reply.rb b/lib/pio/open_flow10/flow_stats/reply.rb
index 4c1750f2..a04fc57f 100644
--- a/lib/pio/open_flow10/flow_stats/reply.rb
+++ b/lib/pio/open_flow10/flow_stats/reply.rb
@@ -27,12 +27,11 @@ class FlowStatsEntry < BinData::Record
uint64 :cookie
uint64 :packet_count
uint64 :byte_count
- actions :actions, length: -> { entry_length - 88 }
+ actions10 :actions, length: -> { entry_length - 88 }
end
- open_flow_header version: 1,
- message_type: 17,
- message_length: -> { 12 + stats.to_binary_s.length }
+ open_flow_header version: 1, type: 17,
+ length: -> { 12 + stats.to_binary_s.length }
stats_type :stats_type, value: -> { :flow }
uint16 :flags
diff --git a/lib/pio/open_flow10/flow_stats/request.rb b/lib/pio/open_flow10/flow_stats/request.rb
index 35881438..68a2cda3 100644
--- a/lib/pio/open_flow10/flow_stats/request.rb
+++ b/lib/pio/open_flow10/flow_stats/request.rb
@@ -8,9 +8,7 @@ module OpenFlow10
module FlowStats
# OpenFlow 1.0 Flow Stats Request message
class Request < OpenFlow::Message
- open_flow_header version: 1,
- message_type: 16,
- message_length: 56
+ open_flow_header version: 1, type: 16, length: 56
stats_type :stats_type, value: -> { :flow }
uint16 :flags
diff --git a/lib/pio/open_flow10/hello.rb b/lib/pio/open_flow10/hello.rb
index 9fb41094..86143465 100644
--- a/lib/pio/open_flow10/hello.rb
+++ b/lib/pio/open_flow10/hello.rb
@@ -4,10 +4,7 @@ module Pio
module OpenFlow10
# Hello message
class Hello < OpenFlow::Message
- open_flow_header version: 1, message_type: 0
- string :body, length: 0
-
- alias_method :user_data, :body
+ open_flow_header version: 1, type: 0
end
end
end
diff --git a/lib/pio/open_flow10/match.rb b/lib/pio/open_flow10/match.rb
index f6a1f312..642ae1b7 100644
--- a/lib/pio/open_flow10/match.rb
+++ b/lib/pio/open_flow10/match.rb
@@ -1,12 +1,13 @@
require 'English'
require 'bindata'
+require 'pio/open_flow/flow_match'
require 'pio/type/ip_address'
require 'pio/type/mac_address'
module Pio
module OpenFlow10
# Fields to match against flows
- class Match
+ class Match < OpenFlow::FlowMatch
# Flow wildcards
class Wildcards < BinData::Primitive
BITS = {
@@ -34,8 +35,8 @@ class Wildcards < BinData::Primitive
destination_ip_address_all: 1 << 19,
vlan_priority: 1 << 20,
tos: 1 << 21
- }
- NW_FLAGS = [:source_ip_address, :destination_ip_address]
+ }.freeze
+ NW_FLAGS = [:source_ip_address, :destination_ip_address].freeze
FLAGS = BITS.keys.select { |each| !(/^(source|destination)_ip/=~ each) }
endian :big
@@ -45,7 +46,7 @@ class Wildcards < BinData::Primitive
# This method smells of :reek:FeatureEnvy
def get
BITS.each_with_object(Hash.new(0)) do |(key, bit), memo|
- next if flags & bit == 0
+ next if (flags & bit).zero?
if /(source_ip_address|destination_ip_address)(\d)/=~ key
memo[$LAST_MATCH_INFO[1].to_sym] |= 1 << $LAST_MATCH_INFO[2].to_i
else
@@ -56,13 +57,13 @@ def get
def set(params)
self.flags = params.inject(0) do |memo, (key, val)|
- memo | case key
- when :source_ip_address, :destination_ip_address
- (params.fetch(key) & 31) <<
- (key == :source_ip_address ? 8 : 14)
- else
- val ? BITS.fetch(key) : 0
- end
+ memo |
+ case key
+ when :source_ip_address, :destination_ip_address
+ (params.fetch(key) & 31) << (key == :source_ip_address ? 8 : 14)
+ else
+ val ? BITS.fetch(key) : 0
+ end
end
end
@@ -143,7 +144,7 @@ def initialize(user_options = {})
memo["#{each}_all".to_sym] = true
end
end
- @format = MatchFormat.new({ wildcards: flags }.merge user_options)
+ @format = MatchFormat.new({ wildcards: flags }.merge(user_options))
end
# rubocop:enable MethodLength
diff --git a/lib/pio/open_flow10/packet_in.rb b/lib/pio/open_flow10/packet_in.rb
index 79b1317e..ea9ae08f 100644
--- a/lib/pio/open_flow10/packet_in.rb
+++ b/lib/pio/open_flow10/packet_in.rb
@@ -1,39 +1,21 @@
-require 'pio/ethernet_header'
-require 'pio/ipv4_header'
-require 'pio/open_flow'
-require 'pio/parse_error'
+require 'active_support/core_ext/object/try'
+require 'pio/open_flow/message'
+require 'pio/open_flow10/packet_in/reason'
require 'pio/parser'
module Pio
module OpenFlow10
# OpenFlow 1.0 Packet-In message
class PacketIn < OpenFlow::Message
- # Why is this packet being sent to the controller?
- # (enum ofp_packet_in_reason)
- class Reason < BinData::Primitive
- REASONS = { no_match: 0, action: 1 }
-
- uint8 :reason
-
- def get
- REASONS.invert.fetch(reason)
- end
-
- def set(value)
- self.reason = REASONS.fetch(value)
- end
- end
-
open_flow_header version: 1,
- message_type: 10,
- message_length: -> { 18 + raw_data.length }
+ type: 10,
+ length: -> { header_length + 10 + raw_data.length }
uint32 :buffer_id
- uint16 :total_len, value: -> { raw_data.length }
+ uint16 :total_length, initial_value: -> { raw_data.length }
uint16 :in_port
reason :reason
uint8 :padding
- hide :padding
- string :raw_data, read_length: :total_len
+ string :raw_data, read_length: -> { length - header_length - 10 }
def data
@data ||= Pio::Parser.read(raw_data)
@@ -43,13 +25,35 @@ def lldp?
data.is_a? Lldp
end
+ def to_ruby
+ @format.to_ruby
+ end
+
+ # rubocop:disable LineLength
+ def self.inspect
+ 'PacketIn(open_flow_version: uint8, message_type: uint8, message_length: uint16, transaction_id: uint32, buffer_id: uint32, total_length: uint16, in_port: uint16, reason: symbol, raw_data: string)'
+ end
+ # rubocop:enable LineLength
+
+ # rubocop:disable LineLength
+ def inspect
+ data_inspection = if raw_data.empty?
+ %(raw_data: "")
+ else
+ %(data: #{data.inspect})
+ end
+ %(#)
+ end
+ # rubocop:enable LineLength
+
def method_missing(method, *args)
- data.__send__(method, *args).snapshot
+ bindata_value = data.__send__(method, *args)
+ bindata_value.try(:snapshot) || bindata_value
end
attr_accessor :datapath_id
- alias_method :dpid, :datapath_id
- alias_method :dpid=, :datapath_id=
+ alias dpid datapath_id
+ alias dpid= datapath_id=
end
end
end
diff --git a/lib/pio/open_flow10/packet_in/reason.rb b/lib/pio/open_flow10/packet_in/reason.rb
new file mode 100644
index 00000000..226c4bb3
--- /dev/null
+++ b/lib/pio/open_flow10/packet_in/reason.rb
@@ -0,0 +1,25 @@
+module Pio
+ module OpenFlow10
+ class PacketIn < OpenFlow::Message
+ # Why is this packet being sent to the controller?
+ # (enum ofp_packet_in_reason)
+ class Reason < BinData::Primitive
+ REASONS = { no_match: 0, action: 1 }.freeze
+
+ uint8 :reason
+
+ def get
+ REASONS.invert.fetch(reason)
+ end
+
+ def set(value)
+ self.reason = REASONS.fetch(value)
+ end
+
+ def to_bytes
+ reason.to_hex
+ end
+ end
+ end
+ end
+end
diff --git a/lib/pio/open_flow10/packet_out.rb b/lib/pio/open_flow10/packet_out.rb
index c012d1c4..416320fc 100644
--- a/lib/pio/open_flow10/packet_out.rb
+++ b/lib/pio/open_flow10/packet_out.rb
@@ -1,18 +1,17 @@
-require 'pio/open_flow'
+require 'pio/open_flow/message'
require 'pio/open_flow10/actions'
-# Base module.
module Pio
module OpenFlow10
# OpenFlow 1.0 Packet-Out message
class PacketOut < OpenFlow::Message
open_flow_header version: 1,
- message_type: 13,
- message_length: -> { 16 + actions_len + raw_data.length }
+ type: 13,
+ length: -> { 16 + actions_length + raw_data.length }
uint32 :buffer_id
uint16 :in_port
- uint16 :actions_len, initial_value: -> { actions.binary.length }
- actions :actions, length: -> { actions_len }
+ uint16 :actions_length, initial_value: -> { actions.binary.length }
+ actions10 :actions, length: -> { actions_length }
rest :raw_data
end
end
diff --git a/lib/pio/open_flow10/phy_port16.rb b/lib/pio/open_flow10/phy_port16.rb
index d1602009..65dae42c 100644
--- a/lib/pio/open_flow10/phy_port16.rb
+++ b/lib/pio/open_flow10/phy_port16.rb
@@ -1,4 +1,5 @@
require 'bindata'
+require 'pio/open_flow/flags'
require 'pio/type/mac_address'
module Pio
@@ -7,84 +8,61 @@ module OpenFlow10
class PhyPort16 < BinData::Record
extend OpenFlow::Flags
- # enum ofp_port_config
- flags_32bit :port_config,
+ endian :big
+
+ uint16 :number
+ mac_address :mac_address
+ string :name, length: 16, trim_padding: true
+ flags_32bit :config,
[:port_down,
:no_stp,
- :no_recv,
- :no_recv_stp,
+ :no_receive,
+ :no_receive_stp,
:no_flood,
- :no_fwd,
+ :no_forward,
:no_packet_in]
-
- # enum ofp_port_state
- flags_32bit :port_state,
+ flags_32bit :state,
link_down: 1 << 0,
stp_listen: 0 << 8,
stp_learn: 1 << 8,
stp_forward: 2 << 8,
stp_block: 3 << 8
- # enum ofp_port_features
- flags_32bit :port_feature,
- [:port_10mb_hd,
- :port_10mb_fd,
- :port_100mb_hd,
- :port_100mb_fd,
- :port_1gb_hd,
- :port_1gb_fd,
- :port_10gb_fd,
- :port_copper,
- :port_fiber,
- :port_autoneg,
- :port_pause,
- :port_pause_asym]
-
- endian :big
-
- uint16 :port_no
- mac_address :hardware_address
- string :name, length: 16, trim_padding: true
- port_config :config
- port_state :state
+ define_flags_32bit :port_feature,
+ [:port_10mb_hd,
+ :port_10mb_fd,
+ :port_100mb_hd,
+ :port_100mb_fd,
+ :port_1gb_hd,
+ :port_1gb_fd,
+ :port_10gb_fd,
+ :port_copper,
+ :port_fiber,
+ :port_autoneg,
+ :port_pause,
+ :port_pause_asym]
port_feature :curr
port_feature :advertised
port_feature :supported
port_feature :peer
- # rubocop:disable MethodLength
- def snapshot
- super.tap do |ss|
- def ss.datapath_id
- @datapath_id || fail
- end
-
- def ss.dpid
- @datapath_id || fail
- end
+ cattr_reader(:length) { 48 }
- def ss.number
- port_no
- end
+ attr_accessor :datapath_id
+ alias dpid datapath_id
+ alias dpid= datapath_id=
- def ss.mac_address
- hardware_address
- end
-
- def ss.up?
- !down?
- end
+ def up?
+ !down?
+ end
- def ss.down?
- config.include?(:port_down) || state.include?(:link_down)
- end
+ def down?
+ config.include?(:port_down) || state.include?(:link_down)
+ end
- def ss.local?
- port_no == OpenFlow10::Port16.reserved_port_number(:local)
- end
- end
+ def local?
+ number == OpenFlow10::Port16.reserved_port_number(:local)
end
- # rubocop:enable MethodLength
end
end
end
diff --git a/lib/pio/open_flow10/port_stats/request.rb b/lib/pio/open_flow10/port_stats/request.rb
index 59c1383e..b77c61ac 100644
--- a/lib/pio/open_flow10/port_stats/request.rb
+++ b/lib/pio/open_flow10/port_stats/request.rb
@@ -6,9 +6,7 @@ module OpenFlow10
class PortStats
# Port Stats Request message
class Request < OpenFlow::Message
- open_flow_header version: 1,
- message_type: 16,
- message_length: 20
+ open_flow_header version: 1, type: 16, length: 20
stats_type :stats_type, value: -> { :port }
uint16 :flags
diff --git a/lib/pio/open_flow10/port_status.rb b/lib/pio/open_flow10/port_status.rb
index 017c9b96..7cbfb5a8 100644
--- a/lib/pio/open_flow10/port_status.rb
+++ b/lib/pio/open_flow10/port_status.rb
@@ -1,43 +1,36 @@
+require 'active_support/core_ext/module/delegation'
require 'pio/open_flow/message'
+require 'pio/open_flow10/phy_port16'
+require 'pio/open_flow10/port_status/reason'
module Pio
module OpenFlow10
# OpenFlow 1.0 Port Status message
class PortStatus < OpenFlow::Message
- # What changed about the physical port
- class Reason < BinData::Primitive
- REASONS = { add: 0, delete: 1, modify: 2 }
+ open_flow_header version: 1, type: 12,
+ length: -> { header_length + 8 + PhyPort16.length }
- uint8 :reason
-
- def get
- REASONS.invert.fetch(reason)
- end
-
- def set(value)
- self.reason = REASONS.fetch(value)
- end
- end
-
- open_flow_header version: 1,
- message_type: 12,
- message_length: 10
reason :reason
uint56 :padding
hide :padding
- phy_port16 :desc
-
- def reason
- @format.reason.to_sym
- end
-
- attr_writer :datapath_id
-
- def desc
- @desc ||= @format.desc.snapshot
- @desc.instance_variable_set :@datapath_id, @datapath_id
- @desc
- end
+ phy_port16 :description
+
+ attr_accessor :datapath_id
+ alias dpid datapath_id
+ alias dpid= datapath_id=
+
+ delegate :number, to: :description
+ delegate :mac_address, to: :description
+ delegate :name, to: :description
+ delegate :config, to: :description
+ delegate :state, to: :description
+ delegate :curr, to: :description
+ delegate :advertised, to: :description
+ delegate :supported, to: :description
+ delegate :peer, to: :description
+ delegate :up?, to: :description
+ delegate :down?, to: :description
+ delegate :local?, to: :description
end
end
end
diff --git a/lib/pio/open_flow10/port_status/reason.rb b/lib/pio/open_flow10/port_status/reason.rb
new file mode 100644
index 00000000..07a0ad62
--- /dev/null
+++ b/lib/pio/open_flow10/port_status/reason.rb
@@ -0,0 +1,21 @@
+module Pio
+ module OpenFlow10
+ # OpenFlow 1.0 Port Status message
+ class PortStatus < OpenFlow::Message
+ # What changed about the physical port
+ class Reason < BinData::Primitive
+ REASONS = { add: 0, delete: 1, modify: 2 }.freeze
+
+ uint8 :reason
+
+ def get
+ REASONS.invert.fetch(reason)
+ end
+
+ def set(value)
+ self.reason = REASONS.fetch(value)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/pio/open_flow10/queue_stats/request.rb b/lib/pio/open_flow10/queue_stats/request.rb
index 2896bb7c..32fc3f64 100644
--- a/lib/pio/open_flow10/queue_stats/request.rb
+++ b/lib/pio/open_flow10/queue_stats/request.rb
@@ -6,9 +6,7 @@ module OpenFlow10
class QueueStats
# Queue Stats Request message
class Request < OpenFlow::Message
- open_flow_header version: 1,
- message_type: 16,
- message_length: 20
+ open_flow_header version: 1, type: 16, length: 20
stats_type :stats_type, value: -> { :queue }
uint16 :flags
diff --git a/lib/pio/open_flow10/send_out_port.rb b/lib/pio/open_flow10/send_out_port.rb
index 4f46b6dc..0e1ce490 100644
--- a/lib/pio/open_flow10/send_out_port.rb
+++ b/lib/pio/open_flow10/send_out_port.rb
@@ -21,8 +21,8 @@ def initialize(user_options)
end
max_length = options[:max_length]
if max_length && !max_length.unsigned_16bit?
- fail(ArgumentError,
- 'The max_length should be an unsigned 16bit integer.')
+ raise(ArgumentError,
+ 'The max_length should be an unsigned 16bit integer.')
end
super(options)
end
diff --git a/lib/pio/open_flow10/set_tos.rb b/lib/pio/open_flow10/set_tos.rb
index 796a9b33..8db1853d 100644
--- a/lib/pio/open_flow10/set_tos.rb
+++ b/lib/pio/open_flow10/set_tos.rb
@@ -14,8 +14,8 @@ def initialize(type_of_service)
# tos (IP ToS) value consists of 8 bits, of which only the
# 6 high-order bits belong to DSCP, the 2 low-order bits must
# be zero.
- unless type_of_service.unsigned_8bit? && type_of_service % 4 == 0
- fail ArgumentError, 'Invalid type_of_service (ToS) value.'
+ unless type_of_service.unsigned_8bit? && (type_of_service % 4).zero?
+ raise ArgumentError, 'Invalid type_of_service (ToS) value.'
end
super(type_of_service: type_of_service)
end
diff --git a/lib/pio/open_flow10/set_transport_port.rb b/lib/pio/open_flow10/set_transport_port.rb
index 80170010..6cd560e7 100644
--- a/lib/pio/open_flow10/set_transport_port.rb
+++ b/lib/pio/open_flow10/set_transport_port.rb
@@ -13,7 +13,8 @@ class SetTransportSourcePort < OpenFlow::Action
def initialize(number)
port = number.to_i
unless port.unsigned_16bit?
- fail ArgumentError, 'TCP/UDP port must be an unsigned 16-bit integer.'
+ raise ArgumentError,
+ 'TCP/UDP port must be an unsigned 16-bit integer.'
end
super(port: port)
rescue NoMethodError
@@ -31,7 +32,8 @@ class SetTransportDestinationPort < OpenFlow::Action
def initialize(number)
port = number.to_i
unless port.unsigned_16bit?
- fail ArgumentError, 'TCP/UDP port must be an unsigned 16-bit integer.'
+ raise ArgumentError,
+ 'TCP/UDP port must be an unsigned 16-bit integer.'
end
super(port: port)
rescue NoMethodError
diff --git a/lib/pio/open_flow10/set_vlan_priority.rb b/lib/pio/open_flow10/set_vlan_priority.rb
index 3bddbddc..b4e5e600 100644
--- a/lib/pio/open_flow10/set_vlan_priority.rb
+++ b/lib/pio/open_flow10/set_vlan_priority.rb
@@ -12,7 +12,7 @@ class SetVlanPriority < OpenFlow::Action
def initialize(number)
priority = number.to_i
if priority < 0 || priority > 7
- fail ArgumentError, 'VLAN priority must be between 0 and 7 inclusive'
+ raise ArgumentError, 'VLAN priority must be between 0 and 7 inclusive'
end
super(vlan_priority: priority)
rescue NoMethodError
diff --git a/lib/pio/open_flow10/set_vlan_vid.rb b/lib/pio/open_flow10/set_vlan_vid.rb
index 8f506ac7..c8c34755 100644
--- a/lib/pio/open_flow10/set_vlan_vid.rb
+++ b/lib/pio/open_flow10/set_vlan_vid.rb
@@ -12,7 +12,7 @@ class SetVlanVid < OpenFlow::Action
def initialize(number)
vlan_id = number.to_i
unless vlan_id >= 1 && vlan_id <= 4095
- fail ArgumentError, 'VLAN ID must be between 1 and 4095 inclusive'
+ raise ArgumentError, 'VLAN ID must be between 1 and 4095 inclusive'
end
super(vlan_id: vlan_id)
rescue NoMethodError
diff --git a/lib/pio/open_flow10/stats_reply.rb b/lib/pio/open_flow10/stats_reply.rb
index 11bb07d4..76f64221 100644
--- a/lib/pio/open_flow10/stats_reply.rb
+++ b/lib/pio/open_flow10/stats_reply.rb
@@ -7,21 +7,18 @@ module Pio
module OpenFlow10
class Stats
# Stats reply parser.
- class Reply
- TYPES = {
+ class Reply < OpenFlow::Message
+ open_flow_header version: 1, type: 17, length: 10
+ stats_type :stats_type
+
+ TYPE = {
description: OpenFlow10::DescriptionStats::Reply,
flow: OpenFlow10::FlowStats::Reply,
aggregate: OpenFlow10::AggregateStats::Reply
- }
-
- # Stats reply format.
- class Format < OpenFlow::Message
- open_flow_header version: 1, message_type: 17, message_length: 10
- stats_type :stats_type
- end
+ }.freeze
def self.read(binary)
- TYPES.fetch(Format.read(binary).stats_type.to_sym).read(binary)
+ TYPE.fetch(Format.read(binary).stats_type.to_sym).read(binary)
rescue KeyError
raise "Unknown stats type: #{stats_type}"
end
diff --git a/lib/pio/open_flow10/stats_request.rb b/lib/pio/open_flow10/stats_request.rb
index 8ec47469..3c3d3150 100644
--- a/lib/pio/open_flow10/stats_request.rb
+++ b/lib/pio/open_flow10/stats_request.rb
@@ -1,28 +1,29 @@
-require 'pio/open_flow10/table_stats/request'
-require 'pio/open_flow10/port_stats/request'
require 'pio/open_flow/message'
+require 'pio/open_flow10/aggregate_stats/request'
+require 'pio/open_flow10/flow_stats/request'
+require 'pio/open_flow10/port_stats/request'
+require 'pio/open_flow10/queue_stats/request'
+require 'pio/open_flow10/table_stats/request'
module Pio
module OpenFlow10
class Stats
# Stats request parser.
- class Request
- TYPES = {
+ class Request < OpenFlow::Message
+ open_flow_header version: 1, type: 16, length: 10
+ stats_type :stats_type
+
+ TYPE = {
+ aggregate: OpenFlow10::AggregateStats::Request,
description: OpenFlow10::DescriptionStats::Request,
flow: OpenFlow10::FlowStats::Request,
- aggregate: OpenFlow10::AggregateStats::Request,
- table: OpenFlow10::TableStats::Request,
- port: OpenFlow10::PortStats::Request
- }
-
- # Stats request format.
- class Format < OpenFlow::Message
- open_flow_header version: 1, message_type: 16, message_length: 10
- stats_type :stats_type
- end
+ port: OpenFlow10::PortStats::Request,
+ queue: OpenFlow10::QueueStats::Request,
+ table: OpenFlow10::TableStats::Request
+ }.freeze
def self.read(binary)
- TYPES.fetch(Format.read(binary).stats_type.to_sym).read(binary)
+ TYPE.fetch(Format.read(binary).stats_type.to_sym).read(binary)
rescue KeyError
raise "Unknown stats type: #{stats_type}"
end
diff --git a/lib/pio/open_flow10/stats_type.rb b/lib/pio/open_flow10/stats_type.rb
index fa48b2a2..22603c4d 100644
--- a/lib/pio/open_flow10/stats_type.rb
+++ b/lib/pio/open_flow10/stats_type.rb
@@ -10,7 +10,7 @@ class StatsType < BinData::Primitive
port: 4,
queue: 5,
vendor: 0xffff
- }
+ }.freeze
endian :big
uint16 :command
diff --git a/lib/pio/open_flow10/table_stats/request.rb b/lib/pio/open_flow10/table_stats/request.rb
index 23056f4c..af2c2793 100644
--- a/lib/pio/open_flow10/table_stats/request.rb
+++ b/lib/pio/open_flow10/table_stats/request.rb
@@ -7,9 +7,7 @@ module OpenFlow10
module TableStats
# OpenFlow 1.0 Table Stats Request message
class Request < OpenFlow::Message
- open_flow_header version: 1,
- message_type: 16,
- message_length: 12
+ open_flow_header version: 1, type: 16, length: 12
stats_type :stats_type, value: -> { :table }
uint16 :flags
string :body, value: ''
diff --git a/lib/pio/open_flow10/vendor_action.rb b/lib/pio/open_flow10/vendor_action.rb
index ec35e6e3..cc039859 100644
--- a/lib/pio/open_flow10/vendor_action.rb
+++ b/lib/pio/open_flow10/vendor_action.rb
@@ -2,32 +2,34 @@
require 'forwardable'
module Pio
- # Vendor defined action
- class VendorAction
- # OpenFlow 1.0 OFPAT_VENDOR action format.
- class Format < BinData::Record
- endian :big
+ module OpenFlow10
+ # Vendor defined action
+ class VendorAction
+ # OpenFlow 1.0 OFPAT_VENDOR action format.
+ class Format < BinData::Record
+ endian :big
- uint16 :action_type, value: 0xffff
- uint16 :action_length, value: 8
- uint32 :vendor
- end
+ uint16 :action_type, value: 0xffff
+ uint16 :action_length, value: 8
+ uint32 :vendor
+ end
- def self.read(raw_data)
- allocate.tap do |strip_vlan|
- strip_vlan.instance_variable_set :@format, Format.read(raw_data)
+ def self.read(raw_data)
+ allocate.tap do |strip_vlan|
+ strip_vlan.instance_variable_set :@format, Format.read(raw_data)
+ end
end
- end
- extend Forwardable
+ extend Forwardable
- def_delegators :@format, :action_type
- def_delegator :@format, :action_length, :length
- def_delegators :@format, :vendor
- def_delegator :@format, :to_binary_s, :to_binary
+ def_delegators :@format, :action_type
+ def_delegator :@format, :action_length, :length
+ def_delegators :@format, :vendor
+ def_delegator :@format, :to_binary_s, :to_binary
- def initialize(vendor)
- @format = Format.new(vendor: vendor)
+ def initialize(vendor)
+ @format = Format.new(vendor: vendor)
+ end
end
end
end
diff --git a/lib/pio/open_flow13.rb b/lib/pio/open_flow13.rb
index 97b878ae..730ac475 100644
--- a/lib/pio/open_flow13.rb
+++ b/lib/pio/open_flow13.rb
@@ -15,14 +15,21 @@
# Actions
require 'pio/open_flow/nicira_resubmit'
require 'pio/open_flow/nicira_resubmit_table'
+require 'pio/open_flow13/copy_ttl_inwards'
+require 'pio/open_flow13/copy_ttl_outwards'
+require 'pio/open_flow13/decrement_ip_ttl'
require 'pio/open_flow13/nicira_reg_load'
require 'pio/open_flow13/nicira_reg_move'
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_conjunction'
require 'pio/open_flow13/send_out_port'
require 'pio/open_flow13/set_arp_operation'
require 'pio/open_flow13/set_arp_sender_hardware_address'
require 'pio/open_flow13/set_arp_sender_protocol_address'
require 'pio/open_flow13/set_destination_mac_address'
+require 'pio/open_flow13/set_ip_ttl'
require 'pio/open_flow13/set_metadata'
require 'pio/open_flow13/set_source_mac_address'
diff --git a/lib/pio/open_flow13/actions.rb b/lib/pio/open_flow13/actions.rb
index db605449..4e606d29 100644
--- a/lib/pio/open_flow13/actions.rb
+++ b/lib/pio/open_flow13/actions.rb
@@ -1,47 +1,49 @@
require 'bindata'
module Pio
- # Actions not yet implemented.
- class UnsupportedAction < BinData::Record
- endian :big
+ module OpenFlow13
+ # Actions not yet implemented.
+ class UnsupportedAction < BinData::Record
+ endian :big
- uint16 :action_type
- uint16 :action_length
- string :body, length: -> { action_length - 4 }
+ uint16 :action_type
+ uint16 :action_length
+ string :body, length: -> { action_length - 4 }
- def to_binary
- to_binary_s
+ def to_binary
+ to_binary_s
+ end
end
- end
- # Actions list of actions-apply instruction.
- class Actions < BinData::Primitive
- mandatory_parameter :length
+ # Actions list of actions-apply instruction.
+ class Actions13 < BinData::Primitive
+ mandatory_parameter :length
- endian :big
+ endian :big
- string :binary, read_length: :length
+ string :binary, read_length: :length
- def set(actions)
- self.binary = Array(actions).map(&:to_binary).join
- end
+ def set(actions)
+ self.binary = Array(actions).map(&:to_binary).join
+ end
- # rubocop:disable MethodLength
- def get
- actions = []
- tmp = binary
- while tmp.length > 0
- action = case BinData::Uint16be.read(tmp)
- when 0
- SendOutPort.read(tmp)
- else
- UnsupportedAction.read(tmp)
- end
- tmp = tmp[action.action_length..-1]
- actions << action
+ # rubocop:disable MethodLength
+ def get
+ actions = []
+ tmp = binary
+ until tmp.empty?
+ action = case BinData::Uint16be.read(tmp)
+ when 0
+ OpenFlow13::SendOutPort.read(tmp)
+ else
+ UnsupportedAction.read(tmp)
+ end
+ tmp = tmp[action.action_length..-1]
+ actions << action
+ end
+ actions
end
- actions
+ # rubocop:enable MethodLength
end
- # rubocop:enable MethodLength
end
end
diff --git a/lib/pio/open_flow13/apply.rb b/lib/pio/open_flow13/apply.rb
index 1833f88f..1e7e7cbc 100644
--- a/lib/pio/open_flow13/apply.rb
+++ b/lib/pio/open_flow13/apply.rb
@@ -1,36 +1,39 @@
require 'bindata'
require 'forwardable'
+require 'pio/open_flow/instruction'
require 'pio/open_flow13/actions'
module Pio
- # An instruction to apply a list of actions to a packet in-order.
- class Apply
- # OpenFlow 1.3.4 OFPIT_APPLY_ACTIONS instruction format.
- class Format < BinData::Record
- endian :big
+ module OpenFlow13
+ # An instruction to apply a list of actions to a packet in-order.
+ class Apply < OpenFlow::Instruction
+ # OpenFlow 1.3.4 OFPIT_APPLY_ACTIONS instruction format.
+ class Format < BinData::Record
+ endian :big
- uint16 :instruction_type, value: 4
- uint16 :instruction_length,
- initial_value: -> { 8 + actions.binary.length }
- string :padding, length: 4
- actions :actions, length: -> { instruction_length - 8 }
- end
+ uint16 :instruction_type, value: 4
+ uint16 :instruction_length,
+ initial_value: -> { 8 + actions.binary.length }
+ string :padding, length: 4
+ actions13 :actions, length: -> { instruction_length - 8 }
+ end
- def self.read(raw_data)
- allocate.tap do |apply|
- apply.instance_variable_set :@format, Format.read(raw_data)
+ def self.read(raw_data)
+ allocate.tap do |apply|
+ apply.instance_variable_set :@format, Format.read(raw_data)
+ end
end
- end
- extend Forwardable
+ extend Forwardable
- def_delegators :@format, :instruction_type
- def_delegators :@format, :instruction_length
- def_delegators :@format, :actions
- def_delegators :@format, :to_binary_s
+ def_delegators :@format, :instruction_type
+ def_delegators :@format, :instruction_length
+ def_delegators :@format, :actions
+ def_delegators :@format, :to_binary_s
- def initialize(actions = [])
- @format = Format.new(actions: actions)
+ def initialize(actions = [])
+ @format = Format.new(actions: actions)
+ end
end
end
end
diff --git a/lib/pio/open_flow13/buffer_id.rb b/lib/pio/open_flow13/buffer_id.rb
deleted file mode 100644
index 65aa4164..00000000
--- a/lib/pio/open_flow13/buffer_id.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-module Pio
- # Buffered packet to apply to, or :no_buffer.
- class BufferId < BinData::Primitive
- NO_BUFFER = 0xffffffff
-
- endian :big
- uint32 :buffer_id, initial_value: NO_BUFFER
-
- def get
- (buffer_id == NO_BUFFER) ? :no_buffer : buffer_id
- end
-
- def set(value)
- self.buffer_id = (value == :no_buffer ? NO_BUFFER : value)
- end
- end
-end
diff --git a/lib/pio/open_flow13/copy_ttl_inwards.rb b/lib/pio/open_flow13/copy_ttl_inwards.rb
new file mode 100644
index 00000000..82b57129
--- /dev/null
+++ b/lib/pio/open_flow13/copy_ttl_inwards.rb
@@ -0,0 +1,15 @@
+require 'pio/open_flow/action'
+
+module Pio
+ module OpenFlow13
+ # Copies TTL "inwards" -- from outermost to next-to-outermost
+ class CopyTtlInwards < OpenFlow::Action
+ action_header action_type: 12, action_length: 8
+ string :padding, length: 4
+
+ def initialize
+ super({})
+ end
+ end
+ end
+end
diff --git a/lib/pio/open_flow13/copy_ttl_outwards.rb b/lib/pio/open_flow13/copy_ttl_outwards.rb
new file mode 100644
index 00000000..80fd9efa
--- /dev/null
+++ b/lib/pio/open_flow13/copy_ttl_outwards.rb
@@ -0,0 +1,15 @@
+require 'pio/open_flow/action'
+
+module Pio
+ module OpenFlow13
+ # Copies TTL "outwards" -- from next-to-outermost to outermost
+ class CopyTtlOutwards < OpenFlow::Action
+ action_header action_type: 11, action_length: 8
+ string :padding, length: 4
+
+ def initialize
+ super({})
+ end
+ end
+ end
+end
diff --git a/lib/pio/open_flow13/decrement_ip_ttl.rb b/lib/pio/open_flow13/decrement_ip_ttl.rb
new file mode 100644
index 00000000..32327f4f
--- /dev/null
+++ b/lib/pio/open_flow13/decrement_ip_ttl.rb
@@ -0,0 +1,15 @@
+require 'pio/open_flow/action'
+
+module Pio
+ module OpenFlow13
+ # Decrements IP TTL
+ class DecrementIpTtl < OpenFlow::Action
+ action_header action_type: 24, action_length: 8
+ string :padding, length: 4
+
+ def initialize
+ super({})
+ end
+ end
+ end
+end
diff --git a/lib/pio/open_flow13/echo/reply.rb b/lib/pio/open_flow13/echo/reply.rb
index 6eb71bfa..e06e1f9f 100644
--- a/lib/pio/open_flow13/echo/reply.rb
+++ b/lib/pio/open_flow13/echo/reply.rb
@@ -5,10 +5,10 @@ module OpenFlow13
module Echo
# OpenFlow 1.3 Echo Reply message.
class Reply < OpenFlow::Message
- open_flow_header version: 4, message_type: 3
- string :body, read_length: -> { message_length - 8 }
+ open_flow_header version: 4, type: 3
+ string :body, read_length: -> { length - 8 }
- alias_method :user_data, :body
+ alias user_data body
end
end
end
diff --git a/lib/pio/open_flow13/echo/request.rb b/lib/pio/open_flow13/echo/request.rb
index a0cd834a..0e42c211 100644
--- a/lib/pio/open_flow13/echo/request.rb
+++ b/lib/pio/open_flow13/echo/request.rb
@@ -5,10 +5,10 @@ module OpenFlow13
module Echo
# OpenFlow 1.3 Echo Request message.
class Request < OpenFlow::Message
- open_flow_header version: 4, message_type: 2
- string :body, read_length: -> { message_length - 8 }
+ open_flow_header version: 4, type: 2
+ string :body, read_length: -> { length - header_length }
- alias_method :user_data, :body
+ alias user_data body
end
end
end
diff --git a/lib/pio/open_flow13/error.rb b/lib/pio/open_flow13/error.rb
index c27e3705..af44cde9 100644
--- a/lib/pio/open_flow13/error.rb
+++ b/lib/pio/open_flow13/error.rb
@@ -1,33 +1,22 @@
+require 'pio/open_flow/error_message'
+require 'pio/open_flow13/error/bad_request'
require 'pio/open_flow13/error/error_type13'
+require 'pio/open_flow13/error/hello_failed'
module Pio
module OpenFlow13
# Error message parser
module Error
+ mattr_reader(:type) { 1 }
+
+ extend OpenFlow::ErrorMessage
+
# Error message body parser.
class BodyParser < BinData::Record
endian :big
error_type13 :error_type
uint16 :error_code
end
-
- # rubocop:disable MethodLength
- def self.read(binary)
- body = OpenFlowHeaderParser.read(binary).body
- error = BodyParser.read(body).snapshot
- klass = case error.error_type
- when :hello_failed
- HelloFailed
- when :bad_request
- BadRequest
- else
- # Not implemented yet
- fail 'Unknown error message '\
- "(type=#{error.error_type}, code=#{error.error_code})"
- end
- klass.read binary
- end
- # rubocop:enable MethodLength
end
end
end
diff --git a/lib/pio/open_flow13/error/bad_request.rb b/lib/pio/open_flow13/error/bad_request.rb
index c5d5c6cc..bc93998a 100644
--- a/lib/pio/open_flow13/error/bad_request.rb
+++ b/lib/pio/open_flow13/error/bad_request.rb
@@ -1,3 +1,4 @@
+require 'pio/open_flow/error_message'
require 'pio/open_flow/message'
require 'pio/open_flow13/error/error_type13'
@@ -23,7 +24,7 @@ class BadRequestCode < BinData::Primitive
bad_port: 11,
bad_packet: 12,
multipart_buffer_overflow: 13
- }
+ }.freeze
endian :big
uint16 :error_code
@@ -38,8 +39,8 @@ def set(value)
end
open_flow_header version: 4,
- message_type: 1,
- message_length: -> { 12 + raw_data.length }
+ type: OpenFlow::ErrorMessage.type,
+ length: -> { 12 + raw_data.length }
error_type13 :error_type, value: -> { :bad_request }
bad_request_code :error_code
rest :raw_data
diff --git a/lib/pio/open_flow13/error/error_type13.rb b/lib/pio/open_flow13/error/error_type13.rb
index ed17dc9b..ff05b9d1 100644
--- a/lib/pio/open_flow13/error/error_type13.rb
+++ b/lib/pio/open_flow13/error/error_type13.rb
@@ -19,7 +19,7 @@ class ErrorType13 < BinData::Primitive
meter_mod_failed: 12,
table_features_failed: 13,
experimenter: 0xffff
- }
+ }.freeze
endian :big
uint16 :error_type
diff --git a/lib/pio/open_flow13/error/hello_failed.rb b/lib/pio/open_flow13/error/hello_failed.rb
index 4881af85..9d954935 100644
--- a/lib/pio/open_flow13/error/hello_failed.rb
+++ b/lib/pio/open_flow13/error/hello_failed.rb
@@ -1,3 +1,4 @@
+require 'pio/open_flow/error_message'
require 'pio/open_flow/hello_failed_code'
require 'pio/open_flow/message'
require 'pio/open_flow13/error/error_type13'
@@ -8,8 +9,8 @@ module Error
# Hello Failed error message
class HelloFailed < OpenFlow::Message
open_flow_header version: 4,
- message_type: 1,
- message_length: -> { 12 + description.length }
+ type: OpenFlow::ErrorMessage.type,
+ length: -> { 12 + description.length }
error_type13 :error_type
hello_failed_code :error_code
rest :description
diff --git a/lib/pio/open_flow13/features/reply.rb b/lib/pio/open_flow13/features/reply.rb
index d3e81655..e1521793 100644
--- a/lib/pio/open_flow13/features/reply.rb
+++ b/lib/pio/open_flow13/features/reply.rb
@@ -1,3 +1,4 @@
+require 'pio/open_flow/datapath_id'
require 'pio/open_flow/message'
module Pio
@@ -6,8 +7,14 @@ module OpenFlow13
class Features
# Features Reply message.
class Reply < OpenFlow::Message
- extend OpenFlow::Flags
-
+ open_flow_header version: 4, type: 6, length: 32
+ datapath_id :datapath_id
+ alias dpid datapath_id
+ uint32 :n_buffers
+ uint8 :n_tables
+ uint8 :auxiliary_id
+ uint16 :padding
+ hide :padding
flags_32bit(:capabilities,
[:flow_stats,
:table_stats,
@@ -18,21 +25,7 @@ class Reply < OpenFlow::Message
:queue_stats,
:NOT_USED,
:port_blocked])
-
- open_flow_header version: 4, message_type: 6, message_length: 32
- datapath_id :datapath_id
- uint32 :n_buffers
- uint8 :n_tables
- uint8 :auxiliary_id
- uint16 :padding
- hide :padding
- capabilities :capabilities
uint32 :reserved
-
- def datapath_id
- @format.datapath_id.to_i
- end
- alias_method :dpid, :datapath_id
end
end
end
diff --git a/lib/pio/open_flow13/features/request.rb b/lib/pio/open_flow13/features/request.rb
index ebfa4c5e..0e6a29ae 100644
--- a/lib/pio/open_flow13/features/request.rb
+++ b/lib/pio/open_flow13/features/request.rb
@@ -6,7 +6,7 @@ module OpenFlow13
class Features
# Features Request message.
class Request < OpenFlow::Message
- open_flow_header version: 4, message_type: 5
+ open_flow_header version: 4, type: 5
string :body, value: ''
end
end
diff --git a/lib/pio/open_flow13/flow_mod.rb b/lib/pio/open_flow13/flow_mod.rb
index 375a912d..4aa11250 100644
--- a/lib/pio/open_flow13/flow_mod.rb
+++ b/lib/pio/open_flow13/flow_mod.rb
@@ -1,5 +1,5 @@
require 'pio/open_flow'
-require 'pio/open_flow13/buffer_id'
+require 'pio/open_flow/buffer_id'
require 'pio/open_flow13/match'
module Pio
@@ -14,7 +14,7 @@ class Command < BinData::Primitive
modify_strict: 2,
delete: 3,
delete_strict: 4
- }
+ }.freeze
uint8 :command
@@ -36,7 +36,7 @@ class OutPort < BinData::Primitive
uint32 :out_port, initial_value: ANY
def get
- (out_port == ANY) ? :any : out_port
+ out_port == ANY ? :any : out_port
end
def set(value)
@@ -53,7 +53,7 @@ class OutGroup < BinData::Primitive
uint32 :out_group, initial_value: ANY
def get
- (out_group == ANY) ? :any : out_group
+ out_group == ANY ? :any : out_group
end
def set(value)
@@ -76,7 +76,7 @@ def set(instructions)
def get
list = []
tmp = instructions
- while tmp.length > 0
+ until tmp.empty?
instruction_type = BinData::Uint16be.read(tmp)
instruction = case instruction_type
when 1
@@ -88,7 +88,7 @@ def get
when 6
Meter.read(tmp)
else
- fail "Unsupported instruction #{instruction_type}"
+ raise "Unsupported instruction #{instruction_type}"
end
tmp = tmp[instruction.instruction_length..-1]
list << instruction
@@ -102,18 +102,8 @@ def length
end
end
- extend OpenFlow::Flags
-
- flags_16bit :flags,
- [:send_flow_rem,
- :check_overwrap,
- :reset_counts,
- :no_packet_counts,
- :no_byte_counts]
-
- open_flow_header(version: 4,
- message_type: 14,
- message_length: lambda do
+ open_flow_header(version: 4, type: 14,
+ length: lambda do
48 + match.length + instructions.length
end)
uint64 :cookie
@@ -126,7 +116,12 @@ def length
buffer_id :buffer_id
out_port :out_port
out_group :out_group
- flags :flags
+ flags_16bit :flags,
+ [:send_flow_rem,
+ :check_overwrap,
+ :reset_counts,
+ :no_packet_counts,
+ :no_byte_counts]
string :padding, length: 2
hide :padding
oxm :match
diff --git a/lib/pio/open_flow13/goto_table.rb b/lib/pio/open_flow13/goto_table.rb
index 71a2ff12..d6e61f92 100644
--- a/lib/pio/open_flow13/goto_table.rb
+++ b/lib/pio/open_flow13/goto_table.rb
@@ -1,37 +1,39 @@
require 'bindata'
require 'forwardable'
+require 'pio/open_flow/instruction'
-# Base module.
module Pio
- # Resubmit to the table_id
- class GotoTable
- # OpenFlow 1.3.4 OFPIT_GOTO_TABLE instruction format
- class Format < BinData::Record
- endian :big
+ module OpenFlow13
+ # Resubmit to the table_id
+ class GotoTable < OpenFlow::Instruction
+ # OpenFlow 1.3.4 OFPIT_GOTO_TABLE instruction format
+ class Format < BinData::Record
+ endian :big
- uint16 :instruction_type, value: 1
- uint16 :instruction_length, value: 8
- uint8 :table_id
- bit24 :padding
- hide :padding
- end
+ uint16 :instruction_type, value: 1
+ uint16 :instruction_length, value: 8
+ uint8 :table_id
+ bit24 :padding
+ hide :padding
+ end
- def self.read(raw_data)
- allocate.tap do |goto_table|
- goto_table.instance_variable_set :@format, Format.read(raw_data)
+ def self.read(raw_data)
+ allocate.tap do |goto_table|
+ goto_table.instance_variable_set :@format, Format.read(raw_data)
+ end
end
- end
- extend Forwardable
+ extend Forwardable
- def_delegators :@format, :instruction_type
- def_delegators :@format, :instruction_length
- def_delegators :@format, :table_id
- def_delegators :@format, :to_binary_s
- def_delegator :@format, :to_binary_s, :to_binary
+ def_delegators :@format, :instruction_type
+ def_delegators :@format, :instruction_length
+ def_delegators :@format, :table_id
+ def_delegators :@format, :to_binary_s
+ def_delegator :@format, :to_binary_s, :to_binary
- def initialize(table_id)
- @format = Format.new(table_id: table_id)
+ def initialize(table_id)
+ @format = Format.new(table_id: table_id)
+ end
end
end
end
diff --git a/lib/pio/open_flow13/hello.rb b/lib/pio/open_flow13/hello.rb
index f4c4b76b..949d7f35 100644
--- a/lib/pio/open_flow13/hello.rb
+++ b/lib/pio/open_flow13/hello.rb
@@ -41,8 +41,8 @@ def length
end
end
- open_flow_header version: 4, message_type: 0
- body :body
+ open_flow_header version: 4, type: 0
+ body :body, read_length: -> { length - header_length }
def elements
body.elements
@@ -68,11 +68,10 @@ def version_bitmap
end
def initialize(user_options = {})
- validate_user_options user_options
body_options = { elements: [{ element_type: VERSION_BITMAP,
element_length: 8,
element_value: 0b10000 }] }
- @format = Format.new(user_options.merge(body: body_options))
+ super user_options.merge(body: body_options)
end
end
end
diff --git a/lib/pio/open_flow13/match.rb b/lib/pio/open_flow13/match.rb
index 46340793..70116490 100644
--- a/lib/pio/open_flow13/match.rb
+++ b/lib/pio/open_flow13/match.rb
@@ -1,4 +1,5 @@
require 'bindata'
+require 'pio/open_flow/flow_match'
require 'pio/type/ip_address'
require 'pio/type/ipv6_address'
require 'pio/type/mac_address'
@@ -11,7 +12,7 @@ module OpenFlow13
MATCH_TYPE_OXM = 1
# OpenFlow eXtensible Match (OXM)
- class Match
+ class Match < OpenFlow::FlowMatch
# OFPXMC_NXM_1 TLV value
class NiciraMatchExtensionValue < BinData::Record
OXM_CLASS = 0x1
@@ -22,6 +23,11 @@ class OpenFlowBasicValue < BinData::Record
OXM_CLASS = 0x8000
end
+ # OFPXMC_PACKET_REGS TLV value
+ class PacketRegistersValue < BinData::Record
+ OXM_CLASS = 0x8001
+ end
+
# OFPXMC_EXPERIMENTER TLV value
class ExperimenterValue < BinData::Record
OXM_CLASS = 0xFFFF
@@ -759,6 +765,105 @@ def length
end
end
+ # OXM_PACKET_REG0 match field
+ class PacketReg0 < PacketRegistersValue
+ OXM_FIELD = 0
+
+ endian :big
+
+ uint64 :packet_reg0
+
+ def length
+ 8
+ end
+ end
+
+ # Masked OXM_PACKET_REG0 match field
+ class MaskedPacketReg0 < PacketRegistersValue
+ endian :big
+
+ uint64 :packet_reg0
+ uint64 :packet_reg0_mask
+
+ def length
+ 16
+ end
+ end
+ # OXM_PACKET_REG1 match field
+ class PacketReg1 < PacketRegistersValue
+ OXM_FIELD = 1
+
+ endian :big
+
+ uint64 :packet_reg1
+
+ def length
+ 8
+ end
+ end
+
+ # Masked OXM_PACKET_REG1 match field
+ class MaskedPacketReg1 < PacketRegistersValue
+ endian :big
+
+ uint64 :packet_reg1
+ uint64 :packet_reg1_mask
+
+ def length
+ 16
+ end
+ end
+
+ # OXM_PACKET_REG2 match field
+ class PacketReg2 < PacketRegistersValue
+ OXM_FIELD = 2
+
+ endian :big
+
+ uint64 :packet_reg2
+
+ def length
+ 8
+ end
+ end
+
+ # Masked OXM_PACKET_REG2 match field
+ class MaskedPacketReg2 < PacketRegistersValue
+ endian :big
+
+ uint64 :packet_reg2
+ uint64 :packet_reg2_mask
+
+ def length
+ 16
+ end
+ end
+
+ # OXM_PACKET_REG3 match field
+ class PacketReg3 < PacketRegistersValue
+ OXM_FIELD = 3
+
+ endian :big
+
+ uint64 :packet_reg3
+
+ def length
+ 8
+ end
+ end
+
+ # Masked OXM_PACKET_REG3 match field
+ class MaskedPacketReg3 < PacketRegistersValue
+ endian :big
+
+ uint64 :packet_reg3
+ uint64 :packet_reg3_mask
+
+ def length
+ 16
+ end
+ end
+
# OXM format
class Oxm < BinData::Record
# Experimenter part, data will use oxm_length
@@ -840,7 +945,7 @@ def choose_tlv_value
when Reg7::OXM_FIELD
masked? ? MaskedReg7 : Reg7
else
- fail "Unknown OXM field value: #{oxm_field}"
+ raise "Unknown OXM field value: #{oxm_field}"
end
end
# rubocop:enable AbcSize
@@ -998,7 +1103,7 @@ def choose_tlv_value
when TunnelId::OXM_FIELD
masked? ? MaskedTunnelId : TunnelId
else
- fail "Unknown OXM field value: #{oxm_field}"
+ raise "Unknown OXM field value: #{oxm_field}"
end
end
# rubocop:enable MethodLength
@@ -1008,6 +1113,58 @@ def choose_tlv_value
end
# rubocop:enable MethodLength
+ # Packet Register match field.
+ class PacketRegisters < BinData::Record
+ endian :big
+
+ bit7 :oxm_field
+ bit1 :oxm_hasmask
+ uint8 :oxm_length, value: -> { tlv_value.length }
+ choice :tlv_value, selection: :choose_tlv_value do
+ packet_reg0 PacketReg0
+ masked_packet_reg0 MaskedPacketReg0
+ packet_reg1 PacketReg1
+ masked_packet_reg1 MaskedPacketReg1
+ packet_reg2 PacketReg2
+ masked_packet_reg2 MaskedPacketReg2
+ packet_reg3 PacketReg3
+ masked_packet_reg3 MaskedPacketReg3
+ end
+
+ def length
+ tlv_value.length + 2
+ end
+
+ def masked?
+ oxm_hasmask == 1
+ end
+
+ def method_missing(method, *args, &block)
+ tlv_value.__send__ method, *args, &block
+ end
+
+ private
+
+ # rubocop:disable CyclomaticComplexity
+ # rubocop:disable MethodLength
+ def choose_tlv_value
+ case oxm_field
+ when PacketReg0::OXM_FIELD
+ masked? ? MaskedPacketReg0 : PacketReg0
+ when PacketReg1::OXM_FIELD
+ masked? ? MaskedPacketReg1 : PacketReg1
+ when PacketReg2::OXM_FIELD
+ masked? ? MaskedPacketReg2 : PacketReg2
+ when PacketReg3::OXM_FIELD
+ masked? ? MaskedPacketReg3 : PacketReg3
+ else
+ raise "Unknown OXM field value: #{oxm_field}"
+ end
+ end
+ # rubocop:enable CyclomaticComplexity
+ # rubocop:enable MethodLength
+ end
+
# OXM match field.
class MatchField < BinData::Record
endian :big
@@ -1017,6 +1174,7 @@ class MatchField < BinData::Record
NiciraMatchExtension NiciraMatchExtensionValue::OXM_CLASS
OpenflowBasic OpenFlowBasicValue::OXM_CLASS
Experimenter ExperimenterValue::OXM_CLASS
+ PacketRegisters PacketRegistersValue::OXM_CLASS
end
def oxm_field
@@ -1041,8 +1199,10 @@ def method_missing(method, *args, &block)
return class_payload.tlv_value.__send__(method, *args, &block)
when ExperimenterValue::OXM_CLASS
return class_payload.__send__(method, *args, &block)
+ when PacketRegistersValue::OXM_CLASS
+ return class_payload.__send__(method, *args, &block)
else
- fail NoMethodError, method.to_s
+ raise NoMethodError, method.to_s
end
end
end
@@ -1062,18 +1222,21 @@ def length
match_length + padding_length
end
+ # rubocop:disable AbcSize
# rubocop:disable Next
# rubocop:disable LineLength
def method_missing(method, *args, &block)
match_fields.each do |each|
- if each.oxm_class == OpenFlowBasicValue::OXM_CLASS || each.oxm_class == NiciraMatchExtensionValue::OXM_CLASS
+ if each.oxm_class == OpenFlowBasicValue::OXM_CLASS || each.oxm_class == NiciraMatchExtensionValue::OXM_CLASS || each.oxm_class == PacketRegistersValue::OXM_CLASS
next unless each.class_payload.tlv_value.respond_to?(method)
return each.class_payload.tlv_value.__send__(
- method, *args, &block)
+ method, *args, &block
+ )
end
end
- fail NoMethodError, method.to_s
+ raise NoMethodError, method.to_s
end
+ # rubocop:enable AbcSize
# rubocop:enable Next
# rubocop:enable LineLength
@@ -1084,7 +1247,7 @@ def tlv_length_left
end
def tlv_total_length
- if match_fields.size > 0
+ if !match_fields.empty?
match_fields.map(&:length).inject(&:+)
else
0
@@ -1121,7 +1284,8 @@ def initialize(user_attrs)
:arp_sender_protocol_address, :arp_target_protocol_address,
:arp_sender_hardware_address, :arp_target_hardware_address,
:ipv6_source_address, :ipv6_destination_address, :tunnel_id,
- :reg0, :reg1, :reg2, :reg3, :reg4, :reg5, :reg6, :reg7].each do |each|
+ :reg0, :reg1, :reg2, :reg3, :reg4, :reg5, :reg6, :reg7,
+ :packet_reg0, :packet_reg1, :packet_reg2, :packet_reg3].each do |each|
next unless user_attrs.key?(each)
klass = Match.const_get(each.to_s.split('_').map(&:capitalize).join)
mask_key = "#{each}_mask".to_sym
diff --git a/lib/pio/open_flow13/meter.rb b/lib/pio/open_flow13/meter.rb
index eed315fc..3fd2ce3d 100644
--- a/lib/pio/open_flow13/meter.rb
+++ b/lib/pio/open_flow13/meter.rb
@@ -1,33 +1,36 @@
require 'forwardable'
+require 'pio/open_flow/instruction'
# Base module.
module Pio
- # Apply meter (rate limiter)
- class Meter
- # OpenFlow 1.3.4 OFPIT_METER instruction format
- class Format < BinData::Record
- endian :big
+ module OpenFlow13
+ # Apply meter (rate limiter)
+ class Meter < OpenFlow::Instruction
+ # OpenFlow 1.3.4 OFPIT_METER instruction format
+ class Format < BinData::Record
+ endian :big
- uint16 :instruction_type, value: 6
- uint16 :instruction_length, value: 8
- uint32 :meter_id
- end
+ uint16 :instruction_type, value: 6
+ uint16 :instruction_length, value: 8
+ uint32 :meter_id
+ end
- def self.read(raw_data)
- allocate.tap do |meter|
- meter.instance_variable_set :@format, Format.read(raw_data)
+ def self.read(raw_data)
+ allocate.tap do |meter|
+ meter.instance_variable_set :@format, Format.read(raw_data)
+ end
end
- end
- extend Forwardable
+ extend Forwardable
- def_delegators :@format, :instruction_type
- def_delegators :@format, :instruction_length
- def_delegators :@format, :meter_id
- def_delegators :@format, :to_binary_s
+ def_delegators :@format, :instruction_type
+ def_delegators :@format, :instruction_length
+ def_delegators :@format, :meter_id
+ def_delegators :@format, :to_binary_s
- def initialize(meter_id)
- @format = Format.new(meter_id: meter_id)
+ def initialize(meter_id)
+ @format = Format.new(meter_id: meter_id)
+ end
end
end
end
diff --git a/lib/pio/open_flow13/nicira_conjunction.rb b/lib/pio/open_flow13/nicira_conjunction.rb
new file mode 100644
index 00000000..286f3224
--- /dev/null
+++ b/lib/pio/open_flow13/nicira_conjunction.rb
@@ -0,0 +1,26 @@
+require 'pio/open_flow/nicira_action'
+
+module Pio
+ module OpenFlow13
+ # NXAST_CONJUNCTION action
+ class NiciraConjunction < OpenFlow::NiciraAction
+ nicira_action_header action_type: 0xffff,
+ action_length: 16,
+ subtype: 34
+ uint8 :_clause
+ uint8 :n_clauses
+ uint32 :conjunction_id
+
+ def initialize(options)
+ super(_clause: options[:clause] - 1,
+ n_clauses: options[:n_clauses],
+ conjunction_id: options[:conjunction_id])
+ end
+
+ def clause
+ _clause + 1
+ end
+ end
+ end
+ NiciraConjunction = OpenFlow13::NiciraConjunction
+end
diff --git a/lib/pio/open_flow13/nicira_reg_load.rb b/lib/pio/open_flow13/nicira_reg_load.rb
index e8d2f438..ba286353 100644
--- a/lib/pio/open_flow13/nicira_reg_load.rb
+++ b/lib/pio/open_flow13/nicira_reg_load.rb
@@ -1,43 +1,63 @@
-require 'active_support/core_ext/string/inflections'
-require 'pio/open_flow/action'
+require 'pio/open_flow/nicira_action'
+require 'pio/open_flow13/match'
module Pio
module OpenFlow13
# NXAST_REG_LOAD action
- class NiciraRegLoad < OpenFlow::Action
- action_header action_type: 0xffff, action_length: 24
- uint32 :experimenter_id, value: 0x2320
- uint16 :experimenter_type, value: 7
- bit10 :offset_internal, initial_value: 0
- bit6 :n_bits_internal
- uint32 :destination_internal
- uint64 :value_internal
-
- attr_reader :destination
+ class NiciraRegLoad < OpenFlow::NiciraAction
+ nicira_action_header action_type: 0xffff,
+ action_length: 24,
+ subtype: 7
+ bit10 :_offset, initial_value: 0
+ bit6 :_n_bits
+ struct :_destination do
+ uint16 :oxm_class
+ bit7 :oxm_field
+ bit1 :oxm_hasmask, value: 0
+ bit8 :oxm_length
+ end
+ uint64 :_value
- # rubocop:disable AbcSize
- # rubocop:disable LineLength
def initialize(value, destination, options = {})
@destination = destination
- oxm_klass = Match.const_get(destination.to_s.split('_').map(&:capitalize).join)
- super(value_internal: value,
- offset_internal: options[:offset] || 0,
- n_bits_internal: options[:n_bits] ? options[:n_bits] - 1 : oxm_klass.new.length * 8 - 1,
- destination_internal: ((oxm_klass.superclass.const_get(:OXM_CLASS) << 16) | (oxm_klass.const_get(:OXM_FIELD) << 9) | oxm_klass.new.length))
+ super(_value: value,
+ _offset: options[:offset] || 0,
+ _n_bits: (options[:n_bits] || oxm_length * 8) - 1,
+ _destination: { oxm_class: oxm_class,
+ oxm_field: oxm_field,
+ oxm_length: oxm_length })
end
- # rubocop:enable AbcSize
- # rubocop:enable LineLength
+
+ attr_reader :destination
def offset
- offset_internal
+ _offset
end
def n_bits
- n_bits_internal + 1
+ _n_bits + 1
end
def value
- value_internal
+ _value
+ end
+
+ private
+
+ def oxm_class
+ destination_oxm_class.const_get(:OXM_CLASS)
+ end
+
+ def oxm_field
+ destination_oxm_class.const_get(:OXM_FIELD)
+ end
+
+ def oxm_length
+ destination_oxm_class.new.length
+ end
+
+ def destination_oxm_class
+ Match.const_get(@destination.to_s.split('_').map(&:capitalize).join)
end
end
end
diff --git a/lib/pio/open_flow13/nicira_reg_move.rb b/lib/pio/open_flow13/nicira_reg_move.rb
index 524406b9..072efca0 100644
--- a/lib/pio/open_flow13/nicira_reg_move.rb
+++ b/lib/pio/open_flow13/nicira_reg_move.rb
@@ -1,42 +1,84 @@
-require 'active_support/core_ext/string/inflections'
-require 'pio/open_flow/action'
+require 'pio/open_flow/nicira_action'
+require 'pio/open_flow13/match'
module Pio
module OpenFlow13
# NXAST_REG_MOVE action
- class NiciraRegMove < OpenFlow::Action
- action_header action_type: 0xffff, action_length: 24
- uint32 :experimenter_id, value: 0x2320
- uint16 :experimenter_type, value: 6
- uint16 :n_bits, initial_value: -> { source_oxm_length * 8 }
- uint16 :source_offset, value: 0
- uint16 :destination_offset, value: 0
- uint16 :source_oxm_class
- bit7 :source_oxm_field
- bit1 :source_oxm_hasmask, value: 0
- uint8 :source_oxm_length
- uint16 :destination_oxm_class
- bit7 :destination_oxm_field
- bit1 :destination_oxm_hasmask, value: 0
- uint8 :destination_oxm_length
-
- attr_reader :from
- attr_reader :to
-
- # rubocop:disable AbcSize
- def initialize(options)
- @from = options.fetch(:from)
- @to = options.fetch(:to)
- from_klass = Match.const_get(@from.to_s.classify)
- to_klass = Match.const_get(@to.to_s.classify)
- super(source_oxm_class: from_klass.superclass.const_get(:OXM_CLASS),
- source_oxm_field: from_klass.const_get(:OXM_FIELD),
- source_oxm_length: from_klass.new.length,
- destination_oxm_class: to_klass.superclass.const_get(:OXM_CLASS),
- destination_oxm_field: to_klass.const_get(:OXM_FIELD),
- destination_oxm_length: to_klass.new.length)
- end
- # rubocop:enable AbcSize
+ class NiciraRegMove < OpenFlow::NiciraAction
+ nicira_action_header action_type: 0xffff,
+ action_length: 24,
+ subtype: 6
+ uint16 :n_bits, initial_value: -> { _source[:oxm_length] * 8 }
+ uint16 :source_offset, initial_value: 0
+ uint16 :destination_offset, initial_value: 0
+ struct :_source do
+ uint16 :oxm_class
+ bit7 :oxm_field
+ bit1 :oxm_hasmask, value: 0
+ uint8 :oxm_length
+ end
+ struct :_destination do
+ uint16 :oxm_class
+ bit7 :oxm_field
+ bit1 :oxm_hasmask, value: 0
+ uint8 :oxm_length
+ end
+
+ # 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
+ super registers.merge(options)
+ end
+ # rubocop:enable MethodLength
+
+ attr_reader :source
+ attr_reader :destination
+
+ 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
end
end
end
diff --git a/lib/pio/open_flow13/nicira_send_out_port.rb b/lib/pio/open_flow13/nicira_send_out_port.rb
index dfdbdbb7..ff250ffa 100644
--- a/lib/pio/open_flow13/nicira_send_out_port.rb
+++ b/lib/pio/open_flow13/nicira_send_out_port.rb
@@ -1,37 +1,53 @@
-require 'pio/open_flow/action'
+require 'pio/open_flow/nicira_action'
+require 'pio/open_flow13/match'
+require 'pio/open_flow13/send_out_port'
module Pio
module OpenFlow13
# NXAST_OUTPUT_REG action
- class NiciraSendOutPort < OpenFlow::Action
- action_header action_type: 0xffff, action_length: 24
- uint32 :experimenter_id, value: 0x2320
- uint16 :experimenter_type, value: 15
- bit10 :offset_internal, value: 0
- bit6 :n_bits_internal
- uint32 :source_internal
- uint16 :max_length, value: 0
+ class NiciraSendOutPort < OpenFlow::NiciraAction
+ nicira_action_header action_type: 0xffff,
+ action_length: 24,
+ subtype: 15
+ bit10 :_offset
+ bit6 :_n_bits
+ struct :_source do
+ uint16 :oxm_class
+ bit7 :oxm_field
+ bit1 :oxm_hasmask, value: 0
+ bit8 :oxm_length
+ end
+ uint16 :max_length
string :zero, length: 6
- attr_reader :source
-
- # rubocop:disable AbcSize
- # rubocop:disable LineLength
- def initialize(source)
+ def initialize(source, options = {})
@source = source
- oxm_klass = Match.const_get(source.to_s.split('_').map(&:capitalize).join)
- super(n_bits_internal: oxm_klass.new.length * 8 - 1,
- source_internal: ((oxm_klass.superclass.const_get(:OXM_CLASS) << 16) | (oxm_klass.const_get(:OXM_FIELD) << 9) | oxm_klass.new.length))
+ super(_n_bits: (options[:n_bits] || oxm_length * 8) - 1,
+ _offset: options[:offset] || 0,
+ _source: { oxm_class: source_oxm_class.const_get(:OXM_CLASS),
+ oxm_field: source_oxm_class.const_get(:OXM_FIELD),
+ oxm_length: oxm_length },
+ max_length: options[:max_length] || SendOutPort::NO_BUFFER)
end
- # rubocop:enable AbcSize
- # rubocop:enable LineLength
+
+ attr_reader :source
def offset
- offset_internal
+ _offset
end
def n_bits
- n_bits_internal + 1
+ _n_bits + 1
+ end
+
+ private
+
+ def oxm_length
+ source_oxm_class.new.length
+ end
+
+ def source_oxm_class
+ Match.const_get(@source.to_s.split('_').map(&:capitalize).join)
end
end
end
diff --git a/lib/pio/open_flow13/nicira_stack_pop.rb b/lib/pio/open_flow13/nicira_stack_pop.rb
new file mode 100644
index 00000000..a60f7421
--- /dev/null
+++ b/lib/pio/open_flow13/nicira_stack_pop.rb
@@ -0,0 +1,49 @@
+require 'pio/open_flow/nicira_action'
+require 'pio/open_flow13/match'
+
+module Pio
+ module OpenFlow13
+ # NXAST_STACK_POP action
+ class NiciraStackPop < OpenFlow::NiciraAction
+ nicira_action_header action_type: 0xffff,
+ action_length: 24,
+ subtype: 28
+ uint16 :_offset
+ struct :field do
+ uint16 :oxm_class
+ bit7 :oxm_field
+ bit1 :oxm_hasmask, value: 0
+ uint8 :oxm_length
+ end
+ uint16 :_n_bits
+ string :padding, length: 6
+ hide :padding
+
+ def initialize(field, options = {})
+ @field = field
+ super(_offset: options[:offset] || 0,
+ _n_bits: (options[:n_bits] || oxm_length * 8) + 1,
+ field: { oxm_class: field_oxm_class.const_get(:OXM_CLASS),
+ oxm_field: field_oxm_class.const_get(:OXM_FIELD),
+ oxm_length: oxm_length })
+ end
+
+ attr_reader :field
+ alias offset _offset
+
+ def n_bits
+ _n_bits - 1
+ end
+
+ private
+
+ def oxm_length
+ field_oxm_class.new.length
+ end
+
+ def field_oxm_class
+ Match.const_get(@field.to_s.split('_').map(&:capitalize).join)
+ end
+ end
+ end
+end
diff --git a/lib/pio/open_flow13/nicira_stack_push.rb b/lib/pio/open_flow13/nicira_stack_push.rb
new file mode 100644
index 00000000..59e86670
--- /dev/null
+++ b/lib/pio/open_flow13/nicira_stack_push.rb
@@ -0,0 +1,49 @@
+require 'pio/open_flow/nicira_action'
+require 'pio/open_flow13/match'
+
+module Pio
+ module OpenFlow13
+ # NXAST_STACK_PUSH action
+ class NiciraStackPush < OpenFlow::NiciraAction
+ nicira_action_header action_type: 0xffff,
+ action_length: 24,
+ subtype: 27
+ uint16 :_offset
+ struct :field do
+ uint16 :oxm_class
+ bit7 :oxm_field
+ bit1 :oxm_hasmask, value: 0
+ uint8 :oxm_length
+ end
+ uint16 :_n_bits
+ string :padding, length: 6
+ hide :padding
+
+ def initialize(field, options = {})
+ @field = field
+ super(_offset: options[:offset] || 0,
+ _n_bits: (options[:n_bits] || oxm_length * 8) + 1,
+ field: { oxm_class: field_oxm_class.const_get(:OXM_CLASS),
+ oxm_field: field_oxm_class.const_get(:OXM_FIELD),
+ oxm_length: oxm_length })
+ end
+
+ attr_reader :field
+ alias offset _offset
+
+ def n_bits
+ _n_bits - 1
+ end
+
+ private
+
+ def oxm_length
+ field_oxm_class.new.length
+ end
+
+ def field_oxm_class
+ Match.const_get(@field.to_s.split('_').map(&:capitalize).join)
+ end
+ end
+ end
+end
diff --git a/lib/pio/open_flow13/packet_in.rb b/lib/pio/open_flow13/packet_in.rb
index a119d2b2..5f1bda8b 100644
--- a/lib/pio/open_flow13/packet_in.rb
+++ b/lib/pio/open_flow13/packet_in.rb
@@ -1,3 +1,4 @@
+require 'active_support/core_ext/object/try'
require 'pio/open_flow'
require 'pio/open_flow13/match'
require 'pio/parser'
@@ -8,8 +9,9 @@ module OpenFlow13
class PacketIn < OpenFlow::Message
# Why is this packet being sent to the controller?
# (enum ofp_packet_in_reason)
+ # rubocop:disable LineLength
class Reason < BinData::Primitive
- REASONS = { no_match: 0, action: 1, invalid_ttl: 2 }
+ REASONS = { no_match: 0, action: 1, invalid_ttl: 2 }.freeze
uint8 :reason
@@ -22,9 +24,8 @@ def set(value)
end
end
- open_flow_header(version: 4,
- message_type: 10,
- message_length: lambda do
+ open_flow_header(version: 4, type: 10,
+ length: lambda do
24 + match.length + padding.length + raw_data.length
end)
uint32 :buffer_id
@@ -35,11 +36,11 @@ def set(value)
oxm :match
string :padding, length: 2
hide :padding
- string :raw_data, read_length: :total_len
+ string :raw_data, read_length: -> { length - header_length - (16 + match.length + 2) }
attr_accessor :datapath_id
- alias_method :dpid, :datapath_id
- alias_method :dpid=, :datapath_id=
+ alias dpid datapath_id
+ alias dpid= datapath_id=
def data
@data ||= Pio::Parser.read(raw_data)
@@ -50,8 +51,10 @@ def in_port
end
def method_missing(method, *args)
- data.__send__(method, *args).snapshot
+ bindata_value = data.__send__(method, *args)
+ bindata_value.try(:snapshot) || bindata_value
end
+ # rubocop:enable LineLength
end
end
end
diff --git a/lib/pio/open_flow13/packet_out.rb b/lib/pio/open_flow13/packet_out.rb
index bb2eb309..8afa6b20 100644
--- a/lib/pio/open_flow13/packet_out.rb
+++ b/lib/pio/open_flow13/packet_out.rb
@@ -1,6 +1,7 @@
+require 'active_support/core_ext/object/try'
require 'pio/open_flow'
+require 'pio/open_flow/buffer_id'
require 'pio/open_flow13/actions'
-require 'pio/open_flow13/buffer_id'
module Pio
module OpenFlow13
@@ -14,7 +15,7 @@ class InPort < BinData::Primitive
uint32 :in_port
def get
- (in_port == CONTROLLER) ? :controller : in_port
+ in_port == CONTROLLER ? :controller : in_port
end
def set(value)
@@ -22,24 +23,24 @@ def set(value)
end
end
- open_flow_header(version: 4,
- message_type: 13,
- message_length: lambda do
+ open_flow_header(version: 4, type: 13,
+ length: lambda do
24 + actions_length + raw_data.length
end)
buffer_id :buffer_id
in_port :in_port
uint16 :actions_length, initial_value: -> { actions.binary.length }
string :padding, length: 6
- actions :actions, length: :actions_length
- string :raw_data, read_length: -> { message_length - 24 - actions_length }
+ actions13 :actions, length: :actions_length
+ string :raw_data, read_length: -> { length - 24 - actions_length }
def data
@data ||= Pio::Parser.read(raw_data)
end
def method_missing(method, *args)
- data.__send__(method, *args).snapshot
+ bindata_value = data.__send__(method, *args)
+ bindata_value.try(:snapshot) || bindata_value
end
end
end
diff --git a/lib/pio/open_flow13/set_ip_ttl.rb b/lib/pio/open_flow13/set_ip_ttl.rb
new file mode 100644
index 00000000..bdfeab13
--- /dev/null
+++ b/lib/pio/open_flow13/set_ip_ttl.rb
@@ -0,0 +1,16 @@
+require 'pio/open_flow/action'
+
+module Pio
+ module OpenFlow13
+ # Sets IP TTL
+ class SetIpTtl < OpenFlow::Action
+ action_header action_type: 23, action_length: 8
+ uint8 :ttl
+ string :padding, length: 3
+
+ def initialize(ttl)
+ super(ttl: ttl)
+ end
+ end
+ end
+end
diff --git a/lib/pio/open_flow13/stats_request.rb b/lib/pio/open_flow13/stats_request.rb
index 6980c046..d46a831a 100644
--- a/lib/pio/open_flow13/stats_request.rb
+++ b/lib/pio/open_flow13/stats_request.rb
@@ -12,7 +12,7 @@ class StatsType < BinData::Primitive
:table,
:port,
:queue,
- :vendor]
+ :vendor].freeze
endian :big
@@ -27,9 +27,7 @@ def get
end
end
- open_flow_header version: 4,
- message_type: 16,
- message_length: 12
+ open_flow_header version: 4, type: 16, length: 12
stats_type :stats_type
uint16 :stats_flags
end
diff --git a/lib/pio/open_flow13/write_metadata.rb b/lib/pio/open_flow13/write_metadata.rb
index 180f3081..edd70d76 100644
--- a/lib/pio/open_flow13/write_metadata.rb
+++ b/lib/pio/open_flow13/write_metadata.rb
@@ -1,52 +1,54 @@
require 'forwardable'
+require 'pio/open_flow/action'
-# Base module.
module Pio
- # Write metadata
- class WriteMetadata
- # OpenFlow 1.3.4 OFPIT_WRITE_METADATA instruction format
- class Format < BinData::Record
- endian :big
-
- uint16 :instruction_type, value: 2
- uint16 :instruction_length, value: 24
- uint32 :padding
- uint64 :metadata
- uint64 :metadata_mask
- end
+ module OpenFlow13
+ # Write metadata
+ class WriteMetadata < OpenFlow::Action
+ # OpenFlow 1.3.4 OFPIT_WRITE_METADATA instruction format
+ class Format < BinData::Record
+ endian :big
+
+ uint16 :instruction_type, value: 2
+ uint16 :instruction_length, value: 24
+ uint32 :padding
+ uint64 :metadata
+ uint64 :metadata_mask
+ end
- def self.read(raw_data)
- allocate.tap do |write_metadata|
- write_metadata.instance_variable_set :@format, Format.read(raw_data)
+ def self.read(raw_data)
+ allocate.tap do |write_metadata|
+ write_metadata.instance_variable_set :@format, Format.read(raw_data)
+ end
end
- end
- extend Forwardable
+ extend Forwardable
- def_delegators :@format, :instruction_type
- def_delegators :@format, :instruction_length
- def_delegators :@format, :metadata
- def_delegators :@format, :metadata_mask
- def_delegators :@format, :to_binary_s
+ def_delegators :@format, :instruction_type
+ def_delegators :@format, :instruction_length
+ def_delegators :@format, :metadata
+ def_delegators :@format, :metadata_mask
+ def_delegators :@format, :to_binary_s
- def initialize(user_options)
- @options = user_options
- @format = Format.new(options)
- end
+ def initialize(user_options)
+ @options = user_options
+ @format = Format.new(options)
+ end
- def options
- {
- metadata: metadata_option,
- metadata_mask: metadata_mask_option
- }
- end
+ def options
+ {
+ metadata: metadata_option,
+ metadata_mask: metadata_mask_option
+ }
+ end
- def metadata_option
- @options[:metadata]
- end
+ def metadata_option
+ @options[:metadata]
+ end
- def metadata_mask_option
- @options[:metadata_mask]
+ def metadata_mask_option
+ @options[:metadata_mask]
+ end
end
end
end
diff --git a/lib/pio/options.rb b/lib/pio/options.rb
index 3765bb92..006ff292 100644
--- a/lib/pio/options.rb
+++ b/lib/pio/options.rb
@@ -14,6 +14,11 @@ def self.option(name)
const_get(:OPTIONS) << name
end
+ def initialize(options)
+ validate options
+ @options = options
+ end
+
private
def validate(user_options)
@@ -42,7 +47,7 @@ def options
def check_unknown(user_options)
valid_options = mandatory_options + options
user_options.keys.each do |each|
- fail "Unknown option: #{each}." unless valid_options.include?(each)
+ raise "Unknown option: #{each}." unless valid_options.include?(each)
end
end
@@ -54,10 +59,10 @@ def check_mandatory(user_options)
def check_existence(user_options, key)
value = user_options.fetch(key) do |missing_key|
- fail ArgumentError, "The #{missing_key} option should be passed."
+ raise ArgumentError, "The #{missing_key} option should be passed."
end
return if value
- fail(ArgumentError, "The #{key} option shouldn't be #{value.inspect}.")
+ raise(ArgumentError, "The #{key} option shouldn't be #{value.inspect}.")
end
end
end
diff --git a/lib/pio/parser.rb b/lib/pio/parser.rb
index fe16e0e3..af65886a 100644
--- a/lib/pio/parser.rb
+++ b/lib/pio/parser.rb
@@ -1,27 +1,18 @@
+require 'pio/ethernet_frame'
require 'pio/ethernet_header'
require 'pio/ipv4_header'
module Pio
# Raw data parser.
class Parser
- # Ethernet header parser
- class EthernetFrame < BinData::Record
- endian :big
-
- mac_address :destination_mac
- mac_address :source_mac
- uint16 :ether_type
- rest :rest
- end
-
# IPv4 packet parser
class IPv4Packet < BinData::Record
- include EthernetHeader
- include IPv4Header
+ include Ethernet
+ include IPv4
endian :big
- ethernet_header ether_type: EtherType::IPV4
+ ethernet_header ether_type: Ethernet::Type::IPV4
ipv4_header
uint16 :transport_source_port
@@ -33,11 +24,11 @@ class IPv4Packet < BinData::Record
def self.read(raw_data)
ethernet_frame = EthernetFrame.read(raw_data)
case ethernet_frame.ether_type
- when EthernetHeader::EtherType::IPV4, EthernetHeader::EtherType::VLAN
+ when Ethernet::Type::IPV4, Ethernet::Type::VLAN
IPv4Packet.read raw_data
- when EthernetHeader::EtherType::ARP
+ when Ethernet::Type::ARP
Pio::Arp.read raw_data
- when EthernetHeader::EtherType::LLDP
+ when Ethernet::Type::LLDP
Pio::Lldp.read raw_data
else
ethernet_frame
diff --git a/lib/pio/ruby_dumper.rb b/lib/pio/ruby_dumper.rb
new file mode 100644
index 00000000..5e34041e
--- /dev/null
+++ b/lib/pio/ruby_dumper.rb
@@ -0,0 +1,69 @@
+module Pio
+ # defines to_ruby method
+ module RubyDumper
+ # Returns a Ruby code representation of this packet, such that
+ # it can be eval'ed and sent later.
+ #
+ # rubocop:disable AbcSize
+ # rubocop:disable MethodLength
+ # rubocop:disable CyclomaticComplexity
+ # rubocop:disable PerceivedComplexity
+ def to_ruby
+ pack_template = ''
+ bytes = ''
+ bit = false
+ bit_names = []
+ total_bit_length = 0
+ field_names.each do |each|
+ next unless __send__("#{each}?")
+ if /Bit(\d+)$/ =~ __send__(each).class.to_s
+ bit_length = Regexp.last_match(1).to_i
+ total_bit_length += bit_length
+ if bit
+ bit_names << each
+ bytes << format("_%0#{bit_length}b", __send__(each))
+ else
+ bit_names = [each]
+ bytes << format(" 0b%0#{bit_length}b", __send__(each))
+ end
+ bit = true
+ else
+ if bit
+ bytes << ", # #{bit_names.join(', ')}\n"
+ if total_bit_length == 8
+ pack_template << 'C'
+ elsif total_bit_length == 16
+ pack_template << 'n'
+ else
+ raise
+ end
+ total_bit_length = 0
+ bit_names = []
+ bit = false
+ end
+ list = (@format || self).__send__(each).to_bytes
+ next if list.empty?
+ bytes << " #{list}, # #{each}\n"
+ pack_template << 'C' * (list.count(',') + 1)
+ end
+ end.compact
+
+ template = ''
+ until pack_template.empty?
+ if /^(.)(\1+)/ =~ pack_template
+ pack_template.sub!(/^(.)(\1+)/, '')
+ template <<
+ "#{Regexp.last_match(1)}#{Regexp.last_match(2).length + 1}"
+ else
+ pack_template.sub!(/^(.)/, '')
+ template << Regexp.last_match(1)
+ end
+ end
+ "[\n#{bytes}].pack('#{template}')"
+ end
+ # rubocop:enable AbcSize
+ # rubocop:enable MethodLength
+ # rubocop:enable CyclomaticComplexity
+ # rubocop:enable PerceivedComplexity
+ end
+end
diff --git a/lib/pio/type/ether_type.rb b/lib/pio/type/ether_type.rb
new file mode 100644
index 00000000..6a7287f1
--- /dev/null
+++ b/lib/pio/type/ether_type.rb
@@ -0,0 +1,31 @@
+require 'bindata'
+
+module Pio
+ module Type
+ # Ether type
+ class EtherType < BinData::Primitive
+ endian :big
+
+ uint16 :ether_type
+
+ def set(value)
+ self.ether_type = value
+ end
+
+ def get
+ ether_type
+ end
+
+ # This method smells of :reek:UncommunicativeVariableName
+ def to_bytes
+ byte1 = format('%02x', (self & 0xff00) >> 8)
+ byte2 = format('%02x', self & 0xff)
+ "0x#{byte1}, 0x#{byte2}"
+ end
+
+ def inspect
+ Kernel.format '0x%04x', self
+ end
+ end
+ end
+end
diff --git a/lib/pio/type/ip_address.rb b/lib/pio/type/ip_address.rb
index 1972195c..1703b169 100644
--- a/lib/pio/type/ip_address.rb
+++ b/lib/pio/type/ip_address.rb
@@ -26,6 +26,14 @@ def &(other)
def ==(other)
get == other
end
+
+ def to_bytes
+ octets.map(&:to_hex).join(', ')
+ end
+
+ def inspect
+ %("#{get}")
+ end
end
end
end
diff --git a/lib/pio/type/mac_address.rb b/lib/pio/type/mac_address.rb
index d11b478a..12000f95 100644
--- a/lib/pio/type/mac_address.rb
+++ b/lib/pio/type/mac_address.rb
@@ -1,5 +1,7 @@
require 'bindata'
require 'pio/mac'
+require 'pio/monkey_patch/integer'
+require 'pio/monkey_patch/uint'
module Pio
module Type
@@ -16,6 +18,14 @@ def get
str + format('%02x', each)
end.hex)
end
+
+ def to_bytes
+ octets.map(&:to_hex).join(', ')
+ end
+
+ def inspect
+ %("#{get}")
+ end
end
end
end
diff --git a/lib/pio/udp.rb b/lib/pio/udp.rb
index 30cc0dd6..6b3cf335 100644
--- a/lib/pio/udp.rb
+++ b/lib/pio/udp.rb
@@ -6,12 +6,12 @@
module Pio
# UDP packet format
class Udp < BinData::Record
- include EthernetHeader
- include IPv4Header
+ include Ethernet
+ include IPv4
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
rest :udp_payload
diff --git a/lib/pio/udp_header.rb b/lib/pio/udp_header.rb
index 71fb4b2c..d32f0786 100644
--- a/lib/pio/udp_header.rb
+++ b/lib/pio/udp_header.rb
@@ -11,7 +11,7 @@ class PseudoUdpHeader < BinData::Record
ip_address :source_ip_address
ip_address :destination_ip_address
uint8 :padding
- uint8 :ip_protocol, value: IPv4Header::ProtocolNumber::UDP
+ uint8 :ip_protocol, value: IPv4::ProtocolNumber::UDP
uint16 :udp_length
end
diff --git a/lib/pio/version.rb b/lib/pio/version.rb
index 2759bffb..5234bb3b 100644
--- a/lib/pio/version.rb
+++ b/lib/pio/version.rb
@@ -1,5 +1,5 @@
# Base module.
module Pio
# gem version.
- VERSION = '0.30.0'.freeze
+ VERSION = '0.30.1'.freeze
end
diff --git a/pio.gemspec b/pio.gemspec
index 5d7ecfb5..906e5fdc 100644
--- a/pio.gemspec
+++ b/pio.gemspec
@@ -8,57 +8,24 @@ Gem::Specification.new do |gem|
gem.summary = 'Packet parser and generator.'
gem.description = 'Pure ruby packet parser and generator.'
- gem.license = 'GPL3'
+ gem.licenses = %w(GPLv2 MIT)
gem.authors = ['Yasuhito Takamiya']
gem.email = ['yasuhito@gmail.com']
gem.homepage = 'http://github.com/trema/pio'
- gem.files = %w(CONTRIBUTING.md LICENSE Rakefile pio.gemspec)
+ gem.files = %w(CONTRIBUTING.md Rakefile pio.gemspec)
gem.files += Dir.glob('lib/**/*.rb')
- gem.files += Dir.glob('bin/**/*')
gem.files += Dir.glob('spec/**/*')
- gem.files += Dir.glob('examples/**/*')
gem.require_paths = ['lib']
- gem.extra_rdoc_files = %w(README.md CHANGELOG.md LICENSE CONTRIBUTING.md)
+ gem.extra_rdoc_files = %w(README.md CHANGELOG.md CONTRIBUTING.md)
gem.test_files = Dir.glob('spec/**/*')
gem.test_files += Dir.glob('features/**/*')
- gem.required_ruby_version = '>= 2.0.0'
+ gem.required_ruby_version = '>= 2.2.2'
gem.add_dependency 'bindata', '~> 2.1.0'
- gem.add_dependency 'activesupport', '~> 4.2', '>= 4.2.4'
-
- gem.add_development_dependency 'bundler', '~> 1.10.6'
- gem.add_development_dependency 'pry', '~> 0.10.3'
- gem.add_development_dependency 'rake'
-
- # Guard
- gem.add_development_dependency 'guard', '~> 2.13.0'
- gem.add_development_dependency 'guard-bundler', '~> 2.1.0'
- gem.add_development_dependency 'guard-cucumber', '~> 1.6.0'
- gem.add_development_dependency 'guard-rspec', '~> 4.6.4'
- gem.add_development_dependency 'guard-rubocop', '~> 1.2.0'
- gem.add_development_dependency 'rb-fchange', '~> 0.0.6'
- gem.add_development_dependency 'rb-fsevent', '~> 0.9.6'
- gem.add_development_dependency 'rb-inotify', '~> 0.9.5'
- gem.add_development_dependency 'terminal-notifier-guard', '~> 1.6.4'
-
- # Docs
- gem.add_development_dependency 'inch', '~> 0.7.0'
- gem.add_development_dependency 'relish', '~> 0.7.1'
- gem.add_development_dependency 'yard', '~> 0.8.7.6'
-
- # Test
- gem.add_development_dependency 'codeclimate-test-reporter', '~> 0.4.8'
- gem.add_development_dependency 'coveralls', '~> 0.8.3'
- gem.add_development_dependency 'cucumber', '~> 2.1.0'
- gem.add_development_dependency 'flay', '~> 2.6.1'
- gem.add_development_dependency 'flog', '~> 4.3.2'
- gem.add_development_dependency 'reek', '~> 3.6.0'
- gem.add_development_dependency 'rspec', '~> 3.3.0'
- gem.add_development_dependency 'rspec-given', '~> 3.7.1'
- gem.add_development_dependency 'rubocop', '~> 0.34.2'
+ gem.add_dependency 'activesupport', '~> 5.0.2'
end
diff --git a/spec/pio/arp/reply/options_spec.rb b/spec/pio/arp/reply/options_spec.rb
deleted file mode 100644
index 8069c0ba..00000000
--- a/spec/pio/arp/reply/options_spec.rb
+++ /dev/null
@@ -1,143 +0,0 @@
-require 'pio'
-
-describe Pio::Arp::Reply::Options, '.new' do
- Given(:mandatory_options) do
- {
- source_mac: 0x00169d1d9cc4,
- destination_mac: 0x002682ebead1,
- sender_protocol_address: 0xc0a85303,
- target_protocol_address: 0xc0a853fe
- }
- end
-
- context 'with all mandatory options' do
- Given(:options) do
- Pio::Arp::Reply::Options.new(mandatory_options)
- end
-
- describe '#to_hash' do
- When(:result) { options.to_hash }
-
- Then { result[:source_mac] == Pio::Mac.new(0x00169d1d9cc4) }
- Then { result[:destination_mac] == Pio::Mac.new(0x002682ebead1) }
- Then do
- result[:sender_protocol_address] == Pio::IPv4Address.new(0xc0a85303)
- end
- Then do
- result[:target_protocol_address] == Pio::IPv4Address.new(0xc0a853fe)
- end
- end
- end
-
- context 'when source_mac is not passed' do
- Given(:user_options) do
- mandatory_options.delete(:source_mac)
- mandatory_options
- end
-
- When(:result) { Pio::Arp::Reply::Options.new(user_options) }
-
- Then do
- result ==
- Failure(ArgumentError, 'The source_mac option should be passed.')
- end
- end
-
- context 'with source_mac = nil' do
- Given(:user_options) do
- mandatory_options.update(source_mac: nil)
- mandatory_options
- end
-
- When(:result) { Pio::Arp::Reply::Options.new(user_options) }
-
- Then do
- result ==
- Failure(ArgumentError, "The source_mac option shouldn't be nil.")
- end
- end
-
- context 'when destination_mac is not passed' do
- Given(:user_options) do
- mandatory_options.delete(:destination_mac)
- mandatory_options
- end
-
- When(:result) { Pio::Arp::Reply::Options.new(user_options) }
-
- Then do
- result ==
- Failure(ArgumentError, 'The destination_mac option should be passed.')
- end
- end
-
- context 'with destination_mac = nil' do
- Given(:user_options) do
- mandatory_options.update(destination_mac: nil)
- mandatory_options
- end
-
- When(:result) { Pio::Arp::Reply::Options.new(user_options) }
-
- Then do
- result ==
- Failure(ArgumentError, "The destination_mac option shouldn't be nil.")
- end
- end
-
- context 'when sender_protocol_address is not passed' do
- Given(:user_options) do
- mandatory_options.delete(:sender_protocol_address)
- mandatory_options
- end
-
- When(:result) { Pio::Arp::Reply::Options.new(user_options) }
-
- Then do
- result == Failure(ArgumentError,
- 'The sender_protocol_address option should be passed.')
- end
- end
-
- context 'with sender_protocol_address = nil' do
- Given(:user_options) do
- mandatory_options.update(sender_protocol_address: nil)
- mandatory_options
- end
-
- When(:result) { Pio::Arp::Reply::Options.new(user_options) }
-
- Then do
- result == Failure(ArgumentError,
- "The sender_protocol_address option shouldn't be nil.")
- end
- end
-
- context 'when target_protocol_address is not passed' do
- Given(:user_options) do
- mandatory_options.delete(:target_protocol_address)
- mandatory_options
- end
-
- When(:result) { Pio::Arp::Reply::Options.new(user_options) }
-
- Then do
- result == Failure(ArgumentError,
- 'The target_protocol_address option should be passed.')
- end
- end
-
- context 'with target_protocol_address = nil' do
- Given(:user_options) do
- mandatory_options.update(target_protocol_address: nil)
- mandatory_options
- end
-
- When(:result) { Pio::Arp::Reply::Options.new(user_options) }
-
- Then do
- result == Failure(ArgumentError,
- "The target_protocol_address option shouldn't be nil.")
- end
- end
-end
diff --git a/spec/pio/arp/reply_spec.rb b/spec/pio/arp/reply_spec.rb
index ec4cd453..33fd8fc1 100644
--- a/spec/pio/arp/reply_spec.rb
+++ b/spec/pio/arp/reply_spec.rb
@@ -1,9 +1,5 @@
require 'pio'
-describe Pio::ARP::Reply do
- Then { Pio::ARP::Reply == Pio::Arp::Reply }
-end
-
describe Pio::Arp::Reply, '.new' do
ARP_REPLY_DUMP =
[
diff --git a/spec/pio/arp/request/options_spec.rb b/spec/pio/arp/request/options_spec.rb
deleted file mode 100644
index 40bea2ba..00000000
--- a/spec/pio/arp/request/options_spec.rb
+++ /dev/null
@@ -1,113 +0,0 @@
-require 'pio'
-
-describe Pio::Arp::Request::Options, '.new' do
- Given(:mandatory_options) do
- {
- source_mac: 0x002682ebead1,
- sender_protocol_address: 0xc0a85303,
- target_protocol_address: 0xc0a853fe
- }
- end
-
- context 'with all mandatory options' do
- Given(:options) do
- Pio::Arp::Request::Options.new(mandatory_options)
- end
-
- describe '#to_hash' do
- When(:result) { options.to_hash }
-
- Then { result[:source_mac] == Pio::Mac.new(0x002682ebead1) }
- Then do
- result[:sender_protocol_address] == Pio::IPv4Address.new(0xc0a85303)
- end
- Then do
- result[:target_protocol_address] == Pio::IPv4Address.new(0xc0a853fe)
- end
- end
- end
-
- context 'when source_mac is not passed' do
- Given(:user_options) do
- mandatory_options.delete(:source_mac)
- mandatory_options
- end
-
- When(:result) { Pio::Arp::Request::Options.new(user_options) }
-
- Then do
- result ==
- Failure(ArgumentError, 'The source_mac option should be passed.')
- end
- end
-
- context 'with source_mac = nil' do
- Given(:user_options) do
- mandatory_options.update(source_mac: nil)
- mandatory_options
- end
-
- When(:result) { Pio::Arp::Request::Options.new(user_options) }
-
- Then do
- result ==
- Failure(ArgumentError, "The source_mac option shouldn't be nil.")
- end
- end
-
- context 'when sender_protocol_address is not passed' do
- Given(:user_options) do
- mandatory_options.delete(:sender_protocol_address)
- mandatory_options
- end
-
- When(:result) { Pio::Arp::Request::Options.new(user_options) }
-
- Then do
- result == Failure(ArgumentError,
- 'The sender_protocol_address option should be passed.')
- end
- end
-
- context 'with sender_protocol_address = nil' do
- Given(:user_options) do
- mandatory_options.update(sender_protocol_address: nil)
- mandatory_options
- end
-
- When(:result) { Pio::Arp::Request::Options.new(user_options) }
-
- Then do
- result == Failure(ArgumentError,
- "The sender_protocol_address option shouldn't be nil.")
- end
- end
-
- context 'when target_protocol_address is not passed' do
- Given(:user_options) do
- mandatory_options.delete(:target_protocol_address)
- mandatory_options
- end
-
- When(:result) { Pio::Arp::Request::Options.new(user_options) }
-
- Then do
- result == Failure(ArgumentError,
- 'The target_protocol_address option should be passed.')
- end
- end
-
- context 'with target_protocol_address = nil' do
- Given(:user_options) do
- mandatory_options.update(target_protocol_address: nil)
- mandatory_options
- end
-
- When(:result) { Pio::Arp::Request::Options.new(user_options) }
-
- Then do
- result == Failure(ArgumentError,
- "The target_protocol_address option shouldn't be nil.")
- end
- end
-end
diff --git a/spec/pio/arp/request_spec.rb b/spec/pio/arp/request_spec.rb
index bfb8fa10..dd2926e2 100644
--- a/spec/pio/arp/request_spec.rb
+++ b/spec/pio/arp/request_spec.rb
@@ -1,9 +1,5 @@
require 'pio'
-describe Pio::ARP::Request do
- Then { Pio::ARP::Request == Pio::Arp::Request }
-end
-
describe Pio::Arp::Request, '.new' do
ARP_REQUEST_DUMP =
[
diff --git a/spec/pio/arp_spec.rb b/spec/pio/arp_spec.rb
index f980bc46..c786577f 100644
--- a/spec/pio/arp_spec.rb
+++ b/spec/pio/arp_spec.rb
@@ -1,9 +1,5 @@
require 'pio'
-describe Pio::ARP do
- Then { Pio::ARP == Pio::Arp }
-end
-
describe Pio::Arp do
describe '.read' do
context 'with an Arp Request packet' do
diff --git a/spec/pio/icmp/reply_spec.rb b/spec/pio/icmp/reply_spec.rb
index 10a757c2..bfcc7fff 100644
--- a/spec/pio/icmp/reply_spec.rb
+++ b/spec/pio/icmp/reply_spec.rb
@@ -1,9 +1,5 @@
require 'pio'
-describe Pio::ICMP::Reply do
- Then { Pio::ICMP::Reply == Pio::Icmp::Reply }
-end
-
describe Pio::Icmp::Reply, '.new' do
context 'with echo_data' do
Given(:icmp_reply) do
@@ -12,8 +8,8 @@
source_mac: '24:db:ac:41:e5:5b',
source_ip_address: '8.8.8.8',
destination_ip_address: '192.168.1.102',
- identifier: 0x123,
- sequence_number: 0x321,
+ icmp_identifier: 0x123,
+ icmp_sequence_number: 0x321,
echo_data: 'abcdefghijklmnopqrstuvwabcdefghi'
)
end
@@ -77,8 +73,8 @@
source_mac: '24:db:ac:41:e5:5b',
source_ip_address: '8.8.8.8',
destination_ip_address: '192.168.1.102',
- identifier: 0x123,
- sequence_number: 0x321
+ icmp_identifier: 0x123,
+ icmp_sequence_number: 0x321
)
end
diff --git a/spec/pio/icmp/request_spec.rb b/spec/pio/icmp/request_spec.rb
index 84543706..2efd47c7 100644
--- a/spec/pio/icmp/request_spec.rb
+++ b/spec/pio/icmp/request_spec.rb
@@ -1,9 +1,5 @@
require 'pio'
-describe Pio::ICMP::Request do
- Then { Pio::ICMP::Request == Pio::Icmp::Request }
-end
-
describe Pio::Icmp do
Given(:icmp_request) do
Pio::Icmp::Request.new(
@@ -11,8 +7,8 @@
source_mac: '74:e5:0b:2a:18:f8',
source_ip_address: '192.168.1.101',
destination_ip_address: '8.8.8.8',
- identifier: 0x123,
- sequence_number: 0x321,
+ icmp_identifier: 0x123,
+ icmp_sequence_number: 0x321,
echo_data: 'abcdefghijklmnopqrstuvwabcdefghi'
).to_binary
end
@@ -31,8 +27,8 @@
source_mac: '74:e5:0b:2a:18:f8',
source_ip_address: '192.168.1.101',
destination_ip_address: '8.8.8.8',
- identifier: 0x123,
- sequence_number: 0x321,
+ icmp_identifier: 0x123,
+ icmp_sequence_number: 0x321,
echo_data: 'abcdefghijklmnopqrstuvwabcdefghi'
)
end
@@ -97,8 +93,8 @@
source_mac: '74:e5:0b:2a:18:f8',
source_ip_address: '192.168.1.101',
destination_ip_address: '8.8.8.8',
- identifier: 0x123,
- sequence_number: 0x321
+ icmp_identifier: 0x123,
+ icmp_sequence_number: 0x321
)
end
diff --git a/spec/pio/icmp_spec.rb b/spec/pio/icmp_spec.rb
index 65b5d2a4..4a88ca6c 100644
--- a/spec/pio/icmp_spec.rb
+++ b/spec/pio/icmp_spec.rb
@@ -1,9 +1,5 @@
require 'pio'
-describe Pio::ICMP do
- Then { Pio::ICMP == Pio::Icmp }
-end
-
describe Pio::Icmp, '.read' do
context 'with an ICMP request frame' do
Given(:icmp_request_dump) do
@@ -64,17 +60,17 @@
Then { icmp_request.ether_type == 0x0800 }
Then { icmp_request.ip_version == 0x4 }
Then { icmp_request.ip_header_length == 0x5 }
- Then { icmp_request.ip_type_of_service == 0x0 }
+ Then { icmp_request.ip_type_of_service.zero? }
Then { icmp_request.ip_total_length == 60 }
Then { icmp_request.ip_identifier == 0x39d3 }
- Then { icmp_request.ip_fragment == 0 }
+ Then { icmp_request.ip_fragment.zero? }
Then { icmp_request.ip_ttl == 128 }
Then { icmp_request.ip_protocol == 1 }
Then { icmp_request.ip_header_checksum == 0x2ed0 }
Then { icmp_request.source_ip_address.to_s == '192.168.1.102' }
Then { icmp_request.destination_ip_address.to_s == '8.8.8.8' }
Then { icmp_request.icmp_type == 8 }
- Then { icmp_request.icmp_code == 0 }
+ Then { icmp_request.icmp_code.zero? }
Then { icmp_request.icmp_checksum == 0x4c5b }
Then { icmp_request.icmp_identifier == 0x0100 }
Then { icmp_request.icmp_sequence_number == 0x0001 }
@@ -137,17 +133,17 @@
Then { icmp_reply.ether_type == 0x0800 }
Then { icmp_reply.ip_version == 0x4 }
Then { icmp_reply.ip_header_length == 0x5 }
- Then { icmp_reply.ip_type_of_service == 0 }
+ Then { icmp_reply.ip_type_of_service.zero? }
Then { icmp_reply.ip_total_length == 60 }
- Then { icmp_reply.ip_identifier == 0 }
- Then { icmp_reply.ip_fragment == 0 }
+ Then { icmp_reply.ip_identifier.zero? }
+ Then { icmp_reply.ip_fragment.zero? }
Then { icmp_reply.ip_ttl == 45 }
Then { icmp_reply.ip_protocol == 1 }
Then { icmp_reply.ip_header_checksum == 0xbba3 }
Then { icmp_reply.source_ip_address.to_s == '8.8.8.8' }
Then { icmp_reply.destination_ip_address.to_s == '192.168.1.102' }
- Then { icmp_reply.icmp_type == 0 }
- Then { icmp_reply.icmp_code == 0 }
+ Then { icmp_reply.icmp_type.zero? }
+ Then { icmp_reply.icmp_code.zero? }
Then { icmp_reply.icmp_checksum == 0x545b }
Then { icmp_reply.icmp_identifier == 0x0100 }
Then { icmp_reply.icmp_sequence_number == 0x0001 }
diff --git a/spec/pio/monkey_patch/integer_spec.rb b/spec/pio/monkey_patch/integer_spec.rb
new file mode 100644
index 00000000..58bfcda4
--- /dev/null
+++ b/spec/pio/monkey_patch/integer_spec.rb
@@ -0,0 +1,23 @@
+require 'pio/monkey_patch/integer'
+
+describe MonkeyPatch::Integer::BaseConversions do
+ describe '0#to_hex' do
+ When(:result) { 0.to_hex }
+ Then { result == '0x00' }
+ end
+
+ describe '1#to_hex' do
+ When(:result) { 1.to_hex }
+ Then { result == '0x01' }
+ end
+
+ describe '250#to_hex' do
+ When(:result) { 250.to_hex }
+ Then { result == '0xfa' }
+ end
+
+ describe '4207849484#to_hex' do
+ When(:result) { 4_207_849_484.to_hex }
+ Then { result == '0xfaceb00c' }
+ end
+end
diff --git a/spec/pio/open_flow/nicira_resubmit_spec.rb b/spec/pio/open_flow/nicira_resubmit_spec.rb
new file mode 100644
index 00000000..04101375
--- /dev/null
+++ b/spec/pio/open_flow/nicira_resubmit_spec.rb
@@ -0,0 +1,19 @@
+require 'pio/open_flow/nicira_resubmit'
+
+describe Pio::OpenFlow::NiciraResubmit do
+ describe '.new' do
+ When(:nicira_resubmit) do
+ Pio::OpenFlow::NiciraResubmit.new(port_number)
+ end
+
+ context 'with 1' do
+ Given(:port_number) { 1 }
+
+ Then { nicira_resubmit.action_type == 0xffff }
+ Then { nicira_resubmit.action_length == 16 }
+ Then { nicira_resubmit.vendor == 0x2320 }
+ Then { nicira_resubmit.subtype == 1 }
+ Then { nicira_resubmit.in_port == 1 }
+ end
+ end
+end
diff --git a/spec/pio/open_flow/nicira_resubmit_table_spec.rb b/spec/pio/open_flow/nicira_resubmit_table_spec.rb
new file mode 100644
index 00000000..225ffdb9
--- /dev/null
+++ b/spec/pio/open_flow/nicira_resubmit_table_spec.rb
@@ -0,0 +1,20 @@
+require 'pio/open_flow/nicira_resubmit_table'
+
+describe Pio::OpenFlow::NiciraResubmitTable do
+ describe '.new' do
+ When(:nicira_resubmit_table) do
+ Pio::OpenFlow::NiciraResubmitTable.new(options)
+ end
+
+ context 'with in_port: 1, table: 1' do
+ Given(:options) { { in_port: 1, table: 1 } }
+
+ Then { nicira_resubmit_table.action_type == 0xffff }
+ Then { nicira_resubmit_table.action_length == 16 }
+ Then { nicira_resubmit_table.vendor == 0x2320 }
+ Then { nicira_resubmit_table.subtype == 14 }
+ Then { nicira_resubmit_table.in_port == 1 }
+ Then { nicira_resubmit_table.table == 1 }
+ end
+ end
+end
diff --git a/spec/pio/open_flow10/error/hello_failed_spec.rb b/spec/pio/open_flow10/error/hello_failed_spec.rb
index 318654e5..bf801863 100644
--- a/spec/pio/open_flow10/error/hello_failed_spec.rb
+++ b/spec/pio/open_flow10/error/hello_failed_spec.rb
@@ -10,7 +10,7 @@
context 'with {}' do
Given(:options) { {} }
- Then { hello_failed.message_length == 12 }
+ Then { hello_failed.length == 12 }
Then { hello_failed.error_type == :hello_failed }
Then { hello_failed.error_code == :incompatible }
Then { hello_failed.description == '' }
@@ -19,7 +19,7 @@
context "with description: 'error description'" do
Given(:options) { { description: 'error description' } }
- Then { hello_failed.message_length == 29 }
+ Then { hello_failed.length == 29 }
Then { hello_failed.description == 'error description' }
end
end
diff --git a/spec/pio/open_flow10/flow_mod_spec.rb b/spec/pio/open_flow10/flow_mod_spec.rb
index 98a84e6a..0cda05a7 100644
--- a/spec/pio/open_flow10/flow_mod_spec.rb
+++ b/spec/pio/open_flow10/flow_mod_spec.rb
@@ -30,9 +30,9 @@
end
Then { flow_mod.class == Pio::OpenFlow10::FlowMod }
- Then { flow_mod.ofp_version == 0x1 }
- Then { flow_mod.message_type == 0xe }
- Then { flow_mod.message_length == 0x50 }
+ Then { flow_mod.version == 0x1 }
+ Then { flow_mod.type == 0xe }
+ Then { flow_mod.length == 0x50 }
Then { flow_mod.transaction_id == 0x15 }
Then { flow_mod.xid == 0x15 }
@@ -54,19 +54,19 @@
Then { flow_mod.match.in_port == 1 }
Then { flow_mod.match.source_mac_address == '00:00:00:00:00:00' }
Then { flow_mod.match.destination_mac_address == '00:00:00:00:00:00' }
- Then { flow_mod.match.vlan_vid == 0 }
- Then { flow_mod.match.vlan_priority == 0 }
- Then { flow_mod.match.ether_type == 0 }
- Then { flow_mod.match.tos == 0 }
- Then { flow_mod.match.ip_protocol == 0 }
+ Then { flow_mod.match.vlan_vid.zero? }
+ Then { flow_mod.match.vlan_priority.zero? }
+ Then { flow_mod.match.ether_type.zero? }
+ Then { flow_mod.match.tos.zero? }
+ Then { flow_mod.match.ip_protocol.zero? }
Then { flow_mod.match.source_ip_address == '0.0.0.0' }
Then { flow_mod.match.destination_ip_address == '0.0.0.0' }
- Then { flow_mod.match.transport_source_port == 0 }
- Then { flow_mod.match.transport_destination_port == 0 }
+ Then { flow_mod.match.transport_source_port.zero? }
+ Then { flow_mod.match.transport_destination_port.zero? }
Then { flow_mod.cookie == 1 }
Then { flow_mod.command == :add }
- Then { flow_mod.idle_timeout == 0 }
- Then { flow_mod.hard_timeout == 0 }
+ Then { flow_mod.idle_timeout.zero? }
+ Then { flow_mod.hard_timeout.zero? }
Then { flow_mod.priority == 0xffff }
Then { flow_mod.buffer_id == 0xffffffff }
Then { flow_mod.out_port == 2 }
@@ -95,9 +95,9 @@
end
Then { flow_mod.class == Pio::OpenFlow10::FlowMod }
- Then { flow_mod.ofp_version == 0x1 }
- Then { flow_mod.message_type == 0xe }
- Then { flow_mod.message_length == 0x50 }
+ Then { flow_mod.version == 0x1 }
+ Then { flow_mod.type == 0xe }
+ Then { flow_mod.length == 0x50 }
Then { flow_mod.transaction_id == 0x15 }
Then { flow_mod.xid == 0x15 }
@@ -119,19 +119,19 @@
Then { flow_mod.match.in_port == 1 }
Then { flow_mod.match.source_mac_address == '00:00:00:00:00:00' }
Then { flow_mod.match.destination_mac_address == '00:00:00:00:00:00' }
- Then { flow_mod.match.vlan_vid == 0 }
- Then { flow_mod.match.vlan_priority == 0 }
- Then { flow_mod.match.ether_type == 0 }
- Then { flow_mod.match.tos == 0 }
- Then { flow_mod.match.ip_protocol == 0 }
+ Then { flow_mod.match.vlan_vid.zero? }
+ Then { flow_mod.match.vlan_priority.zero? }
+ Then { flow_mod.match.ether_type.zero? }
+ Then { flow_mod.match.tos.zero? }
+ Then { flow_mod.match.ip_protocol.zero? }
Then { flow_mod.match.source_ip_address == '0.0.0.0' }
Then { flow_mod.match.destination_ip_address == '0.0.0.0' }
- Then { flow_mod.match.transport_source_port == 0 }
- Then { flow_mod.match.transport_destination_port == 0 }
+ Then { flow_mod.match.transport_source_port.zero? }
+ Then { flow_mod.match.transport_destination_port.zero? }
Then { flow_mod.cookie == 1 }
Then { flow_mod.command == :add }
- Then { flow_mod.idle_timeout == 0 }
- Then { flow_mod.hard_timeout == 0 }
+ Then { flow_mod.idle_timeout.zero? }
+ Then { flow_mod.hard_timeout.zero? }
Then { flow_mod.priority == 0xffff }
Then { flow_mod.buffer_id == 0xffffffff }
Then { flow_mod.out_port == 2 }
diff --git a/spec/pio/open_flow10/flow_stats_request_spec.rb b/spec/pio/open_flow10/flow_stats_request_spec.rb
index ffaa7b01..bd5ade5c 100644
--- a/spec/pio/open_flow10/flow_stats_request_spec.rb
+++ b/spec/pio/open_flow10/flow_stats_request_spec.rb
@@ -17,9 +17,9 @@
end
Then { flow_stats_request.class == Pio::OpenFlow10::FlowStats::Request }
- Then { flow_stats_request.ofp_version == 1 }
- Then { flow_stats_request.message_type == 16 }
- Then { flow_stats_request.message_length == 56 }
+ Then { flow_stats_request.version == 1 }
+ Then { flow_stats_request.type == 16 }
+ Then { flow_stats_request.length == 56 }
Then { flow_stats_request.transaction_id == 13 }
Then { flow_stats_request.xid == 13 }
Then { flow_stats_request.stats_type == :flow }
diff --git a/spec/pio/open_flow10/hello_spec.rb b/spec/pio/open_flow10/hello_spec.rb
index 21c884a5..bdeadd70 100644
--- a/spec/pio/open_flow10/hello_spec.rb
+++ b/spec/pio/open_flow10/hello_spec.rb
@@ -2,6 +2,4 @@
describe Pio::OpenFlow10::Hello do
it_should_behave_like('an OpenFlow message', Pio::OpenFlow10::Hello)
- it_should_behave_like('an OpenFlow message with no body',
- Pio::OpenFlow10::Hello)
end
diff --git a/spec/pio/open_flow10/match_spec.rb b/spec/pio/open_flow10/match_spec.rb
index 10854385..0cb980a3 100644
--- a/spec/pio/open_flow10/match_spec.rb
+++ b/spec/pio/open_flow10/match_spec.rb
@@ -43,15 +43,15 @@
Then { match.in_port == 1 }
Then { match.source_mac_address == '00:00:00:00:00:00' }
Then { match.destination_mac_address == '00:00:00:00:00:00' }
- Then { match.vlan_vid == 0 }
- Then { match.vlan_priority == 0 }
- Then { match.ether_type == 0 }
- Then { match.tos == 0 }
- Then { match.ip_protocol == 0 }
+ Then { match.vlan_vid.zero? }
+ Then { match.vlan_priority.zero? }
+ Then { match.ether_type.zero? }
+ Then { match.tos.zero? }
+ Then { match.ip_protocol.zero? }
Then { match.source_ip_address == '0.0.0.0' }
Then { match.destination_ip_address == '0.0.0.0' }
- Then { match.transport_source_port == 0 }
- Then { match.transport_destination_port == 0 }
+ Then { match.transport_source_port.zero? }
+ Then { match.transport_destination_port.zero? }
end
context 'with a Match binary generated with Pio::OpenFlow10::Match.new' do
@@ -78,19 +78,19 @@
]
end
And { match.wildcards[:source_ip_address] = 12 }
- Then { match.in_port == 0 }
+ Then { match.in_port.zero? }
Then { match.source_mac_address == '00:00:00:00:00:00' }
Then { match.destination_mac_address == '00:00:00:00:00:00' }
- Then { match.vlan_vid == 0 }
- Then { match.vlan_priority == 0 }
- Then { match.ether_type == 0 }
- Then { match.tos == 0 }
- Then { match.ip_protocol == 0 }
+ Then { match.vlan_vid.zero? }
+ Then { match.vlan_priority.zero? }
+ Then { match.ether_type.zero? }
+ Then { match.tos.zero? }
+ Then { match.ip_protocol.zero? }
Then { match.source_ip_address == '192.168.1.0' }
Then { match.source_ip_address.prefixlen == 24 }
Then { match.destination_ip_address == '0.0.0.0' }
- Then { match.transport_source_port == 0 }
- Then { match.transport_destination_port == 0 }
+ Then { match.transport_source_port.zero? }
+ Then { match.transport_destination_port.zero? }
end
end
@@ -117,15 +117,15 @@
Then { match.in_port == 1 }
Then { match.source_mac_address == '00:00:00:00:00:00' }
Then { match.destination_mac_address == '00:00:00:00:00:00' }
- Then { match.vlan_vid == 0 }
- Then { match.vlan_priority == 0 }
- Then { match.ether_type == 0 }
- Then { match.tos == 0 }
- Then { match.ip_protocol == 0 }
+ Then { match.vlan_vid.zero? }
+ Then { match.vlan_priority.zero? }
+ Then { match.ether_type.zero? }
+ Then { match.tos.zero? }
+ Then { match.ip_protocol.zero? }
Then { match.source_ip_address == '0.0.0.0' }
Then { match.destination_ip_address == '0.0.0.0' }
- Then { match.transport_source_port == 0 }
- Then { match.transport_destination_port == 0 }
+ Then { match.transport_source_port.zero? }
+ Then { match.transport_destination_port.zero? }
describe '#==' do
When(:result) { match == other }
@@ -156,18 +156,18 @@
]
end
Then { match.wildcards.fetch(:source_ip_address) == 8 }
- Then { match.in_port == 0 }
+ Then { match.in_port.zero? }
Then { match.source_mac_address == '00:00:00:00:00:00' }
Then { match.destination_mac_address == '00:00:00:00:00:00' }
- Then { match.vlan_vid == 0 }
- Then { match.vlan_priority == 0 }
- Then { match.ether_type == 0 }
- Then { match.tos == 0 }
- Then { match.ip_protocol == 0 }
+ Then { match.vlan_vid.zero? }
+ Then { match.vlan_priority.zero? }
+ Then { match.ether_type.zero? }
+ Then { match.tos.zero? }
+ Then { match.ip_protocol.zero? }
Then { match.source_ip_address == '192.168.1.0/24' }
Then { match.destination_ip_address == '0.0.0.0' }
- Then { match.transport_source_port == 0 }
- Then { match.transport_destination_port == 0 }
+ Then { match.transport_source_port.zero? }
+ Then { match.transport_destination_port.zero? }
end
context "with destination_ip_address: '192.168.1.0/24'" do
@@ -189,18 +189,18 @@
]
end
Then { match.wildcards.fetch(:destination_ip_address) == 8 }
- Then { match.in_port == 0 }
+ Then { match.in_port.zero? }
Then { match.source_mac_address == '00:00:00:00:00:00' }
Then { match.destination_mac_address == '00:00:00:00:00:00' }
- Then { match.vlan_vid == 0 }
- Then { match.vlan_priority == 0 }
- Then { match.ether_type == 0 }
- Then { match.tos == 0 }
- Then { match.ip_protocol == 0 }
+ Then { match.vlan_vid.zero? }
+ Then { match.vlan_priority.zero? }
+ Then { match.ether_type.zero? }
+ Then { match.tos.zero? }
+ Then { match.ip_protocol.zero? }
Then { match.source_ip_address == '0.0.0.0' }
Then { match.destination_ip_address == '192.168.1.0/24' }
- Then { match.transport_source_port == 0 }
- Then { match.transport_destination_port == 0 }
+ Then { match.transport_source_port.zero? }
+ Then { match.transport_destination_port.zero? }
end
end
end
diff --git a/spec/pio/open_flow10/packet_out_spec.rb b/spec/pio/open_flow10/packet_out_spec.rb
index b9340446..d085ed4b 100644
--- a/spec/pio/open_flow10/packet_out_spec.rb
+++ b/spec/pio/open_flow10/packet_out_spec.rb
@@ -1,356 +1,7 @@
require 'pio/open_flow10/packet_out'
-require 'pio/parse_error'
-# rubocop:disable LineLength
describe Pio::OpenFlow10::PacketOut do
- Given(:header_dump) do
- [
- 0x01,
- 0x0d,
- 0x00, 0x58,
- 0x00, 0x00, 0x00, 0x16
- ].pack('C*')
- end
-
- Given(:data_dump) do
- [
- 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e, 0x01, 0x02, 0x03, 0x04,
- 0x05, 0x06, 0x88, 0xcc, 0x02, 0x09, 0x07, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x01, 0x23, 0x04, 0x05, 0x07, 0x00, 0x00,
- 0x00, 0x0c, 0x06, 0x02, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00
- ].pack('C*')
- end
-
- Given(:body_dump) do
- [
- 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff,
- 0x00, 0x08,
- 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0xff, 0xff
- ].pack('C*') + data_dump
- end
-
- describe '.read' do
- When(:result) { Pio::OpenFlow10::PacketOut.read(binary) }
-
- context 'with a Packet-Out message' do
- When(:binary) { header_dump + body_dump }
-
- Then { result.class == Pio::OpenFlow10::PacketOut }
- Then { result.ofp_version == 0x1 }
- Then { result.message_type == 0xd }
- Then { result.message_length == 0x58 }
- Then { result.transaction_id == 0x16 }
- Then { result.xid == 0x16 }
-
- Then { result.buffer_id == 0xffffffff }
- Then { result.in_port == 0xffff }
- Then { result.actions_len == 0x8 }
- Then { result.actions.length == 1 }
- Then { result.actions[0].is_a? Pio::OpenFlow10::SendOutPort }
- Then { result.actions[0].port == 2 }
- Then { result.actions[0].max_length == 2**16 - 1 }
- Then { result.raw_data.length == 64 }
- end
-
- context 'with a Packet-Out message generated with PacketOut.new' do
- When(:binary) do
- Pio::OpenFlow10::PacketOut.new(
- transaction_id: 0x16,
- buffer_id: 0xffffffff,
- in_port: 0xffff,
- actions: Pio::OpenFlow10::SendOutPort.new(2),
- raw_data: data_dump
- ).to_binary
- end
-
- Then { result.class == Pio::OpenFlow10::PacketOut }
- Then { result.ofp_version == 0x1 }
- Then { result.message_type == 0xd }
- Then { result.message_length == 0x58 }
- Then { result.transaction_id == 0x16 }
- Then { result.xid == 0x16 }
-
- Then { result.buffer_id == 0xffffffff }
- Then { result.in_port == 0xffff }
- Then { result.actions_len == 0x8 }
- Then { result.actions.length == 1 }
- Then { result.actions[0].is_a? Pio::OpenFlow10::SendOutPort }
- Then { result.actions[0].port == 2 }
- Then { result.actions[0].max_length == 2**16 - 1 }
- Then { result.raw_data.length == 64 }
- end
-
- context 'with a Hello message' do
- When(:binary) { [1, 0, 0, 8, 0, 0, 0, 0].pack('C*') }
-
- Then do
- result == Failure(Pio::ParseError,
- 'Invalid OpenFlow10 PacketOut message.')
- end
- end
- end
-
describe '.new' do
it_should_behave_like('an OpenFlow message', Pio::OpenFlow10::PacketOut)
-
- When(:result) { Pio::OpenFlow10::PacketOut.new(user_options) }
-
- context 'with a SendOutPort action' do
- When(:user_options) do
- {
- transaction_id: 0x16,
- buffer_id: 0xffffffff,
- in_port: 0xffff,
- actions: Pio::OpenFlow10::SendOutPort.new(2),
- raw_data: data_dump
- }
- end
-
- Then { result.ofp_version == 0x1 }
- Then { result.message_type == 0xd }
- Then { result.message_length == 0x58 }
- Then { result.transaction_id == 0x16 }
- Then { result.xid == 0x16 }
-
- Then { result.buffer_id == 0xffffffff }
- Then { result.in_port == 0xffff }
- Then { result.actions_len == 0x8 }
- Then { result.actions.length == 1 }
- Then { result.actions[0].is_a? Pio::OpenFlow10::SendOutPort }
- Then { result.actions[0].port == 2 }
- Then { result.actions[0].max_length == 2**16 - 1 }
- Then { result.raw_data.length == 64 }
-
- context '#to_binary' do
- When(:binary) { result.to_binary }
-
- Then { binary == header_dump + body_dump }
- end
- end
-
- context 'with a SetVlanVid action' do
- When(:user_options) do
- {
- transaction_id: 0x16,
- buffer_id: 0xffffffff,
- in_port: 0xffff,
- actions: Pio::OpenFlow10::SetVlanVid.new(10),
- raw_data: data_dump
- }
- end
-
- Then { result.message_length == 0x58 }
- Then { result.actions_len == 0x8 }
- Then { result.actions.length == 1 }
- Then { result.actions[0].is_a? Pio::OpenFlow10::SetVlanVid }
- Then { result.actions[0].vlan_id == 10 }
- end
-
- context 'with a SetVlanPriority action' do
- When(:user_options) do
- {
- transaction_id: 0x16,
- buffer_id: 0xffffffff,
- in_port: 0xffff,
- actions: Pio::OpenFlow10::SetVlanPriority.new(3),
- raw_data: data_dump
- }
- end
-
- Then { result.message_length == 0x58 }
- Then { result.actions_len == 0x8 }
- Then { result.actions.length == 1 }
- Then { result.actions[0].is_a? Pio::OpenFlow10::SetVlanPriority }
- Then { result.actions[0].vlan_priority == 3 }
- end
-
- context 'with a StripVlanHeader action' do
- When(:user_options) do
- {
- transaction_id: 0x16,
- buffer_id: 0xffffffff,
- in_port: 0xffff,
- actions: Pio::OpenFlow10::StripVlanHeader.new,
- raw_data: data_dump
- }
- end
-
- Then { result.message_length == 0x58 }
- Then { result.actions_len == 0x8 }
- Then { result.actions.length == 1 }
- Then { result.actions[0].is_a? Pio::OpenFlow10::StripVlanHeader }
- end
-
- context 'with a SetSourceMacAddress action' do
- When(:user_options) do
- {
- transaction_id: 0x16,
- buffer_id: 0xffffffff,
- in_port: 0xffff,
- actions: Pio::OpenFlow10::SetSourceMacAddress.new('11:22:33:44:55:66'),
- raw_data: data_dump
- }
- end
-
- Then { result.message_length == 0x60 }
- Then { result.actions_len == 0x10 }
- Then { result.actions.length == 1 }
- Then { result.actions[0].is_a? Pio::OpenFlow10::SetSourceMacAddress }
- Then { result.actions[0].mac_address == '11:22:33:44:55:66' }
- end
-
- context 'with a SetDestinationMacAddress action' do
- When(:user_options) do
- {
- transaction_id: 0x16,
- buffer_id: 0xffffffff,
- in_port: 0xffff,
- actions: Pio::OpenFlow10::SetDestinationMacAddress.new('11:22:33:44:55:66'),
- raw_data: data_dump
- }
- end
-
- Then { result.message_length == 0x60 }
- Then { result.actions_len == 0x10 }
- Then { result.actions.length == 1 }
- Then { result.actions[0].is_a? Pio::OpenFlow10::SetDestinationMacAddress }
- Then { result.actions[0].mac_address == '11:22:33:44:55:66' }
- end
-
- context 'with a SetSourceIpAddress action' do
- When(:user_options) do
- {
- transaction_id: 0x16,
- buffer_id: 0xffffffff,
- in_port: 0xffff,
- actions: Pio::OpenFlow10::SetSourceIpAddress.new('1.2.3.4'),
- raw_data: data_dump
- }
- end
-
- Then { result.message_length == 0x58 }
- Then { result.actions_len == 0x8 }
- Then { result.actions.length == 1 }
- Then { result.actions[0].is_a? Pio::OpenFlow10::SetSourceIpAddress }
- Then { result.actions[0].ip_address == '1.2.3.4' }
- end
-
- context 'with a SetDestinationIpAddress action' do
- When(:user_options) do
- {
- transaction_id: 0x16,
- buffer_id: 0xffffffff,
- in_port: 0xffff,
- actions: Pio::OpenFlow10::SetDestinationIpAddress.new('1.2.3.4'),
- raw_data: data_dump
- }
- end
-
- Then { result.message_length == 0x58 }
- Then { result.actions_len == 0x8 }
- Then { result.actions.length == 1 }
- Then { result.actions[0].is_a? Pio::OpenFlow10::SetDestinationIpAddress }
- Then { result.actions[0].ip_address == '1.2.3.4' }
- end
-
- context 'with a SetTos action' do
- When(:user_options) do
- {
- transaction_id: 0x16,
- buffer_id: 0xffffffff,
- in_port: 0xffff,
- actions: Pio::OpenFlow10::SetTos.new(32),
- raw_data: data_dump
- }
- end
-
- Then { result.message_length == 0x58 }
- Then { result.actions_len == 0x8 }
- Then { result.actions.length == 1 }
- Then { result.actions[0].is_a? Pio::OpenFlow10::SetTos }
- Then { result.actions[0].type_of_service == 32 }
- end
-
- context 'with a SetTransportSourcePort action' do
- When(:user_options) do
- {
- transaction_id: 0x16,
- buffer_id: 0xffffffff,
- in_port: 0xffff,
- actions: Pio::OpenFlow10::SetTransportSourcePort.new(100),
- raw_data: data_dump
- }
- end
-
- Then { result.message_length == 0x58 }
- Then { result.actions_len == 0x8 }
- Then { result.actions.length == 1 }
- Then { result.actions[0].is_a? Pio::OpenFlow10::SetTransportSourcePort }
- Then { result.actions[0].port == 100 }
- end
-
- context 'with a SetTransportDestinationPort action' do
- When(:user_options) do
- {
- transaction_id: 0x16,
- buffer_id: 0xffffffff,
- in_port: 0xffff,
- actions: Pio::OpenFlow10::SetTransportDestinationPort.new(100),
- raw_data: data_dump
- }
- end
-
- Then { result.message_length == 0x58 }
- Then { result.actions_len == 0x8 }
- Then { result.actions.length == 1 }
- Then { result.actions[0].is_a? Pio::OpenFlow10::SetTransportDestinationPort }
- Then { result.actions[0].port == 100 }
- end
-
- context 'with a Enqueue action' do
- When(:user_options) do
- {
- transaction_id: 0x16,
- buffer_id: 0xffffffff,
- in_port: 0xffff,
- actions: Pio::OpenFlow10::Enqueue.new(port: 1, queue_id: 2),
- raw_data: data_dump
- }
- end
-
- Then { result.message_length == 0x60 }
- Then { result.actions_len == 0x10 }
- Then { result.actions.length == 1 }
- Then { result.actions[0].is_a? Pio::OpenFlow10::Enqueue }
- Then { result.actions[0].port == 1 }
- Then { result.actions[0].queue_id == 2 }
- end
-
- context 'with SendOutPort and SetVlanVid action' do
- When(:user_options) do
- {
- transaction_id: 0x16,
- buffer_id: 0xffffffff,
- in_port: 0xffff,
- actions: [Pio::OpenFlow10::SendOutPort.new(2),
- Pio::OpenFlow10::SetVlanVid.new(10)],
- raw_data: data_dump
- }
- end
-
- Then { result.message_length == 0x60 }
- Then { result.actions_len == 0x10 }
- Then { result.actions.length == 2 }
- Then { result.actions[0].is_a? Pio::OpenFlow10::SendOutPort }
- Then { result.actions[0].port == 2 }
- Then { result.actions[0].max_length == 2**16 - 1 }
- Then { result.actions[1].is_a? Pio::OpenFlow10::SetVlanVid }
- Then { result.actions[1].vlan_id == 10 }
- end
end
end
-# rubocop:enable LineLength
diff --git a/spec/pio/open_flow10/phy_port16_spec.rb b/spec/pio/open_flow10/phy_port16_spec.rb
index 4d7cd3e5..38e36fac 100644
--- a/spec/pio/open_flow10/phy_port16_spec.rb
+++ b/spec/pio/open_flow10/phy_port16_spec.rb
@@ -3,16 +3,16 @@
describe Pio::OpenFlow10::PhyPort16 do
describe '.new' do
When(:phy_port) do
- Pio::OpenFlow10::PhyPort16.new(port_no: 1,
- hardware_address: '11:22:33:44:55:66',
+ Pio::OpenFlow10::PhyPort16.new(number: 1,
+ mac_address: '11:22:33:44:55:66',
name: 'port123',
config: [:port_down],
state: [:link_down],
curr: [:port_10gb_fd, :port_copper])
end
- Then { phy_port.port_no == 1 }
- Then { phy_port.hardware_address == '11:22:33:44:55:66' }
+ Then { phy_port.number == 1 }
+ Then { phy_port.mac_address == '11:22:33:44:55:66' }
Then { phy_port.name == 'port123' }
Then { phy_port.config == [:port_down] }
Then { phy_port.state == [:link_down] }
@@ -20,6 +20,6 @@
Then { phy_port.advertised.empty? }
Then { phy_port.supported.empty? }
Then { phy_port.peer.empty? }
- Then { phy_port.to_binary_s.length > 0 }
+ Then { !phy_port.to_binary_s.empty? }
end
end
diff --git a/spec/pio/open_flow13/error/hello_failed_spec.rb b/spec/pio/open_flow13/error/hello_failed_spec.rb
index 013f8032..d149daad 100644
--- a/spec/pio/open_flow13/error/hello_failed_spec.rb
+++ b/spec/pio/open_flow13/error/hello_failed_spec.rb
@@ -10,7 +10,7 @@
context 'with {}' do
Given(:options) { {} }
- Then { hello_failed.message_length == 12 }
+ Then { hello_failed.length == 12 }
Then { hello_failed.error_type == :hello_failed }
Then { hello_failed.error_code == :incompatible }
Then { hello_failed.description == '' }
@@ -19,7 +19,7 @@
context "with description: 'error description'" do
Given(:options) { { description: 'error description' } }
- Then { hello_failed.message_length == 29 }
+ Then { hello_failed.length == 29 }
Then { hello_failed.description == 'error description' }
end
end
diff --git a/spec/pio/open_flow13/goto_table_spec.rb b/spec/pio/open_flow13/goto_table_spec.rb
index ec75eeb3..ff7fac6f 100644
--- a/spec/pio/open_flow13/goto_table_spec.rb
+++ b/spec/pio/open_flow13/goto_table_spec.rb
@@ -1,8 +1,8 @@
require 'pio/open_flow13/goto_table'
-describe Pio::GotoTable do
+describe Pio::OpenFlow13::GotoTable do
describe '.new' do
- When(:goto_table) { Pio::GotoTable.new(options) }
+ When(:goto_table) { Pio::OpenFlow13::GotoTable.new(options) }
context 'with 1' do
Given(:options) { 1 }
diff --git a/spec/pio/open_flow13/hello_spec.rb b/spec/pio/open_flow13/hello_spec.rb
index 401cf163..1e988dd6 100644
--- a/spec/pio/open_flow13/hello_spec.rb
+++ b/spec/pio/open_flow13/hello_spec.rb
@@ -9,11 +9,11 @@
Given(:binary) { [4, 0, 0, 8, 0, 0, 0, 0].pack('C*') }
Then { result.class == Pio::OpenFlow13::Hello }
- Then { result.ofp_version == 4 }
- Then { result.message_type == 0 }
- Then { result.message_length == 8 }
- Then { result.transaction_id == 0 }
- Then { result.xid == 0 }
+ Then { result.version == 4 }
+ Then { result.type.zero? }
+ Then { result.length == 8 }
+ Then { result.transaction_id.zero? }
+ Then { result.xid.zero? }
Then { result.supported_versions.empty? }
end
@@ -23,11 +23,11 @@
end
Then { result.class == Pio::OpenFlow13::Hello }
- Then { result.ofp_version == 4 }
- Then { result.message_type == 0 }
- Then { result.message_length == 16 }
- Then { result.transaction_id == 0 }
- Then { result.xid == 0 }
+ Then { result.version == 4 }
+ Then { result.type.zero? }
+ Then { result.length == 16 }
+ Then { result.transaction_id.zero? }
+ Then { result.xid.zero? }
Then { result.supported_versions == [:open_flow10, :open_flow13] }
end
@@ -46,11 +46,11 @@
context 'with no arguments' do
When(:result) { Pio::OpenFlow13::Hello.new }
- Then { result.ofp_version == 4 }
- Then { result.message_type == 0 }
- Then { result.message_length == 16 }
- Then { result.transaction_id == 0 }
- Then { result.xid == 0 }
+ Then { result.version == 4 }
+ Then { result.type.zero? }
+ Then { result.length == 16 }
+ Then { result.transaction_id.zero? }
+ Then { result.xid.zero? }
Then { result.supported_versions == [:open_flow13] }
Then do
result.to_binary ==
diff --git a/spec/pio/open_flow13/match_spec.rb b/spec/pio/open_flow13/match_spec.rb
index 45721698..fdef21b8 100644
--- a/spec/pio/open_flow13/match_spec.rb
+++ b/spec/pio/open_flow13/match_spec.rb
@@ -386,7 +386,7 @@
end
Then { match.ether_type == 0x0800 }
Then { match.ip_protocol == 1 }
- Then { match.icmpv4_code == 0 }
+ Then { match.icmpv4_code.zero? }
And { match.class == Pio::OpenFlow13::Match }
And { match.length == 24 }
And { match.match_type == Pio::OpenFlow13::MATCH_TYPE_OXM }
@@ -696,15 +696,15 @@
end
def read_raw_data_file(name)
- IO.read File.join(__dir__, '..', '..', '..', name)
+ IO.read File.join(__dir__, '..', '..', '..', 'fixtures', name)
end
describe '.read' do
When(:match) { Pio::OpenFlow13::Match.read(raw_data) }
- context 'with file "features/open_flow13/oxm_no_fields.raw"' do
+ context 'with file "open_flow13/oxm_no_fields.raw"' do
Given(:raw_data) do
- read_raw_data_file 'features/open_flow13/oxm_no_fields.raw'
+ read_raw_data_file 'open_flow13/oxm_no_fields.raw'
end
Then { match.match_fields == [] }
And { match.class == Pio::OpenFlow13::Match }
@@ -713,9 +713,9 @@ def read_raw_data_file(name)
And { match.match_length == 4 }
end
- context 'with file "features/open_flow13/oxm_in_port_field.raw"' do
+ context 'with file "open_flow13/oxm_in_port_field.raw"' do
Given(:raw_data) do
- read_raw_data_file 'features/open_flow13/oxm_in_port_field.raw'
+ read_raw_data_file 'open_flow13/oxm_in_port_field.raw'
end
Then { match.in_port == 1 }
And { match.class == Pio::OpenFlow13::Match }
@@ -735,9 +735,9 @@ def read_raw_data_file(name)
And { match.match_fields[0].oxm_length == 4 }
end
- context 'with file "features/open_flow13/oxm_ether_destination_field.raw"' do
+ context 'with file "open_flow13/oxm_ether_destination_field.raw"' do
Given(:raw_data) do
- read_raw_data_file 'features/open_flow13/oxm_ether_destination_field.raw'
+ read_raw_data_file 'open_flow13/oxm_ether_destination_field.raw'
end
Then { match.destination_mac_address == 'ff:ff:ff:ff:ff:ff' }
And { match.class == Pio::OpenFlow13::Match }
@@ -757,9 +757,9 @@ def read_raw_data_file(name)
And { match.match_fields[0].oxm_length == 6 }
end
- context 'with file "features/open_flow13/oxm_ether_source_field.raw"' do
+ context 'with file "open_flow13/oxm_ether_source_field.raw"' do
Given(:raw_data) do
- read_raw_data_file 'features/open_flow13/oxm_ether_source_field.raw'
+ read_raw_data_file 'open_flow13/oxm_ether_source_field.raw'
end
Then { match.source_mac_address == '01:02:03:04:05:06' }
And { match.class == Pio::OpenFlow13::Match }
@@ -779,9 +779,9 @@ def read_raw_data_file(name)
And { match.match_fields[0].oxm_length == 6 }
end
- context 'with file "features/open_flow13/oxm_masked_ether_destination_field.raw"' do
+ context 'with file "open_flow13/oxm_masked_ether_destination_field.raw"' do
Given(:raw_data) do
- read_raw_data_file 'features/open_flow13/oxm_masked_ether_destination_field.raw'
+ read_raw_data_file 'open_flow13/oxm_masked_ether_destination_field.raw'
end
Then { match.destination_mac_address == 'ff:ff:ff:ff:ff:ff' }
Then { match.destination_mac_address_mask == 'ff:ff:ff:00:00:00' }
@@ -802,9 +802,9 @@ def read_raw_data_file(name)
And { match.match_fields[0].oxm_length == 12 }
end
- context 'with file "features/open_flow13/oxm_masked_ether_source_field.raw"' do
+ context 'with file "open_flow13/oxm_masked_ether_source_field.raw"' do
Given(:raw_data) do
- read_raw_data_file 'features/open_flow13/oxm_masked_ether_source_field.raw'
+ read_raw_data_file 'open_flow13/oxm_masked_ether_source_field.raw'
end
Then { match.source_mac_address == '01:02:03:04:05:06' }
Then { match.source_mac_address_mask == 'ff:ff:ff:00:00:00' }
@@ -825,11 +825,11 @@ def read_raw_data_file(name)
And { match.match_fields[0].oxm_length == 12 }
end
- context 'with file "features/open_flow13/oxm_ether_type_field.raw"' do
+ context 'with file "open_flow13/oxm_ether_type_field.raw"' do
Given(:raw_data) do
- read_raw_data_file 'features/open_flow13/oxm_ether_type_field.raw'
+ read_raw_data_file 'open_flow13/oxm_ether_type_field.raw'
end
- Then { match.ether_type == 0 }
+ Then { match.ether_type.zero? }
And { match.class == Pio::OpenFlow13::Match }
And { match.length == 16 }
And { match.match_type == Pio::OpenFlow13::MATCH_TYPE_OXM }
@@ -847,9 +847,9 @@ def read_raw_data_file(name)
And { match.match_fields[0].oxm_length == 2 }
end
- context 'with file "features/open_flow13/oxm_vlan_vid_field.raw"' do
+ context 'with file "open_flow13/oxm_vlan_vid_field.raw"' do
Given(:raw_data) do
- read_raw_data_file 'features/open_flow13/oxm_vlan_vid_field.raw'
+ read_raw_data_file 'open_flow13/oxm_vlan_vid_field.raw'
end
Then { match.vlan_vid == 10 }
And { match.class == Pio::OpenFlow13::Match }
@@ -869,9 +869,9 @@ def read_raw_data_file(name)
And { match.match_fields[0].oxm_length == 2 }
end
- context 'with file "features/open_flow13/oxm_vlan_pcp_field.raw"' do
+ context 'with file "open_flow13/oxm_vlan_pcp_field.raw"' do
Given(:raw_data) do
- read_raw_data_file 'features/open_flow13/oxm_vlan_pcp_field.raw'
+ read_raw_data_file 'open_flow13/oxm_vlan_pcp_field.raw'
end
Then { match.vlan_pcp == 5 }
And { match.class == Pio::OpenFlow13::Match }
@@ -891,9 +891,9 @@ def read_raw_data_file(name)
And { match.match_fields[0].oxm_length == 2 }
end
- context 'with file "features/open_flow13/oxm_ip_dscp_field.raw"' do
+ context 'with file "open_flow13/oxm_ip_dscp_field.raw"' do
Given(:raw_data) do
- read_raw_data_file 'features/open_flow13/oxm_ip_dscp_field.raw'
+ read_raw_data_file 'open_flow13/oxm_ip_dscp_field.raw'
end
Then { match.ip_dscp == 0x2e }
And { match.class == Pio::OpenFlow13::Match }
@@ -913,9 +913,9 @@ def read_raw_data_file(name)
And { match.match_fields[0].oxm_length == 2 }
end
- context 'with file "features/open_flow13/oxm_ip_ecn_field.raw"' do
+ context 'with file "open_flow13/oxm_ip_ecn_field.raw"' do
Given(:raw_data) do
- read_raw_data_file 'features/open_flow13/oxm_ip_ecn_field.raw'
+ read_raw_data_file 'open_flow13/oxm_ip_ecn_field.raw'
end
Then { match.ip_ecn == 3 }
And { match.class == Pio::OpenFlow13::Match }
@@ -935,9 +935,9 @@ def read_raw_data_file(name)
And { match.match_fields[0].oxm_length == 2 }
end
- context 'with file "features/open_flow13/oxm_ipv4_source_field.raw"' do
+ context 'with file "open_flow13/oxm_ipv4_source_field.raw"' do
Given(:raw_data) do
- read_raw_data_file 'features/open_flow13/oxm_ipv4_source_field.raw'
+ read_raw_data_file 'open_flow13/oxm_ipv4_source_field.raw'
end
Then { match.ether_type == 0x0800 }
Then { match.ipv4_source_address == '1.2.3.4' }
@@ -968,9 +968,9 @@ def read_raw_data_file(name)
And { match.match_fields[1].oxm_length == 4 }
end
- context 'with file "features/open_flow13/oxm_ipv4_destination_field.raw"' do
+ context 'with file "open_flow13/oxm_ipv4_destination_field.raw"' do
Given(:raw_data) do
- read_raw_data_file 'features/open_flow13/oxm_ipv4_destination_field.raw'
+ read_raw_data_file 'open_flow13/oxm_ipv4_destination_field.raw'
end
Then { match.ether_type == 0x0800 }
Then { match.ipv4_destination_address == '11.22.33.44' }
diff --git a/spec/pio/open_flow13/meter_spec.rb b/spec/pio/open_flow13/meter_spec.rb
index 6b961466..b3450536 100644
--- a/spec/pio/open_flow13/meter_spec.rb
+++ b/spec/pio/open_flow13/meter_spec.rb
@@ -1,8 +1,8 @@
require 'pio/open_flow13/meter'
-describe Pio::Meter do
+describe Pio::OpenFlow13::Meter do
describe '.new' do
- When(:meter) { Pio::Meter.new(options) }
+ When(:meter) { Pio::OpenFlow13::Meter.new(options) }
context 'with 1' do
Given(:options) { 1 }
diff --git a/spec/pio/open_flow13/nicira_reg_load_spec.rb b/spec/pio/open_flow13/nicira_reg_load_spec.rb
new file mode 100644
index 00000000..6e024c05
--- /dev/null
+++ b/spec/pio/open_flow13/nicira_reg_load_spec.rb
@@ -0,0 +1,71 @@
+require 'pio/open_flow13/nicira_reg_load'
+
+describe Pio::OpenFlow13::NiciraRegLoad do
+ describe '.new' do
+ When(:nicira_reg_load) do
+ Pio::OpenFlow13::NiciraRegLoad.new(value, destination, options)
+ end
+
+ context 'with 0xdeadbeef, :reg0' do
+ Given(:value) { 0xdeadbeef }
+ Given(:destination) { :reg0 }
+ Given(:options) { {} }
+
+ Invariant do
+ nicira_reg_load.n_bits == nicira_reg_load._destination[:oxm_length] * 8
+ end
+
+ Then { nicira_reg_load.action_type == 0xffff }
+ Then { nicira_reg_load.action_length == 24 }
+ Then { nicira_reg_load.vendor == 0x2320 }
+ Then { nicira_reg_load.subtype == 7 }
+ Then { nicira_reg_load.offset.zero? }
+ Then { nicira_reg_load.n_bits == 32 }
+ Then { nicira_reg_load.destination == :reg0 }
+ Then { nicira_reg_load._destination[:oxm_class] == 1 }
+ Then { nicira_reg_load._destination[:oxm_field].zero? }
+ Then { nicira_reg_load._destination[:oxm_length] == 4 }
+ Then { nicira_reg_load.value == 0xdeadbeef }
+ end
+
+ context 'with 0xdeadbeef, :metadata' do
+ Given(:value) { 0xdeadbeef }
+ Given(:destination) { :metadata }
+ Given(:options) { {} }
+
+ Invariant do
+ nicira_reg_load.n_bits == nicira_reg_load._destination[:oxm_length] * 8
+ end
+
+ Then { nicira_reg_load.action_type == 0xffff }
+ Then { nicira_reg_load.action_length == 24 }
+ Then { nicira_reg_load.vendor == 0x2320 }
+ Then { nicira_reg_load.subtype == 7 }
+ Then { nicira_reg_load.offset.zero? }
+ Then { nicira_reg_load.n_bits == 64 }
+ Then { nicira_reg_load.destination == :metadata }
+ Then { nicira_reg_load._destination[:oxm_class] == 0x8000 }
+ Then { nicira_reg_load._destination[:oxm_field] == 2 }
+ Then { nicira_reg_load._destination[:oxm_length] == 8 }
+ Then { nicira_reg_load.value == 0xdeadbeef }
+ end
+
+ context 'with 0xdeadbeef, :reg0, offset: 16, n_bits: 16' do
+ Given(:value) { 0xdeadbeef }
+ Given(:destination) { :reg0 }
+ Given(:options) { { offset: 16, n_bits: 16 } }
+
+ Then { nicira_reg_load.action_type == 0xffff }
+ Then { nicira_reg_load.action_length == 24 }
+ Then { nicira_reg_load.vendor == 0x2320 }
+ Then { nicira_reg_load.subtype == 7 }
+ Then { nicira_reg_load.offset == 16 }
+ Then { nicira_reg_load.n_bits == 16 }
+ Then { nicira_reg_load.destination == :reg0 }
+ Then { nicira_reg_load._destination[:oxm_class] == 1 }
+ Then { nicira_reg_load._destination[:oxm_field].zero? }
+ Then { nicira_reg_load._destination[:oxm_length] == 4 }
+ Then { nicira_reg_load.value == 0xdeadbeef }
+ end
+ end
+end
diff --git a/spec/pio/open_flow13/nicira_reg_move_spec.rb b/spec/pio/open_flow13/nicira_reg_move_spec.rb
new file mode 100644
index 00000000..68fee624
--- /dev/null
+++ b/spec/pio/open_flow13/nicira_reg_move_spec.rb
@@ -0,0 +1,40 @@
+require 'pio/open_flow13/nicira_reg_move'
+
+describe Pio::OpenFlow13::NiciraRegMove do
+ describe '.new' do
+ When(:nicira_reg_move) do
+ Pio::OpenFlow13::NiciraRegMove.new(options)
+ end
+
+ context 'with source: :arp_sender_hardware_address,'\
+ ' destination: :arp_target_hardware_address' do
+ Given(:options) do
+ { source: :arp_sender_hardware_address,
+ destination: :arp_target_hardware_address }
+ end
+
+ Invariant do
+ nicira_reg_move.n_bits ==
+ nicira_reg_move._source[:oxm_length] * 8
+ end
+
+ Then { nicira_reg_move.action_type == 0xffff }
+ Then { nicira_reg_move.action_length == 24 }
+ Then { nicira_reg_move.vendor == 0x2320 }
+ Then { nicira_reg_move.subtype == 6 }
+ Then { nicira_reg_move.n_bits == 48 }
+ Then { nicira_reg_move.source_offset.zero? }
+ Then { nicira_reg_move.destination_offset.zero? }
+ Then { nicira_reg_move.source == :arp_sender_hardware_address }
+ Then { nicira_reg_move._source[:oxm_class] == 0x8000 }
+ Then { nicira_reg_move._source[:oxm_field] == 24 }
+ Then { nicira_reg_move._source[:oxm_hasmask].zero? }
+ Then { nicira_reg_move._source[:oxm_length] == 6 }
+ Then { nicira_reg_move.destination == :arp_target_hardware_address }
+ Then { nicira_reg_move._destination[:oxm_class] == 0x8000 }
+ Then { nicira_reg_move._destination[:oxm_field] == 25 }
+ Then { nicira_reg_move._destination[:oxm_hasmask].zero? }
+ Then { nicira_reg_move._destination[:oxm_length] == 6 }
+ end
+ end
+end
diff --git a/spec/pio/open_flow13/nicira_send_out_port_spec.rb b/spec/pio/open_flow13/nicira_send_out_port_spec.rb
new file mode 100644
index 00000000..f21e5774
--- /dev/null
+++ b/spec/pio/open_flow13/nicira_send_out_port_spec.rb
@@ -0,0 +1,29 @@
+require 'pio/open_flow13/nicira_send_out_port'
+
+describe Pio::OpenFlow13::NiciraSendOutPort do
+ describe '.new' do
+ When(:nicira_send_out_port) do
+ Pio::OpenFlow13::NiciraSendOutPort.new(source)
+ end
+
+ context 'with :reg0' do
+ Given(:source) { :reg0 }
+
+ Invariant do
+ nicira_send_out_port.n_bits ==
+ nicira_send_out_port._source[:oxm_length] * 8
+ end
+
+ Then { nicira_send_out_port.action_type == 0xffff }
+ Then { nicira_send_out_port.action_length == 24 }
+ Then { nicira_send_out_port.vendor == 0x2320 }
+ Then { nicira_send_out_port.subtype == 15 }
+ Then { nicira_send_out_port.offset.zero? }
+ Then { nicira_send_out_port.n_bits == 32 }
+ Then { nicira_send_out_port.source == :reg0 }
+ Then { nicira_send_out_port._source[:oxm_class] == 1 }
+ Then { nicira_send_out_port._source[:oxm_field].zero? }
+ Then { nicira_send_out_port._source[:oxm_length] == 4 }
+ end
+ end
+end
diff --git a/spec/pio/open_flow13/write_metadata_spec.rb b/spec/pio/open_flow13/write_metadata_spec.rb
index 91799758..3a3a17ea 100644
--- a/spec/pio/open_flow13/write_metadata_spec.rb
+++ b/spec/pio/open_flow13/write_metadata_spec.rb
@@ -1,8 +1,8 @@
require 'pio/open_flow13/write_metadata'
-describe Pio::WriteMetadata do
+describe Pio::OpenFlow13::WriteMetadata do
describe '.new' do
- When(:write_metadata) { Pio::WriteMetadata.new(options) }
+ When(:write_metadata) { Pio::OpenFlow13::WriteMetadata.new(options) }
context 'with metadata: 1' do
Given(:options) do
@@ -11,7 +11,7 @@
}
end
Then { write_metadata.metadata == 1 }
- Then { write_metadata.metadata_mask == 0 }
+ Then { write_metadata.metadata_mask.zero? }
end
context 'with metadata: 1, metadata_mask: 1' do
diff --git a/spec/pio/open_flow_spec.rb b/spec/pio/open_flow_spec.rb
index 5d0931fd..658bcf1f 100644
--- a/spec/pio/open_flow_spec.rb
+++ b/spec/pio/open_flow_spec.rb
@@ -2,32 +2,32 @@
describe Pio::OpenFlow do
describe 'switch_version' do
- When(:error) { Pio::OpenFlow.switch_version(version) }
+ When(:result) { Pio::OpenFlow.version = version }
context 'with :OpenFlow10' do
Given(:version) { :OpenFlow10 }
- Then { Pio::OpenFlow.version == 'OpenFlow10' }
+ Then { Pio::OpenFlow.version == :OpenFlow10 }
end
context 'with "OpenFlow10"' do
Given(:version) { 'OpenFlow10' }
- Then { Pio::OpenFlow.version == 'OpenFlow10' }
+ Then { Pio::OpenFlow.version == :OpenFlow10 }
end
context 'with :OpenFlow13' do
Given(:version) { :OpenFlow13 }
- Then { Pio::OpenFlow.version == 'OpenFlow13' }
+ Then { Pio::OpenFlow.version == :OpenFlow13 }
end
context 'with "OpenFlow13"' do
Given(:version) { 'OpenFlow13' }
- Then { Pio::OpenFlow.version == 'OpenFlow13' }
+ Then { Pio::OpenFlow.version == :OpenFlow13 }
end
context 'with :OpenFlow100' do
Given(:version) { :OpenFlow100 }
Then do
- error == Failure(RuntimeError, 'OpenFlow100 is not supported yet.')
+ result == Failure(RuntimeError, 'OpenFlow100 is not supported yet.')
end
end
end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 77b6ce10..975d0ba6 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -8,7 +8,7 @@
formatters << CodeClimate::TestReporter::Formatter
end
-SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[*formatters]
+SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new(formatters)
SimpleCov.start { add_filter '/vendor/' }
require 'rspec/given'
diff --git a/spec/support/shared_examples_for_openflow_messages.rb b/spec/support/shared_examples_for_openflow_messages.rb
index a61338b0..2d95b335 100644
--- a/spec/support/shared_examples_for_openflow_messages.rb
+++ b/spec/support/shared_examples_for_openflow_messages.rb
@@ -38,18 +38,6 @@
end
end
-shared_examples 'an OpenFlow message with no body' do |klass|
- describe '.new' do
- When(:message) { klass.new(options) }
-
- context "with body: 'DEADBEEF'" do
- When(:options) { { body: 'DEADBEEF' } }
-
- Then { message.body == '' }
- end
- end
-end
-
shared_examples 'an OpenFlow message with Datapath ID' do |klass|
When(:message) { klass.new(options) }
@@ -63,14 +51,18 @@
context 'with { datapath_id: 0 }' do
Given(:options) { { datapath_id: 0 } }
- Then { message.datapath_id == 0 }
+ Then { message.datapath_id.zero? }
+ And { message.dpid.zero? }
And { message.datapath_id.is_a? Integer }
+ And { message.dpid.is_a? Integer }
end
context 'with { datapath_id: 2**64 - 1 }' do
Given(:options) { { datapath_id: 2**64 - 1 } }
Then { message.datapath_id == 2**64 - 1 }
+ And { message.dpid == 2**64 - 1 }
And { message.datapath_id.is_a? Integer }
+ And { message.dpid.is_a? Integer }
end
context 'with { datapath_id: 2**64 }' do
diff --git a/tasks/LICENSE b/tasks/LICENSE
deleted file mode 100644
index 6b156fe1..00000000
--- a/tasks/LICENSE
+++ /dev/null
@@ -1,675 +0,0 @@
-GNU GENERAL PUBLIC LICENSE
- Version 3, 29 June 2007
-
- Copyright (C) 2007 Free Software Foundation, Inc.
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
- Preamble
-
- The GNU General Public License is a free, copyleft license for
-software and other kinds of works.
-
- The licenses for most software and other practical works are designed
-to take away your freedom to share and change the works. By contrast,
-the GNU General Public License is intended to guarantee your freedom to
-share and change all versions of a program--to make sure it remains free
-software for all its users. We, the Free Software Foundation, use the
-GNU General Public License for most of our software; it applies also to
-any other work released this way by its authors. You can apply it to
-your programs, too.
-
- When we speak of free software, we are referring to freedom, not
-price. Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-them if you wish), that you receive source code or can get it if you
-want it, that you can change the software or use pieces of it in new
-free programs, and that you know you can do these things.
-
- To protect your rights, we need to prevent others from denying you
-these rights or asking you to surrender the rights. Therefore, you have
-certain responsibilities if you distribute copies of the software, or if
-you modify it: responsibilities to respect the freedom of others.
-
- For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must pass on to the recipients the same
-freedoms that you received. You must make sure that they, too, receive
-or can get the source code. And you must show them these terms so they
-know their rights.
-
- Developers that use the GNU GPL protect your rights with two steps:
-(1) assert copyright on the software, and (2) offer you this License
-giving you legal permission to copy, distribute and/or modify it.
-
- For the developers' and authors' protection, the GPL clearly explains
-that there is no warranty for this free software. For both users' and
-authors' sake, the GPL requires that modified versions be marked as
-changed, so that their problems will not be attributed erroneously to
-authors of previous versions.
-
- Some devices are designed to deny users access to install or run
-modified versions of the software inside them, although the manufacturer
-can do so. This is fundamentally incompatible with the aim of
-protecting users' freedom to change the software. The systematic
-pattern of such abuse occurs in the area of products for individuals to
-use, which is precisely where it is most unacceptable. Therefore, we
-have designed this version of the GPL to prohibit the practice for those
-products. If such problems arise substantially in other domains, we
-stand ready to extend this provision to those domains in future versions
-of the GPL, as needed to protect the freedom of users.
-
- Finally, every program is threatened constantly by software patents.
-States should not allow patents to restrict development and use of
-software on general-purpose computers, but in those that do, we wish to
-avoid the special danger that patents applied to a free program could
-make it effectively proprietary. To prevent this, the GPL assures that
-patents cannot be used to render the program non-free.
-
- The precise terms and conditions for copying, distribution and
-modification follow.
-
- TERMS AND CONDITIONS
-
- 0. Definitions.
-
- "This License" refers to version 3 of the GNU General Public License.
-
- "Copyright" also means copyright-like laws that apply to other kinds of
-works, such as semiconductor masks.
-
- "The Program" refers to any copyrightable work licensed under this
-License. Each licensee is addressed as "you". "Licensees" and
-"recipients" may be individuals or organizations.
-
- To "modify" a work means to copy from or adapt all or part of the work
-in a fashion requiring copyright permission, other than the making of an
-exact copy. The resulting work is called a "modified version" of the
-earlier work or a work "based on" the earlier work.
-
- A "covered work" means either the unmodified Program or a work based
-on the Program.
-
- To "propagate" a work means to do anything with it that, without
-permission, would make you directly or secondarily liable for
-infringement under applicable copyright law, except executing it on a
-computer or modifying a private copy. Propagation includes copying,
-distribution (with or without modification), making available to the
-public, and in some countries other activities as well.
-
- To "convey" a work means any kind of propagation that enables other
-parties to make or receive copies. Mere interaction with a user through
-a computer network, with no transfer of a copy, is not conveying.
-
- An interactive user interface displays "Appropriate Legal Notices"
-to the extent that it includes a convenient and prominently visible
-feature that (1) displays an appropriate copyright notice, and (2)
-tells the user that there is no warranty for the work (except to the
-extent that warranties are provided), that licensees may convey the
-work under this License, and how to view a copy of this License. If
-the interface presents a list of user commands or options, such as a
-menu, a prominent item in the list meets this criterion.
-
- 1. Source Code.
-
- The "source code" for a work means the preferred form of the work
-for making modifications to it. "Object code" means any non-source
-form of a work.
-
- A "Standard Interface" means an interface that either is an official
-standard defined by a recognized standards body, or, in the case of
-interfaces specified for a particular programming language, one that
-is widely used among developers working in that language.
-
- The "System Libraries" of an executable work include anything, other
-than the work as a whole, that (a) is included in the normal form of
-packaging a Major Component, but which is not part of that Major
-Component, and (b) serves only to enable use of the work with that
-Major Component, or to implement a Standard Interface for which an
-implementation is available to the public in source code form. A
-"Major Component", in this context, means a major essential component
-(kernel, window system, and so on) of the specific operating system
-(if any) on which the executable work runs, or a compiler used to
-produce the work, or an object code interpreter used to run it.
-
- The "Corresponding Source" for a work in object code form means all
-the source code needed to generate, install, and (for an executable
-work) run the object code and to modify the work, including scripts to
-control those activities. However, it does not include the work's
-System Libraries, or general-purpose tools or generally available free
-programs which are used unmodified in performing those activities but
-which are not part of the work. For example, Corresponding Source
-includes interface definition files associated with source files for
-the work, and the source code for shared libraries and dynamically
-linked subprograms that the work is specifically designed to require,
-such as by intimate data communication or control flow between those
-subprograms and other parts of the work.
-
- The Corresponding Source need not include anything that users
-can regenerate automatically from other parts of the Corresponding
-Source.
-
- The Corresponding Source for a work in source code form is that
-same work.
-
- 2. Basic Permissions.
-
- All rights granted under this License are granted for the term of
-copyright on the Program, and are irrevocable provided the stated
-conditions are met. This License explicitly affirms your unlimited
-permission to run the unmodified Program. The output from running a
-covered work is covered by this License only if the output, given its
-content, constitutes a covered work. This License acknowledges your
-rights of fair use or other equivalent, as provided by copyright law.
-
- You may make, run and propagate covered works that you do not
-convey, without conditions so long as your license otherwise remains
-in force. You may convey covered works to others for the sole purpose
-of having them make modifications exclusively for you, or provide you
-with facilities for running those works, provided that you comply with
-the terms of this License in conveying all material for which you do
-not control copyright. Those thus making or running the covered works
-for you must do so exclusively on your behalf, under your direction
-and control, on terms that prohibit them from making any copies of
-your copyrighted material outside their relationship with you.
-
- Conveying under any other circumstances is permitted solely under
-the conditions stated below. Sublicensing is not allowed; section 10
-makes it unnecessary.
-
- 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
-
- No covered work shall be deemed part of an effective technological
-measure under any applicable law fulfilling obligations under article
-11 of the WIPO copyright treaty adopted on 20 December 1996, or
-similar laws prohibiting or restricting circumvention of such
-measures.
-
- When you convey a covered work, you waive any legal power to forbid
-circumvention of technological measures to the extent such circumvention
-is effected by exercising rights under this License with respect to
-the covered work, and you disclaim any intention to limit operation or
-modification of the work as a means of enforcing, against the work's
-users, your or third parties' legal rights to forbid circumvention of
-technological measures.
-
- 4. Conveying Verbatim Copies.
-
- You may convey verbatim copies of the Program's source code as you
-receive it, in any medium, provided that you conspicuously and
-appropriately publish on each copy an appropriate copyright notice;
-keep intact all notices stating that this License and any
-non-permissive terms added in accord with section 7 apply to the code;
-keep intact all notices of the absence of any warranty; and give all
-recipients a copy of this License along with the Program.
-
- You may charge any price or no price for each copy that you convey,
-and you may offer support or warranty protection for a fee.
-
- 5. Conveying Modified Source Versions.
-
- You may convey a work based on the Program, or the modifications to
-produce it from the Program, in the form of source code under the
-terms of section 4, provided that you also meet all of these conditions:
-
- a) The work must carry prominent notices stating that you modified
- it, and giving a relevant date.
-
- b) The work must carry prominent notices stating that it is
- released under this License and any conditions added under section
- 7. This requirement modifies the requirement in section 4 to
- "keep intact all notices".
-
- c) You must license the entire work, as a whole, under this
- License to anyone who comes into possession of a copy. This
- License will therefore apply, along with any applicable section 7
- additional terms, to the whole of the work, and all its parts,
- regardless of how they are packaged. This License gives no
- permission to license the work in any other way, but it does not
- invalidate such permission if you have separately received it.
-
- d) If the work has interactive user interfaces, each must display
- Appropriate Legal Notices; however, if the Program has interactive
- interfaces that do not display Appropriate Legal Notices, your
- work need not make them do so.
-
- A compilation of a covered work with other separate and independent
-works, which are not by their nature extensions of the covered work,
-and which are not combined with it such as to form a larger program,
-in or on a volume of a storage or distribution medium, is called an
-"aggregate" if the compilation and its resulting copyright are not
-used to limit the access or legal rights of the compilation's users
-beyond what the individual works permit. Inclusion of a covered work
-in an aggregate does not cause this License to apply to the other
-parts of the aggregate.
-
- 6. Conveying Non-Source Forms.
-
- You may convey a covered work in object code form under the terms
-of sections 4 and 5, provided that you also convey the
-machine-readable Corresponding Source under the terms of this License,
-in one of these ways:
-
- a) Convey the object code in, or embodied in, a physical product
- (including a physical distribution medium), accompanied by the
- Corresponding Source fixed on a durable physical medium
- customarily used for software interchange.
-
- b) Convey the object code in, or embodied in, a physical product
- (including a physical distribution medium), accompanied by a
- written offer, valid for at least three years and valid for as
- long as you offer spare parts or customer support for that product
- model, to give anyone who possesses the object code either (1) a
- copy of the Corresponding Source for all the software in the
- product that is covered by this License, on a durable physical
- medium customarily used for software interchange, for a price no
- more than your reasonable cost of physically performing this
- conveying of source, or (2) access to copy the
- Corresponding Source from a network server at no charge.
-
- c) Convey individual copies of the object code with a copy of the
- written offer to provide the Corresponding Source. This
- alternative is allowed only occasionally and noncommercially, and
- only if you received the object code with such an offer, in accord
- with subsection 6b.
-
- d) Convey the object code by offering access from a designated
- place (gratis or for a charge), and offer equivalent access to the
- Corresponding Source in the same way through the same place at no
- further charge. You need not require recipients to copy the
- Corresponding Source along with the object code. If the place to
- copy the object code is a network server, the Corresponding Source
- may be on a different server (operated by you or a third party)
- that supports equivalent copying facilities, provided you maintain
- clear directions next to the object code saying where to find the
- Corresponding Source. Regardless of what server hosts the
- Corresponding Source, you remain obligated to ensure that it is
- available for as long as needed to satisfy these requirements.
-
- e) Convey the object code using peer-to-peer transmission, provided
- you inform other peers where the object code and Corresponding
- Source of the work are being offered to the general public at no
- charge under subsection 6d.
-
- A separable portion of the object code, whose source code is excluded
-from the Corresponding Source as a System Library, need not be
-included in conveying the object code work.
-
- A "User Product" is either (1) a "consumer product", which means any
-tangible personal property which is normally used for personal, family,
-or household purposes, or (2) anything designed or sold for incorporation
-into a dwelling. In determining whether a product is a consumer product,
-doubtful cases shall be resolved in favor of coverage. For a particular
-product received by a particular user, "normally used" refers to a
-typical or common use of that class of product, regardless of the status
-of the particular user or of the way in which the particular user
-actually uses, or expects or is expected to use, the product. A product
-is a consumer product regardless of whether the product has substantial
-commercial, industrial or non-consumer uses, unless such uses represent
-the only significant mode of use of the product.
-
- "Installation Information" for a User Product means any methods,
-procedures, authorization keys, or other information required to install
-and execute modified versions of a covered work in that User Product from
-a modified version of its Corresponding Source. The information must
-suffice to ensure that the continued functioning of the modified object
-code is in no case prevented or interfered with solely because
-modification has been made.
-
- If you convey an object code work under this section in, or with, or
-specifically for use in, a User Product, and the conveying occurs as
-part of a transaction in which the right of possession and use of the
-User Product is transferred to the recipient in perpetuity or for a
-fixed term (regardless of how the transaction is characterized), the
-Corresponding Source conveyed under this section must be accompanied
-by the Installation Information. But this requirement does not apply
-if neither you nor any third party retains the ability to install
-modified object code on the User Product (for example, the work has
-been installed in ROM).
-
- The requirement to provide Installation Information does not include a
-requirement to continue to provide support service, warranty, or updates
-for a work that has been modified or installed by the recipient, or for
-the User Product in which it has been modified or installed. Access to a
-network may be denied when the modification itself materially and
-adversely affects the operation of the network or violates the rules and
-protocols for communication across the network.
-
- Corresponding Source conveyed, and Installation Information provided,
-in accord with this section must be in a format that is publicly
-documented (and with an implementation available to the public in
-source code form), and must require no special password or key for
-unpacking, reading or copying.
-
- 7. Additional Terms.
-
- "Additional permissions" are terms that supplement the terms of this
-License by making exceptions from one or more of its conditions.
-Additional permissions that are applicable to the entire Program shall
-be treated as though they were included in this License, to the extent
-that they are valid under applicable law. If additional permissions
-apply only to part of the Program, that part may be used separately
-under those permissions, but the entire Program remains governed by
-this License without regard to the additional permissions.
-
- When you convey a copy of a covered work, you may at your option
-remove any additional permissions from that copy, or from any part of
-it. (Additional permissions may be written to require their own
-removal in certain cases when you modify the work.) You may place
-additional permissions on material, added by you to a covered work,
-for which you have or can give appropriate copyright permission.
-
- Notwithstanding any other provision of this License, for material you
-add to a covered work, you may (if authorized by the copyright holders of
-that material) supplement the terms of this License with terms:
-
- a) Disclaiming warranty or limiting liability differently from the
- terms of sections 15 and 16 of this License; or
-
- b) Requiring preservation of specified reasonable legal notices or
- author attributions in that material or in the Appropriate Legal
- Notices displayed by works containing it; or
-
- c) Prohibiting misrepresentation of the origin of that material, or
- requiring that modified versions of such material be marked in
- reasonable ways as different from the original version; or
-
- d) Limiting the use for publicity purposes of names of licensors or
- authors of the material; or
-
- e) Declining to grant rights under trademark law for use of some
- trade names, trademarks, or service marks; or
-
- f) Requiring indemnification of licensors and authors of that
- material by anyone who conveys the material (or modified versions of
- it) with contractual assumptions of liability to the recipient, for
- any liability that these contractual assumptions directly impose on
- those licensors and authors.
-
- All other non-permissive additional terms are considered "further
-restrictions" within the meaning of section 10. If the Program as you
-received it, or any part of it, contains a notice stating that it is
-governed by this License along with a term that is a further
-restriction, you may remove that term. If a license document contains
-a further restriction but permits relicensing or conveying under this
-License, you may add to a covered work material governed by the terms
-of that license document, provided that the further restriction does
-not survive such relicensing or conveying.
-
- If you add terms to a covered work in accord with this section, you
-must place, in the relevant source files, a statement of the
-additional terms that apply to those files, or a notice indicating
-where to find the applicable terms.
-
- Additional terms, permissive or non-permissive, may be stated in the
-form of a separately written license, or stated as exceptions;
-the above requirements apply either way.
-
- 8. Termination.
-
- You may not propagate or modify a covered work except as expressly
-provided under this License. Any attempt otherwise to propagate or
-modify it is void, and will automatically terminate your rights under
-this License (including any patent licenses granted under the third
-paragraph of section 11).
-
- However, if you cease all violation of this License, then your
-license from a particular copyright holder is reinstated (a)
-provisionally, unless and until the copyright holder explicitly and
-finally terminates your license, and (b) permanently, if the copyright
-holder fails to notify you of the violation by some reasonable means
-prior to 60 days after the cessation.
-
- Moreover, your license from a particular copyright holder is
-reinstated permanently if the copyright holder notifies you of the
-violation by some reasonable means, this is the first time you have
-received notice of violation of this License (for any work) from that
-copyright holder, and you cure the violation prior to 30 days after
-your receipt of the notice.
-
- Termination of your rights under this section does not terminate the
-licenses of parties who have received copies or rights from you under
-this License. If your rights have been terminated and not permanently
-reinstated, you do not qualify to receive new licenses for the same
-material under section 10.
-
- 9. Acceptance Not Required for Having Copies.
-
- You are not required to accept this License in order to receive or
-run a copy of the Program. Ancillary propagation of a covered work
-occurring solely as a consequence of using peer-to-peer transmission
-to receive a copy likewise does not require acceptance. However,
-nothing other than this License grants you permission to propagate or
-modify any covered work. These actions infringe copyright if you do
-not accept this License. Therefore, by modifying or propagating a
-covered work, you indicate your acceptance of this License to do so.
-
- 10. Automatic Licensing of Downstream Recipients.
-
- Each time you convey a covered work, the recipient automatically
-receives a license from the original licensors, to run, modify and
-propagate that work, subject to this License. You are not responsible
-for enforcing compliance by third parties with this License.
-
- An "entity transaction" is a transaction transferring control of an
-organization, or substantially all assets of one, or subdividing an
-organization, or merging organizations. If propagation of a covered
-work results from an entity transaction, each party to that
-transaction who receives a copy of the work also receives whatever
-licenses to the work the party's predecessor in interest had or could
-give under the previous paragraph, plus a right to possession of the
-Corresponding Source of the work from the predecessor in interest, if
-the predecessor has it or can get it with reasonable efforts.
-
- You may not impose any further restrictions on the exercise of the
-rights granted or affirmed under this License. For example, you may
-not impose a license fee, royalty, or other charge for exercise of
-rights granted under this License, and you may not initiate litigation
-(including a cross-claim or counterclaim in a lawsuit) alleging that
-any patent claim is infringed by making, using, selling, offering for
-sale, or importing the Program or any portion of it.
-
- 11. Patents.
-
- A "contributor" is a copyright holder who authorizes use under this
-License of the Program or a work on which the Program is based. The
-work thus licensed is called the contributor's "contributor version".
-
- A contributor's "essential patent claims" are all patent claims
-owned or controlled by the contributor, whether already acquired or
-hereafter acquired, that would be infringed by some manner, permitted
-by this License, of making, using, or selling its contributor version,
-but do not include claims that would be infringed only as a
-consequence of further modification of the contributor version. For
-purposes of this definition, "control" includes the right to grant
-patent sublicenses in a manner consistent with the requirements of
-this License.
-
- Each contributor grants you a non-exclusive, worldwide, royalty-free
-patent license under the contributor's essential patent claims, to
-make, use, sell, offer for sale, import and otherwise run, modify and
-propagate the contents of its contributor version.
-
- In the following three paragraphs, a "patent license" is any express
-agreement or commitment, however denominated, not to enforce a patent
-(such as an express permission to practice a patent or covenant not to
-sue for patent infringement). To "grant" such a patent license to a
-party means to make such an agreement or commitment not to enforce a
-patent against the party.
-
- If you convey a covered work, knowingly relying on a patent license,
-and the Corresponding Source of the work is not available for anyone
-to copy, free of charge and under the terms of this License, through a
-publicly available network server or other readily accessible means,
-then you must either (1) cause the Corresponding Source to be so
-available, or (2) arrange to deprive yourself of the benefit of the
-patent license for this particular work, or (3) arrange, in a manner
-consistent with the requirements of this License, to extend the patent
-license to downstream recipients. "Knowingly relying" means you have
-actual knowledge that, but for the patent license, your conveying the
-covered work in a country, or your recipient's use of the covered work
-in a country, would infringe one or more identifiable patents in that
-country that you have reason to believe are valid.
-
- If, pursuant to or in connection with a single transaction or
-arrangement, you convey, or propagate by procuring conveyance of, a
-covered work, and grant a patent license to some of the parties
-receiving the covered work authorizing them to use, propagate, modify
-or convey a specific copy of the covered work, then the patent license
-you grant is automatically extended to all recipients of the covered
-work and works based on it.
-
- A patent license is "discriminatory" if it does not include within
-the scope of its coverage, prohibits the exercise of, or is
-conditioned on the non-exercise of one or more of the rights that are
-specifically granted under this License. You may not convey a covered
-work if you are a party to an arrangement with a third party that is
-in the business of distributing software, under which you make payment
-to the third party based on the extent of your activity of conveying
-the work, and under which the third party grants, to any of the
-parties who would receive the covered work from you, a discriminatory
-patent license (a) in connection with copies of the covered work
-conveyed by you (or copies made from those copies), or (b) primarily
-for and in connection with specific products or compilations that
-contain the covered work, unless you entered into that arrangement,
-or that patent license was granted, prior to 28 March 2007.
-
- Nothing in this License shall be construed as excluding or limiting
-any implied license or other defenses to infringement that may
-otherwise be available to you under applicable patent law.
-
- 12. No Surrender of Others' Freedom.
-
- If conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License. If you cannot convey a
-covered work so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you may
-not convey it at all. For example, if you agree to terms that obligate you
-to collect a royalty for further conveying from those to whom you convey
-the Program, the only way you could satisfy both those terms and this
-License would be to refrain entirely from conveying the Program.
-
- 13. Use with the GNU Affero General Public License.
-
- Notwithstanding any other provision of this License, you have
-permission to link or combine any covered work with a work licensed
-under version 3 of the GNU Affero General Public License into a single
-combined work, and to convey the resulting work. The terms of this
-License will continue to apply to the part which is the covered work,
-but the special requirements of the GNU Affero General Public License,
-section 13, concerning interaction through a network will apply to the
-combination as such.
-
- 14. Revised Versions of this License.
-
- The Free Software Foundation may publish revised and/or new versions of
-the GNU General Public License from time to time. Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
- Each version is given a distinguishing version number. If the
-Program specifies that a certain numbered version of the GNU General
-Public License "or any later version" applies to it, you have the
-option of following the terms and conditions either of that numbered
-version or of any later version published by the Free Software
-Foundation. If the Program does not specify a version number of the
-GNU General Public License, you may choose any version ever published
-by the Free Software Foundation.
-
- If the Program specifies that a proxy can decide which future
-versions of the GNU General Public License can be used, that proxy's
-public statement of acceptance of a version permanently authorizes you
-to choose that version for the Program.
-
- Later license versions may give you additional or different
-permissions. However, no additional obligations are imposed on any
-author or copyright holder as a result of your choosing to follow a
-later version.
-
- 15. Disclaimer of Warranty.
-
- THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
-APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
-HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
-OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
-THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
-IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
-ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
-
- 16. Limitation of Liability.
-
- IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
-THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
-GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
-USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
-DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
-PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
-EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGES.
-
- 17. Interpretation of Sections 15 and 16.
-
- If the disclaimer of warranty and limitation of liability provided
-above cannot be given local legal effect according to their terms,
-reviewing courts shall apply local law that most closely approximates
-an absolute waiver of all civil liability in connection with the
-Program, unless a warranty or assumption of liability accompanies a
-copy of the Program in return for a fee.
-
- END OF TERMS AND CONDITIONS
-
- How to Apply These Terms to Your New Programs
-
- If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
- To do so, attach the following notices to the program. It is safest
-to attach them to the start of each source file to most effectively
-state the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
- {one line to give the program's name and a brief idea of what it does.}
- Copyright (C) {year} {name of author}
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see .
-
-Also add information on how to contact you by electronic and paper mail.
-
- If the program does terminal interaction, make it output a short
-notice like this when it starts in an interactive mode:
-
- {project} Copyright (C) {year} {fullname}
- This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
- This is free software, and you are welcome to redistribute it
- under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License. Of course, your program's commands
-might be different; for a GUI interface, you would use an "about box".
-
- You should also get your employer (if you work as a programmer) or school,
-if any, to sign a "copyright disclaimer" for the program, if necessary.
-For more information on this, and how to apply and follow the GNU GPL, see
-.
-
- The GNU General Public License does not permit incorporating your program
-into proprietary programs. If your program is a subroutine library, you
-may consider it more useful to permit linking proprietary applications with
-the library. If this is what you want to do, use the GNU Lesser General
-Public License instead of this License. But first, please read
-.
-