diff --git a/.gitignore b/.gitignore
index fbb3d27..e3270a5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -24,3 +24,5 @@ NOTES*
.DS_Store
vendor/db_generator
vendor/db_generator-*
+vendor/parameter_files
+vendor/parameter_files-*
diff --git a/Gemfile b/Gemfile
index 22b3d23..d360bdd 100644
--- a/Gemfile
+++ b/Gemfile
@@ -9,3 +9,4 @@ end
gem "docopt"
gem "ruby-ip"
gem "open4"
+gem "ipaddress"
diff --git a/Gemfile.lock b/Gemfile.lock
index 50da9c7..5df3e23 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -2,6 +2,7 @@ GEM
remote: http://rubygems.org/
specs:
docopt (0.5.0)
+ ipaddress (0.8.3)
open4 (1.3.4)
ruby-ip (0.9.3)
@@ -11,8 +12,9 @@ PLATFORMS
DEPENDENCIES
bundler
docopt
+ ipaddress
open4
ruby-ip
BUNDLED WITH
- 1.10.6
+ 1.15.4
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..9cecc1d
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,674 @@
+ 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/Makefile b/Makefile
index e9f0190..02f37af 100644
--- a/Makefile
+++ b/Makefile
@@ -3,3 +3,6 @@
# (downloads, patches and compiles ClassBench)
all:
make -C vendor all
+
+clean:
+ $(MAKE) -C vendor $@
diff --git a/README.md b/README.md
index ee8bda4..e1f179d 100644
--- a/README.md
+++ b/README.md
@@ -1,42 +1,114 @@
-# Classbench
-
-Utility for generation of firewall/OpenFlow rules based on original (no longer maintained) [Classbench](http://www.arl.wustl.edu/classbench/).
+# ClassBench-ng
+A tool for generation of synthetic classification rule sets for benchmarking, which is based on original (no longer maintained) [ClassBench](http://www.arl.wustl.edu/classbench/).
+The format of the generated rules can be one of the following:
+- IPv4 5-tuple
+- IPv6 5-tuple
+- OpenFlow
## Requirements
+- Python3
- Ruby 1.9.3+
- RubyGems
-
```
sudo gem install open4 ruby-ip docopt ipaddress
```
+
## Installation
```
-git clone https://github.com/lucansky/classbench-ng.git
-make # Downloads, patches and compiles db_generator in ./vendor/db_generator/db_generator
+git clone https://github.com/classbench-ng/classbench-ng.git
+make # Downloads, patches and compiles original ClassBench in ./vendor/db_generator
+ # Downloads the parameter files of original ClassBench to ./vendor/parameter_files
```
-### Patching classbench
-Due to statically initialized arrays in ClassBench, patching is required which increases the limit.
-Patch is automatically applied by make in process of downloading ClassBench.
-(see vendor/Makefile)
+### Patching ClassBench
+Original ClassBench is patched using `./patches/improvements_ipv6.patch` and the size of its statically initialized arrays is increased, where necessary.
+These changes are automatically applied on downloaded original ClassBench during ClassBench-ng installation.
+
+By modifying `./vendor/Makefile` the user can select a different patch from `./patches` directory to be applied during installation.
+Basic characteristics of patches available in `./patches` directory and suggestions when to use them are following:
+
+`improvements.patch`
+- improves precision of IPv4 prefixes generation
+- use when IPv6 prefixes generation is not required
+
+`ipv6.patch`
+- adds support for IPv6 prefixes generation
+- use when IPv6 prefixes generation is required and precision of IPv4/IPv6 prefixes generation is not crucial
+
+`improvements_ipv6.patch`
+- adds support for IPv6 prefixes generation and improves precision of both IPv4 and IPv6 prefixes generation
+- use when IPv6 prefixes generation is required and precision of IPv4/IPv6 prefixes generation is crucial
## Usage
+ClassBench-ng can be used in two different ways:
+- To analyse an existing rule set and extract a corresponding SEED.
+- To generate a synthetic rule set from an input SEED.
+
```
-./classbench analyse FILE
+./classbench -h | --help
```
-Analyses file, expecting FILE to be ovs-ofctl dump.
-Fields extracted from dump are:
-- dl_dst, dl_src, dl_type, dl_vlan, dl_vlan_pcp,
-- eth_type, in_port,
-- nw_dst, nw_proto, nw_src, nw_tos,
-- tp_dst, tp_src
+Prints detailed usage information.
-Output's original Classbench seed with openflow YAML structure as last section.
+### ClassBench-ng Analyser
+The current version can successfully analyse IPv4 5-tuples, IPv6 5-tuples, and OpenFlow rules.
```
-./classbench generate v4 SEED [--count=100] [--db-generator=]
+./classbench analyse tuples FILE FORMAT [-l]
+```
+Analyses FILE, expecting FILE to be in the format specified in FORMAT (see `./lib/tuples_analyzer/README` for more information on how to specify the format).
+- `-l` enables printing analysis error logs.
+
+The output is an original ClassBench seed.
+
```
-Generates --count of OpenFlow rules.
-If seed without OpenFlow section is provided, regular 5-tuples are generated.
-Output format is "attribute=value", joined by ", ".
+./classbench analyse of FILE
+```
+Analyses FILE, expecting FILE to be in the format used by `ovs-ofctl`.
+Fields extracted from FILE are:
+- in_port,
+- dl_src, dl_dst, eth_type, dl_vlan, dl_vlan_pcp,
+- nw_src, nw_dst, nw_tos, nw_proto,
+- tp_src, tp_dst
+
+The output is an original ClassBench seed with an OpenFlow YAML structure as the last section.
+
+### ClassBench-ng Rule Generator
+The current version can successfully generate IPv4, IPv6 and OpenFlow 1.0 flow rules.
+- IPv4 SEEDs can be found in `./vendor/parameter_files`
+- OpenFlow SEEDs can be found in `./seeds`
+
+```
+./classbench generate v4 SEED [--count=] [--db-generator=]
+```
+Generates IPv4 5-tuples following the properties from SEED.
+- `--count=` specifies the number of 5-tuples to be generated (default: `100`)
+- `--db-generator=` specifies the path to an original ClassBench binary (default: `./vendor/db_generator/db_generator`)
+
+The output format is the same as of original ClassBench outputs.
+
+```
+./classbench generate v6 SEED [--count=] [--db-generator=]
+```
+Generates IPv6 5-tuples following the properties from SEED.
+- `--count=` specifies the number of 5-tuples to be generated (default: `100`)
+- `--db-generator=` specifies the path to an original ClassBench binary (default: `./vendor/db_generator/db_generator`)
+
+The output format is the same as of original ClassBench outputs.
+
+```
+./classbench generate of SEED [--count=] [--db-generator=]
+```
+Generates OpenFlow rules following the properties from SEED that has to contain an OpenFlow section.
+- `--count=` specifies the number of rules to be generated (default: `100`)
+- `--db-generator=` specifies the path to an original ClassBench binary (default: `./vendor/db_generator/db_generator`)
+
+The output consists of `attribute=value` pairs joined by `, `.
+
+## Known Issues
+- the number of generated rules is usually lower than in original ClassBench (i.e., ClassBench-ng generates higher number of redundant rules that are removed in the last phase)
+- ClassBench-ng Analyser does not correctly analyses source/destination port prefixes specified using a bit map in the ovs-ofctl format
+## How to Contribute
+Contributions are welcome via:
+- pull requests (preferred)
+- e-mail to imatousek at fit.vutbr.cz
diff --git a/classbench b/classbench
index 7d88359..e030cd3 100755
--- a/classbench
+++ b/classbench
@@ -5,47 +5,62 @@ require_relative "lib/classbench"
require "pp"
require "docopt"
doc = < [--count=] [--db-generator=]
- #{__FILE__} generate v6 [--count=]
+ #{__FILE__} analyse tuples FILE FORMAT [-l]
+ #{__FILE__} analyse of FILE
+ #{__FILE__} generate v4 SEED [--count=] [--db-generator=]
+ #{__FILE__} generate v6 SEED [--count=] [--db-generator=]
+ #{__FILE__} generate of SEED [--count=] [--db-generator=]
#{__FILE__} -h | --help
- #{__FILE__} version
Options:
- --db-generator= Path to binary of original db_generator [default: ./vendor/db_generator/db_generator]
- --count= Count of rules to generate [default: 100]
+ -l Enable printing analysis error logs.
+ --db-generator= Path to an original ClassBench binary.
+ [default: ./vendor/db_generator/db_generator]
+ --count= The number of rules to be generated.
+ [default: 100]
-h --help Show this screen.
-Analyser accept's as input ovs-ofctl dump.
-Fields extracted from dump are:
- - dl_dst, dl_src, dl_type, (dl_vlan, dl_vlan_pcp,)
- - eth_type, in_port,
- - nw_dst, nw_proto, nw_src, nw_tos,
- - tp_dst, tp_src
-Output is original Classbench seed
- with openflow YAML structure as last section.
+Analyser accepts as an input either a rule set in a given format in case of
+"analyse tuples" (the expected structure of a format file is described in
+lib/tuples_analyzer/README, section 7) or an ovs-ofctl dump in case of
+"analyse of".
+The output is an original ClassBench seed or this seed with an OpenFlow YAML
+structure as the last section, respectively.
-Generator accept's Classbench seed with openflow section.
-Output's one rule per line in format "attribute=value", joined by ", ".
+Generator accepts as an input an original ClassBench seed that has to contain
+an OpenFlow section in case of "generate of".
+The output is either the same as of original ClassBench outputs or it consists
+of "attribute=value" pairs joined by ", ".
DOCOPT
+# Add the following line to the DOCOPT's "Options" pattern to show support of
+# "version" parameter.
+# #{__FILE__} version
begin
opts = Docopt::docopt(doc)
if opts["analyse"]
- Classbench::analyse(opts["FILE"])
+ if opts["tuples"]
+ Classbench::analyse_tuples(opts["FILE"], opts["FORMAT"], opts["-l"])
+ elsif opts["of"]
+ Classbench::analyse_of(opts["FILE"])
+ end
elsif opts["generate"]
- #pp opts
- Classbench::generate(opts[""], (opts["--count"].to_i), opts["--db-generator"])
+ if opts["v4"]
+ Classbench::generate("v4", opts["SEED"], (opts["--count"].to_i), opts["--db-generator"])
+ elsif opts["v6"]
+ Classbench::generate("v6", opts["SEED"], (opts["--count"].to_i), opts["--db-generator"])
+ elsif opts["of"]
+ Classbench::generate("of", opts["SEED"], (opts["--count"].to_i), opts["--db-generator"])
+ end
elsif opts["version"]
puts "Version: #{Classbench::VERSION}"
- end
- # TODO: --version
+ end
-rescue Docopt::Exit => e
- STDERR.puts e.message
+ rescue Docopt::Exit => e
+ STDERR.puts e.message
end
diff --git a/lib/classbench.rb b/lib/classbench.rb
index b594031..756cd46 100644
--- a/lib/classbench.rb
+++ b/lib/classbench.rb
@@ -32,23 +32,49 @@ def self.load_prefixes_from_file(filename)
t
end
- def self.analyse(filename)
+ def self.analyse_tuples(rules_filename, format_filename, logs_enabled)
+ if logs_enabled
+ pid, stdin, stdout, stderr = Open4::popen4("python3", "-m", "lib.tuples_analyzer", "-r", rules_filename, "-f", format_filename, "-l")
+ else
+ pid, stdin, stdout, stderr = Open4::popen4("python3", "-m", "lib.tuples_analyzer", "-r", rules_filename, "-f", format_filename)
+ end
+
+ ignored, status = Process::waitpid2 pid
+
+ if status.exitstatus == 0
+ STDOUT.puts stdout.read.strip
+ if logs_enabled
+ warnings = stderr.read.strip
+ if !warnings.to_s.empty?
+ STDERR.puts warnings
+ end
+ end
+ else
+ STDERR.puts stderr.read.strip
+ exit(status.exitstatus)
+ end
+
+ end
+
+ def self.analyse_of(filename)
analyser = Analyser.new
analyser.parse_openflow(File.read(filename))
- analyser.calculate_stats
-
puts analyser.generate_seed
end
- def self.generate(filename, count, db_generator_path)
+ def self.generate(format, filename, count, db_generator_path)
generator = Generator.new(filename, db_generator_path)
- has_openflow = generator.parse_seed
+ if format == "of"
+ if !generator.parse_seed
+ return
+ end
+ end
#puts YAML.dump(generator.openflow_section)
- rules = generator.generate_rules(count)
- if has_openflow
+ rules = generator.generate_rules(format, count)
+ if format == "of"
rules.map!(&:to_vswitch_format)
end
diff --git a/lib/classbench/analyser.rb b/lib/classbench/analyser.rb
index 8b2ffcf..df1a01b 100644
--- a/lib/classbench/analyser.rb
+++ b/lib/classbench/analyser.rb
@@ -34,6 +34,8 @@ def parse_openflow(lines)
end
self.rules << Rule.new(rule)
end
+
+ calculate_stats
end
def rules_per_port_class(class_name)
@@ -41,8 +43,6 @@ def rules_per_port_class(class_name)
end
def generate_seed
- calculate_stats
-
seed = ""
seed += "-scale\n#{rules.size}\n#\n"
@@ -188,7 +188,7 @@ def calculate_stats
lengths_of_class = port_class_prefix_lengths[r.port_class_name]
specific_length = lengths_of_class[r.src_length + r.dst_length] ||= {}
- specific_length[r.dst_length] = (specific_length[r.dst_length] || 0) + 1
+ specific_length[r.src_length] = (specific_length[r.src_length] || 0) + 1
end
end
diff --git a/lib/classbench/generator.rb b/lib/classbench/generator.rb
index 42ac08c..4c7aa40 100644
--- a/lib/classbench/generator.rb
+++ b/lib/classbench/generator.rb
@@ -57,7 +57,7 @@ def parse_seed
#p self.openflow_section
rescue NoMethodError
- STDERR.puts "No openflow section found in seed."
+ STDERR.puts "No openflow section found in the seed."
return false
end
@@ -113,15 +113,23 @@ def pregenerate_eth_types
end
##########################
- def generate_classbench_rules(count)
+ def generate_classbench_rules(format, count)
current_dir = File.dirname(__FILE__)
tmp_filters = Tempfile.new('filters')
- # db_generator -c filename #{count} 0 0 0 tmp/#{rand}
- # Call classbench
- #system(current_dir+"/db_generator", "-c", self.seed_path, count.to_s, "0", "0", "0", tmp_filters.path, " > /dev/null")
- pid, stdin, stdout, stderr = Open4::popen4(self.db_generator_path, "-c", self.seed_path, count.to_s, "0", "0", "0", tmp_filters.path)
- ignored, status = Process::waitpid2 pid
+ if format == "v6"
+ # db_generator -c6 filename #{count} 0 0 0 tmp/#{rand}
+ # Call classbench
+ #system(current_dir+"/db_generator", "-c6", self.seed_path, count.to_s, "0", "0", "0", tmp_filters.path, " > /dev/null")
+ pid, stdin, stdout, stderr = Open4::popen4(self.db_generator_path, "-c6", self.seed_path, count.to_s, "0", "0", "0", tmp_filters.path)
+ ignored, status = Process::waitpid2 pid
+ else # format == "v4" || format format == "of"
+ # db_generator -c filename #{count} 0 0 0 tmp/#{rand}
+ # Call classbench
+ #system(current_dir+"/db_generator", "-c", self.seed_path, count.to_s, "0", "0", "0", tmp_filters.path, " > /dev/null")
+ pid, stdin, stdout, stderr = Open4::popen4(self.db_generator_path, "-c", self.seed_path, count.to_s, "0", "0", "0", tmp_filters.path)
+ ignored, status = Process::waitpid2 pid
+ end
#STDERR.puts "done"
#puts status
@@ -135,15 +143,24 @@ def generate_classbench_rules(count)
return raw_rules
end
- def generate_rules(count)
- generate_classbench_rules(count)
+ def generate_rules(format, count)
+ generate_classbench_rules(format, count)
- if not self.openflow_section
+ if format != "of"
return self.raw_rules
end
return classbench_rules.map do |rule|
- random_openflow_type = pregenerated_rule_types.sample
+
+ begin
+ random_openflow_type = pregenerated_rule_types.sample
+ end while ((random_openflow_type.include?("nw_proto") and rule.attributes["nw_proto"] == 0) or
+ ((not random_openflow_type.include?("nw_proto")) and (not rule.attributes["nw_proto"] == 0)) or
+ (random_openflow_type.include?("tp_src") and rule.attributes["tp_src"] == (0..65535)) or
+ ((not random_openflow_type.include?("tp_src")) and (not rule.attributes["tp_src"] == (0..65535))) or
+ (random_openflow_type.include?("tp_dst") and rule.attributes["tp_dst"] == (0..65535)) or
+ ((not random_openflow_type.include?("tp_dst")) and (not rule.attributes["tp_dst"] == (0..65535))))
+
rule.remove_missing_attributes(random_openflow_type)
#p random_openflow_type
@@ -159,15 +176,16 @@ def generate_rules(count)
rule.attributes["eth_type"] = pregenerated_eth_types.sample
end
- random_device_mac = (1..3).collect { "%02x" % [rand(255)] }.join(":")
+ random_dst_device_mac = (1..3).collect { "%02x" % [rand(255)] }.join(":")
if attribute == "dl_dst"
random_vendor = pregenerated_dl_dsts.sample
- rule.attributes["dl_dst"] = random_vendor + ":" + random_device_mac
+ rule.attributes["dl_dst"] = random_vendor + ":" + random_dst_device_mac
end
+ random_src_device_mac = (1..3).collect { "%02x" % [rand(255)] }.join(":")
if attribute == "dl_src"
random_vendor = pregenerated_dl_srcs.sample
- rule.attributes["dl_src"] = random_vendor + ":" + random_device_mac
+ rule.attributes["dl_src"] = random_vendor + ":" + random_src_device_mac
end
if attribute == "dl_vlan"
diff --git a/lib/classbench/rule.rb b/lib/classbench/rule.rb
index 47ce8d5..41aa04c 100644
--- a/lib/classbench/rule.rb
+++ b/lib/classbench/rule.rb
@@ -32,11 +32,11 @@ def self.from_classbench_format(line)
end
def src_length
- IPAddress.parse(attributes["nw_src"] || '0.0.0.0').prefix.to_i
+ IPAddress.parse(attributes["nw_src"] || '0.0.0.0/0').prefix.to_i
end
def dst_length
- IPAddress.parse(attributes["nw_dst"] || '0.0.0.0').prefix.to_i
+ IPAddress.parse(attributes["nw_dst"] || '0.0.0.0/0').prefix.to_i
end
def remove_missing_attributes(attrs)
@@ -101,6 +101,7 @@ def l2_vendor(type)
def to_vswitch_format
attributes.to_a.
+ reject {|k,v| k == "nw_proto" and v == 0}. # nw_proto is removed when wildcard
reject {|k,v| v == (0..65535)}. # tp_src and tp_dst is removed when wildcard
map {|k,v| (v.is_a?(Range) and v.first == v.last) ? [k, v.first] : [k,v] }. # if port range is [x..x] => x
map {|k,v| "#{k}=#{v}" }.join(", ") # Openflow format
diff --git a/lib/tuples_analyzer/README b/lib/tuples_analyzer/README
new file mode 100644
index 0000000..8ab9143
--- /dev/null
+++ b/lib/tuples_analyzer/README
@@ -0,0 +1,111 @@
+1.PROGRAM:
+ tuples_analyzer
+
+2.FUNCTION:
+ Program creates parameter file from statistics and distributions of real filter set.
+ File with format of rules is needed for processing filter rules. Format is described in section 7.
+ Computed parameters are then used to generate synthetic filter set by tools ClassBench and ClassBench-ng.
+
+3.USAGE:
+ python3 -m tuples_analyzer -r -f [-o -l -h]
+
+4.USAGE EXAMPLES:
+ a.) python3 -m tuples_analyzer -h
+ b.) python3 -m tuples_analyzer -r rules.txt -f format.txt
+ c.) python3 -m tuples_analyzer -r rules.txt -f format.txt -l
+ d.) python3 -m tuples_analyzer -r rules.txt -f format.txt -o output.txt
+ e.) python3 -m tuples_analyzer -r rules.txt -f format.txt -o output.txt -l
+
+5.MANDATORY ARGUMENTS:
+ -r rules_file | --rules=rules_file specify path to file with filter rules
+ -f format_file | --format=format_file specify path to file with format of rules
+
+6.OPTIONAL ARGUMENTS:
+ -o output_file | --output=output_file specify path to file, which will be created
+ to store computed parameters
+
+ -l | --logs printing error logs during computation is enabled
+ -h | --help display manual
+
+7.FORMAT OF RULES:
+ Format tells, how one rule in set should look like. Program loads format from first line of format file. Specify path to
+ file with argument '-f'.
+
+ Format of rule is sentence consisting of parameters and keywords. Parameters (specified uppercase words) are like
+ variables. They always represent different value in filter rule than its notation in format. E.g. parameter 'PROTOCOL'
+ in format, represents network protocol abbreviation in rule (e.g. 'tcp'). Keywords in format are exactly same strings
+ in rule (e.g. 'from', 'to').
+
+ Order of placement parameters and keywords in format sentence is important. In process of parsing
+ rule, is from beginning of rule to its end checked, if every part of rule matches to its definition
+ in format. E.g. if format starts with 'from SRC_IP', it is expected, that first part of rule will be
+ 'from' (same as 'from' in format) and second part will be ip address for example '10.10.10.0/24'
+ (parameter 'SRC_IP' representation). If order is not correct, rule will be ignored.
+
+ a.)parameters
+
+ Parameters (PROTOCOL, SRC_IP, SRC_PORT, DST_IP, DST_PORT, NUMBER, WILDCARD) in format definition tell, what type of
+ value is expected on same position in rule. What every parameter represents is described in this list:
+
+ PROTOCOL network protocol abbreviation
+ SRC_IP source IPv4 or IPv6 address
+ SRC_PORT source port or port range
+ DST_IP destination IPv4 or IPv6 address
+ DST_PORT destination port or port range
+ NUMBER some number, not used for computation parameters
+ WILDCARD some string, not used for computation parameters
+
+ b.)supported representation of parameters in rules
+
+ Supported IPv4 and IPv6 address definitions are with or without mask in digit form, e.g.: 2001:db8:a::/64, 192.168.0.33
+ Supported port definitions are port number or port range, e.g.: 80, 0:1023
+ All supported network protocol case-insensitive abbreviations are:
+ 'ICMP', 'IGMP', 'GGP', 'ST', 'TCP', 'EGP', 'UDP', 'GRE', 'ESP', 'AH', 'EIGRP', 'OSPFIGP'
+ All supported wildcard values of parameters are: 'any', 'all', '*', 'ip', '0'
+
+ b.)keywords
+
+ Keywords are every possible strings except all parameters and character '?'. Keywords in format definition tell,
+ that exactly same string value as keywords is expected on same position in rule. They does not represent valuable
+ content for computation parameters. They are usually used to introduce parameters. E.g. if you define your format
+ starting with 'from=SRC_IP', then rule definition in filter set can start with 'from=10.10.10.0/24'.
+
+ Characters ' ', ',', '=' (empty space, comma, equal) are used as separators between parameters and keywords
+ in format and also separators between parts in filter rules.
+
+ c.)optional parts
+
+ You can define optional parts of rule in your format definition, if you after parameter or keyword
+ add character '?'. E.g. if format ends with 'ip-port?=DST_PORT?', then at the end of rule is not
+ required any of these words 'ip-port' or '0:1023' (example of port range) but they can both
+ be there (of course in correct order).
+
+
+8.FORMAT OF RULES EXAMPLES:
+ Example sets and formats are located in examples folder.
+
+ a.) custom1.format:
+ PROTOCOL from SRC_IP SRC_PORT to DST_IP DST_PORT
+
+ b.) custom2.format:
+ PROTOCOL destination DST_IP ip-port? DST_PORT? source SRC_IP ip-port? SRC_PORT?
+
+ c.)custom_with_ipv6.format:
+ rule NUMBER WILDCARD PROTOCOL source? SRC_IP? destination? DST_IP? destination-port DST_PORT?
+
+ d.) openflow.format:
+ nw_proto=PROTOCOL, nw_src?=SRC_IP?, nw_dst?=DST_IP?, tp_src?=SRC_PORT?, tp_dst?=DST_PORT?
+
+ e.)iptables_long.format (long format):
+ iptables WILDCARD WILDCARD --source? SRC_IP? --destination? DST_IP? --protocol? PROTOCOL? --source-port? SRC_PORT? --destination-port? DST_PORT? -j WILDCARD
+
+ f.)iptables_short.format (short format):
+ iptables WILDCARD WILDCARD -s? SRC_IP? -d? DST_IP? -p? PROTOCOL? --sport? SRC_PORT? --dport? DST_PORT? -j WILDCARD
+
+ g.)ipfw.format:
+ ipfw add WILDCARD PROTOCOL? from SRC_IP? SRC_PORT? to DST_IP? DST_PORT?
+
+
+9.AUTHOR:
+ Jozef Sabo
+ xsaboj00@stud.fit.vutbr.cz
diff --git a/lib/tuples_analyzer/__init__.py b/lib/tuples_analyzer/__init__.py
new file mode 100644
index 0000000..9429a2c
--- /dev/null
+++ b/lib/tuples_analyzer/__init__.py
@@ -0,0 +1 @@
+"""Main package of program."""
diff --git a/lib/tuples_analyzer/__main__.py b/lib/tuples_analyzer/__main__.py
new file mode 100644
index 0000000..251f817
--- /dev/null
+++ b/lib/tuples_analyzer/__main__.py
@@ -0,0 +1,26 @@
+#!/usr/bin/env python
+"""Main module for launching program."""
+
+from .calculation_parameters.parameter_file import ParameterFile
+from .processing_arguments.processed_arguments import ProcessedArguments
+from .processing_files.processing_file_helper import ProcessingFileHelper
+from .processing_rules.filter_rule_generator import FilterRuleGenerator
+
+if __name__ == "__main__":
+ # instance of class ProcessedArguments contains all processed command line arguments
+ processed_arguments = ProcessedArguments()
+
+ # generator of lines from filter set file
+ lines_generator = ProcessingFileHelper.create_lines_generator(processed_arguments.rules_path)
+
+ # string with format of rules in filter set
+ rule_format = ProcessingFileHelper.get_format(processed_arguments.format_path)
+
+ # generator of FilterRule class instances
+ rule_generator = FilterRuleGenerator.create_generator(lines_generator, rule_format, processed_arguments.is_stderr_on)
+
+ # instance of class ParameterFile creates and holds all parameters
+ parameter_file = ParameterFile(rule_generator, processed_arguments.output_path)
+
+ # printing calculated parameters to output
+ parameter_file.print_parameters()
diff --git a/lib/tuples_analyzer/calculation_parameters/__init__.py b/lib/tuples_analyzer/calculation_parameters/__init__.py
new file mode 100644
index 0000000..c97a818
--- /dev/null
+++ b/lib/tuples_analyzer/calculation_parameters/__init__.py
@@ -0,0 +1 @@
+"""Package with classes for computation parameters."""
diff --git a/lib/tuples_analyzer/calculation_parameters/distribution.py b/lib/tuples_analyzer/calculation_parameters/distribution.py
new file mode 100644
index 0000000..56a8d30
--- /dev/null
+++ b/lib/tuples_analyzer/calculation_parameters/distribution.py
@@ -0,0 +1,32 @@
+#!/usr/bin/env python
+"""Module containing definition of class Distribution."""
+
+from collections import Counter
+
+
+class Distribution:
+ """
+ Class representing distribution over different type of values.
+ """
+
+ def __init__(self):
+ """
+ Constructor initialize instance variable total_counts with zero.
+ Variable distribution is initialized as new instance of class Counter.
+ Counter is collection for counting objects.
+ """
+ self.total_count = 0
+ """Total count of values in distribution."""
+ self.distribution = Counter()
+ """Variable holding count of every unique value in distribution."""
+
+ def add_value(self, value):
+ """
+ When adding new value to distribution, total counts of values has to be incremented by one and
+ instance variable distribution has to be updated with new value (updating counts of unique values in collection
+ Counter).
+
+ :param value: Distribution value.
+ """
+ self.total_count += 1
+ self.distribution.update({value: 1})
diff --git a/lib/tuples_analyzer/calculation_parameters/enums/__init__.py b/lib/tuples_analyzer/calculation_parameters/enums/__init__.py
new file mode 100644
index 0000000..2997f06
--- /dev/null
+++ b/lib/tuples_analyzer/calculation_parameters/enums/__init__.py
@@ -0,0 +1 @@
+"""Package with enum classes used while computation parameters."""
diff --git a/lib/tuples_analyzer/calculation_parameters/enums/port_distribution_type.py b/lib/tuples_analyzer/calculation_parameters/enums/port_distribution_type.py
new file mode 100644
index 0000000..c26fe79
--- /dev/null
+++ b/lib/tuples_analyzer/calculation_parameters/enums/port_distribution_type.py
@@ -0,0 +1,19 @@
+#!/usr/bin/env python
+"""Module containing definition of enum class PortDistributionType."""
+
+from enum import Enum
+
+
+class PortDistributionType(Enum):
+ """
+ Enum for every possible distribution type of port values.
+ """
+
+ SPAR = 0
+ """Source arbitrary port ranges distribution."""
+ SPEM = 1
+ """Source exact match ports distribution."""
+ DPAR = 2
+ """Destination arbitrary port ranges distribution."""
+ DPEM = 3
+ """Destination exact match ports distribution."""
diff --git a/lib/tuples_analyzer/calculation_parameters/enums/trie_type.py b/lib/tuples_analyzer/calculation_parameters/enums/trie_type.py
new file mode 100644
index 0000000..dfa63cc
--- /dev/null
+++ b/lib/tuples_analyzer/calculation_parameters/enums/trie_type.py
@@ -0,0 +1,15 @@
+#!/usr/bin/env python
+"""Module containing definition of enum class TrieType."""
+
+from enum import Enum
+
+
+class TrieType(Enum):
+ """
+ Enum for types of trie (binary prefix tree).
+ """
+
+ SPT = 0
+ """Trie created from source prefixes of filter rules."""
+ DPT = 1
+ """Trie created from destination prefixes of filter rules."""
diff --git a/lib/tuples_analyzer/calculation_parameters/interfaces/IParameter.py b/lib/tuples_analyzer/calculation_parameters/interfaces/IParameter.py
new file mode 100644
index 0000000..70a39ba
--- /dev/null
+++ b/lib/tuples_analyzer/calculation_parameters/interfaces/IParameter.py
@@ -0,0 +1,39 @@
+#!/usr/bin/env python
+"""Module containing definition of interface IParameter."""
+
+from abc import ABCMeta, abstractmethod
+
+
+class IParameter:
+ """
+ Interface which has to be implemented by all parameter computation classes.
+ """
+ __metaclass__ = ABCMeta
+
+ @abstractmethod
+ def extract_data(self, filter_rule):
+ """
+ Function extracts all needed data from FilterRule instance to compute specific parameter.
+ Extracted data are stored in instance variables.
+
+ :param filter_rule: FilterRule instance.
+ """
+ pass
+
+ @abstractmethod
+ def compute_parameter(self):
+ """
+ Function computes parameter from all extracted data.
+
+ :return: Computed parameter.
+ """
+ pass
+
+ @abstractmethod
+ def print_parameter(self, output):
+ """
+ Function prints parameter to output in ClassBench format.
+
+ :param output: OutputPrint instance.
+ """
+ pass
diff --git a/lib/tuples_analyzer/calculation_parameters/interfaces/__init__.py b/lib/tuples_analyzer/calculation_parameters/interfaces/__init__.py
new file mode 100644
index 0000000..91d2233
--- /dev/null
+++ b/lib/tuples_analyzer/calculation_parameters/interfaces/__init__.py
@@ -0,0 +1 @@
+"""Package with interface implemented by all parameter classes."""
diff --git a/lib/tuples_analyzer/calculation_parameters/parameter_file.py b/lib/tuples_analyzer/calculation_parameters/parameter_file.py
new file mode 100644
index 0000000..cd03da3
--- /dev/null
+++ b/lib/tuples_analyzer/calculation_parameters/parameter_file.py
@@ -0,0 +1,112 @@
+#!/usr/bin/env python
+"""Module containing definition of class ParameterFile."""
+
+from .enums.port_distribution_type import PortDistributionType
+from .enums.trie_type import TrieType
+from .port_distribution import PortDistribution
+from .ppc_prefixlen_distribution import PpcPrefixLenDistribution
+from .prefix_correlation_distribution import PrefixCorrelationDistribution
+from .protocol_ppc_distribution import ProtocolPpcDistribution
+from .trie_distributions import TrieDistributions
+from ..printing_output.output_print import OutputPrint
+from ..processing_rules.enums.port_pair_class import PPC
+
+
+class ParameterFile:
+ """
+ Class representing parameter file.
+ Every parameter located in ClassBench parameter file has its own class.
+ This class holds all instances of parameter classes required to create desired parameter file.
+ """
+ loaded_rules = 0
+ """Count of processed rules in process of extraction data."""
+
+ def __init__(self, rule_generator, output_path):
+ """
+ Constructor initialize instance variables as new instances of parameter classes, rule generator class
+ and output class.
+ It also calls function load_rules() to load parameter instances with extracted data from filter rules.
+
+ :param rule_generator: Generator of FilterRule instances.
+
+ :param output_path: Path to file, where user wants to save computed parameters.
+ """
+ self.rule_generator = rule_generator
+ """Generator of FilterRule instances."""
+ self.output = OutputPrint(output_path)
+ """Instance of class used to print computed parameters to output."""
+ self.protocol_ppc_distribution = ProtocolPpcDistribution()
+ """Instance of class ProtocolPpcDistribution."""
+ self.port_distributions = []
+ """List of instances of class PortDistribution."""
+ i = 0
+ while i < 4:
+ self.port_distributions.insert(i, PortDistribution(PortDistributionType(i)))
+ i += 1
+
+ self.ppc_prefix_length_distributions = []
+ """List of instances of class PpcPrefixLenDistribution."""
+ i = 0
+ while i < 25:
+ self.ppc_prefix_length_distributions.insert(i, PpcPrefixLenDistribution(PPC(i)))
+ i += 1
+
+ self.trie_distributions = []
+ """List of instances of class TrieDistributions."""
+ i = 0
+ while i < 2:
+ self.trie_distributions.insert(i, TrieDistributions(TrieType(i)))
+ i += 1
+
+ self.prefix_correlation_distribution = PrefixCorrelationDistribution()
+ """Instance of class PrefixCorrelationDistribution."""
+
+ self.load_parameters()
+
+ def load_parameters(self):
+ """
+ Function fills structures of all parameter instances with data needed to calculate parameters.
+ Data are extracted from generator of FilterRule instances.
+ """
+ for rule in self.rule_generator:
+ self.protocol_ppc_distribution.extract_data(rule)
+
+ for port_distribution in self.port_distributions:
+ port_distribution.extract_data(rule)
+
+ for ppc_prefix_length_distribution in self.ppc_prefix_length_distributions:
+ ppc_prefix_length_distribution.extract_data(rule)
+
+ for trie_distributions in self.trie_distributions:
+ trie_distributions.extract_data(rule)
+
+ self.prefix_correlation_distribution.extract_data(rule)
+ ParameterFile.loaded_rules += 1
+
+ def print_parameters(self):
+ """
+ Function calls print functions of all parameter instances to print calculated parameters in ClassBench format.
+ """
+ self.output.print('-scale')
+ self.output.print(str(ParameterFile.loaded_rules))
+ self.output.print('#')
+
+ self.protocol_ppc_distribution.print_parameter(self.output)
+
+ self.output.print('-flags')
+ self.output.print('#')
+
+ self.output.print('-extra')
+ self.output.print('0')
+ self.output.print('#')
+
+ for port_distribution in self.port_distributions:
+ port_distribution.print_parameter(self.output)
+
+ for ppc_prefix_length_distribution in self.ppc_prefix_length_distributions:
+ ppc_prefix_length_distribution.print_parameter(self.output)
+
+ for trie_distributions in self.trie_distributions:
+ trie_distributions.print_parameter(self.output)
+
+ self.prefix_correlation_distribution.print_parameter(self.output)
diff --git a/lib/tuples_analyzer/calculation_parameters/port_distribution.py b/lib/tuples_analyzer/calculation_parameters/port_distribution.py
new file mode 100644
index 0000000..1db1f92
--- /dev/null
+++ b/lib/tuples_analyzer/calculation_parameters/port_distribution.py
@@ -0,0 +1,79 @@
+#!/usr/bin/env python
+"""Module containing definition of class PortDistribution."""
+
+from .distribution import Distribution
+from .enums.port_distribution_type import PortDistributionType
+from .interfaces.IParameter import IParameter
+from ..value_formats.value_format import Format
+from ..processing_rules.enums.port_class import PC
+
+
+class PortDistribution(IParameter):
+ """
+ Class representing parameter: port values distribution.
+ There are 4 types of port values distributions (SPAR, SPEM, DPAR, DPEM).
+ Type of distribution tells from which port values of filter rules will be created distribution.
+ Types are more described in enum class PortDistributionClass.
+ """
+
+ def __init__(self, port_distribution_type):
+ """
+ Parameter of constructor decides which type of port values distribution will be represented by created instance
+ of that class.
+
+ :param port_distribution_type: Enum value of type of port values distribution.
+ """
+ self.distribution_type = port_distribution_type
+ """Type of port values distribution."""
+ self.distribution = Distribution()
+ """Distribution of unique port values."""
+
+ def extract_data(self, filter_rule):
+ """
+ Function updates instance variable distribution with new counts of unique port values extracted from filter rules.
+
+ :param filter_rule: FilterRule instance.
+ """
+ if self.distribution_type == PortDistributionType.SPAR and filter_rule.src_port_class == PC.AR:
+ self.distribution.add_value(filter_rule.src_port)
+
+ elif self.distribution_type == PortDistributionType.SPEM and filter_rule.src_port_class == PC.EM:
+ self.distribution.add_value(filter_rule.src_port)
+
+ elif self.distribution_type == PortDistributionType.DPAR and filter_rule.dst_port_class == PC.AR:
+ self.distribution.add_value(filter_rule.dst_port)
+
+ elif self.distribution_type == PortDistributionType.DPEM and filter_rule.dst_port_class == PC.EM:
+ self.distribution.add_value(filter_rule.dst_port)
+
+ def compute_parameter(self):
+ """
+ Function computes parameter from all counts of unique port values stored in instance variable distribution.
+
+ :return: Port values distribution in string format.
+ """
+ parameter = ""
+
+ first = True
+ for port_range in self.distribution.distribution.most_common():
+
+ # for first port range we do not want print new line
+ if first:
+ first = False
+ else:
+ parameter += '\n'
+
+ parameter += str(Format.decimal(port_range[1] / self.distribution.total_count)) + '\t' + str(port_range[0])
+ return parameter
+
+ def print_parameter(self, output):
+ """
+ Function calls method compute_parameter() to compute parameter and then prints parameter to output in
+ ClassBench format.
+
+ :param output: OutputPrint instance.
+ """
+ output.print('-' + str(self.distribution_type)[-4:].lower())
+ if self.distribution.total_count != 0:
+ output.print(self.compute_parameter())
+ output.print('#')
diff --git a/lib/tuples_analyzer/calculation_parameters/ppc_prefixlen_distribution.py b/lib/tuples_analyzer/calculation_parameters/ppc_prefixlen_distribution.py
new file mode 100644
index 0000000..f9a76e9
--- /dev/null
+++ b/lib/tuples_analyzer/calculation_parameters/ppc_prefixlen_distribution.py
@@ -0,0 +1,92 @@
+# !/usr/bin/env python
+"""Module containing definition of class PpcPrefixLenDistribution."""
+
+from .distribution import Distribution
+from .interfaces.IParameter import IParameter
+from ..value_formats.value_format import Format
+
+
+class PpcPrefixLenDistribution(IParameter):
+ """
+ Class representing parameter: prefix lengths distribution from rules with same port pair class (PPC).
+ There are total 25 PPCs, so for every group of rules with same PPC program has to create one independent instance of
+ class.
+ """
+
+ def __init__(self, port_pair_class):
+ """
+ Constructor decides from which rules will be made prefix lengths distribution. Decision is made by enum value of
+ PPC given by parameter of constructor.
+
+ :param port_pair_class: Enum value of PPC.
+ """
+ self.port_pair_class = port_pair_class
+ """PPC of rules from which is made distribution."""
+ self.distribution = dict()
+ """Instance variable of type dictionary. Elements in dictionary have total prefix length as key and
+ instance of class Distribution as value. In value of dictionary are stored counts of unique source prefix
+ lengths."""
+
+ def extract_data(self, filter_rule):
+ """
+ Function fills dictionary (instance variable distribution) with new elements and data (counts of unique lengths).
+ If total prefix length extracted from filter rule is new key in dictionary, then add new element to dictionary
+ with this key.
+ Value (instance of class Distribution) of element with extracted total prefix length as key is always updated
+ with source prefix length of filter rule.
+
+ :param filter_rule: FilterRule instance.
+ """
+ if self.port_pair_class == filter_rule.get_ppc_class():
+ total_length = filter_rule.src_ip_add_prefix_length + filter_rule.dst_ip_add_prefix_length
+
+ if total_length not in self.distribution:
+ self.distribution[total_length] = Distribution()
+
+ self.distribution.get(total_length).add_value(filter_rule.src_ip_add_prefix_length)
+
+ def compute_parameter(self):
+ """
+ Function computes parameter from all elements stored in dictionary (instance variable distribution).
+
+ :return: Prefix lengths distribution in string format.
+ """
+ parameter = ""
+
+ # total count of processed rules with same PPC
+ rules_count = sum([value.total_count for key, value in self.distribution.items()])
+
+ first = True
+ for total_prefix_length, value in sorted(self.distribution.items()):
+
+ # for first total prefix length we do not want print new line
+ if first:
+ first = False
+ else:
+ parameter += '\n'
+
+ # total count of rules with same total prefix length
+ total_prefix_len_count = value.total_count
+
+ parameter += str(total_prefix_length) + ',' + str(Format.decimal(total_prefix_len_count / rules_count))
+
+ for source_prefix_length in sorted(value.distribution):
+ # total count of rules with same source prefix length
+ src_prefix_len_count = value.distribution[source_prefix_length]
+
+ parameter += '\t' + str(source_prefix_length) + ','
+ parameter += str(Format.decimal(src_prefix_len_count / total_prefix_len_count))
+
+ return parameter
+
+ def print_parameter(self, output):
+ """
+ Function calls method compute_parameter() to compute parameter and then prints parameter to output in
+ ClassBench format.
+
+ :param output: OutputPrint instance.
+ """
+ output.print('-' + str(self.port_pair_class)[-5:].lower())
+ if self.distribution:
+ output.print(self.compute_parameter())
+ output.print('#')
diff --git a/lib/tuples_analyzer/calculation_parameters/prefix_correlation_distribution.py b/lib/tuples_analyzer/calculation_parameters/prefix_correlation_distribution.py
new file mode 100644
index 0000000..a3f4281
--- /dev/null
+++ b/lib/tuples_analyzer/calculation_parameters/prefix_correlation_distribution.py
@@ -0,0 +1,104 @@
+#!/usr/bin/env python
+"""Module containing definition of class PrefixCorrelationDistribution."""
+
+from .interfaces.IParameter import IParameter
+from ..value_formats.value_format import Format
+
+
+class PrefixCorrelationDistribution(IParameter):
+ """
+ Class representing parameter: prefix correlation distribution.
+ Correlation means source and destination prefixes of one filter rule are continuing to be same,
+ in length where are both defined.
+ """
+
+ def __init__(self):
+ """
+ Constructor initialize two instance variable as lists with zero values.
+ Both lists are sizes of 128. Indexes of lists are representing prefix levels.
+ """
+ self.all_on_level = [0] * 128
+ """List with counts of rules which have same source and destination prefixes until previous level."""
+ self.same_on_level = [0] * 128
+ """List with counts of rules which source and destination prefixes are same on level."""
+
+ def extract_data(self, filter_rule):
+ """
+ Function updates counts of rules on levels (indexes) in list instance variables.
+
+ :param filter_rule: FilterRule instance.
+ """
+ # do not care about prefixes with wildcards
+ if filter_rule.src_ip_add_bin != '*' and filter_rule.dst_ip_add_bin != '*':
+
+ # prefix length where are both prefixes defined
+ valid_length = PrefixCorrelationDistribution.get_smaller_prefix(filter_rule)
+
+ i = 0
+ # going through bits of prefix
+ for b in filter_rule.src_ip_add_bin[:valid_length]:
+
+ self.all_on_level[i] += 1
+
+ # if prefixes do not continue to be same break the cycle
+ if b == filter_rule.dst_ip_add_bin[i]:
+ self.same_on_level[i] += 1
+ else:
+ break
+
+ i += 1
+
+ def compute_parameter(self):
+ """
+ Function computes parameter from counts of rules stored in list instance variables.
+
+ :return: Prefix correlation distribution in string format.
+ """
+ parameter = ""
+ i = 1
+ first = True
+
+ for rules_count in self.all_on_level:
+
+ # at least 32 will be printed
+ if i > 32 and rules_count == 0:
+ break
+
+ # for first correlation we do not want print new line
+ if first:
+ first = False
+ else:
+ parameter += '\n'
+
+ parameter += str(i) + '\t'
+ if rules_count == 0:
+ parameter += '0.00000000'
+ else:
+ parameter += str(Format.decimal(self.same_on_level[i - 1] / rules_count))
+ i += 1
+
+ return parameter
+
+ def print_parameter(self, output):
+ """
+ Function calls method compute_parameter() to compute parameter and then prints parameter to output in
+ ClassBench format.
+
+ :param output: OutputPrint instance.
+ """
+ output.print('-pcorr')
+ output.print(self.compute_parameter())
+ output.print('#')
+
+ @staticmethod
+ def get_smaller_prefix(filter_rule):
+ """
+ Function returns smaller one prefix length of source and destination prefixes in filter rule.
+
+ :param filter_rule: FilterRule instance.
+ :return: Smaller prefix length of prefixes in filter rule.
+ """
+ if filter_rule.src_ip_add_prefix_length > filter_rule.dst_ip_add_prefix_length:
+ return filter_rule.dst_ip_add_prefix_length
+ else:
+ return filter_rule.src_ip_add_prefix_length
diff --git a/lib/tuples_analyzer/calculation_parameters/protocol_ppc_distribution.py b/lib/tuples_analyzer/calculation_parameters/protocol_ppc_distribution.py
new file mode 100644
index 0000000..20f3550
--- /dev/null
+++ b/lib/tuples_analyzer/calculation_parameters/protocol_ppc_distribution.py
@@ -0,0 +1,79 @@
+#!/usr/bin/env python
+"""Module containing definition of class ProtocolPpcDistribution."""
+
+from .distribution import Distribution
+from .interfaces.IParameter import IParameter
+from ..value_formats.value_format import Format
+from ..processing_rules.enums.port_pair_class import PPC
+
+
+class ProtocolPpcDistribution(IParameter):
+ """
+ Class representing parameter: port pair classes (PPCs) distribution over unique protocols in filter set.
+ """
+
+ def __init__(self):
+ """
+ Constructor only initialize instance variable distribution as new instance of dictionary.
+ """
+ self.distribution = dict()
+ """Instance variable of type dictionary. Elements in dictionary have protocol enum value as key and
+ instance of class Distribution as value."""
+
+ def extract_data(self, filter_rule):
+ """
+ Function fills dictionary (instance variable distribution) with new elements and data.
+ If protocol extracted from filter rule is new key in dictionary, then it adds new element
+ with this key to dictionary.
+ Value (instance of class Distribution) of element with extracted protocol as key is always updated
+ with PPC class of filter rule.
+
+ :param filter_rule: FilterRule instance.
+ """
+ if filter_rule.protocol not in self.distribution:
+ self.distribution[filter_rule.protocol] = Distribution()
+
+ self.distribution.get(filter_rule.protocol).add_value(filter_rule.get_ppc_class())
+
+ def compute_parameter(self):
+ """
+ Function computes parameter from all elements stored in dictionary (instance variable distribution).
+
+ :return: PPCs distribution over protocols in string format.
+ """
+ parameter = ""
+
+ # total count of processed protocols in all filter rules
+ rules_count = sum([value.total_count for key, value in self.distribution.items()])
+ first = True
+
+ for protocol, value in sorted(self.distribution.items()):
+
+ # for first protocol we do not want print new line
+ if first:
+ first = False
+ else:
+ parameter += '\n'
+
+ # total count of processed filter rules with specific protocol
+ protocol_count = value.total_count
+ parameter += str(protocol.value) + '\t' + str(Format.decimal(protocol_count / rules_count))
+
+ i = 0
+ while i < 25:
+ ppc_distribution = value.distribution[PPC(i)]
+ parameter += '\t' + str(Format.decimal(ppc_distribution / protocol_count))
+ i += 1
+
+ return parameter
+
+ def print_parameter(self, output):
+ """
+ Function calls method compute_parameter() to compute parameter and then prints parameter to output in
+ ClassBench format.
+
+ :param output: OutputPrint instance.
+ """
+ output.print('-prots')
+ output.print(self.compute_parameter())
+ output.print('#')
diff --git a/lib/tuples_analyzer/calculation_parameters/trie_distributions.py b/lib/tuples_analyzer/calculation_parameters/trie_distributions.py
new file mode 100644
index 0000000..afd1a00
--- /dev/null
+++ b/lib/tuples_analyzer/calculation_parameters/trie_distributions.py
@@ -0,0 +1,156 @@
+#!/usr/bin/env python
+"""Module containing definition of class TrieDistributions."""
+
+from .enums.trie_type import TrieType
+from .interfaces.IParameter import IParameter
+from .trie_node import TrieNode
+from ..value_formats.value_format import Format
+
+
+class TrieDistributions(IParameter):
+ """
+ Class representing 3 parameters gained by constructing binary prefix tree (trie).
+ These parameters are: distributions over prefix branching probability, skew average and prefix nesting threshold.
+ """
+
+ def __init__(self, trie_type):
+ """
+ Constructor decides from which prefixes (source/destination) will be created trie. Decision is made by enum
+ value of trie type given by parameter.
+
+ :param trie_type: Enum value of trie type.
+ """
+ self.trie_type = trie_type
+ """Type of trie is telling from which prefixes is trie constructed (source/destination prefixes)."""
+ self.root_node = TrieNode('*')
+ """Root node of trie. Trie is constructed by adding new prefixes to root."""
+
+ def extract_data(self, filter_rule):
+ """
+ Function is filling trie with source or destination prefixes extracted from filter rule.
+
+ :param filter_rule: FilterRule instance.
+ """
+ if self.trie_type == TrieType.SPT and filter_rule.src_ip_add_bin != '*':
+ self.root_node.add_prefix(filter_rule.src_ip_add_bin)
+
+ elif self.trie_type == TrieType.DPT and filter_rule.dst_ip_add_bin != '*':
+ self.root_node.add_prefix(filter_rule.dst_ip_add_bin)
+
+ def compute_parameter(self):
+ """
+ Function computes parameters from constructed trie.
+
+ :return: Computed parameters of trie distributions in string form.
+ """
+ parameter = ""
+ first = True
+ i = 0
+
+ # checking if trie is not empty
+ if self.root_node.children:
+ # current level nodes
+ nodes = [self.root_node]
+
+ while self.root_node.max_level > i:
+
+ # for first level we do not want print new line
+ if first:
+ first = False
+ else:
+ parameter += '\n'
+
+ count_1child = 0
+ count_2children = 0
+ # skew values on level
+ skew_list = []
+ # nodes on next level are stored here
+ temp_list = []
+
+ for node in nodes:
+ if len(node.children) == 1:
+ count_1child += 1
+ temp_list.extend(node.children)
+ elif len(node.children) == 2:
+ count_2children += 1
+ temp_list.extend(node.children)
+ skew_list.append(node.count_skew())
+
+ nodes = temp_list
+ parameter += str(i) + '\t' + TrieDistributions.get_branching_probability(count_1child, count_2children)
+ parameter += '\t' + TrieDistributions.get_average_skews(skew_list)
+ i += 1
+
+ # for undefined levels of trie, lines have this format:
+ while i < 33:
+ if first:
+ first = False
+ else:
+ parameter += '\n'
+ parameter += str(i) + "\t0.00000000\t0.00000000\t0.00000000"
+ i += 1
+
+ return parameter
+
+ def print_parameter(self, output):
+ """
+ Function calls method compute_parameter() to compute parameter and then prints parameter to output in
+ ClassBench format.
+
+ :param output: OutputPrint instance.
+ """
+ if self.trie_type == TrieType.SPT:
+ output.print('-snest')
+ output.print(str(self.root_node.threshold))
+ output.print('#')
+ output.print('-sskew')
+ output.print(self.compute_parameter())
+ output.print('#')
+
+ elif self.trie_type == TrieType.DPT:
+ output.print('-dnest')
+ output.print(str(self.root_node.threshold))
+ output.print('#')
+ output.print('-dskew')
+ output.print(self.compute_parameter())
+ output.print('#')
+
+ @staticmethod
+ def get_branching_probability(count_1child, count_2children):
+ """
+ Function returns probability of node having 1 child or 2 children on some level of prefix trie.
+
+ :param count_1child: Count of nodes with 1 child on level.
+
+ :param count_2children: Count of nodes with 2 children on level.
+
+ :return: Probability of node having 1 child or 2 children on level.
+ """
+ sum = count_1child + count_2children
+ probability_1child = Format.decimal(count_1child / sum)
+ probability_2children = Format.decimal(count_2children / sum)
+ probability = str(probability_1child) + '\t' + str(probability_2children)
+ return probability
+
+ @staticmethod
+ def get_average_skews(skew_list):
+ """
+ Function returns average of skews in list given by parameter.
+
+ :param skew_list: List with skew values.
+
+ :return: Average of skew values in list.
+ """
+ # skew list is empty
+ if len(skew_list) == 0:
+ return '0.00000000'
+
+ count_skews = 0
+ sum_skews = 0
+
+ for skew in skew_list:
+ sum_skews += skew
+ count_skews += 1
+
+ skew_average = str(Format.decimal(sum_skews / count_skews))
+ return skew_average
diff --git a/lib/tuples_analyzer/calculation_parameters/trie_node.py b/lib/tuples_analyzer/calculation_parameters/trie_node.py
new file mode 100644
index 0000000..bcc042e
--- /dev/null
+++ b/lib/tuples_analyzer/calculation_parameters/trie_node.py
@@ -0,0 +1,183 @@
+#!/usr/bin/env python
+"""Module containing definition of class TrieNode."""
+
+
+class TrieNode:
+ """
+ Class is representing node of trie (binary prefix tree).
+ """
+
+ def __init__(self, bit):
+ """
+ Constructor initialize node instance variable bit with value of parameter bit.
+ Other instance variable are initialized as empty list, zero or None.
+
+ :param bit: Bit value.
+ """
+ self.parent = None
+ """Parent of node. Only root node does not have parent."""
+ self.bit = bit
+ """Last bit of prefix. For trie nodes can be bit value '0' or '1'. Bit of root node is '*'."""
+ self.prefix = False
+ """Instance variable prefix is boolean type and is telling, if node is prefix or only node on path to prefix."""
+ self.level = 0
+ """Prefix level of node in trie."""
+ self.children = []
+ """List holding instances of children nodes. Trie node can have max. 2 children."""
+ self.prefixes = [0, 0]
+ """List with prefix counts under children nodes."""
+ self.threshold = 0
+ """Prefix nesting threshold."""
+ self.max_level = 0
+ """Highest prefix level of node in trie. Only root node uses this variable while printing distributions."""
+
+ def get_prefix(self, prefix):
+ """
+ Function returns instance of child node in trie represented by prefix string given by parameter.
+
+ :param prefix: String consisting of numbers 0 and 1.
+
+ :return: If node was found return instance of that node, otherwise return None.
+ """
+ # trying to find node in trie, which is representing wanted string of prefix
+ node = self
+ for bit in prefix:
+ bit_not_found = True
+
+ for child in node.children:
+ if child.bit == bit:
+ bit_not_found = False
+ node = child
+ break
+
+ if bit_not_found:
+ return None
+
+ # checking if found node is prefix or only path to some different prefix
+ if node.prefix:
+ return node
+ else:
+ return None
+
+ def add_prefix(self, prefix):
+ """
+ Function adds new prefix to trie.
+ Process of adding new prefix always starts from root node.
+
+ :param prefix: String consisting of numbers 0 and 1.
+ """
+ already_in_trie = self.get_prefix(prefix)
+
+ # if prefix is already in trie, function only needs recalculate prefix counts of parent nodes
+ if already_in_trie:
+ TrieNode.recalculate_prefix_counts(already_in_trie)
+
+ else:
+ node = self
+
+ # search for bit in children of present node, or add a new node to trie if not found
+ for bit in prefix:
+ found_in_child = False
+
+ for child in node.children:
+ if child.bit == bit:
+ node = child
+ found_in_child = True
+ break
+
+ if not found_in_child:
+ new_node = TrieNode(bit)
+ new_node.level = node.level + 1
+ new_node.parent = node
+ node.children.append(new_node)
+ node = new_node
+
+ # mark new node as prefix
+ node.prefix = True
+
+ # update max level of root node
+ if node.level > self.max_level:
+ self.max_level = node.level
+
+ # recalculate prefix counts of parent nodes
+ TrieNode.recalculate_prefix_counts(node)
+
+ # recalculate prefix nesting thresholds of current and parent nodes
+ TrieNode.recalculate_prefix_thresholds(node)
+
+ def count_skew(self):
+ """
+ Function computes skew of current node. It is called only on nodes, which have 2 children.
+
+ :return: Computed skew.
+ """
+ if self.prefixes[0] > self.prefixes[1]:
+ bigger = self.prefixes[0]
+ smaller = self.prefixes[1]
+ else:
+ bigger = self.prefixes[1]
+ smaller = self.prefixes[0]
+
+ skew = 1 - smaller / bigger
+ return skew
+
+ @staticmethod
+ def recalculate_prefix_counts(node):
+ """
+ After adding node to trie, recalculation of prefix counts of parent nodes has to be done.
+
+ :param node: Instance of newly added node to trie.
+ """
+ child = node
+ parent = child.parent
+
+ while parent:
+ if child.bit == '0':
+ parent.prefixes[0] += 1
+ elif child.bit == '1':
+ parent.prefixes[1] += 1
+
+ child = parent
+ parent = child.parent
+
+ @staticmethod
+ def recalculate_prefix_thresholds(node):
+ """
+ After adding new node to trie, calculation of prefix nesting threshold of current node and recalculation of
+ parent thresholds has to be done.
+
+ :param node: Instance of newly added node to trie.
+ """
+ # finding bigger of children thresholds
+ bigger_child_threshold = 0
+
+ for child in node.children:
+ child_threshold = 0
+
+ if child.prefix:
+ child_threshold += 1
+
+ child_threshold += child.threshold
+
+ if child_threshold > bigger_child_threshold:
+ bigger_child_threshold = child_threshold
+
+ # setting threshold of added node
+ node.threshold = bigger_child_threshold
+
+ # recalculate prefix nesting thresholds of parent nodes
+ child = node
+ parent = child.parent
+ threshold = bigger_child_threshold + 1
+
+ while parent:
+ if threshold > parent.threshold:
+ parent.threshold = threshold
+ else:
+ break
+
+ if parent.prefix:
+ threshold += 1
+
+ child = parent
+ parent = child.parent
diff --git a/lib/tuples_analyzer/examples/formats/custom1.format b/lib/tuples_analyzer/examples/formats/custom1.format
new file mode 100644
index 0000000..0afa4aa
--- /dev/null
+++ b/lib/tuples_analyzer/examples/formats/custom1.format
@@ -0,0 +1 @@
+PROTOCOL from SRC_IP SRC_PORT to DST_IP DST_PORT
diff --git a/lib/tuples_analyzer/examples/formats/custom2.format b/lib/tuples_analyzer/examples/formats/custom2.format
new file mode 100644
index 0000000..6259e4d
--- /dev/null
+++ b/lib/tuples_analyzer/examples/formats/custom2.format
@@ -0,0 +1 @@
+PROTOCOL destination DST_IP ip-port? DST_PORT? source SRC_IP ip-port? SRC_PORT?
diff --git a/lib/tuples_analyzer/examples/formats/custom_with_ipv6.format b/lib/tuples_analyzer/examples/formats/custom_with_ipv6.format
new file mode 100644
index 0000000..33aa4d3
--- /dev/null
+++ b/lib/tuples_analyzer/examples/formats/custom_with_ipv6.format
@@ -0,0 +1 @@
+rule NUMBER WILDCARD PROTOCOL source? SRC_IP? destination? DST_IP? destination-port DST_PORT?
diff --git a/lib/tuples_analyzer/examples/formats/ipfw.format b/lib/tuples_analyzer/examples/formats/ipfw.format
new file mode 100644
index 0000000..86004fa
--- /dev/null
+++ b/lib/tuples_analyzer/examples/formats/ipfw.format
@@ -0,0 +1 @@
+ipfw add WILDCARD PROTOCOL? from SRC_IP? SRC_PORT? to DST_IP? DST_PORT?
\ No newline at end of file
diff --git a/lib/tuples_analyzer/examples/formats/iptables.format b/lib/tuples_analyzer/examples/formats/iptables.format
new file mode 100644
index 0000000..6d1933f
--- /dev/null
+++ b/lib/tuples_analyzer/examples/formats/iptables.format
@@ -0,0 +1 @@
+iptables WILDCARD INPUT -s? SRC_IP? -d? DST_IP? -p? PROTOCOL? --sport? SRC_PORT? --dport? DST_PORT? -j WILDCARD
\ No newline at end of file
diff --git a/lib/tuples_analyzer/examples/formats/openflow.format b/lib/tuples_analyzer/examples/formats/openflow.format
new file mode 100644
index 0000000..0daaf50
--- /dev/null
+++ b/lib/tuples_analyzer/examples/formats/openflow.format
@@ -0,0 +1,2 @@
+nw_proto=PROTOCOL, nw_src?=SRC_IP?, nw_dst?=DST_IP?, tp_src?=SRC_PORT?, tp_dst?=DST_PORT?
+
diff --git a/lib/tuples_analyzer/examples/sets/custom1.set b/lib/tuples_analyzer/examples/sets/custom1.set
new file mode 100644
index 0000000..568440e
--- /dev/null
+++ b/lib/tuples_analyzer/examples/sets/custom1.set
@@ -0,0 +1,58 @@
+any from 147.229.35.0/24 any to 147.229.37.9 any
+tcp from any any to 147.229.37.9 443
+tcp from 147.229.32.0/20 any to 147.229.37.9/32 80
+tcp from 147.229.82.0/23 any to 147.229.37.9/32 80
+tcp from 147.229.84.0/22 any to 147.229.37.9/32 80
+tcp from 147.229.128.0/20 any to 147.229.37.9/32 80
+tcp from any any to 147.229.37.9/32 any
+any from 147.229.35.0/24 any to 147.229.87.12/30 any
+any from 147.229.37.0/24 any to 147.229.87.12/30 any
+any from 147.229.87.0/24 any to 147.229.87.12/30 any
+any from 147.229.128.248/29 any to 147.229.87.12/30 any
+any from any any to 147.229.87.12/30 any
+any from 147.229.35.0/24 any to 147.229.128.0/24 any
+any from 147.229.37.0/24 any to 147.229.128.0/24 any
+any from 147.229.128.0/24 any to 147.229.128.0/24 any
+any from 147.229.87.12/30 any to 147.229.128.248/29 any
+any from 147.229.131.230/32 any to 147.229.128.64/28 any
+any from 147.229.0.0/16 any to 147.229.128.79/32 any
+any from 147.229.153.0/24 any to 147.229.128.128/28 any
+any from 147.229.0.0/16 any to 147.229.128.248/29 any
+any from any any to 147.229.128.0/24 any
+any from 147.229.35.0/24 any to 147.229.131.240/28 any
+any from 147.229.37.0/24 any to 147.229.131.240/28 any
+any from 147.229.185.0/24 any to 147.229.131.240/28 any
+tcp from 147.229.131.240/28 any to any any
+tcp from any any to 147.229.131.240/28 any
+any from 147.229.34.0/23 any to 147.229.34.0/24 any
+any from 147.229.36.0/23 any to 147.229.34.0/24 any
+any from any any to 147.229.34.0/28 any
+any from any any to 147.229.34.152/29 any
+tcp from any any to 147.229.34.0/24 any
+tcp from any any to 147.229.37.10/31 25
+tcp from any any to 147.229.37.12/31 25
+tcp from 147.229.32.0/20 any to any 25
+tcp from 147.229.82.0/23 any to any 25
+tcp from 147.229.84.0/22 any to any 25
+tcp from 147.229.128.0/20 any to any 25
+tcp from any any to 147.229.32.0/20 25
+tcp from any any to 147.229.82.0/23 25
+tcp from any any to 147.229.84.0/22 25
+tcp from any any to 147.229.128.0/20 25
+tcp from 147.229.0.0/16 any to 147.229.37.128/25 80
+tcp from any any to 147.229.37.128/25 80
+tcp from any any to 147.229.35.90/32 any
+any from 147.229.32.0/20 any to 147.229.35.90/32 any
+any from 147.229.2.218/31 any to 147.229.131.240/28 any
+any from 147.229.131.243/32 any to 147.229.2.218/31 any
+any from 147.229.2.218/31 any to 147.229.131.243/32 any
+any from 147.229.131.243 any to any any
+any from 147.229.131.230 any to 147.229.128.80/28 any
+tcp from 147.229.137.176 any to 147.229.131.244/30 80
+tcp from 62.84.154.90/32 any to 147.229.131.244/30 80
+any from 147.229.43.128/28 any to 147.229.43.0/24 any
+any from 147.229.43.144/30 any to 147.229.43.0/24 any
+any from 147.229.43.128/28 any to 147.229.37.0/25 any
+any from 147.229.43.144/30 any to 147.229.37.0/25 any
+any from 147.229.43.128/28 any to any any
+any from 147.229.43.144/30 any to any any
diff --git a/lib/tuples_analyzer/examples/sets/custom2.set b/lib/tuples_analyzer/examples/sets/custom2.set
new file mode 100644
index 0000000..2f10acc
--- /dev/null
+++ b/lib/tuples_analyzer/examples/sets/custom2.set
@@ -0,0 +1,58 @@
+ip destination 147.229.37.9/32 source 147.229.35.0/24 permit ports any precedence 101
+tcp destination 147.229.37.9/32 ip-port 443 source any ip-port any permit ports any precedence 111
+tcp destination 147.229.37.9/32 ip-port 80 source 147.229.32.0/20 ip-port any permit ports any precedence 121
+tcp destination 147.229.37.9/32 ip-port 80 source 147.229.82.0/23 ip-port any permit ports any precedence 122
+tcp destination 147.229.37.9/32 ip-port 80 source 147.229.84.0/22 ip-port any permit ports any precedence 123
+tcp destination 147.229.37.9/32 ip-port 80 source 147.229.128.0/20 ip-port any permit ports any precedence 124
+tcp destination 147.229.37.9/32 ip-port any source any ip-port any permit-established ports any precedence 141
+ip destination 147.229.87.12/30 source 147.229.35.0/24 permit ports any precedence 201
+ip destination 147.229.87.12/30 source 147.229.37.0/24 permit ports any precedence 202
+ip destination 147.229.87.12/30 source 147.229.87.0/24 permit ports any precedence 203
+ip destination 147.229.87.12/30 source 147.229.128.248/29 permit ports any precedence 204
+ip destination 147.229.87.12/30 source any deny ports any precedence 241
+ip destination 147.229.128.0/24 source 147.229.35.0/24 permit ports any precedence 301
+ip destination 147.229.128.0/24 source 147.229.37.0/24 permit ports any precedence 302
+ip destination 147.229.128.0/24 source 147.229.128.0/24 permit ports any precedence 303
+ip destination 147.229.128.248/29 source 147.229.87.12/30 permit ports any precedence 311
+ip destination 147.229.128.64/28 source 147.229.131.230/32 permit ports any precedence 321
+ip destination 147.229.128.79/32 source 147.229.0.0/16 permit ports any precedence 322
+ip destination 147.229.128.128/28 source 147.229.153.0/24 permit ports any precedence 331
+ip destination 147.229.128.248/29 source 147.229.0.0/16 permit ports any precedence 341
+ip destination 147.229.128.0/24 source any deny ports any precedence 361
+ip destination 147.229.131.240/28 source 147.229.35.0/24 permit ports any precedence 401
+ip destination 147.229.131.240/28 source 147.229.37.0/24 permit ports any precedence 402
+ip destination 147.229.131.240/28 source 147.229.185.0/24 permit ports any precedence 403
+tcp destination any ip-port any source 147.229.131.240/28 ip-port any permit ports any precedence 411
+tcp destination 147.229.131.240/28 ip-port any source any ip-port any permit-established ports any precedence 441
+ip destination 147.229.34.0/24 source 147.229.34.0/23 permit ports any precedence 501
+ip destination 147.229.34.0/24 source 147.229.36.0/23 permit ports any precedence 502
+ip destination 147.229.34.0/28 source any permit ports any precedence 511
+ip destination 147.229.34.152/29 source any permit ports any precedence 512
+tcp destination 147.229.34.0/24 ip-port any source any ip-port any permit-established ports any precedence 541
+tcp destination 147.229.37.10/31 ip-port 25 source any ip-port any permit ports any precedence 601
+tcp destination 147.229.37.12/31 ip-port 25 source any ip-port any permit ports any precedence 602
+tcp destination any ip-port 25 source 147.229.32.0/20 ip-port any permit ports any precedence 621
+tcp destination any ip-port 25 source 147.229.82.0/23 ip-port any permit ports any precedence 622
+tcp destination any ip-port 25 source 147.229.84.0/22 ip-port any permit ports any precedence 623
+tcp destination any ip-port 25 source 147.229.128.0/20 ip-port any permit ports any precedence 624
+tcp destination 147.229.32.0/20 ip-port 25 source any ip-port any deny ports any precedence 641
+tcp destination 147.229.82.0/23 ip-port 25 source any ip-port any deny ports any precedence 642
+tcp destination 147.229.84.0/22 ip-port 25 source any ip-port any deny ports any precedence 643
+tcp destination 147.229.128.0/20 ip-port 25 source any ip-port any deny ports any precedence 644
+tcp destination 147.229.37.128/25 ip-port 80 source 147.229.0.0/16 ip-port any permit ports any precedence 191
+tcp destination 147.229.37.128/25 ip-port 80 source any ip-port any deny ports any precedence 192
+tcp destination 147.229.35.90/32 ip-port any source any ip-port any permit-established ports any precedence 709
+ip destination 147.229.35.90/32 source 147.229.32.0/20 permit ports any precedence 701
+ip destination 147.229.131.240/28 source 147.229.2.218/31 permit ports any precedence 404
+ip destination 147.229.2.218/31 source 147.229.131.243/32 permit ports any precedence 390
+ip destination 147.229.131.243/32 source 147.229.2.218/31 permit ports any precedence 391
+ip destination any source 147.229.131.243/32 permit ports any precedence 393
+ip destination 147.229.128.80/28 source 147.229.131.230/32 permit ports any precedence 323
+tcp destination 147.229.131.244/30 ip-port 80 source 147.229.137.176/32 ip-port any permit ports any precedence 421
+tcp destination 147.229.131.244/30 ip-port 80 source 62.84.154.90/32 ip-port any permit ports any precedence 422
+ip destination 147.229.43.0/24 source 147.229.43.128/28 permit ports any precedence 551
+ip destination 147.229.43.0/24 source 147.229.43.144/30 permit ports any precedence 552
+ip destination 147.229.37.0/25 source 147.229.43.128/28 permit ports any precedence 553
+ip destination 147.229.37.0/25 source 147.229.43.144/30 permit ports any precedence 554
+ip destination any source 147.229.43.128/28 deny ports any precedence 561
+ip destination any source 147.229.43.144/30 deny ports any precedence 562
diff --git a/lib/tuples_analyzer/examples/sets/custom_with_ipv6.set b/lib/tuples_analyzer/examples/sets/custom_with_ipv6.set
new file mode 100644
index 0000000..cddcdaa
--- /dev/null
+++ b/lib/tuples_analyzer/examples/sets/custom_with_ipv6.set
@@ -0,0 +1,6 @@
+rule 1 permit tcp source 147.240.1.0/24 destination-port 1
+rule 2 permit tcp destination 147.10.10.1/32 destination-port smtp
+rule 3 permit udp source fd6d:fa5:24bc:a286::/64 destination-port 2:10
+rule 4 deny udp destination 2001:ABC:1230::/48 destination-port 11
+rule 5 permit tcp destination 2001:ABC:1230::1234:123/128 destination-port 11
+rule 6 permit tcp source 2001:67B:1220:F000::/52 destination 2001:FED:1230::/48 destination-port 12:300
diff --git a/lib/tuples_analyzer/examples/sets/ipfw.set b/lib/tuples_analyzer/examples/sets/ipfw.set
new file mode 100644
index 0000000..6ca835d
--- /dev/null
+++ b/lib/tuples_analyzer/examples/sets/ipfw.set
@@ -0,0 +1,4 @@
+ipfw add allow tcp from to 10.0.0.0/24 80
+ipfw add accept udp from to 192.0.0.14
+ipfw add deny any from to 5000:5100
+ipfw add drop from 443 to
\ No newline at end of file
diff --git a/lib/tuples_analyzer/examples/sets/iptables.set b/lib/tuples_analyzer/examples/sets/iptables.set
new file mode 100644
index 0000000..efce55c
--- /dev/null
+++ b/lib/tuples_analyzer/examples/sets/iptables.set
@@ -0,0 +1,5 @@
+iptables -A INPUT -s 10.0.0.0/24 -p tcp --dport 0:1023 -j ACCEPT
+iptables -A INPUT -s 128.0.0.0/24 -p tcp --sport all -j ACCEPT
+iptables -I INPUT -s 192.0.0.1 -p udp --dport 80 -j DROP
+iptables -I INPUT -s 192.0.0.0/3 -p udp --dport 1024:65535 -j DROP
+iptables -I INPUT -s 192.0.0.0/3 -p udp --dport 5000:5100 -j ACCEPT
\ No newline at end of file
diff --git a/lib/tuples_analyzer/examples/sets/openflow.set b/lib/tuples_analyzer/examples/sets/openflow.set
new file mode 100644
index 0000000..c214288
--- /dev/null
+++ b/lib/tuples_analyzer/examples/sets/openflow.set
@@ -0,0 +1,58 @@
+nw_proto=0 ,nw_src=147.229.35.0/24 ,nw_dst=147.229.37.9/32
+nw_proto=6 ,nw_dst=147.229.37.9/32 ,tp_dst=443
+nw_proto=6 ,nw_src=147.229.32.0/20 ,nw_dst=147.229.37.9/32 ,tp_dst=80
+nw_proto=6 ,nw_src=147.229.82.0/23 ,nw_dst=147.229.37.9/32 ,tp_dst=80
+nw_proto=6 ,nw_src=147.229.84.0/22 ,nw_dst=147.229.37.9/32 ,tp_dst=80
+nw_proto=6 ,nw_src=147.229.128.0/20 ,nw_dst=147.229.37.9/32 ,tp_dst=80
+nw_proto=6 ,nw_dst=147.229.37.9/32
+nw_proto=0 ,nw_src=147.229.35.0/24 ,nw_dst=147.229.87.12/30
+nw_proto=0 ,nw_src=147.229.37.0/24 ,nw_dst=147.229.87.12/30
+nw_proto=0 ,nw_src=147.229.87.0/24 ,nw_dst=147.229.87.12/30
+nw_proto=0 ,nw_src=147.229.128.248/29 ,nw_dst=147.229.87.12/30
+nw_proto=0 ,nw_dst=147.229.87.12/30
+nw_proto=0 ,nw_src=147.229.35.0/24 ,nw_dst=147.229.128.0/24
+nw_proto=0 ,nw_src=147.229.37.0/24 ,nw_dst=147.229.128.0/24
+nw_proto=0 ,nw_src=147.229.128.0/24 ,nw_dst=147.229.128.0/24
+nw_proto=0 ,nw_src=147.229.87.12/30 ,nw_dst=147.229.128.248/29
+nw_proto=0 ,nw_src=147.229.131.230/32 ,nw_dst=147.229.128.64/28
+nw_proto=0 ,nw_src=147.229.0.0/16 ,nw_dst=147.229.128.79/32
+nw_proto=0 ,nw_src=147.229.153.0/24 ,nw_dst=147.229.128.128/28
+nw_proto=0 ,nw_src=147.229.0.0/16 ,nw_dst=147.229.128.248/29
+nw_proto=0 ,nw_dst=147.229.128.0/24
+nw_proto=0 ,nw_src=147.229.35.0/24 ,nw_dst=147.229.131.240/28
+nw_proto=0 ,nw_src=147.229.37.0/24 ,nw_dst=147.229.131.240/28
+nw_proto=0 ,nw_src=147.229.185.0/24 ,nw_dst=147.229.131.240/28
+nw_proto=6 ,nw_src=147.229.131.240/28
+nw_proto=6 ,nw_dst=147.229.131.240/28
+nw_proto=0 ,nw_src=147.229.34.0/23 ,nw_dst=147.229.34.0/24
+nw_proto=0 ,nw_src=147.229.36.0/23 ,nw_dst=147.229.34.0/24
+nw_proto=0 ,nw_dst=147.229.34.0/28
+nw_proto=0 ,nw_dst=147.229.34.152/29
+nw_proto=6 ,nw_dst=147.229.34.0/24
+nw_proto=6 ,nw_dst=147.229.37.10/31 ,tp_dst=25
+nw_proto=6 ,nw_dst=147.229.37.12/31 ,tp_dst=25
+nw_proto=6 ,nw_src=147.229.32.0/20 ,tp_dst=25
+nw_proto=6 ,nw_src=147.229.82.0/23 ,tp_dst=25
+nw_proto=6 ,nw_src=147.229.84.0/22 ,tp_dst=25
+nw_proto=6 ,nw_src=147.229.128.0/20 ,tp_dst=25
+nw_proto=6 ,nw_dst=147.229.32.0/20 ,tp_dst=25
+nw_proto=6 ,nw_dst=147.229.82.0/23 ,tp_dst=25
+nw_proto=6 ,nw_dst=147.229.84.0/22 ,tp_dst=25
+nw_proto=6 ,nw_dst=147.229.128.0/20 ,tp_dst=25
+nw_proto=6 ,nw_src=147.229.0.0/16 ,nw_dst=147.229.37.128/25 ,tp_dst=80
+nw_proto=6 ,nw_dst=147.229.37.128/25 ,tp_dst=80
+nw_proto=6 ,nw_dst=147.229.35.90/32
+nw_proto=0 ,nw_src=147.229.32.0/20 ,nw_dst=147.229.35.90/32
+nw_proto=0 ,nw_src=147.229.2.218/31 ,nw_dst=147.229.131.240/28
+nw_proto=0 ,nw_src=147.229.131.243/32 ,nw_dst=147.229.2.218/31
+nw_proto=0 ,nw_src=147.229.2.218/31 ,nw_dst=147.229.131.243/32
+nw_proto=0 ,nw_src=147.229.131.243/32
+nw_proto=0 ,nw_src=147.229.131.230/32 ,nw_dst=147.229.128.80/28
+nw_proto=6 ,nw_src=147.229.137.176/32 ,nw_dst=147.229.131.244/30 ,tp_dst=80
+nw_proto=6 ,nw_src=62.84.154.90/32 ,nw_dst=147.229.131.244/30 ,tp_dst=80
+nw_proto=0 ,nw_src=147.229.43.128/28 ,nw_dst=147.229.43.0/24
+nw_proto=0 ,nw_src=147.229.43.144/30 ,nw_dst=147.229.43.0/24
+nw_proto=0 ,nw_src=147.229.43.128/28 ,nw_dst=147.229.37.0/25
+nw_proto=0 ,nw_src=147.229.43.144/30 ,nw_dst=147.229.37.0/25
+nw_proto=0 ,nw_src=147.229.43.128/28
+nw_proto=0 ,nw_src=147.229.43.144/30
diff --git a/lib/tuples_analyzer/printing_output/__init__.py b/lib/tuples_analyzer/printing_output/__init__.py
new file mode 100644
index 0000000..3e1765d
--- /dev/null
+++ b/lib/tuples_analyzer/printing_output/__init__.py
@@ -0,0 +1 @@
+"""Package with classes for printing output data to different directions."""
diff --git a/lib/tuples_analyzer/printing_output/enums/__init__.py b/lib/tuples_analyzer/printing_output/enums/__init__.py
new file mode 100644
index 0000000..7462f7d
--- /dev/null
+++ b/lib/tuples_analyzer/printing_output/enums/__init__.py
@@ -0,0 +1 @@
+"""Package with enum classes used while printing output data."""
diff --git a/lib/tuples_analyzer/printing_output/enums/output_direction.py b/lib/tuples_analyzer/printing_output/enums/output_direction.py
new file mode 100644
index 0000000..a52955c
--- /dev/null
+++ b/lib/tuples_analyzer/printing_output/enums/output_direction.py
@@ -0,0 +1,15 @@
+#!/usr/bin/env python
+"""Module containing definition of enum class OutputDirection."""
+
+from enum import Enum
+
+
+class OutputDirection(Enum):
+ """
+ Enum for every possible output direction of program.
+ """
+
+ STDOUT = 0
+ """Standard output."""
+ FILE = 1
+ """Output to file."""
diff --git a/lib/tuples_analyzer/printing_output/output_print.py b/lib/tuples_analyzer/printing_output/output_print.py
new file mode 100644
index 0000000..bee605b
--- /dev/null
+++ b/lib/tuples_analyzer/printing_output/output_print.py
@@ -0,0 +1,69 @@
+#!/usr/bin/env python
+"""Module containing definition of class OutputDirection."""
+
+import os
+from .enums.output_direction import OutputDirection
+from ..processing_errors.enums.Error import Error
+from ..processing_errors.error_process import ErrorProcess
+
+
+class OutputPrint:
+ """
+ Class used to print computed parameters in same way to different directions (STDOUT or to file).
+ """
+
+ def __init__(self, output_file_path):
+ """
+ Constructor is calling function decide_direction() to initialize instance variables.
+
+ :param output_file_path: Path to file, where user wants to save computed parameters.
+ """
+ self.output_direction = None
+ """Printing output direction."""
+ self.output_file_path = None
+ """Path to file, where user wants to save computed parameters."""
+ self.decide_direction(output_file_path)
+
+ def decide_direction(self, output_file_path):
+ """
+ Function decides, where will be printed output.
+ If parameter output_file_path is None, output will be printed on STDOUT,
+ otherwise output will be printed to file specified by parameter.
+
+ :param output_file_path: Path to file, where user wants to save computed parameters.
+
+ :return: If file where user wants to save computed parameters already exists or is not at least empty,
+ then program prints error message and exits with error code 40.
+ """
+ if output_file_path is None:
+ self.output_direction = OutputDirection.STDOUT
+
+ else:
+ if not os.path.isfile(output_file_path) or os.stat(output_file_path).st_size == 0:
+ try:
+ with open(output_file_path, 'a+'):
+ pass
+
+ except EnvironmentError:
+ ErrorProcess.process_error(Error.CREATING_FILE_ERROR,
+ f'Wrong path to file where you want to save computed parameters: '
+ f'\'{output_file_path}\'.')
+ self.output_direction = OutputDirection.FILE
+ self.output_file_path = output_file_path
+ else:
+ ErrorProcess.process_error(Error.CREATING_FILE_ERROR,
+ f'File where you want to save computed parameters: '
+ f'\'{output_file_path}\' '
+ f'already exists.')
+
+ def print(self, message):
+ """
+ Function prints message to output direction specified in instance variable output_direction.
+
+ :param message: String message.
+ """
+ if self.output_direction == OutputDirection.STDOUT:
+ print(message)
+ else:
+ with open(self.output_file_path, 'a+') as the_file:
+ the_file.write(str(message) + '\n')
diff --git a/lib/tuples_analyzer/processing_arguments/__init__.py b/lib/tuples_analyzer/processing_arguments/__init__.py
new file mode 100644
index 0000000..14e8641
--- /dev/null
+++ b/lib/tuples_analyzer/processing_arguments/__init__.py
@@ -0,0 +1 @@
+"""Package with classes for processing arguments from command line."""
diff --git a/lib/tuples_analyzer/processing_arguments/processed_arguments.py b/lib/tuples_analyzer/processing_arguments/processed_arguments.py
new file mode 100644
index 0000000..0d6972c
--- /dev/null
+++ b/lib/tuples_analyzer/processing_arguments/processed_arguments.py
@@ -0,0 +1,97 @@
+#!/usr/bin/env python
+"""Module containing definition of class ProcessedArguments."""
+
+import sys
+import getopt
+from ..processing_errors.enums.Error import Error
+from ..processing_errors.error_process import ErrorProcess
+
+
+class ProcessedArguments:
+ """
+ Class for processing and storing command line arguments from user.
+ """
+
+ def __init__(self):
+ """
+ Constructor is calling function process_arguments() to process arguments and
+ to initialize instance variables with argument values.
+ """
+ self.rules_path = None
+ """Path to file with filter rules."""
+ self.format_path = None
+ """Path to file with format of filter rules."""
+ self.output_path = None
+ """Path to file, where user wants to save computed parameters."""
+ self.is_stderr_on = False
+ """Variable telling, if printing error logs while parsing filter rules is enabled."""
+ self.process_arguments()
+
+ def process_arguments(self):
+ """
+ Function processing command line arguments of program.
+
+ :return: If it processed wrong or no arguments, it prints error message and exits with error code 10.
+
+ If it processed argument -h, it calls function usage() and exit.
+
+ If it processed both mandatory arguments: -r and -f with specified path to file with rules and
+ path to file with format of rules, then it initializes instance variables.
+ """
+ argv = sys.argv[1:]
+
+ rules_path = None
+ format_path = None
+ output_path = None
+ is_stderr_on = False
+
+ try:
+ opts, args = getopt.getopt(argv, "r:f:o:lh", ["rules=", "format=", "output=", "logs", "help"])
+ except getopt.GetoptError as err:
+ ErrorProcess.process_error(Error.ARGUMENTS_ERROR, f'{err}')
+
+ if len(opts) < 1 or len(opts) > 4 or len(args) > 0:
+ ErrorProcess.process_error(Error.ARGUMENTS_ERROR, f'Wrong or no arguments selected.')
+
+ for o, a in opts:
+ if o in ("-h", "--help"):
+ ProcessedArguments.print_manual()
+ sys.exit()
+ elif o in ("-r", "--rules"):
+ rules_path = a
+ elif o in ("-f", "--format"):
+ format_path = a
+ elif o in ("-o", "--output"):
+ output_path = a
+ elif o in ("-l", "--logs"):
+ is_stderr_on = True
+
+ if rules_path is None or format_path is None:
+ ErrorProcess.process_error(Error.ARGUMENTS_ERROR, f'Missing argument -r or -f.')
+ else:
+ self.rules_path = rules_path
+ self.format_path = format_path
+ self.output_path = output_path
+ self.is_stderr_on = is_stderr_on
+
+ @staticmethod
+ def print_manual():
+ """
+ Function prints manual of program.
+ """
+ print('1.PROGRAM:\n\ttuples_analyzer\n\n'
+ '2.FUNCTION:\n\tProgram creates parameter file from statistics and distributions of real filter set.\n\t'
+ 'File with format of rules is needed for processing filter rules.\n\t'
+ 'Computed parameters are then used to generate synthetic filter set by tools ClassBench and '
+ 'ClassBench-ng. \n\tFormat of rules and examples usages of program are described in README file.\n\n'
+ '3.USAGE:\n\tpython3 -m tuples_analyzer -r -f [-o -l -h]'
+ '\n\n'
+ '4.MANDATORY ARGUMENTS:\n'
+ '\t-r rules_file | --rules=rules_file\t\tspecify path to file with filter rules\n'
+ '\t-f format_file | --format=format_file\t\tspecify path to file with format of rules\n\n'
+ '5.OPTIONAL ARGUMENTS:\n'
+ '\t-o output_file | --output=output_file\t\tspecify path to file, which will be created\n'
+ '\t\t\t\t\t\t\tto store computed parameters\n\n'
+ '\t-l | --logs\t\t\t\t\tprinting error logs during computation is enabled\n'
+ '\t-h | --help\t\t\t\t\tdisplay this manual\n'
+ '\n6.AUTHOR:\n\tJozef Sabo')
diff --git a/lib/tuples_analyzer/processing_errors/__init__.py b/lib/tuples_analyzer/processing_errors/__init__.py
new file mode 100644
index 0000000..d1b98d4
--- /dev/null
+++ b/lib/tuples_analyzer/processing_errors/__init__.py
@@ -0,0 +1 @@
+"""Package with classes to correctly end program while error occurred and for printing error logs to stderr."""
diff --git a/lib/tuples_analyzer/processing_errors/enums/Error.py b/lib/tuples_analyzer/processing_errors/enums/Error.py
new file mode 100644
index 0000000..6610337
--- /dev/null
+++ b/lib/tuples_analyzer/processing_errors/enums/Error.py
@@ -0,0 +1,21 @@
+#!/usr/bin/env python
+"""Module containing definition of enum class Error."""
+
+from enum import Enum
+
+
+class Error(Enum):
+ """
+ Enum for every possible error, which can occur in program.
+ """
+
+ ARGUMENTS_ERROR = 10
+ """Error which occurred while processing command line arguments."""
+ FILE_OPENING_ERROR = 20
+ """Error which occurred while opening file."""
+ RULE_FORMAT_ERROR = 30
+ """Error which occurred while processing file with format of rules."""
+ CREATING_FILE_ERROR = 40
+ """Error which occurred while creating file for storing output parameters."""
+ NO_VALUE_FILTER_SET_ERROR = 50
+ """Error telling that file with filter rules has no valuable content in it."""
diff --git a/lib/tuples_analyzer/processing_errors/enums/__init__.py b/lib/tuples_analyzer/processing_errors/enums/__init__.py
new file mode 100644
index 0000000..cf8e378
--- /dev/null
+++ b/lib/tuples_analyzer/processing_errors/enums/__init__.py
@@ -0,0 +1 @@
+"""Package with enum classes used while processing errors."""
diff --git a/lib/tuples_analyzer/processing_errors/error_process.py b/lib/tuples_analyzer/processing_errors/error_process.py
new file mode 100644
index 0000000..bc654bf
--- /dev/null
+++ b/lib/tuples_analyzer/processing_errors/error_process.py
@@ -0,0 +1,47 @@
+#!/usr/bin/env python
+"""Module containing definition of class ErrorProcess."""
+
+import sys
+from .enums.Error import Error
+
+
+class ErrorProcess:
+ """
+ Class used for correctly ending program while different type of error occurred.
+ Class also used for printing error logs to stderr.
+ """
+
+ @staticmethod
+ def print_warning(message):
+ """
+ Function prints warning log message to stderr.
+
+ :param message: Message which will be printed to stderr.
+ """
+ sys.stderr.write(f'{sys.argv[0]}:\nWARNING: {message}\n\n')
+
+ @staticmethod
+ def process_error(error, message=''):
+ """
+ Function prints error message and exits program with value of parameter error.
+
+ :param error: Enum value representing error which occurred.
+
+ :param message: Message which will be printed to stderr.
+ """
+ if error == Error.ARGUMENTS_ERROR:
+ sys.stderr.write(f'{sys.argv[0]}:\nERROR: {message}\nTry \'python3 -m filter_rule_analyzer --help\'.\n')
+
+ elif error == Error.FILE_OPENING_ERROR:
+ sys.stderr.write(f'{sys.argv[0]}:\nERROR: Error while opening file with path: \'{message}\'.\n')
+
+ elif error == Error.RULE_FORMAT_ERROR:
+ sys.stderr.write(f'{sys.argv[0]}:\nERROR: Error while processing file with format: \'{message}\'.\n')
+
+ elif error == Error.CREATING_FILE_ERROR:
+ sys.stderr.write(f'{sys.argv[0]}:\nERROR: {message}\n')
+
+ elif error == Error.NO_VALUE_FILTER_SET_ERROR:
+ sys.stderr.write(f'{sys.argv[0]}:\nERROR: File with filter rules is empty or has not valuable content.\n')
+
+ exit(error.value)
diff --git a/lib/tuples_analyzer/processing_files/__init__.py b/lib/tuples_analyzer/processing_files/__init__.py
new file mode 100644
index 0000000..2a703be
--- /dev/null
+++ b/lib/tuples_analyzer/processing_files/__init__.py
@@ -0,0 +1 @@
+"""Package with classes for processing data from files."""
diff --git a/lib/tuples_analyzer/processing_files/processing_file_helper.py b/lib/tuples_analyzer/processing_files/processing_file_helper.py
new file mode 100644
index 0000000..993b09b
--- /dev/null
+++ b/lib/tuples_analyzer/processing_files/processing_file_helper.py
@@ -0,0 +1,49 @@
+#!/usr/bin/env python
+"""Module containing definition of class ProcessingFileHelper."""
+
+from ..processing_errors.enums.Error import Error
+from ..processing_errors.error_process import ErrorProcess
+
+
+class ProcessingFileHelper:
+ """
+ Class contains only static functions to help with processing of files.
+ """
+
+ @staticmethod
+ def create_lines_generator(file_path):
+ """
+ Function creates generator of lines from file.
+
+ :param file_path: Path to file.
+
+ :return: Generator of lines in file.
+ If function can not open file, it prints error message and exits with error code 20.
+ """
+ try:
+ with open(file_path, 'r') as file:
+ for line in file:
+ yield line.strip()
+ except EnvironmentError:
+ ErrorProcess.process_error(Error.FILE_OPENING_ERROR, file_path)
+
+ @staticmethod
+ def get_format(file_path):
+ """
+ Function processes file with format of rules and returns format from first line of file.
+
+ :param file_path: Path to file with format of rule.
+
+ :return: First line of file.
+ If line is empty, it prints error message and exits with error code 30.
+ """
+ rule_format = ''
+
+ for line in ProcessingFileHelper.create_lines_generator(file_path):
+ rule_format = line
+ break
+
+ if not rule_format or rule_format.isspace():
+ ErrorProcess.process_error(Error.RULE_FORMAT_ERROR, file_path)
+ else:
+ return rule_format
diff --git a/lib/tuples_analyzer/processing_rules/__init__.py b/lib/tuples_analyzer/processing_rules/__init__.py
new file mode 100644
index 0000000..69d6693
--- /dev/null
+++ b/lib/tuples_analyzer/processing_rules/__init__.py
@@ -0,0 +1 @@
+"""Package with classes for processing filter rules from filter set file into generator of FilterRule instances."""
diff --git a/lib/tuples_analyzer/processing_rules/enums/__init__.py b/lib/tuples_analyzer/processing_rules/enums/__init__.py
new file mode 100644
index 0000000..8bdc644
--- /dev/null
+++ b/lib/tuples_analyzer/processing_rules/enums/__init__.py
@@ -0,0 +1 @@
+"""Package with enum classes used while processing filter rules into generator of FilterRule instances."""
diff --git a/lib/tuples_analyzer/processing_rules/enums/filter_rule_part_location.py b/lib/tuples_analyzer/processing_rules/enums/filter_rule_part_location.py
new file mode 100644
index 0000000..ad48e64
--- /dev/null
+++ b/lib/tuples_analyzer/processing_rules/enums/filter_rule_part_location.py
@@ -0,0 +1,15 @@
+#!/usr/bin/env python
+"""Module containing definition of enum class FilterRulePartLocation."""
+
+from enum import Enum
+
+
+class FilterRulePartLocation(Enum):
+ """
+ Enum for every possible 'location' of these filter rule parameters: port and ip address.
+ """
+
+ SOURCE = 0
+ """Source location of filter rule parameter."""
+ DESTINATION = 1
+ """Destination location of filter rule parameter."""
diff --git a/lib/tuples_analyzer/processing_rules/enums/filter_rule_part_processing_result.py b/lib/tuples_analyzer/processing_rules/enums/filter_rule_part_processing_result.py
new file mode 100644
index 0000000..f52ac94
--- /dev/null
+++ b/lib/tuples_analyzer/processing_rules/enums/filter_rule_part_processing_result.py
@@ -0,0 +1,19 @@
+#!/usr/bin/env python
+"""Module containing definition of enum class FilterRulePartProcessingResult."""
+
+from enum import Enum
+
+
+class FilterRulePartProcessingResult(Enum):
+ """
+ Enum for every possible result scenario while processing filter rule part into FilterRule instance.
+ """
+
+ MANDATORY_PART_PROCESSED = 0
+ """Mandatory part of filter rule was processed."""
+ OPTIONAL_PART_PROCESSED = 1
+ """Optional part of filter rule was processed."""
+ MANDATORY_PART_MISSING = 2
+ """Mandatory part of filter rule is missing."""
+ OPTIONAL_PART_MISSING = 3
+ """Optional part of filter rule is missing."""
diff --git a/lib/tuples_analyzer/processing_rules/enums/filter_rule_part_type.py b/lib/tuples_analyzer/processing_rules/enums/filter_rule_part_type.py
new file mode 100644
index 0000000..ee15c74
--- /dev/null
+++ b/lib/tuples_analyzer/processing_rules/enums/filter_rule_part_type.py
@@ -0,0 +1,33 @@
+#!/usr/bin/env python
+"""Module containing definition of enum class FilterRulePartType."""
+
+from enum import Enum
+
+
+class FilterRulePartType(Enum):
+ """
+ Enum for every possible type of filter rule part. One word of filter rule is considered as filter part.
+ """
+
+ ANY = 0
+ """Representation of wildcard value."""
+ PROTOCOL = 1
+ """Network protocol abbreviation."""
+ IPV4_ADDRESS = 2
+ """IPv4 address without mask."""
+ IPV4_ADDRESS_MASK = 3
+ """IPv4 address with mask."""
+ PORT = 4
+ """Port value."""
+ PORT_RANGE = 5
+ """Port range value."""
+ NUMBER = 6
+ """Number bigger than 65535 (not used for computation parameters e.g. precedence number)."""
+ KEYWORD = 7
+ """Word which is part of rule format definition e.g. from, to."""
+ WORD = 8
+ """Every other value."""
+ IPV6_ADDRESS = 9
+ """IPv6 address without mask."""
+ IPV6_ADDRESS_MASK = 10
+ """IPv6 address with mask."""
diff --git a/lib/tuples_analyzer/processing_rules/enums/ordered_enum.py b/lib/tuples_analyzer/processing_rules/enums/ordered_enum.py
new file mode 100644
index 0000000..c486b90
--- /dev/null
+++ b/lib/tuples_analyzer/processing_rules/enums/ordered_enum.py
@@ -0,0 +1,26 @@
+#!/usr/bin/env python
+"""Module containing definition of enum class OrderedEnum."""
+
+from enum import Enum
+
+
+class OrderedEnum(Enum):
+ """
+ Class inheriting from base enum class and making enum values comparable.
+ """
+
+ def __ge__(self, other):
+ if self.__class__ is other.__class__:
+ return self.value >= other.value
+
+ def __gt__(self, other):
+ if self.__class__ is other.__class__:
+ return self.value > other.value
+
+ def __le__(self, other):
+ if self.__class__ is other.__class__:
+ return self.value <= other.value
+
+ def __lt__(self, other):
+ if self.__class__ is other.__class__:
+ return self.value < other.value
diff --git a/lib/tuples_analyzer/processing_rules/enums/port_class.py b/lib/tuples_analyzer/processing_rules/enums/port_class.py
new file mode 100644
index 0000000..a5ff3bb
--- /dev/null
+++ b/lib/tuples_analyzer/processing_rules/enums/port_class.py
@@ -0,0 +1,21 @@
+#!/usr/bin/env python
+"""Module containing definition of enum class PC."""
+
+from enum import Enum
+
+
+class PC(Enum):
+ """
+ Enum for every possible port class (PC).
+ """
+
+ WC = 0
+ """Wildcard."""
+ LO = 1
+ """Port range 0:1023."""
+ HI = 2
+ """Port range 1024:65535."""
+ EM = 3
+ """Exact match e.g port 80."""
+ AR = 4
+ """Arbitrary range e.g. port range 50000:51000."""
diff --git a/lib/tuples_analyzer/processing_rules/enums/port_pair_class.py b/lib/tuples_analyzer/processing_rules/enums/port_pair_class.py
new file mode 100644
index 0000000..eb315c5
--- /dev/null
+++ b/lib/tuples_analyzer/processing_rules/enums/port_pair_class.py
@@ -0,0 +1,63 @@
+#!/usr/bin/env python
+"""Module containing definition of enum class PPC."""
+
+from enum import Enum
+
+
+class PPC(Enum):
+ """
+ Enum for every possible port pair class (PPC).
+ PPC is pair consisting of source and destination port classes.
+ There are 25 total PPC for every combination of source and destination port classes.
+ """
+
+ WC_WC = 0
+ """Source port class is WC and destination port class is WC."""
+ WC_HI = 1
+ """Source port class is WC and destination port class is HI."""
+ HI_WC = 2
+ """Source port class is HI and destination port class is WC."""
+ HI_HI = 3
+ """Source port class is HI and destination port class is HI."""
+ WC_LO = 4
+ """Source port class is WC and destination port class is LO."""
+ LO_WC = 5
+ """Source port class is LO and destination port class is WC."""
+ HI_LO = 6
+ """Source port class is HI and destination port class is LO."""
+ LO_HI = 7
+ """Source port class is LO and destination port class is HI."""
+ LO_LO = 8
+ """Source port class is LO and destination port class is LO."""
+ WC_AR = 9
+ """Source port class is WC and destination port class is AR."""
+ AR_WC = 10
+ """Source port class is AR and destination port class is WC."""
+ HI_AR = 11
+ """Source port class is HI and destination port class is AR."""
+ AR_HI = 12
+ """Source port class is AR and destination port class is HI."""
+ WC_EM = 13
+ """Source port class is WC and destination port class is EM."""
+ EM_WC = 14
+ """Source port class is EM and destination port class is WC."""
+ HI_EM = 15
+ """Source port class is HI and destination port class is EM."""
+ EM_HI = 16
+ """Source port class is EM and destination port class is HI."""
+ LO_AR = 17
+ """Source port class is LO and destination port class is AR."""
+ AR_LO = 18
+ """Source port class is AR and destination port class is LO."""
+ LO_EM = 19
+ """Source port class is LO and destination port class is EM."""
+ EM_LO = 20
+ """Source port class is EM and destination port class is LO."""
+ AR_AR = 21
+ """Source port class is AR and destination port class is AR."""
+ AR_EM = 22
+ """Source port class is AR and destination port class is EM."""
+ EM_AR = 23
+ """Source port class is EM and destination port class is AR."""
+ EM_EM = 24
+ """Source port class is EM and destination port class is EM."""
diff --git a/lib/tuples_analyzer/processing_rules/enums/protocol_numbers.py b/lib/tuples_analyzer/processing_rules/enums/protocol_numbers.py
new file mode 100644
index 0000000..d52a92f
--- /dev/null
+++ b/lib/tuples_analyzer/processing_rules/enums/protocol_numbers.py
@@ -0,0 +1,38 @@
+#!/usr/bin/env python
+"""Module containing definition of enum class Protocol."""
+
+from .ordered_enum import OrderedEnum
+
+
+class Protocol(OrderedEnum):
+ """
+ Enum for every protocol located in ClassBench example parameter files.
+ Enum values are given by protocol number definition by IANA.
+ """
+
+ ANY = 0
+ """Wildcard protocol."""
+ ICMP = 1
+ """Internet Control Message."""
+ IGMP = 2
+ """Internet Group Management."""
+ GGP = 3
+ """Gateway-to-Gateway."""
+ ST = 5
+ """Stream."""
+ TCP = 6
+ """Transmission Control."""
+ EGP = 8
+ """Exterior Gateway Protocol."""
+ UDP = 17
+ """User Datagram."""
+ GRE = 47
+ """Generic Routing Encapsulation."""
+ ESP = 50
+ """Encap Security Payload."""
+ AH = 51
+ """Authentication Header."""
+ EIGRP = 88
+ """EIGRP."""
+ OSPFIGP = 89
+ """OSPFIGP."""
diff --git a/lib/tuples_analyzer/processing_rules/filter_rule.py b/lib/tuples_analyzer/processing_rules/filter_rule.py
new file mode 100644
index 0000000..f4dfc9d
--- /dev/null
+++ b/lib/tuples_analyzer/processing_rules/filter_rule.py
@@ -0,0 +1,255 @@
+#!/usr/bin/env python
+"""Module containing definition of class FilterRule."""
+
+from .enums.filter_rule_part_location import FilterRulePartLocation
+from .enums.port_class import PC
+from .enums.port_pair_class import PPC
+from .enums.protocol_numbers import Protocol
+import ipaddress
+
+
+class FilterRule:
+ """
+ Class representing filter rule.
+ """
+
+ def __init__(self):
+ """
+ Constructor initialize all filter rule attributes with wildcard values.
+ """
+ self.protocol = Protocol.ANY
+ """Protocol number defined by IANA."""
+ self.src_port_class = PC.WC
+ """Source port class."""
+ self.src_port = None
+ """Source port value."""
+ self.dst_port_class = PC.WC
+ """Destination port class."""
+ self.dst_port = None
+ """Destination port value."""
+ self.src_ip_add_bin = '*'
+ """Source IP address in binary form."""
+ self.src_ip_add_prefix_length = 0
+ """Source IP address prefix length."""
+ self.dst_ip_add_bin = '*'
+ """Destination IP address in binary form."""
+ self.dst_ip_add_prefix_length = 0
+ """Destination IP address prefix length."""
+
+ def get_ppc_class(self):
+ """
+ Function returns enum value representing port pair class (PPC) of this filter rule.
+
+ :return: Enum value representing PPC of this filter rule.
+ """
+ if self.src_port_class == PC.WC and self.dst_port_class == PC.WC:
+ return PPC.WC_WC
+
+ elif self.src_port_class == PC.WC and self.dst_port_class == PC.HI:
+ return PPC.WC_HI
+
+ elif self.src_port_class == PC.HI and self.dst_port_class == PC.WC:
+ return PPC.HI_WC
+
+ elif self.src_port_class == PC.HI and self.dst_port_class == PC.HI:
+ return PPC.HI_HI
+
+ elif self.src_port_class == PC.WC and self.dst_port_class == PC.LO:
+ return PPC.WC_LO
+
+ elif self.src_port_class == PC.LO and self.dst_port_class == PC.WC:
+ return PPC.LO_WC
+
+ elif self.src_port_class == PC.HI and self.dst_port_class == PC.LO:
+ return PPC.HI_LO
+
+ elif self.src_port_class == PC.LO and self.dst_port_class == PC.HI:
+ return PPC.LO_HI
+
+ elif self.src_port_class == PC.LO and self.dst_port_class == PC.LO:
+ return PPC.LO_LO
+
+ elif self.src_port_class == PC.WC and self.dst_port_class == PC.AR:
+ return PPC.WC_AR
+
+ elif self.src_port_class == PC.AR and self.dst_port_class == PC.WC:
+ return PPC.AR_WC
+
+ elif self.src_port_class == PC.HI and self.dst_port_class == PC.AR:
+ return PPC.HI_AR
+
+ elif self.src_port_class == PC.AR and self.dst_port_class == PC.HI:
+ return PPC.AR_HI
+
+ elif self.src_port_class == PC.WC and self.dst_port_class == PC.EM:
+ return PPC.WC_EM
+
+ elif self.src_port_class == PC.EM and self.dst_port_class == PC.WC:
+ return PPC.EM_WC
+
+ elif self.src_port_class == PC.HI and self.dst_port_class == PC.EM:
+ return PPC.HI_EM
+
+ elif self.src_port_class == PC.EM and self.dst_port_class == PC.HI:
+ return PPC.EM_HI
+
+ elif self.src_port_class == PC.LO and self.dst_port_class == PC.AR:
+ return PPC.LO_AR
+
+ elif self.src_port_class == PC.AR and self.dst_port_class == PC.LO:
+ return PPC.AR_LO
+
+ elif self.src_port_class == PC.LO and self.dst_port_class == PC.EM:
+ return PPC.LO_EM
+
+ elif self.src_port_class == PC.EM and self.dst_port_class == PC.LO:
+ return PPC.EM_LO
+
+ elif self.src_port_class == PC.AR and self.dst_port_class == PC.AR:
+ return PPC.AR_AR
+
+ elif self.src_port_class == PC.AR and self.dst_port_class == PC.EM:
+ return PPC.AR_EM
+
+ elif self.src_port_class == PC.EM and self.dst_port_class == PC.AR:
+ return PPC.EM_AR
+
+ elif self.src_port_class == PC.EM and self.dst_port_class == PC.EM:
+ return PPC.EM_EM
+
+ def set_protocol_number(self, protocol):
+ """
+ Function takes network protocol abbreviation as parameter and sets instance variable protocol_number as
+ enum value of that protocol.
+
+ :param protocol: Network protocol abbreviation.
+ """
+ if protocol == 'any':
+ self.protocol = Protocol.ANY
+
+ elif protocol == 'tcp':
+ self.protocol = Protocol.TCP
+
+ elif protocol == 'udp':
+ self.protocol = Protocol.UDP
+
+ elif protocol == 'icmp':
+ self.protocol = Protocol.ICMP
+
+ elif protocol == 'igmp':
+ self.protocol = Protocol.IGMP
+
+ elif protocol == 'ggp':
+ self.protocol = Protocol.GGP
+
+ elif protocol == 'st':
+ self.protocol = Protocol.ST
+
+ elif protocol == 'egp':
+ self.protocol = Protocol.EGP
+
+ elif protocol == 'gre':
+ self.protocol = Protocol.GRE
+
+ elif protocol == 'esp':
+ self.protocol = Protocol.ESP
+
+ elif protocol == 'ah':
+ self.protocol = Protocol.AH
+
+ elif protocol == 'eigrp':
+ self.protocol = Protocol.EIGRP
+
+ elif protocol == 'ospfigp':
+ self.protocol = Protocol.OSPFIGP
+
+ def set_port(self, port, location):
+ """
+ Function takes port value as parameter and sets instance variables source or destination port and port_class
+ with parameter value and port class of parameter.
+
+ :param port: Port or port range value.
+
+ :param location: Location of port or port range value (source/destination).
+ """
+ if location == FilterRulePartLocation.SOURCE:
+ if port == 'any':
+ self.src_port_class = PC.WC
+ elif port == '0:1023':
+ self.src_port = port
+ self.src_port_class = PC.LO
+ elif port == '1024:65535':
+ self.src_port = port
+ self.src_port_class = PC.HI
+ elif len(port.split(':')) == 2:
+ self.src_port = port
+ self.src_port_class = PC.AR
+ else:
+ self.src_port = port + ':' + port
+ self.src_port_class = PC.EM
+
+ elif location == FilterRulePartLocation.DESTINATION:
+ if port == 'any':
+ self.dst_port_class = PC.WC
+ elif port == '0:1023':
+ self.dst_port = port
+ self.dst_port_class = PC.LO
+ elif port == '1024:65535':
+ self.dst_port = port
+ self.dst_port_class = PC.HI
+ elif len(port.split(':')) == 2:
+ self.dst_port = port
+ self.dst_port_class = PC.AR
+ else:
+ self.dst_port = port + ':' + port
+ self.dst_port_class = PC.EM
+
+ def set_ip_add(self, ip_address, location, is_ipv6):
+ """
+ Function takes parameter ip address and sets instance variable source or destination ip_add_bin
+ as binary form of that ip address. It also sets instance variable source or destination ip_add_prefix_length
+ with ip address prefix length.
+
+ :param ip_address: Ip address with prefix length.
+
+ :param location: Location of ip address (source/destination).
+
+ :param is_ipv6: True, if it is IPv6 address.
+ """
+ if location == FilterRulePartLocation.SOURCE:
+ if ip_address == 'any':
+ self.src_ip_add_bin = '*'
+ self.src_ip_add_prefix_length = 0
+ else:
+ ip_address = ip_address.split('/')
+ prefix_length = int(ip_address[1])
+ self.src_ip_add_prefix_length = prefix_length
+
+ if not is_ipv6:
+ bin_ip_address = bin(int(ipaddress.IPv4Address(ip_address[0])))
+ bin_ip_address = bin_ip_address[2:].zfill(32)
+ else:
+ bin_ip_address = bin(int(ipaddress.IPv6Address(ip_address[0])))
+ bin_ip_address = bin_ip_address[2:].zfill(128)
+
+ binary_ip_address_cut = bin_ip_address[:prefix_length]
+ self.src_ip_add_bin = binary_ip_address_cut
+
+ elif location == FilterRulePartLocation.DESTINATION:
+ if ip_address == 'any':
+ self.dst_ip_add_bin = '*'
+ self.dst_ip_add_prefix_length = 0
+ else:
+ ip_address = ip_address.split('/')
+ prefix_length = int(ip_address[1])
+ self.dst_ip_add_prefix_length = prefix_length
+
+ if not is_ipv6:
+ bin_ip_address = bin(int(ipaddress.IPv4Address(ip_address[0])))
+ bin_ip_address = bin_ip_address[2:].zfill(32)
+ else:
+ bin_ip_address = bin(int(ipaddress.IPv6Address(ip_address[0])))
+ bin_ip_address = bin_ip_address[2:].zfill(128)
+
+ binary_ip_address_cut = bin_ip_address[:prefix_length]
+ self.dst_ip_add_bin = binary_ip_address_cut
diff --git a/lib/tuples_analyzer/processing_rules/filter_rule_generator.py b/lib/tuples_analyzer/processing_rules/filter_rule_generator.py
new file mode 100644
index 0000000..3bc3196
--- /dev/null
+++ b/lib/tuples_analyzer/processing_rules/filter_rule_generator.py
@@ -0,0 +1,460 @@
+#!/usr/bin/env python
+"""Module containing definition of class FilterRuleGenerator."""
+
+from ..processing_errors.enums.Error import Error
+from ..processing_errors.error_process import ErrorProcess
+from .enums.filter_rule_part_location import FilterRulePartLocation
+from .enums.filter_rule_part_processing_result import FilterRulePartProcessingResult
+from .enums.filter_rule_part_type import FilterRulePartType
+from .filter_rule import FilterRule
+from .supported_values import *
+import ipaddress
+
+
+class FilterRuleGenerator:
+ """
+ Class containing only static functions for creating generator of FilterRule instances from filter set file and
+ specified rule format.
+ """
+
+ @staticmethod
+ def create_generator(lines_generator, rule_format, is_stderr_on):
+ """
+ Function creates FilterRule class instances generator from all filter rules in filter set file.
+ Format of rules is needed for processing rules into instances.
+
+ :param lines_generator: Generator of lines from file with filter rules.
+
+ :param rule_format: String with rule format.
+
+ :param is_stderr_on: If variable is true, printing error logs to stderr during parsing is enabled.
+
+ :return: Generator of FilterRule instances.
+ """
+ format_words = FilterRuleGenerator.split_line(rule_format)
+ format_length = len(format_words)
+ mandatory_words_count = FilterRuleGenerator.how_many_mandatory(format_words)
+ successful_yields = 0
+
+ # going through all lines
+ for line in lines_generator:
+
+ # empty line check
+ if not line:
+ continue
+
+ rule_parts = FilterRuleGenerator.split_line(line)
+ filter_rule = FilterRule()
+
+ format_position = 0
+ mandatory_processed = 0
+ successful_assigment = 0
+
+ missing_mandatory = False
+ standalone_question_mark = False
+
+ # going through all parts of line
+ for part in rule_parts:
+
+ if part == '?':
+ standalone_question_mark = True
+ break
+
+ # going through format parameters and keywords
+ while format_position != format_length:
+
+ expected_part_type = format_words[format_position]
+ result = FilterRuleGenerator.part_processing(expected_part_type, part, filter_rule, format_words)
+
+ if result == FilterRulePartProcessingResult.MANDATORY_PART_PROCESSED:
+ mandatory_processed += 1
+
+ if expected_part_type in format_parameters:
+ successful_assigment += 1
+
+ format_position += 1
+ break
+
+ elif result == FilterRulePartProcessingResult.MANDATORY_PART_MISSING:
+ missing_mandatory = True
+ break
+
+ elif result == FilterRulePartProcessingResult.OPTIONAL_PART_PROCESSED:
+
+ if expected_part_type[:-1] in format_parameters:
+ successful_assigment += 1
+
+ format_position += 1
+ break
+
+ elif result == FilterRulePartProcessingResult.OPTIONAL_PART_MISSING:
+ format_position += 1
+
+ # if condition is true, stop iterating through parts of filter rule
+ if format_position == format_length or missing_mandatory:
+ break
+
+ # processing errors or yielding instance of FilterRule class
+ if (missing_mandatory or mandatory_processed != mandatory_words_count or successful_assigment == 0
+ or standalone_question_mark) and not is_stderr_on:
+ pass
+
+ elif missing_mandatory:
+ ErrorProcess.print_warning(f'Mandatory part: \'{expected_part_type}\' was expected '
+ f'instead of: \'{str(FilterRuleGenerator.get_type(part, format_words).name)}'
+ f': {part}\', following rule is ignored: \'{line}\'.')
+
+ elif mandatory_processed != mandatory_words_count:
+ ErrorProcess.print_warning(f'Not all mandatory parts of rule have been processed, '
+ f'following rule is ignored: \'{line}\'.')
+
+ elif successful_assigment == 0:
+ ErrorProcess.print_warning(f'Rule has not valuable content, '
+ f'following rule is ignored: \'{line}\'.')
+
+ elif standalone_question_mark:
+ ErrorProcess.print_warning(f'Character \'?\' can not be standalone part of rule, '
+ f'following rule is ignored: \'{line}\'.')
+
+ else:
+ successful_yields += 1
+ yield filter_rule
+
+ if successful_yields == 0:
+ ErrorProcess.process_error(Error.NO_VALUE_FILTER_SET_ERROR)
+
+ @staticmethod
+ def part_processing(expected_part_type, part, filter_rule, format_words):
+ """
+ If expected part (from format) matches with real part (from filter rule line), function sets one of attributes
+ in FilterRule instance to value of part and returns enum telling that mandatory or optional part was
+ processed. If mandatory or optional part is missing, function returns corresponding enum.
+
+ :param expected_part_type: Parameter or keyword from rule format file.
+
+ :param part: Part from filter rule line.
+
+ :param filter_rule: Instance of FilterRule class.
+
+ :param format_words: List with all format words.
+
+ :return: Result type of processing filter rule part.
+ """
+ part_type = FilterRuleGenerator.get_type(part, format_words)
+
+ if part_type == FilterRulePartType.ANY:
+ part = "any"
+
+ if part_type == FilterRulePartType.IPV4_ADDRESS:
+ part += '/32'
+
+ if part_type == FilterRulePartType.IPV6_ADDRESS:
+ part += '/128'
+
+ if ((expected_part_type == "PROTOCOL" or expected_part_type == "PROTOCOL?") and
+ (part_type == FilterRulePartType.ANY or part_type == FilterRulePartType.PROTOCOL)):
+
+ filter_rule.set_protocol_number(part)
+
+ elif ((expected_part_type == "PROTOCOL" or expected_part_type == "PROTOCOL?") and
+ (part.isdigit() and int(part) in [e.value for e in Protocol])):
+
+ filter_rule.set_protocol_number(Protocol(int(part)).name.lower())
+
+ elif ((expected_part_type == "SRC_PORT" or expected_part_type == "SRC_PORT?") and
+ (part_type == FilterRulePartType.ANY or part_type == FilterRulePartType.PORT or
+ part_type == FilterRulePartType.PORT_RANGE)):
+
+ filter_rule.set_port(part, FilterRulePartLocation.SOURCE)
+
+ elif ((expected_part_type == "DST_PORT" or expected_part_type == "DST_PORT?") and
+ (part_type == FilterRulePartType.ANY or part_type == FilterRulePartType.PORT or
+ part_type == FilterRulePartType.PORT_RANGE)):
+
+ filter_rule.set_port(part, FilterRulePartLocation.DESTINATION)
+
+ elif ((expected_part_type == "SRC_IP" or expected_part_type == "SRC_IP?") and
+ (part_type == FilterRulePartType.ANY or part_type == FilterRulePartType.IPV4_ADDRESS_MASK
+ or part_type == FilterRulePartType.IPV6_ADDRESS or part_type == FilterRulePartType.IPV6_ADDRESS_MASK
+ or part_type == FilterRulePartType.IPV4_ADDRESS)):
+
+ is_ipv6 = part_type == FilterRulePartType.IPV6_ADDRESS or part_type == FilterRulePartType.IPV6_ADDRESS_MASK
+ filter_rule.set_ip_add(part, FilterRulePartLocation.SOURCE, is_ipv6)
+
+ elif ((expected_part_type == "DST_IP" or expected_part_type == "DST_IP?") and
+ (part_type == FilterRulePartType.ANY or part_type == FilterRulePartType.IPV4_ADDRESS_MASK
+ or part_type == FilterRulePartType.IPV6_ADDRESS or part_type == FilterRulePartType.IPV6_ADDRESS_MASK
+ or part_type == FilterRulePartType.IPV4_ADDRESS)):
+
+ is_ipv6 = part_type == FilterRulePartType.IPV6_ADDRESS or part_type == FilterRulePartType.IPV6_ADDRESS_MASK
+ filter_rule.set_ip_add(part, FilterRulePartLocation.DESTINATION, is_ipv6)
+
+ elif ((expected_part_type == "NUMBER" or expected_part_type == "NUMBER?") and
+ (part_type == FilterRulePartType.NUMBER or part_type == FilterRulePartType.PORT)):
+ pass
+
+ elif ((expected_part_type == "WILDCARD" or expected_part_type == "WILDCARD?") and
+ (part_type == FilterRulePartType.WORD)):
+ pass
+
+ elif ((expected_part_type == part or expected_part_type == part + '?') and
+ part_type == FilterRulePartType.KEYWORD):
+ pass
+
+ elif FilterRuleGenerator.is_word_mandatory(expected_part_type):
+ return FilterRulePartProcessingResult.MANDATORY_PART_MISSING
+
+ else:
+ return FilterRulePartProcessingResult.OPTIONAL_PART_MISSING
+
+ if FilterRuleGenerator.is_word_mandatory(expected_part_type):
+ return FilterRulePartProcessingResult.MANDATORY_PART_PROCESSED
+
+ else:
+ return FilterRulePartProcessingResult.OPTIONAL_PART_PROCESSED
+
+ @staticmethod
+ def how_many_mandatory(rule_format):
+ """
+ Function counts how many words are mandatory in rule format definition.
+
+ :param rule_format: String with rule format.
+
+ :return: Count of mandatory words in format.
+ """
+ count = 0
+ for part in rule_format:
+ if FilterRuleGenerator.is_word_mandatory(part):
+ count = count + 1
+ return count
+
+ @staticmethod
+ def is_word_mandatory(word):
+ """
+ Every parameter or keyword in format is holding information, if part in filter rule is mandatory or optional.
+ Part is optional, if its definition in format ends with character '?', otherwise part is mandatory.
+
+ :param word: Parameter or keyword from format.
+
+ :return: Function returns true, if parameter word ends with '?'.
+ """
+ string_length = len(word)
+
+ if word[string_length - 1] != '?':
+ return True
+ else:
+ return False
+
+ @staticmethod
+ def get_type(part, format_words):
+ """
+ Function determines type of part of filter rule. Decision is made by acceptable format of part.
+ E.g. function knows it is a port, if part of rule is number between 0-65535.
+
+ :param part: String with filter rule part.
+
+ :param format_words: List with all format words.
+
+ :return: Type of filter rule part.
+ """
+ if FilterRuleGenerator.is_wildcard(part):
+ return FilterRulePartType.ANY
+
+ elif FilterRuleGenerator.is_protocol(part):
+ return FilterRulePartType.PROTOCOL
+
+ elif FilterRuleGenerator.is_ipv4_address(part):
+ return FilterRulePartType.IPV4_ADDRESS
+
+ elif FilterRuleGenerator.is_ipv4_address_with_mask(part):
+ return FilterRulePartType.IPV4_ADDRESS_MASK
+
+ elif FilterRuleGenerator.is_ipv6_address(part):
+ return FilterRulePartType.IPV6_ADDRESS
+
+ elif FilterRuleGenerator.is_ipv6_address_with_mask(part):
+ return FilterRulePartType.IPV6_ADDRESS_MASK
+
+ elif FilterRuleGenerator.is_port(part):
+ return FilterRulePartType.PORT
+
+ elif part.isdigit():
+ return FilterRulePartType.NUMBER
+
+ elif FilterRuleGenerator.is_port_range(part):
+ return FilterRulePartType.PORT_RANGE
+
+ elif FilterRuleGenerator.is_keyword(part, format_words):
+ return FilterRulePartType.KEYWORD
+
+ else:
+ return FilterRulePartType.WORD
+
+ @staticmethod
+ def is_wildcard(value):
+ """
+ Function returns true, if parameter value is wildcard.
+
+ :param value: String with possible wildcard.
+
+ :return: True, if parameter is representation of wildcard, otherwise false.
+ """
+
+ if not isinstance(value, str):
+ return False
+
+ value = value.lower()
+
+ if value in supported_wildcard_list:
+ return True
+ else:
+ return False
+
+ @staticmethod
+ def is_protocol(protocol):
+ """
+ Function returns true, if parameter is network protocol abbreviation (TCP, UDP, ip, ...).
+
+ :param protocol: String with possible network protocol abbreviation.
+
+ :return: True, if parameter is network protocol abbreviation, otherwise false.
+ """
+ if not isinstance(protocol, str):
+ return False
+
+ case_insensitive_protocol = protocol.lower()
+
+ if case_insensitive_protocol in supported_protocols:
+ return True
+ else:
+ return False
+
+ @staticmethod
+ def is_port(port):
+ """
+ Function finds out, if parameter is port (number between 0-65535).
+
+ :param port: String with possible port.
+
+ :return: Function returns true, if parameter of function is port.
+ """
+ if port.isdigit() and 0 <= int(port) < 65536:
+ return True
+ else:
+ return False
+
+ @staticmethod
+ def is_port_range(port_range):
+ """
+ Function finds out, if parameter is port range (two ports with ':'as delimiter).
+
+ :param port_range: String with possible port range.
+
+ :return: Function returns true, if parameter of function is port range.
+ """
+ port_range = port_range.split(':')
+
+ if (len(port_range) == 2 and
+ FilterRuleGenerator.is_port(port_range[0]) and
+ FilterRuleGenerator.is_port(port_range[1]) and
+ int(port_range[0]) < int(port_range[1])):
+ return True
+ else:
+ return False
+
+ @staticmethod
+ def is_keyword(keyword, format_words):
+ """
+ Function finds out, if parameter keyword is part of rule format definition.
+
+ :param keyword: String with possible keyword.
+
+ :param format_words: List with all format words.
+
+ :return: True, if parameter of function is keyword in format.
+ """
+ if keyword in format_words or keyword + '?' in format_words:
+ return True
+ else:
+ return False
+
+ @staticmethod
+ def is_ipv4_address(ip_address):
+ """
+ Function returns true, if parameter of function is IPv4 address in decimal form without mask (prefix length).
+
+ :param ip_address: String with possible IPv4 address in decimal form.
+
+ :return: True, if parameter of function is IPv4 address.
+ """
+ try:
+ ipaddress.IPv4Address(ip_address)
+ except ipaddress.AddressValueError:
+ return False
+
+ return True
+
+ @staticmethod
+ def is_ipv4_address_with_mask(ip_address):
+ """
+ Function returns true, if parameter of function is IPv4 address in decimal form with mask (prefix length).
+
+ :param ip_address: String with possible IPv4 address with mask in decimal form.
+
+ :return: True, if parameter of function is IPv4 address with mask.
+ """
+ possible_address = ip_address.split('/')
+
+ if len(possible_address) != 2 or not possible_address[1].isdigit() or int(possible_address[1]) < 0 or int(
+ possible_address[1]) > 32 or not FilterRuleGenerator.is_ipv4_address(possible_address[0]):
+ return False
+
+ return True
+
+
+ @staticmethod
+ def is_ipv6_address(ip_address):
+ """
+ Function returns true, if parameter of function is IPv6 address without mask (prefix length).
+
+ :param ip_address: String with possible IPv6 address.
+
+ :return: True, if parameter of function is IPv6 address.
+ """
+ try:
+ ipaddress.IPv6Address(ip_address)
+ except ipaddress.AddressValueError:
+ return False
+
+ return True
+
+ @staticmethod
+ def is_ipv6_address_with_mask(ip_address):
+ """
+ Function returns true, if parameter of function is IPv6 address with mask (prefix length).
+
+ :param ip_address: String with possible IPv6 address with mask.
+
+ :return: True, if parameter of function is IPv6 address with mask.
+ """
+ possible_address = ip_address.split('/')
+
+ if len(possible_address) != 2 or not possible_address[1].isdigit() or int(possible_address[1]) < 0 or int(
+ possible_address[1]) > 128 or not FilterRuleGenerator.is_ipv6_address(possible_address[0]):
+ return False
+
+ return True
+
+ @staticmethod
+ def split_line(line):
+ """
+ Function firstly replaces line characters ',' and '=' with empty space. Then split the line by whitespace
+ into parts and returns list with splitted parts from line.
+
+ :param line: String which will be splitted to parts.
+
+ :return: List with splitted parts by whitespace from line.
+ """
+ line = line.replace("=", " ")
+ line = line.replace(",", " ")
+ return line.split()
diff --git a/lib/tuples_analyzer/processing_rules/supported_values.py b/lib/tuples_analyzer/processing_rules/supported_values.py
new file mode 100644
index 0000000..b49fc12
--- /dev/null
+++ b/lib/tuples_analyzer/processing_rules/supported_values.py
@@ -0,0 +1,8 @@
+#!/usr/bin/env python
+"""Module containing global lists of supported protocols, wildcard representations and valuable format parameters."""
+from .enums.protocol_numbers import Protocol
+
+supported_protocols = [str(e.name).lower() for e in Protocol]
+format_parameters = ["PROTOCOL", "SRC_IP", "SRC_PORT", "DST_IP", "DST_PORT"]
+supported_wildcard_list = ["any", "all", "*", "ip", "0"]
+
diff --git a/lib/tuples_analyzer/value_formats/__init__.py b/lib/tuples_analyzer/value_formats/__init__.py
new file mode 100644
index 0000000..aa33cb3
--- /dev/null
+++ b/lib/tuples_analyzer/value_formats/__init__.py
@@ -0,0 +1 @@
+"""Package with classes for formatting different types of values to specified form."""
diff --git a/lib/tuples_analyzer/value_formats/value_format.py b/lib/tuples_analyzer/value_formats/value_format.py
new file mode 100644
index 0000000..ef636f0
--- /dev/null
+++ b/lib/tuples_analyzer/value_formats/value_format.py
@@ -0,0 +1,19 @@
+#!/usr/bin/env python
+"""Module containing definition of class Format."""
+
+
+class Format:
+ """
+ Class contains functions for formatting different types of values to specified form.
+ """
+
+ @staticmethod
+ def decimal(decimal_value):
+ """
+ Function formats decimal value to form with 8 decimal numbers.
+
+ :param decimal_value: Decimal value.
+
+ :return: Decimal value with 8 decimal numbers.
+ """
+ return format(decimal_value, '.8f')
diff --git a/patches/improvements.patch b/patches/improvements.patch
new file mode 100644
index 0000000..2323a82
--- /dev/null
+++ b/patches/improvements.patch
@@ -0,0 +1,9463 @@
+diff --git a/ExtraList.cc b/ExtraList.cc
+index d09f0aa..4b309fe 100644
+--- a/ExtraList.cc
++++ b/ExtraList.cc
+@@ -17,6 +17,7 @@ ExtraList::ExtraList(int P1) {
+ for (int i = 1; i <= P; i++){
+ // Create header list
+ struct ExtraListHeader *temp = new struct ExtraListHeader;
++ temp->field = NULL;
+ temp->next = NULL;
+ temp->prev = last;
+ if (i == 1) {
+@@ -39,11 +40,12 @@ ExtraList::~ExtraList() {
+ for (int j = 0; j < N; j++){
+ tempI = temp->field[j];
+ // Delete list of values
+- delete(tempI->value);
+- delete(tempI->prob);
++ delete[] (tempI->value);
++ delete[] (tempI->prob);
++ delete(tempI);
+ }
+ first = first->next;
+- delete(temp->field);
++ delete[] (temp->field);
+ delete(temp);
+ }
+ }
+diff --git a/FilterList.cc b/FilterList.cc
+index c123529..2467dfd 100644
+--- a/FilterList.cc
++++ b/FilterList.cc
+@@ -17,9 +17,12 @@ FilterList::FilterList() {
+ num = 0;
+ }
+
+-FilterList::~FilterList() {
++FilterList::~FilterList() {
+ struct FilterList_item *temp;
+ while (first != NULL) {
++ if (first->filt.num_ext_field > 0) {
++ delete[] (first->filt.ext_field);
++ }
+ temp = first->next;
+ delete(first);
+ first = temp;
+@@ -41,7 +44,7 @@ void FilterList::clear() {
+ struct FilterList_item* FilterList::operator()(int i) {
+ if (i <= 0 || (i > num && num > 0)) {
+ fprintf(stderr,"FilterList::operator(): item %d out of range, num = %d\n",i,num);
+- exit(1);
++ exit(1);
+ }
+ // Items are maintained in-order
+ // Index i items to the "right"
+@@ -57,7 +60,7 @@ struct FilterList_item* FilterList::operator()(int i) {
+ void FilterList::insert(struct FilterList_item *item, struct filter filt) {
+ struct FilterList_item *newitem;
+ newitem = new struct FilterList_item;
+- newitem->filt = filt;
++ copy_filter(newitem->filt, filt);
+ newitem->prev = item->prev;
+ newitem->next = item;
+ if (first == item) first = newitem;
+@@ -72,7 +75,7 @@ void FilterList::insert(struct FilterList_item *item, struct filter filt) {
+ void FilterList::operator&=(struct filter filt) {
+ struct FilterList_item *temp;
+ temp = new struct FilterList_item;
+- temp->filt = filt;
++ copy_filter(temp->filt, filt);
+ temp->prev = last;
+ temp->next = NULL;
+ if (num == 0){
+@@ -110,7 +113,7 @@ void FilterList::operator=(FilterList* L) {
+ void FilterList::push(struct filter filt) {
+ struct FilterList_item *temp;
+ temp = new struct FilterList_item;
+- temp->filt = filt;
++ copy_filter(temp->filt, filt);
+ temp->next = first;
+ temp->prev = NULL;
+ if (num == 0){
+@@ -121,14 +124,14 @@ void FilterList::push(struct filter filt) {
+ first = temp;
+ num++;
+ return;
+-}
++}
+
+ // Print the contents of the FilterList.
+ void FilterList::print(FILE* fp) {
+ int addr[4];
+ unsigned temp;
+ struct FilterList_item *tempfilt;
+-
++
+ for (tempfilt = first; tempfilt != NULL; tempfilt = tempfilt->next){
+ // Print new filter character
+ fprintf(fp,"@");
+@@ -143,7 +146,7 @@ void FilterList::print(FILE* fp) {
+ fprintf(fp, "%d.%d.%d.%d/%d\t",
+ addr[0], addr[1], addr[2], addr[3],
+ tempfilt->filt.sa_len);
+- // Print destination address
++ // Print destination address
+ addr[0] = addr[1] = addr[2] = addr[3] = 0;
+ temp = 0;
+ temp = tempfilt->filt.da;
+@@ -154,13 +157,13 @@ void FilterList::print(FILE* fp) {
+ fprintf(fp, "%d.%d.%d.%d/%d\t",
+ addr[0], addr[1], addr[2], addr[3],
+ tempfilt->filt.da_len);
+- // Print source port
++ // Print source port
+ fprintf(fp, "%d : %d\t",
+ tempfilt->filt.sp[0], tempfilt->filt.sp[1]);
+- // Print destination port
++ // Print destination port
+ fprintf(fp, "%d : %d\t",
+ tempfilt->filt.dp[0], tempfilt->filt.dp[1]);
+- // Print protocol
++ // Print protocol
+ if (tempfilt->filt.prot_num == 0) fprintf(fp, "0x00/0x00\t");
+ else fprintf(fp, "0x%02x/0xFF\t", tempfilt->filt.prot_num);
+ // Print flags
+@@ -170,8 +173,8 @@ void FilterList::print(FILE* fp) {
+ for (int j = 0; j < tempfilt->filt.num_ext_field; j++){
+ fprintf(fp, "%d\t",
+ tempfilt->filt.ext_field[j]);
+- }
+- // Print newline
++ }
++ // Print newline
+ fprintf(fp,"\n");
+ }
+ }
+diff --git a/FlagList.cc b/FlagList.cc
+index 074c4e7..9a214cf 100644
+--- a/FlagList.cc
++++ b/FlagList.cc
+@@ -27,8 +27,8 @@ FlagList::~FlagList() {
+ first[i] = temp;
+ }
+ }
+- delete(first);
+- delete(last);
++ delete[] (first);
++ delete[] (last);
+ }
+
+ void FlagList::choose(float p, int prot, unsigned *flags, unsigned *flags_mask){
+diff --git a/PortList.cc b/PortList.cc
+index 34ad3c2..4b26d15 100644
+--- a/PortList.cc
++++ b/PortList.cc
+@@ -22,7 +22,7 @@ PortList::PortList(int N1) {
+ }
+ }
+
+-PortList::~PortList() { delete ports; }
++PortList::~PortList() { delete[] ports; }
+
+ void PortList::read(int t, FILE *fp) {
+ int done = 0;
+diff --git a/PrefixList.cc b/PrefixList.cc
+index a3df1dc..58f75e1 100644
+--- a/PrefixList.cc
++++ b/PrefixList.cc
+@@ -24,8 +24,8 @@ PrefixList::PrefixList() {
+ }
+
+ PrefixList::~PrefixList() {
+- for (int type = 0; type < 25; type++) delete prefixes[type];
+- delete prefixes;
++ for (int type = 0; type < 25; type++) delete[] prefixes[type];
++ delete[] prefixes;
+ }
+
+ void PrefixList::read(FILE* fp){
+diff --git a/ProtList.cc b/ProtList.cc
+index a4bc451..b9bd739 100644
+--- a/ProtList.cc
++++ b/ProtList.cc
+@@ -24,8 +24,8 @@ ProtList::ProtList() {
+ }
+
+ ProtList::~ProtList() {
+- for (int i = 0; i < 25; i++) delete protocols[i].pt_prob;
+- delete protocols;
++ for (int i = 0; i < 25; i++) delete[] protocols[i].pt_prob;
++ delete[] protocols;
+ }
+
+ void ProtList::read(FILE *fp) {
+diff --git a/README b/README
+index 116aac2..f104e85 100644
+--- a/README
++++ b/README
+@@ -75,17 +75,17 @@ PPC
+ 10 AR/WC source port arbitrary range, destination port wildcard
+ 11 HI/AR source port [1024:65535], destination port arbitrary range
+ 12 AR/HI source port arbitrary range, destination port [1024:65535]
+-13 LO/AR source port [0:1023], destination port arbitrary range
+-14 AR/LO source port arbitrary range, destination port [0:1023]
+-15 AR/AR source port arbitrary range, destination port arbitrary range
+-16 WC/EM source port wildcard, destination port exact match
+-17 EM/WC source port exact match, destination port wildcard
+-18 HI/EM source port [1024:65535], destination port exact match
+-19 EM/HI source port exact match, destination port [1024:65535]
+-20 LO/EM source port [0:1023], destination port exact match
+-21 EM/LO source port exact match, destination port [0:1023]
++13 WC/EM source port wildcard, destination port exact match
++14 EM/WC source port exact match, destination port wildcard
++15 HI/EM source port [1024:65535], destination port exact match
++16 EM/HI source port exact match, destination port [1024:65535]
++17 LO/AR source port [0:1023], destination port arbitrary range
++18 AR/LO source port arbitraty range, destination port [0:1023]
++19 LO/EM source port [0:1023], destination port exact match
++20 EM/LO source port exact match, destination port [0:1023]
++21 AR/AR source port arbitraty range, destination port arbitrary range
+ 22 AR/EM source port arbitrary range, destination port exact match
+-23 EM/AR source port exact match, destination port exact match arbitrary range
++23 EM/AR source port exact match, destination port arbitrary range
+ 24 EM/EM source port exact match, destination port exact match
+
+ -flags
+diff --git a/TupleBST.cc b/TupleBST.cc
+index 9163d9b..da91dcc 100644
+--- a/TupleBST.cc
++++ b/TupleBST.cc
+@@ -17,7 +17,7 @@ TupleBST::TupleBST() {
+
+ TupleBST::~TupleBST() {
+ if (root != NULL) cleanup(root);
+- delete(ListOfFilterIndexPtrs);
++ delete[] (ListOfFilterIndexPtrs);
+ }
+
+ void TupleBST::cleanup(TupleBST_item* node){
+diff --git a/custom_db.cc b/custom_db.cc
+index 6f8d193..3ec8a52 100644
+--- a/custom_db.cc
++++ b/custom_db.cc
+@@ -20,15 +20,58 @@
+ #include "redundant_filter_check.h"
+ #include "TupleBST.h"
+ #include "custom_db.h"
++#include "ip_prefix.h"
++#include "trie.h"
++#include "filter_graph.h"
++
++
++/*
++ * Transforms a pruned source/destination trie into a set of edges from/to the
++ * 's'/'t' node in the filter graph.
++ * The given trie is recursively traversed. When the traversed node is a prefix
++ * node, adding of the corresponding edge from/to the 's'/'t' node of the
++ * filter graph is triggered. Weight of the added edge is set according to the
++ * number of prefixes represented by the current prefix node.
++ * CAUTION:
++ * * This function uses recursive calls!
++ * @param node A pointer to the current trie node.
++ * @param src_trie The trie node referenced by parameter node belongs to
++ * the source trie (TRUE) or the destination trie (FALSE).
++ * @param prefix_str The prefix represented by the current node encoded as a
++ * string of bit values.
++ * @param graph A pointer to the filter graph object.
++ */
++void trie_to_graph(const trie_node* node, bool src_trie, string prefix_str, Filter_graph* graph) {
++ if (node != NULL) { // non-empty subtree
++ int prefix_count = node->prefixes;
++ if (prefix_count > 0) { // prefix node
++ // add current prefix to the filter graph, either as "s_prefix" or as "t_prefix"
++ IP_prefix prefix(prefix_str, true);
++ if (src_trie) {
++ graph->add_s_prefix(prefix, prefix_count);
++ } else {
++ graph->add_t_prefix(prefix, prefix_count);
++ }
++ }
++ // call this function on both zero and one subtrees
++ trie_to_graph(node->zero, src_trie, prefix_str + "0", graph);
++ trie_to_graph(node->one, src_trie, prefix_str + "1", graph);
++ } else { // empty subtree
++ return;
++ }
++} // end trie_to_graph()
+
+
+ int custom_db_gen(int num_filters, FilterList* filters, FILE* fp_in, int smoothness, float addr_scope, float port_scope, int branch){
+
++ // generate 100 times more filters (because of successive trie pruning)
++ int temp_num_filters = num_filters * 100;
++
+ printf("Initializing data structures...\n");
+ // Read in scale
+ int scale = read_scale(fp_in);
+ // printf("scale = %d\n",scale);
+- float scale_factor = (float)num_filters/(float)scale;
++ float scale_factor = (float)temp_num_filters/(float)scale;
+ // printf("scale_factor = %.4f\n",scale);
+
+ // Read protocol parameters, initialize data structure
+@@ -80,7 +123,10 @@ int custom_db_gen(int num_filters, FilterList* filters, FILE* fp_in, int smoothn
+
+ // Temporary filter
+ // struct filter temp_filter;
+- struct filter *temp_filters = new struct filter[num_filters+1];
++ struct filter *temp_filters = new struct filter[temp_num_filters+1];
++ for (int i = 0; i < temp_num_filters+1; i++) {
++ temp_filters[i].num_ext_field = 0;
++ }
+ dlist *Flist = new dlist;
+ struct range temp_range;
+ struct ppair temp_ppair;
+@@ -89,7 +135,7 @@ int custom_db_gen(int num_filters, FilterList* filters, FILE* fp_in, int smoothn
+ printf("Creating application specifications...\n");
+
+ // For all filters:
+- for (int i = 1; i <= num_filters; i++){
++ for (int i = 1; i <= temp_num_filters; i++){
+ // Select a protocol via random number
+ p = drand48();
+ temp_filters[i].prot_num = protL->choose_prot((float)p);
+@@ -143,6 +189,8 @@ int custom_db_gen(int num_filters, FilterList* filters, FILE* fp_in, int smoothn
+ printf(" \tdone\n");
+ // Free up memory
+ delete(protL);
++ delete(flagL);
++ delete(extraL);
+ delete(sparL);
+ delete(spemL);
+ delete(dparL);
+@@ -180,10 +228,9 @@ int custom_db_gen(int num_filters, FilterList* filters, FILE* fp_in, int smoothn
+ }
+ // printf("Flist = "); (*Flist).print(stdout); printf("\n");
+ (*Stree).build_tree(Flist,temp_filters);
+- delete(Stree);
+ /*
+ printf("Flist = "); (*Flist).print(stdout); printf("\n");
+- for (int i = 1; i <= num_filters; i++)
++ for (int i = 1; i <= temp_num_filters; i++)
+ printf("filter[%d].sa = %u/%d\n",i,temp_filters[i].sa,temp_filters[i].sa_len);
+ */
+ printf(" \tdone\n");
+@@ -203,13 +250,245 @@ int custom_db_gen(int num_filters, FilterList* filters, FILE* fp_in, int smoothn
+
+ (*Dtree).build_tree(Flist,temp_filters);
+ // printf("Flist = "); (*Flist).print(stdout); printf("\n");
+- delete(Dtree);
+ printf(" \tdone\n");
+
+ delete(Flist);
+
++
++// ****************************************************************************
++// START of IMPROVEMENTS implemented in ClassBench-ng
++// ****************************************************************************
++
++ // transform filter set into filter graph and insert source/destination
++ // prefixes of filters into corresponding tries
++ Filter_graph graph;
++ Trie src_trie;
++ Trie dst_trie;
++ for (int i = 1; i <= temp_num_filters; i++) {
++ graph.add_filter(&(temp_filters[i]));
++ src_trie.insert(IP_prefix(temp_filters[i].sa, temp_filters[i].sa_len));
++ dst_trie.insert(IP_prefix(temp_filters[i].da, temp_filters[i].da_len));
++ }
++
++ // get statistics of source and destination tries
++ trie_stats src_trie_stats, dst_trie_stats;
++ src_trie.get_stats(src_trie_stats);
++ dst_trie.get_stats(dst_trie_stats);
++
++ // initialize data structures for trie-related distributions
++ vector src_prefixes(129,0);
++ vector src_one_child(129,0);
++ vector src_two_children(129,0);
++ vector src_skew(129,0);
++ vector dst_prefixes(129,0);
++ vector dst_one_child(129,0);
++ vector dst_two_children(129,0);
++ vector dst_skew(129,0);
++
++ // get source and destination prefix length distributions
++ // Use prefix length distributions from already generated source and
++ // destination prefix sets as ClassBench-generated rule sets follow them
++ // quite precisely.
++ for (int i = 0; i < 129; i++) {
++ src_prefixes[i] = (float)src_trie_stats.classbench.prefix_lengths[i] /
++ (float)src_trie_stats.classbench.prefixes;
++ dst_prefixes[i] = (float)dst_trie_stats.classbench.prefix_lengths[i] /
++ (float)dst_trie_stats.classbench.prefixes;
++ }
++
++ // get other src distributions
++ // Copy the values that were already read from the parameter file into Stree.
++ for (int i = 0; i <= 32; i++) {
++ src_one_child[i] = Stree->get_p1child()[i];
++ src_two_children[i] = Stree->get_p2child()[i];
++ src_skew[i] = Stree->get_skew()[i];
++ }
++ delete(Stree);
++
++ // get other dst distributions
++ // Copy the values that were already read from the parameter file into Dtree.
++ for (int i = 0; i <= 32; i++) {
++ dst_one_child[i] = Dtree->get_p1child()[i];
++ dst_two_children[i] = Dtree->get_p2child()[i];
++ dst_skew[i] = Dtree->get_skew()[i];
++ }
++ delete(Dtree);
++
++ // prune source and destination tries to 1/100 of the original prefix sets
++ src_trie.prune(num_filters, src_prefixes, src_one_child,
++ src_two_children, src_skew);
++ dst_trie.prune(num_filters, dst_prefixes, dst_one_child,
++ dst_two_children, dst_skew);
++
++ // extend filter graph according to pruned tries
++ trie_to_graph(src_trie.get_root(), true, "", &graph);
++ trie_to_graph(dst_trie.get_root(), false, "", &graph);
++
++ // modify the filter graph to conform with the flow network specification
++ graph.to_flow_network();
++
++ // compute maximum flow for the current flow network
++ int max_flow = graph.max_flow();
++
++ // auxiliary variables for construction of the set of pruned filters
++ struct filter* pruned_filters = new struct filter[temp_num_filters+1];
++ for (int i = 0; i < temp_num_filters+1; i++) {
++ pruned_filters[i].num_ext_field = 0;
++ }
++ int pruned_filters_i = 1;
++
++ /*
++ * 1st phase of construction of the set of pruned filters
++ */
++ // iterate over all filters
++ const node_list_item* node;
++ node = graph.get_src_nodes();
++ while (node != NULL) {
++ neighbour_list_item* neighbour = node->neighbours;
++ while (neighbour != NULL) {
++ filter_list_item* filter = neighbour->filters;
++ for (int i = 0; i < neighbour->flow; i++) {
++ // leave the cycle if enough filters have been selected
++ if (pruned_filters_i == num_filters+1) {
++ break;
++ }
++ // copy filters from the maximum flow to the pruned_filters array
++ copy_filter(pruned_filters[pruned_filters_i], *(filter->filter));
++ pruned_filters_i++;
++ filter = filter->next;
++ }
++ // leave the cycle if enough filters have been selected
++ if (pruned_filters_i == num_filters+1) {
++ break;
++ }
++ neighbour = neighbour->next;
++ }
++ // leave the cycle if enough filters have been selected
++ if (pruned_filters_i == num_filters+1) {
++ break;
++ }
++ node = node->next;
++ }
++
++ /*
++ * 2nd phase of construction of the set of pruned filters
++ */
++ // auxiliary variables for looking for not fully utilized source prefixes
++ neighbour_list_item* s_neighbour = (graph.get_s_node() != NULL) ?
++ graph.get_s_node()->neighbours : NULL;
++ // iterate over all destination prefixes
++ node = graph.get_dst_nodes();
++ while (node != NULL) {
++ neighbour_list_item* neighbour = node->neighbours;
++ if (neighbour != NULL) {
++ int free_weight = neighbour->weight - neighbour->flow;
++ // auxiliary variables for looking for not fully utilized filters with
++ // current destination prefix
++ const node_list_item* src_node = graph.get_src_nodes();
++ neighbour_list_item* src_neighbour;
++ filter_list_item* src_filter;
++ int src_filter_index = 0;
++ // for each not fully utilized destination prefix
++ for (int i = 0; i < free_weight; i++) {
++ // leave the cycle if enough filters have been selected
++ if (pruned_filters_i == num_filters+1) {
++ break;
++ }
++
++// FILTERS start --------------------------------------------------------------
++
++ // iterate over all filters with current destination prefix
++ while (src_node != NULL) {
++ src_neighbour = src_node->neighbours;
++ while (src_neighbour != NULL) {
++ if (src_neighbour->node->prefix == node->prefix) {
++ src_filter = src_neighbour->filters;
++ // skip over all filters from the maximum flow
++ for (;
++ src_filter_index < src_neighbour->flow;
++ src_filter_index++) {
++ src_filter = src_filter->next;
++ }
++ if (src_filter != NULL) {// this filter is not fully
++ // utilized
++
++// SOURCE PREFIXES start ------------------------------------------------------
++
++ // find the first not fully utilized source prefix
++ while (s_neighbour != NULL) {
++ // if this source prefix is not fully utilized
++ if (s_neighbour->flow < s_neighbour->weight) {
++ // create local copy of the selected filter and
++ // modify its source prefix
++ struct filter pruned_filter;
++ copy_filter(pruned_filter, *(src_filter->filter));
++ pruned_filter.sa =
++ s_neighbour->node->prefix.get_prefix_unsigned();
++ pruned_filter.sa_len =
++ s_neighbour->node->prefix.get_length();
++ // inset the filter into the pruned_filters array
++ pruned_filters[pruned_filters_i++] =
++ pruned_filter;
++ // increment flow value through used edges
++ s_neighbour->flow++;
++ src_neighbour->flow++;
++ neighbour->flow++;
++ // terminate looking for the next not fully
++ // utilized source prefix
++ break;
++ }
++ s_neighbour = s_neighbour->next;
++ }
++ // always terminate looking for not fully utilized
++ // filters because of the following reasons:
++ // * if not fully utilized source prefix was found,
++ // move to the next not fully utilized destination
++ // prefix
++ // * if not fully utilized source prefix was not
++ // found, terminate inserting filters into the
++ // pruned_filters array at all
++ break;
++
++// SOURCE PREFIES end ---------------------------------------------------------
++
++ } else { // this filter is fully utilized
++ src_filter_index = 0;
++ }
++ }
++ src_neighbour = src_neighbour->next;
++ }
++ // if the previous cycle was broken, break also this cycle
++ // (because of the same reasons)
++ if (src_neighbour != NULL) {
++ break;
++ }
++ src_node = src_node->next;
++ }
++ // if either all filters or prefixes are fully utilized, terminate
++ // looking for not fully utilized filters
++ if ((src_node == NULL) || (s_neighbour == NULL)) {
++ break;
++ }
++
++// FILTERS end ----------------------------------------------------------------
++
++ }
++ }
++ // if all source prefixes are fully utilized or enough filters have been
++ // selected, terminate looking for not fully utilized destination prefixes
++ if ((s_neighbour == NULL) || (pruned_filters_i == num_filters+1)) {
++ break;
++ }
++ node = node->next;
++ }
++
++// ****************************************************************************
++// END of IMPROVEMENTS implemented in ClassBench-ng
++// ****************************************************************************
++
++
+ printf("Removing redundant filters and ordering nested filters...\n");
+- int filter_cnt = remove_redundant_filters(num_filters,filters,temp_filters);
++ int filter_cnt = remove_redundant_filters(pruned_filters_i-1,filters,pruned_filters);
+ printf(" \tdone\n");
+
+ // Resolve conflicts, throw away filters if necessary
+@@ -219,7 +498,16 @@ int custom_db_gen(int num_filters, FilterList* filters, FILE* fp_in, int smoothn
+ // printf(" \tdone\n");
+
+ // Delete data structures
+- delete(temp_filters);
++ for (int i = 0; i < temp_num_filters+1; i++) {
++ if (temp_filters[i].num_ext_field > 0) {
++ delete[] (temp_filters[i].ext_field);
++ }
++ if (pruned_filters[i].num_ext_field > 0) {
++ delete[] (pruned_filters[i].ext_field);
++ }
++ }
++ delete[] (temp_filters);
++ delete[] (pruned_filters);
+ // printf("Done with custom_db\n");
+
+ return filter_cnt;
+diff --git a/dbintree.cc b/dbintree.cc
+index 9274710..bb45062 100644
+--- a/dbintree.cc
++++ b/dbintree.cc
+@@ -28,10 +28,10 @@ dbintree::dbintree() {
+ }
+
+ dbintree::~dbintree() {
+- delete(skew);
+- delete(corr);
+- delete(p1child);
+- delete(p2child);
++ delete[] (skew);
++ delete[] (corr);
++ delete[] (p1child);
++ delete[] (p2child);
+ // call recursive node destructor
+ if (root != NULL) delete_node(root);
+ }
+@@ -39,6 +39,7 @@ dbintree::~dbintree() {
+ void dbintree::delete_node(struct tnode *me){
+ if (me->child0 != NULL) delete_node(me->child0);
+ if (me->child1 != NULL) delete_node(me->child1);
++ delete(me->stubList);
+ delete(me);
+ return;
+ }
+diff --git a/dbintree.h b/dbintree.h
+index 5509fc3..d189380 100644
+--- a/dbintree.h
++++ b/dbintree.h
+@@ -51,4 +51,15 @@ class dbintree {
+ void print_corr(FILE*); // print correlation per level
+ void build_tree(dlist* Flist, struct filter filters[]);
+ void lsort(); // sort nodes by level
++
++ // get methods for selected private members
++ inline float* get_skew() {
++ return skew;
++ };
++ inline float* get_p1child() {
++ return p1child;
++ };
++ inline float* get_p2child() {
++ return p2child;
++ };
+ };
+diff --git a/filter_graph.cc b/filter_graph.cc
+new file mode 100644
+index 0000000..726869a
+--- /dev/null
++++ b/filter_graph.cc
+@@ -0,0 +1,785 @@
++// filter_graph.cc: Filter_graph class definition
++//
++// Jiri Matousek, 2017
++// imatousek@fit.vutbr.cz
++
++
++// User includes
++#include "stdinc.h"
++#include "flow_network.h"
++#include "filter_graph.h"
++
++// Library includes
++#include
++#include
++
++// Default namespace
++using namespace std;
++
++
++// ****************************************************************************
++// Function definitions
++// ****************************************************************************
++
++
++// ***** Auxiliary (non-member) functions *************************************
++
++
++/*
++ * Private function that builds a flow network corresponding to the filter
++ * graph.
++ * In three iterations the function traverses all edges of the filter graph
++ * (i.e., s_node->src_nodes, src_nodes->dst_nodes, and dst_nodes->t_node edges)
++ * and constructs the flow network corresponding to this graph. Each edge of
++ * the filter graph can be represented by at most two edges of the flow
++ * network -- a forward and backward edge. Note that they are included into the
++ * flow network only if their residual capacity is greater than zero.
++ * The function expects that the input graph is already a valid flow network,
++ * i.e., it contains only one node without input edges (the 's' node) and only
++ * one node without output edges (the 't' node).
++ * @param network Reference to the (most probably empty) flow network object.
++ * @param graph Pointer to the constant filter graph object.
++ */
++void build_flow_network(Flow_network& network, const Filter_graph* graph) {
++ // declatarion of "phase" variables
++ const node_list_item* list;
++ int src_list;
++ int dst_list;
++
++ // iteration through three phases
++ for (int i = 0; i < 3; i++) {
++ // setting of phase variables
++ switch (i) {
++ case 0: // edges from the 's' node to destination nodes
++ list = graph->get_s_node();
++ src_list = 0;
++ dst_list = 1;
++ break;
++ case 1: // edges from source nodes to destination nodes
++ list = graph->get_src_nodes();
++ src_list = 1;
++ dst_list = 2;
++ break;
++ case 2: // edges from destination nodes to the 'd' node
++ list = graph->get_dst_nodes();
++ src_list = 2;
++ dst_list = 3;
++ break;
++ }
++
++ // iteration over all nodes in the list
++ while (list != NULL) {
++ neighbour_list_item* neighbour = list->neighbours;
++ // iteration over all neighbours (i.e., edges of the filter graph)
++ while (neighbour != NULL) {
++ // compute residual capacity of forward and backward edges
++ int capacity_forward = neighbour->weight - neighbour->flow;
++ int capacity_backward = neighbour->flow;
++ // insert the forward edge, if it has capacity > 0
++ if (capacity_forward > 0) {
++ network.add_edge(list->prefix, src_list,
++ neighbour->node->prefix, dst_list,
++ neighbour, true, capacity_forward);
++ }
++ // insert the backward edge, if it has capacity > 0
++ if (capacity_backward > 0) {
++ network.add_edge(neighbour->node->prefix, dst_list,
++ list->prefix, src_list,
++ neighbour, false, capacity_backward);
++ }
++ // move to the next edge
++ neighbour = neighbour->next;
++ }
++ // move to the next node
++ list = list->next;
++ }
++ }
++} // end build_flow_network()
++
++
++// ***** Private functions ****************************************************
++
++
++/*
++ * Private static function that creates deep copy of the original list.
++ */
++node_list_item* Filter_graph::copy_node_list(const node_list_item* orig) {
++ // initialize pointer to copied node list
++ node_list_item* result = (orig == NULL) ?
++ NULL :
++ new node_list_item;
++
++ // node list level of copying
++ node_list_item* copy = result;
++ while (orig != NULL) {
++ // list item members initialization
++ copy->prefix = orig->prefix;
++ copy->pruned = orig->pruned;
++ copy->neighbours = (orig->neighbours == NULL) ?
++ NULL :
++ new neighbour_list_item;
++ copy->next = (orig->next == NULL) ?
++ NULL :
++ new node_list_item;
++
++ // neighbour list level of copying
++ neighbour_list_item* orig_neighbours = orig->neighbours;
++ neighbour_list_item* copy_neighbours = copy->neighbours;
++ while (orig_neighbours != NULL) {
++ // list item members initialization
++ copy_neighbours->node = NULL; // cannot be initialized directly
++ // (points to different node list)
++ copy_neighbours->weight = orig_neighbours->weight;
++ copy_neighbours->flow = orig_neighbours->flow;
++ copy_neighbours->filters = (orig_neighbours->filters == NULL) ?
++ NULL :
++ new filter_list_item;
++ copy_neighbours->next = (orig_neighbours->next == NULL) ?
++ NULL :
++ new neighbour_list_item;
++
++ // filter list level of copying
++ filter_list_item* orig_filters = orig_neighbours->filters;
++ filter_list_item* copy_filters = copy_neighbours->filters;
++ while (orig_filters != NULL) {
++ // list item members initialization
++ copy_filters->filter = orig_filters->filter;
++ copy_filters->next = (orig_filters->next == NULL) ?
++ NULL :
++ new filter_list_item;
++ // move to the next item of filter list
++ copy_filters = copy_filters->next;
++ orig_filters = orig_filters->next;
++ }
++
++ // move to the next item of neighbour list
++ copy_neighbours = copy_neighbours->next;
++ orig_neighbours = orig_neighbours->next;
++ }
++
++ // move to the next item of node list
++ copy = copy->next;
++ orig = orig->next;
++ }
++
++ return result;
++} // end copy_node_list()
++
++
++/*
++ * Private static function that correctly deallocates the whole node list.
++ */
++void Filter_graph::remove_node_list(node_list_item** list) {
++ // traverse all nodes
++ while ((*list) != NULL) {
++ // get pointer to the first node
++ node_list_item* first_node = *list;
++
++ // correctly deallocate its whole neighbour list
++ remove_neighbour_list(&(first_node->neighbours));
++
++ // move to the next node list item and deallocate the current one
++ node_list_item* next_node = first_node->next;
++ delete first_node;
++ *list = next_node;
++ }
++} // end remove_node_list()
++
++
++/*
++ * Private static function that correctly deallocates the whole neighbour
++ * list.
++ */
++void Filter_graph::remove_neighbour_list(neighbour_list_item** list) {
++ // traverse all neighbours
++ while ((*list) != NULL) {
++ // get pointer to the first neighbour
++ neighbour_list_item* first_neighbour = *list;
++
++ // correctly deallocate it whole filter list
++ remove_filter_list(&(first_neighbour->filters));
++
++ // move to the next neighbour list item and deallocate the current one
++ neighbour_list_item* next_neighbour = first_neighbour->next;
++ delete first_neighbour;
++ *list = next_neighbour;
++ }
++} // end remove_neighbour_list()
++
++
++/*
++ * Private static function that correctly deallocates the whole filter list.
++ */
++void Filter_graph::remove_filter_list(filter_list_item** list) {
++ // traverse all filters
++ while ((*list) != NULL) {
++ // get pointer to the first filter
++ filter_list_item* first_filter = *list;
++
++ // move to the next filter list item and deallocate the current one
++ filter_list_item* next_filter = first_filter->next;
++ delete first_filter;
++ *list = next_filter;
++ }
++} // end remove_filter_list()
++
++
++/*
++ * Private static function that sets correct values of neighbour node pointers,
++ * which cannot be correctly initialized during copying.
++ */
++void Filter_graph::set_neighbour_nodes(const node_list_item* orig,
++ node_list_item* src,
++ node_list_item* dst) {
++ // traverse all nodes
++ while (orig != NULL) {
++ neighbour_list_item* orig_neighbours = orig->neighbours;
++ neighbour_list_item* src_neighbours = src->neighbours;
++
++ // traverse all neighbours
++ while (orig_neighbours != NULL) {
++ // set correct neighbour node pointer
++ src_neighbours->node = find_node(orig_neighbours->node->prefix, dst);
++
++ // move to the next item of neighbour list
++ orig_neighbours = orig_neighbours->next;
++ src_neighbours = src_neighbours->next;
++ }
++
++ // move to the next item of node list
++ orig = orig->next;
++ src = src->next;
++ }
++
++ return;
++} // end set_neighbour_nodes()
++
++
++/*
++ * Private static function that looks for node with the given prefix in the
++ * given list of nodes.
++ */
++node_list_item* Filter_graph::find_node(const IP_prefix& prefix,
++ node_list_item* list) {
++ // traverse all nodes
++ while (list != NULL) {
++ if (list->prefix == prefix) {
++ // corresponding node - return pointer to it
++ return list;
++ } else {
++ // node with different prefix - move to the next item of node list
++ list = list->next;
++ }
++ }
++
++ // return NULL if corresponding node was not found
++ return NULL;
++} // end find_node()
++
++
++/*
++ * Private static function that looks for the corresponding neighbour node in
++ * the given list of neighbours.
++ */
++neighbour_list_item* Filter_graph::find_neighbour(
++ const node_list_item* neighbour,
++ neighbour_list_item* list) {
++ // traverse all neighbour nodes
++ while (list != NULL) {
++ if (list->node->prefix == neighbour->prefix) {
++ // corresponding neighbour node - return pointer to it
++ return list;
++ } else {
++ // neighbour node corresponding to different prefix node - move to the
++ // next item of neighbour list
++ list = list->next;
++ }
++ }
++
++ // return NULL if corresponding neighbour node was not found
++ return NULL;
++} // end find_neighbour()
++
++
++/*
++ * Private static function that looks for the filter node pointing to the
++ * specified filter in the given list of filters.
++ */
++filter_list_item* Filter_graph::find_filter(const struct filter* filter,
++ filter_list_item* list) {
++ // traverse all filter nodes
++ while (list != NULL) {
++ if (list->filter == filter) {
++ // corresponding filter node - return pointer to it
++ return list;
++ } else {
++ // filter node pointing to different filter - move to the next item of
++ // filter list
++ list = list->next;
++ }
++ }
++
++ // return NULL if corresponding filter node was not found
++ return NULL;
++} // end find_filter()
++
++
++/*
++ * Private static function that inserts a node representing the given IP prefix
++ * at the beginning of the given list of nodes.
++ */
++void Filter_graph::insert_node(const IP_prefix& prefix,
++ node_list_item** list) {
++ // allocate and initialize new prefix node
++ node_list_item* node = new node_list_item;
++ node->prefix = prefix;
++ node->pruned = false;
++ node->neighbours = NULL;
++
++ // insert new prefix node at the beginning of the list
++ node->next = *list;
++ *list = node;
++
++ return;
++} // end insert_node()
++
++
++/*
++ * Private static function that inserts a neighbour node corresponding to the
++ * given prefix node at the beginning of the given list of neighbour nodes.
++ */
++void Filter_graph::insert_neighbour(node_list_item* neighbour,
++ neighbour_list_item** list) {
++ // allocate and initialize new neighbour node
++ neighbour_list_item* node = new neighbour_list_item;
++ node->node = neighbour;
++ node->weight = 0;
++ node->flow = 0;
++ node->filters = NULL;
++
++ // insert new neighbour node at the beginning of the list
++ node->next = *list;
++ *list = node;
++
++ return;
++} // end insert_neighbour()
++
++
++/*
++ * Private static function that inserts a filter node pointing to the given
++ * filter at the beginning of the given list of filter nodes.
++ */
++void Filter_graph::insert_filter(const struct filter* filter,
++ filter_list_item** list) {
++ // allocate and initialize new filter node
++ filter_list_item* node = new filter_list_item;
++ node->filter = filter;
++
++ // insert new filter node at the beginning of the list
++ node->next = *list;
++ *list = node;
++
++ return;
++} // end insert_filter()
++
++
++/*
++ * Private static function that prints specified node list, including all
++ * sublists (i.e., neighbour lists and filter lists).
++ */
++void Filter_graph::print_node_list(const node_list_item* nodes) {
++ while (nodes != NULL) {
++ // print node list items
++ cout << "+-> " << nodes->prefix.get_prefix() << "/"
++ << nodes->prefix.get_length() << endl;
++ neighbour_list_item* neighbours = nodes->neighbours;
++ while (neighbours != NULL) {
++ // print neighbour list items
++ cout << "| +-> " << neighbours->node->prefix.get_prefix() << "/"
++ << neighbours->node->prefix.get_length() << " "
++ << "("
++ << neighbours->flow << "/"
++ << neighbours->weight
++ << ")" << endl;
++ filter_list_item* filters = neighbours->filters;
++ while (filters != NULL) {
++ // print filter list items
++ cout << "| | +-> " << filters->filter->sp[0] << ":"
++ << filters->filter->sp[1] << " "
++ << filters->filter->dp[0] << ":"
++ << filters->filter->dp[1] << " "
++ << filters->filter->prot_num << endl;
++ // move to the next filter
++ filters = filters->next;
++ }
++ // moce tothe next neighbour
++ neighbours = neighbours->next;
++ }
++ // move to the next node
++ nodes = nodes->next;
++ }
++} // end print_node_list()
++
++
++/*
++ * Private function that removes non-pruned nodes and resets the "pruned"
++ * flag of pruned nodes of the filter graph.
++ */
++void Filter_graph::remove_and_reset() {
++ for (int i = 0; i < 4; i++) {
++ // select the correct node list for iteration
++ node_list_item** node_ptr;
++ switch (i) {
++ case 0:
++ node_ptr = &s_node;
++ break;
++ case 1:
++ node_ptr = &src_nodes;
++ break;
++ case 2:
++ node_ptr = &dst_nodes;
++ break;
++ case 3:
++ node_ptr = &t_node;
++ break;
++ }
++
++ // iterate over all nodes in the list
++ while ((*node_ptr) != NULL) {
++ node_list_item* node = *node_ptr;
++ if (node->pruned == false) { // remove the non-pruned node
++ (*node_ptr) = node->next;
++ delete node;
++ } else { // reset the "pruned" flag of the pruned node
++ node->pruned = false;
++ node_ptr = &(node->next);
++ }
++ }
++ }
++} // end remove_and_reset()
++
++
++// ***** Public functions *****************************************************
++
++
++/*
++ * Default constructor.
++ */
++Filter_graph::Filter_graph() {
++ src_nodes = NULL;
++ dst_nodes = NULL;
++ s_node = NULL;
++ t_node = NULL;
++} // end Filter_graph()
++
++
++/*
++ * Copy constructor.
++ */
++Filter_graph::Filter_graph(const Filter_graph& orig) {
++ // acquire members of the original object
++ const node_list_item* orig_src_nodes = orig.get_src_nodes();
++ const node_list_item* orig_dst_nodes = orig.get_dst_nodes();
++ const node_list_item* orig_s_node = orig.get_s_node();
++ const node_list_item* orig_t_node = orig.get_t_node();
++
++ // copy member node lists one by one
++ src_nodes = copy_node_list(orig_src_nodes);
++ dst_nodes = copy_node_list(orig_dst_nodes);
++ s_node = copy_node_list(orig_s_node);
++ t_node = copy_node_list(orig_t_node);
++
++ // set pointers to neighbour nodes
++ set_neighbour_nodes(orig_t_node, t_node, NULL);
++ set_neighbour_nodes(orig_dst_nodes, dst_nodes, t_node);
++ set_neighbour_nodes(orig_src_nodes, src_nodes, dst_nodes);
++ set_neighbour_nodes(orig_s_node, s_node, src_nodes);
++} // end Filter_graph()
++
++
++/*
++ * Destructor.
++ */
++Filter_graph::~Filter_graph() {
++ remove_node_list(&s_node);
++ remove_node_list(&src_nodes);
++ remove_node_list(&dst_nodes);
++ remove_node_list(&t_node);
++} // end ~Filter_graph()
++
++
++/*
++ * Copy assignment.
++ */
++Filter_graph& Filter_graph::operator=(const Filter_graph& copy) {
++ // destruct the original object
++ remove_node_list(&s_node);
++ remove_node_list(&src_nodes);
++ remove_node_list(&dst_nodes);
++ remove_node_list(&t_node);
++
++ // acquire members of the copied object
++ const node_list_item* copy_src_nodes = copy.get_src_nodes();
++ const node_list_item* copy_dst_nodes = copy.get_dst_nodes();
++ const node_list_item* copy_s_node = copy.get_s_node();
++ const node_list_item* copy_t_node = copy.get_t_node();
++
++ // copy member node lists one by one
++ src_nodes = copy_node_list(copy_src_nodes);
++ dst_nodes = copy_node_list(copy_dst_nodes);
++ s_node = copy_node_list(copy_s_node);
++ t_node = copy_node_list(copy_t_node);
++
++ // set pointers to neighbour nodes
++ set_neighbour_nodes(copy_t_node, t_node, NULL);
++ set_neighbour_nodes(copy_dst_nodes, dst_nodes, t_node);
++ set_neighbour_nodes(copy_src_nodes, src_nodes, dst_nodes);
++ set_neighbour_nodes(copy_s_node, s_node, src_nodes);
++
++ // return the original object with new content
++ return *this;
++} // end operator=()
++
++
++/*
++ * Modifies filter graph to add the specified filter into the set of filters
++ * represented by the graph.
++ */
++void Filter_graph::add_filter(const struct filter* filter) {
++ // acquire source and destination prefixes
++ IP_prefix src_pref(filter->sa, filter->sa_len);
++ IP_prefix dst_pref(filter->da, filter->da_len);
++
++ // find source prefix node and insert such node if it does not exist
++ node_list_item* src_node = find_node(src_pref, src_nodes);
++ if (src_node == NULL) {
++ // node is inserted at the beginning of the list
++ insert_node(src_pref, &src_nodes);
++ src_node = src_nodes;
++ }
++
++ // find destination prefix node and insert such node if it does not exist
++ node_list_item* dst_node = find_node(dst_pref, dst_nodes);
++ if (dst_node == NULL) {
++ // node is inserted at the beginning of the list
++ insert_node(dst_pref, &dst_nodes);
++ dst_node = dst_nodes;
++ }
++
++ // find neighbour node corresponding to the filter and insert such node if
++ // it does not exist
++ neighbour_list_item* neighbour = find_neighbour(dst_node,
++ src_node->neighbours);
++ if (neighbour == NULL) {
++ // neighbour is inserted at the beginning of the list
++ insert_neighbour(dst_node, &(src_node->neighbours));
++ neighbour = src_node->neighbours;
++ }
++
++ // find filter node pointing to the specified filter and insert such node if
++ // it does not exist
++ filter_list_item* filter_node = find_filter(filter, neighbour->filters);
++ if (filter_node == NULL) {
++ insert_filter(filter, &(neighbour->filters));
++ neighbour->weight += 1;
++ }
++} // end add_filter()
++
++
++/*
++ * Adds an edge from the 's' node to a node representing the given prefix
++ * within the source nodes list and sets its weight to the given value.
++ */
++void Filter_graph::add_s_prefix(const IP_prefix& prefix, const int weight) {
++ // check existence of the 's' node and allocate it if it does not exist
++ if (s_node == NULL) {
++ insert_node(IP_prefix(), &s_node);
++ s_node->pruned = true;
++ }
++
++ // find a node representing the given prefix within the source nodes list
++ // (existence of such node is not checked since it should always exist)
++ node_list_item* src_node = find_node(prefix, src_nodes);
++
++ // find a neighbour node corresponding to the given prefix
++ neighbour_list_item* neighbour = find_neighbour(src_node,
++ s_node->neighbours);
++ if (neighbour == NULL) { // the neighbour node does not exist
++ // insert the node at the beginning of the list
++ insert_neighbour(src_node, &(s_node->neighbours));
++ neighbour = s_node->neighbours;
++ // set neighbour node's weight member
++ neighbour->weight = weight;
++ } else { // the neighbour node exists
++ // adjust neighbour node's weight member accordingly
++ neighbour->weight += weight;
++ }
++
++ // set source node's pruned flag to true
++ src_node->pruned = true;
++} // end add_s_prefix()
++
++
++/*
++ * Adds an edge from a node representing the given prefix within the
++ * destination nodes list to the 't' node and sets its weight to the given
++ * value.
++ */
++void Filter_graph::add_t_prefix(const IP_prefix& prefix, const int weight) {
++ // check existence of the 't' node and allocate it if it does not exist
++ if (t_node == NULL) {
++ insert_node(IP_prefix(), &t_node);
++ t_node->pruned = true;
++ }
++
++ // find a node representing the given prefix within the destination nodes
++ // list
++ // (existence of such node is not checked since it should always exist)
++ node_list_item* dst_node = find_node(prefix, dst_nodes);
++
++ // find a neighbour node corresponding to the 't' node
++ neighbour_list_item* neighbour = find_neighbour(t_node,
++ dst_node->neighbours);
++ if (neighbour == NULL) { // the neighbour node does not exist
++ // insert the node at the beginning of the list
++ insert_neighbour(t_node, &(dst_node->neighbours));
++ neighbour = dst_node->neighbours;
++ // set neighbour node's weight member
++ neighbour->weight = weight;
++ } else { // the neighbour node exists
++ // adjust neighbour node's weight member accordingly
++ neighbour->weight += weight;
++ }
++
++ // set destination node's pruned flag to true
++ dst_node->pruned = true;
++} // end add_t_prefix()
++
++
++/*
++ * Modifies the filter graph such that it conforms with the flow network
++ * specification -- i.e., it has only one node without input edges (the
++ * 's' node) and only one node without output edges (the 't' node).
++ */
++void Filter_graph::to_flow_network() {
++ // 1) remove filters containing at least one non-pruned prefix
++ node_list_item* node = src_nodes;
++ while (node != NULL) {
++ if (node->pruned == false) { // remove all neighbours of this node
++ remove_neighbour_list(&(node->neighbours));
++ } else { // remove only neighbours pointing to a non-pruned node
++ neighbour_list_item** neighbour_ptr = &(node->neighbours);
++ while ((*neighbour_ptr) != NULL) {
++ neighbour_list_item* neighbour = *neighbour_ptr;
++ if (neighbour->node->pruned == false) { // remove this neighbour
++ remove_filter_list(&(neighbour->filters));
++ *neighbour_ptr = neighbour->next;
++ delete neighbour;
++ } else { // just move to the next neighbour
++ neighbour_ptr = &(neighbour->next);
++ }
++ }
++ }
++ node = node->next;
++ }
++
++ // 2) remove non-pruned nodes and reset the "pruned" flag of other nodes
++ remove_and_reset();
++
++ // 3) BFS to set the "pruned" flag of visited nodes with at least one
++ // neighbour and the 's' and 't' nodes
++ queue q;
++ q.push(s_node);
++ // do the BFS
++ while (!q.empty()) {
++ // dequeue the front element of the queue
++ node_list_item* node = q.front();
++ q.pop();
++ // set the "pruned" flag if the list of neighbours is non-empty
++ if (node->neighbours != NULL) {
++ node->pruned = true;
++ }
++ // insert neighbours into the queue
++ neighbour_list_item* neighbour = node->neighbours;
++ while (neighbour != NULL) {
++ q.push(neighbour->node);
++ neighbour = neighbour->next;
++ }
++ }
++ // set the "pruned" flag of the 's' and 't' nodes
++ s_node->pruned = true;
++ t_node->pruned = true;
++
++ // 4) remove edges from the 's' node going to non-pruned nodes
++ neighbour_list_item** neighbour_ptr = &(s_node->neighbours);
++ while ((*neighbour_ptr) != NULL) {
++ neighbour_list_item* neighbour = *neighbour_ptr;
++ if (neighbour->node->pruned == false) { // remove this edge
++ // there are no filters represented by edges from the 's' node
++ (*neighbour_ptr) = neighbour->next;
++ delete neighbour;
++ } else { // move to the next edge
++ neighbour_ptr = &(neighbour->next);
++ }
++ }
++
++ // 5) remove edges to the 't' node going from non-pruned nodes
++ node = dst_nodes;
++ while (node != NULL) {
++ if (node->pruned == false) { // remove all edges going from this node
++ remove_neighbour_list(&(node->neighbours));
++ }
++ // move to the next node
++ node = node->next;
++ }
++
++ // 6) remove non-pruned nodes (and reset the "pruned" flag of other nodes)
++ remove_and_reset();
++} // end to_flow_network()
++
++
++/*
++ * Computes maximum flow using Dinic's algorithm.
++ */
++int Filter_graph::max_flow() {
++ int max_flow = 0;
++ int flow_inc;
++
++ do { // iterate until the flow cannot be improved
++ // build flow network corresponding to the filter graph
++ Flow_network network;
++ build_flow_network(network, this);
++
++ // transform the flow network into the level graph
++ network.to_level_graph();
++
++ // compute a blocking flow in the flow network and update the flow in the
++ // filter graph accordingly
++ flow_inc = network.find_blocking_flow();
++ max_flow += flow_inc;
++ } while (flow_inc != 0);
++
++ return max_flow;
++} // end max_flow()
++
++
++/*
++ * Prints the filter graph.
++ */
++void Filter_graph::print() {
++ cout << "S_NODE:" << endl;
++ print_node_list(s_node);
++ cout << "--------------------" << endl;
++
++ cout << "SRC_NODES:" << endl;
++ print_node_list(src_nodes);
++ cout << "--------------------" << endl;
++
++ cout << "DST_NODES:" << endl;
++ print_node_list(dst_nodes);
++ cout << "--------------------" << endl;
++
++ cout << "T_NODE:" << endl;
++ print_node_list(t_node);
++ cout << "--------------------" << endl;
++}
+diff --git a/filter_graph.h b/filter_graph.h
+new file mode 100644
+index 0000000..601f2cd
+--- /dev/null
++++ b/filter_graph.h
+@@ -0,0 +1,437 @@
++// filter_graph.h: header file for Filter_graph class
++//
++// Jiri Matousek, 2017
++// imatousek@fit.vutbr.cz
++
++
++#ifndef FILTER_GRAPH_H
++#define FILTER_GRAPH_H
++
++
++// User includes
++#include "ip_prefix.h"
++
++// Library includes
++
++// Default namespace
++using namespace std;
++
++
++// ****************************************************************************
++// Structures declaration
++// ****************************************************************************
++
++
++// Forward declarations (including typedef) to resolve circular dependency
++typedef struct node_list_item node_list_item;
++typedef struct neighbour_list_item neighbour_list_item;
++typedef struct filter_list_item filter_list_item;
++
++/*
++ * A structure representing an item in a list of graph nodes.
++ */
++struct node_list_item {
++ // IP prefix represented by this node
++ IP_prefix prefix;
++
++ // Flag that is utilized for "pruning" filters once the complete filter
++ // graph is constructed
++ bool pruned;
++
++ // List of neighbours
++ neighbour_list_item* neighbours;
++
++ // Next item in list of graph nodes
++ node_list_item* next;
++};
++
++/*
++ * A structure representing an item in a list of neighbours of a graph node.
++ */
++struct neighbour_list_item {
++ // Pointer to neighbour node within list of graph nodes
++ node_list_item* node;
++
++ // Weight of filter graph's edge between neighbouring nodes
++ int weight;
++
++ // Flow through filter graph's edge between neighbouring nodes
++ int flow;
++
++ // List of filters that specify prefixes of neighbouring nodes
++ filter_list_item* filters;
++
++ // Next item in list of neighbours
++ neighbour_list_item* next;
++};
++
++/*
++ * A Structure representing an item in a list of filters that specify prefixes
++ * of neighbouring graph nodes.
++ */
++struct filter_list_item {
++ // Pointer to representation of filter
++ // (struct filter is defined in stdinc.h included in custom_db.cc)
++ const struct filter* filter;
++
++ // Next item in list of filters
++ filter_list_item* next;
++};
++
++
++// ****************************************************************************
++// Class declaration
++// ****************************************************************************
++
++/*
++ * Class for representation of a set of filters as a filter graph -- i.e.,
++ * weighted directed bipartite graph with special source (s) and terminate (t)
++ * nodes -- that is constructed according to filters' source and destination
++ * prefixes and pruned source and destination prefix tries.
++ */
++class Filter_graph {
++
++
++ // ***** Private members ***************************************************
++
++
++ private:
++ /*
++ * Lists of source and destination nodes of the filter graph.
++ */
++ node_list_item* src_nodes;
++ node_list_item* dst_nodes;
++
++ /*
++ * Special nodes 's' and 't' of the filter graph.
++ */
++ node_list_item* s_node;
++ node_list_item* t_node;
++
++ /*
++ * Private static function that creates deep copy of the original list.
++ * The function creates new instances of all items from the original list
++ * (as well as all sublists) and sets the value of their components
++ * according to this original list. The only exception is pointer to
++ * neighbour node, which is initialized to NULL (it points to different
++ * list, thus it cannot be initialized to correct value during copying).
++ * @param orig Pointer to the constant original node list.
++ * @return Pointer to the copy of the original node list.
++ */
++ static node_list_item* copy_node_list(const node_list_item* orig);
++
++ /*
++ * Private static function that correctly deallocates the whole node
++ * list.
++ * The function traverses the given node list and all its sublists and
++ * starting from the inner-most list it deallocates all the traversed
++ * list items.
++ * @ param list Pointer to pointer to node list that is to be
++ * deallocated.
++ */
++ static void remove_node_list(node_list_item** list);
++
++ /*
++ * Private static function that correctly deallocates the whole neighbour
++ * list.
++ * The function traverses the given neighbour list and all its sublists
++ * and starting from the inner-most list it deallocates all the traversed
++ * list items.
++ * @param Pointer to pointer to the neighbour list that is to be
++ * deallocated.
++ */
++ static void remove_neighbour_list(neighbour_list_item** list);
++
++ /*
++ * Private static function that correctly deallocates the whole filter
++ * list.
++ * The function traverses the given filter list and deallocates all the
++ * traversed list items.
++ * @param Pointer to pointer to the filter list that is to be
++ * deallocated.
++ */
++ static void remove_filter_list(filter_list_item** list);
++
++ /*
++ * Private static function that sets correct values of neighbour node
++ * pointers, which cannot be correctly initialized during copying.
++ * The function simultaneously traverses neighbours lists in orig and src
++ * node lists. For each neighbour node from orig list it looks for
++ * corresponding item in dst node list and stores pointer to this node to
++ * current item in src list.
++ * @param orig Pointer to the constant original node list.
++ * @param src Pointer to the list of source nodes (neighbour node
++ * pointers of this list are set).
++ * @param dst Pointer to the list of destination nodes (nodes of this
++ * list act as targets of neighbour node pointers within
++ * src node list).
++ */
++ static void set_neighbour_nodes(const node_list_item* orig,
++ node_list_item* src,
++ node_list_item* dst);
++
++// PROBABLY IT IS NOT NECESSARY TO HAVE THIS FUNCTION PRIVATE
++ /*
++ * Private static function that looks for node with the given prefix in
++ * the given list of nodes.
++ * @param prefix Reference to a constant IP prefix of node that the
++ * function looks for in the list.
++ * @param list Pointer to list of nodes that is traversed during
++ * looking for node with the given IP prefix.
++ * @return Pointer to found node or NULL.
++ */
++ static node_list_item* find_node(const IP_prefix& prefix,
++ node_list_item* list);
++
++// PROBABLY IT IS NOT NECESSARY TO HAVE THIS FUNCTION PRIVATE
++ /*
++ * Private static function that looks for the corresponding neighbour
++ * node in the given list of neighbours.
++ * @param neighbour Pointer to a constant prefix node whose neighbour
++ * node the function looks for in the list.
++ * @param list Pointer to the list of neighbour nodes that is
++ * traversed during looking for the node corresponding
++ * to the given prefix node.
++ * @return Pointer to the found neighbour node or NULL.
++ */
++ static neighbour_list_item* find_neighbour(
++ const node_list_item* neighbour,
++ neighbour_list_item* list);
++
++// PROBABLY IT IS NOT NECESSARY TO HAVE THIS FUNCTION PRIVATE
++ /*
++ * Private static function that looks for the filter node pointing to the
++ * specified filter in the given list of filters.
++ * @param filter Pointer to the constant filter that is referenced by
++ * the filter node the function looks for in the list.
++ * @param list Pointer to the list of filter nodes that is traversed
++ * during looking for a node pointing to the given
++ * filter.
++ * @return Pointer to the found filter node or NULL.
++ */
++ static filter_list_item* find_filter(const struct filter* filter,
++ filter_list_item* list);
++
++ /*
++ * Private static function that inserts a node representing the given IP
++ * prefix at the beginning of the given list of nodes.
++ * @param prefix Reference to a constant IP prefix that is going to be
++ * represented by the inserted node.
++ * @param list Pointer to pointer to the list of nodes that is going
++ * to be extended by the inserted node.
++ */
++ static void insert_node(const IP_prefix& prefix,
++ node_list_item** list);
++
++ /*
++ * Private static function that inserts a neighbour node corresponding to
++ * the given prefix node at the beginning of the given list of
++ * neighbour nodes.
++ * @param neighbour Pointer to a prefix node whose neighbour node is
++ * going to be inserted.
++ * @param list Pointer to pointer to the list of neighbours that
++ * is going to be extended by the inserted node.
++ */
++ static void insert_neighbour(node_list_item* neighbour,
++ neighbour_list_item** list);
++
++ /*
++ * Private static function that inserts a filter node pointing to the
++ * given filter at the beginning of the given list of filter nodes.
++ * @param node Pointer to the constant filter that is going to be
++ * referenced by the inserted filter node.
++ * @param list Pointer to pointer to the list of filters that is going
++ * to be extended by the inserted node.
++ */
++ static void insert_filter(const struct filter* filter,
++ filter_list_item** list);
++
++ /*
++ * Private static function that prints specified node list, including all
++ * sublists (i.e., neighbour lists and filter lists).
++ * @param nodes Pointer to the constant list of nodes that is to be
++ * printed.
++ */
++ static void print_node_list(const node_list_item* nodes);
++
++ /*
++ * Private function that removes non-pruned nodes and resets the "pruned"
++ * flag of pruned nodes of the filter graph.
++ * The function performs the specified function on all nodes of the
++ * filter graph (i.e., source and destination nodes as well as the 's'
++ * and 't' nodes).
++ * The function expects that neighbours list referenced from the
++ * non-pruned nodes have been correctly deallocated before calling this
++ * function.
++ */
++ void remove_and_reset();
++
++
++ // ***** Public members ****************************************************
++
++
++ public:
++ /*
++ * Default constructor.
++ * All pointers are initialized to NULL.
++ */
++ Filter_graph();
++
++ /*
++ * Copy constructor.
++ * All pointers are initialized to a deep copy of corresponding members
++ * of the original object.
++ * @param orig Reference to the constant original object.
++ */
++ Filter_graph(const Filter_graph& orig);
++
++ /*
++ * Destructor.
++ * Correctly deallocates all lists that are referenced by object members.
++ */
++ ~Filter_graph();
++
++ /*
++ * Copy assignment.
++ * The original object is destructed and its new content is constructed
++ * in similar way as in the copy constructor.
++ * @param copy Reference to the constant copied object.
++ * @return Reference to the original object with new content.
++ */
++ Filter_graph& operator= (const Filter_graph& copy);
++
++ /*
++ * Get function for the src_nodes member.
++ * @return Pointer to the constant list of source nodes.
++ */
++ inline const node_list_item* get_src_nodes() const {
++ return src_nodes;
++ } // end get_src_nodes()
++
++ /*
++ * Get function for the dst_nodes member.
++ * @return Pointer to the constant list of destination nodes.
++ */
++ inline const node_list_item* get_dst_nodes() const {
++ return dst_nodes;
++ } // end get_dst_nodes()
++
++ /*
++ * Get function for the s_node member.
++ * @return Pointer to the constant special 's' node.
++ */
++ inline const node_list_item* get_s_node() const {
++ return s_node;
++ } // end get_s_node()
++
++ /*
++ * Get function for the t_node member.
++ * @return Pointer to the constant special 't' node.
++ */
++ inline const node_list_item* get_t_node() const {
++ return t_node;
++ } // end get_t_node()
++
++ /*
++ * Modifies filter graph to add the specified filter into the set of
++ * filters represented by the graph.
++ * The function searches src_nodes and dst_nodes lists for nodes
++ * representing source and destination prefixes of the given filter and
++ * inserts such nodes into these lists if they are not found. Next, the
++ * function seraches neighbours list of source prefix node for neighbour
++ * node representing destination prefix of the given filter and inserts
++ * such neighbour node into the list if it is not found. Finally, the
++ * function searches filters list of neighbouring node for pointer to the
++ * given filter and inserts such pointer into the list if it is not
++ * found. Along with inserting new filter pointer to the list, the
++ * function increments weight item of neighbouring node representation.
++ * @param filter Pointer to a constant structure representing inserted
++ * filter.
++ */
++ void add_filter(const struct filter* filter);
++
++ /*
++ * Adds an edge from the 's' node to a node representing the given prefix
++ * within the source nodes list and sets its weight to the given value.
++ * First of all, the function checks whether the 's' node has already
++ * been allocated and allocates this node in case it does not yet exist.
++ * Next, the function searches the neighbours list of the 's' node for a
++ * neighbour node representing the given prefix. If such neighbour node
++ * exists, the function just adds the given weight to the current value
++ * of its weight counter. If such neighbour node does not exist, the
++ * function inserts the node into the list and sets its weight counter to
++ * the given value. The function also sets the "pruned" flag of the node
++ * representing the given prefix to true.
++ * The implementation expects that all filters have already been added to
++ * the filter graph and that only prefixes of a pruned source trie are
++ * added using this function. In such a case the source nodes list always
++ * contains a node representing the given prefix.
++ * @param prefix Reference to the IP_prefix object that determines a
++ * target node of the edge from the 's' node.
++ * @param weight Weight of the newly created edge.
++ */
++ void add_s_prefix(const IP_prefix& prefix, const int weight);
++
++ /*
++ * Adds an edge from a node representing the given prefix within the
++ * destination nodes list to the 't' node and sets its weight to the
++ * given value.
++ * First of all, the function checks whether the 't' node has already
++ * been allocated and allocates this node in case it does not yet exist.
++ * Next, the function searches the neighbours list of the node
++ * representing the given prefix within the destination nodes list for a
++ * neighbour node representing the 't' node. If such neighbour node
++ * exists, the function just adds the given weight to the current value
++ * of its weight counter. If such neighbour node does not exist, the
++ * function inserts the node into the list and sets its weight counter to
++ * the given value. The function also sets the "pruned" flag of the node
++ * representing the given prefix to true.
++ * The implementation expects that all filters have already been added to
++ * the filter graph and that only prefixes of a pruned destination trie
++ * are added using this function. In such a case the destination nodes
++ * list always contains a node representing the given prefix.
++ * @param prefix Reference to the IP_prefix object that determines a
++ * source node of the edge towards the 't' node.
++ * @param weight Weight of the newly created edge.
++ */
++ void add_t_prefix(const IP_prefix& prefix, const int weight);
++
++ /*
++ * Modifies the filter graph such that it conforms with the flow network
++ * specification -- i.e., it has only one node without input edges (the
++ * 's' node) and only one node without output edges (the 't' node).
++ * First of all, the function removes all edges representing filters with
++ * at least one non-pruned prefix and all nodes representing non-pruned
++ * prefixes. During this step, the "pruned" flag of the remaining nodes
++ * is also set to false. Next, the filter graph is traversed in a BFS
++ * manner and the "pruned" flag is set to true for all visited nodes with
++ * at least one input edge and one output edge. Finally, the function
++ * removes all edges going from the 's' node/to the 't' node that do not
++ * end/start in a node with the set "pruned" flag. These non-pruned nodes
++ * are also removed from the graph in this final step.
++ * The function expects that all steps of the filter graph construction
++ * (i.e., adding filters, s-prefixes, and t-prefixes) have already been
++ * performed before its invocation.
++ */
++ void to_flow_network();
++
++ /*
++ * Computes maximum flow using Dinic's algorithm.
++ * The function builds a flow network corresponding to the filter graph,
++ * transforms it to a level graph and updates the flow through the filter
++ * graph according to a blocking flow through the level graph. This way
++ * the flow through the filter graph is iteratively updated until there
++ * are paths from the 's' node to the 't' node in the level graph.
++ * The flow network is represented by an object of the Flow_network,
++ * which also allows transformation into the corresponding level graph.
++ * @return The value of the maximum flow through the filter graph.
++ */
++ int max_flow();
++
++ /*
++ * Prints the filter graph.
++ */
++ void print();
++};
++
++#endif
+diff --git a/flow_network.cc b/flow_network.cc
+new file mode 100644
+index 0000000..b5523da
+--- /dev/null
++++ b/flow_network.cc
+@@ -0,0 +1,592 @@
++// flow_network.cc: Flow_network class definition
++//
++// Jiri Matousek, 2017
++// imatousek@fit.vutbr.cz
++
++
++// User includes
++#include "flow_network.h"
++
++// Library includes
++#include
++#include
++#include
++#include
++
++// Default namespace
++using namespace std;
++
++
++// ****************************************************************************
++// Function definitions
++// ****************************************************************************
++
++
++// ***** Private functions (related to filter graph) **************************
++
++
++/*
++ * Private static function that creates deep copy of the original list.
++ */
++net_node_list_item* Flow_network::copy_node_list(const net_node_list_item* orig) {
++ // initialize pointer to copied node list
++ net_node_list_item* result = (orig == NULL) ?
++ NULL :
++ new net_node_list_item;
++
++ // node list level of copying
++ net_node_list_item* copy = result;
++ while (orig != NULL) {
++ // list item members initialization
++ copy->prefix = orig->prefix;
++ copy->visited = orig->visited;
++ copy->neighbours = (orig->neighbours == NULL) ?
++ NULL :
++ new net_neighbour_list_item;
++ copy->next = (orig->next == NULL) ?
++ NULL :
++ new net_node_list_item;
++
++ // neighbour list level of copying
++ net_neighbour_list_item* orig_neighbours = orig->neighbours;
++ net_neighbour_list_item* copy_neighbours = copy->neighbours;
++ while (orig_neighbours != NULL) {
++ // list item members initialization
++ copy_neighbours->src_node = copy;
++ copy_neighbours->dst_node = NULL; // cannot be initialized directly
++ // (points to different node list)
++ copy_neighbours->capacity = orig_neighbours->capacity;
++ copy_neighbours->flow = orig_neighbours->flow;
++ copy_neighbours->orig_edge = orig_neighbours->orig_edge;
++ copy_neighbours->forward_edge = orig_neighbours->forward_edge;
++ copy_neighbours->next = (orig_neighbours->next == NULL) ?
++ NULL :
++ new net_neighbour_list_item;
++
++ // move to the next item of neighbour list
++ copy_neighbours = copy_neighbours->next;
++ orig_neighbours = orig_neighbours->next;
++ }
++
++ // move to the next item of node list
++ copy = copy->next;
++ orig = orig->next;
++ }
++
++ return result;
++} // end copy_node_list()
++
++
++/*
++ * Private static function that correctly deallocates the whole node list.
++ */
++void Flow_network::remove_node_list(net_node_list_item* list) {
++ // traverse all nodes
++ while (list != NULL) {
++ net_neighbour_list_item* neighbours = list->neighbours;
++
++ // traverse all neighbours
++ while (neighbours != NULL) {
++ // move to the next neighbour list item and deallocate the current one
++ net_neighbour_list_item* current_neighbour = neighbours;
++ neighbours = neighbours->next;
++ delete current_neighbour;
++ }
++
++ // move to the next node list item and deallocate the current one
++ net_node_list_item* current_node = list;
++ list = list->next;
++ delete current_node;
++ }
++
++ return;
++} // end remove_node_list()
++
++
++/*
++ * Private static function that sets correct values of destination node
++ * pointers, which cannot be correctly initialized during copying.
++ */
++void Flow_network::set_destination_nodes(const net_node_list_item* orig,
++ net_node_list_item* copy,
++ net_node_list_item* prev,
++ net_node_list_item* next) {
++ // traverse all nodes
++ while (orig != NULL) {
++ net_neighbour_list_item* orig_neighbours = orig->neighbours;
++ net_neighbour_list_item* copy_neighbours = copy->neighbours;
++
++ // traverse all neighbours
++ while (orig_neighbours != NULL) {
++ // set correct destination node pointer
++ if (orig_neighbours->forward_edge) { // forward edge
++ copy_neighbours->dst_node =
++ find_node(orig_neighbours->dst_node->prefix, next);
++ } else { // backward edge
++ copy_neighbours->dst_node =
++ find_node(orig_neighbours->dst_node->prefix, prev);
++ }
++
++ // move to the next item of neighbour list
++ orig_neighbours = orig_neighbours->next;
++ copy_neighbours = copy_neighbours->next;
++ }
++
++ // move to the next item of node list
++ orig = orig->next;
++ copy = copy->next;
++ }
++
++ return;
++} // end set_destination_nodes()
++
++
++/*
++ * Private static function that looks for node with the given prefix in the
++ * given list of nodes.
++ */
++net_node_list_item* Flow_network::find_node(const IP_prefix& prefix,
++ net_node_list_item* list) {
++ // traverse all nodes
++ while (list != NULL) {
++ if (list->prefix == prefix) {
++ // corresponding node - return pointer to it
++ return list;
++ } else {
++ // node with different prefix - move to the next item of node list
++ list = list->next;
++ }
++ }
++
++ // return NULL if corresponding node was not found
++ return NULL;
++} // end find_node()
++
++
++/*
++ * Private static function that looks for the corresponding neighbour node in
++ * the given list of neighbours.
++ */
++net_neighbour_list_item* Flow_network::find_neighbour(
++ const net_node_list_item* neighbour,
++ net_neighbour_list_item* list) {
++ // traverse all neighbour nodes
++ while (list != NULL) {
++ if (list->dst_node->prefix == neighbour->prefix) {
++ // corresponding neighbour node - return pointer to it
++ return list;
++ } else {
++ // neighbour node corresponding to different prefix node - move to the
++ // next item of neighbour list
++ list = list->next;
++ }
++ }
++
++ // return NULL if corresponding neighbour node was not found
++ return NULL;
++} // end find_neighbour()
++
++
++/*
++ * Private static function that inserts a node representing the given IP prefix
++ * at the beginning of the given list of nodes.
++ */
++void Flow_network::insert_node(const IP_prefix& prefix,
++ net_node_list_item** list) {
++ // allocate and initialize new prefix node
++ net_node_list_item* node = new net_node_list_item;
++ node->prefix = prefix;
++ node->visited = false;
++ node->neighbours = NULL;
++
++ // insert new prefix node at the beginning of the list
++ node->next = *list;
++ *list = node;
++
++ return;
++} // end insert_node()
++
++
++/*
++ * Private static function that inserts a neighbour node representing an edge
++ * between the given source and destination nodes at the beginning of the
++ * specified list of neighbour nodes.
++ */
++void Flow_network::insert_neighbour(net_node_list_item* src_node,
++ net_node_list_item* dst_node,
++ int capacity,
++ int flow,
++ neighbour_list_item* orig_edge,
++ bool forward_edge,
++ net_neighbour_list_item** list) {
++ // allocate and initialize new neighbour node
++ net_neighbour_list_item* node = new net_neighbour_list_item;
++ node->src_node = src_node;
++ node->dst_node = dst_node;
++ node->capacity = capacity;
++ node->flow = flow;
++ node->orig_edge = orig_edge;
++ node->forward_edge = forward_edge;
++
++ // insert new neighbour node at the beginning of the list
++ node->next = *list;
++ *list = node;
++
++ return;
++} // end insert_neighbour()
++
++
++/*
++ * Private static function that prints specified node list, including all
++ * sublists.
++ */
++void Flow_network::print_node_list(const net_node_list_item* nodes) {
++ while (nodes != NULL) {
++ // print node list items
++ cout << "+-> " << nodes->prefix.get_prefix() << "/"
++ << nodes->prefix.get_length() << endl;
++ net_neighbour_list_item* neighbours = nodes->neighbours;
++ while (neighbours != NULL) {
++ // print neighbour list items
++ cout << "| +-> ";
++ if (neighbours->forward_edge) { // forward edge
++ cout << "FORWARD: ";
++ } else { // backward edge
++ cout << "BACKWARD: ";
++ }
++ cout << neighbours->src_node->prefix.get_prefix() << "/"
++ << neighbours->src_node->prefix.get_length()
++ << " --> "
++ << neighbours->dst_node->prefix.get_prefix() << "/"
++ << neighbours->dst_node->prefix.get_length()
++ << " "
++ << "("
++ << neighbours->flow << "/"
++ << neighbours->capacity
++ << ")" << endl;
++ // move to the next neighbour
++ neighbours = neighbours->next;
++ }
++ // move to the next node
++ nodes = nodes->next;
++ }
++} // end print_node_list()
++
++
++// ***** Public functions *****************************************************
++
++
++/*
++ * Default constructor.
++ */
++Flow_network::Flow_network() {
++ src_nodes = NULL;
++ dst_nodes = NULL;
++ s_node = NULL;
++ t_node = NULL;
++} // end Flow_network()
++
++
++/*
++ * Copy constructor.
++ */
++Flow_network::Flow_network(const Flow_network& orig) {
++ // acquire members of the original object
++ const net_node_list_item* orig_src_nodes = orig.get_src_nodes();
++ const net_node_list_item* orig_dst_nodes = orig.get_dst_nodes();
++ const net_node_list_item* orig_s_node = orig.get_s_node();
++ const net_node_list_item* orig_t_node = orig.get_t_node();
++
++ // copy member node lists one by one
++ src_nodes = copy_node_list(orig_src_nodes);
++ dst_nodes = copy_node_list(orig_dst_nodes);
++ s_node = copy_node_list(orig_s_node);
++ t_node = copy_node_list(orig_t_node);
++
++ // set pointers to neighbour nodes
++ set_destination_nodes(orig_t_node, t_node, dst_nodes, NULL);
++ set_destination_nodes(orig_dst_nodes, dst_nodes, src_nodes, t_node);
++ set_destination_nodes(orig_src_nodes, src_nodes, s_node, dst_nodes);
++ set_destination_nodes(orig_s_node, s_node, NULL, src_nodes);
++} // end Filter_graph()
++
++
++/*
++ * Copy assignment.
++ */
++Flow_network& Flow_network::operator=(const Flow_network& copy) {
++ // destruct the original object
++ remove_node_list(s_node);
++ remove_node_list(src_nodes);
++ remove_node_list(dst_nodes);
++ remove_node_list(t_node);
++
++ // acquire members of the copied object
++ const net_node_list_item* copy_src_nodes = copy.get_src_nodes();
++ const net_node_list_item* copy_dst_nodes = copy.get_dst_nodes();
++ const net_node_list_item* copy_s_node = copy.get_s_node();
++ const net_node_list_item* copy_t_node = copy.get_t_node();
++
++ // copy member node lists one by one
++ src_nodes = copy_node_list(copy_src_nodes);
++ dst_nodes = copy_node_list(copy_dst_nodes);
++ s_node = copy_node_list(copy_s_node);
++ t_node = copy_node_list(copy_t_node);
++
++ // set pointers to neighbour nodes
++ set_destination_nodes(copy_t_node, t_node, dst_nodes, NULL);
++ set_destination_nodes(copy_dst_nodes, dst_nodes, src_nodes, t_node);
++ set_destination_nodes(copy_src_nodes, src_nodes, s_node, dst_nodes);
++ set_destination_nodes(copy_s_node, s_node, NULL, src_nodes);
++
++ // return the original object with new content
++ return *this;
++} // end operator=()
++
++
++/*
++ * Destructor.
++ */
++Flow_network::~Flow_network() {
++ remove_node_list(s_node);
++ remove_node_list(src_nodes);
++ remove_node_list(dst_nodes);
++ remove_node_list(t_node);
++} // end ~Flow_network()
++
++
++/*
++ * Adds an edge to the flow network.
++ */
++void Flow_network::add_edge(const IP_prefix& src_prefix, int src_list,
++ const IP_prefix& dst_prefix, int dst_list,
++ neighbour_list_item* orig_edge, bool forward_edge,
++ int capacity) {
++ // get pointer to correct source node list pointer
++ net_node_list_item** src_list_ptr;
++ switch (src_list) {
++ case 0 :
++ src_list_ptr = &s_node;
++ break;
++ case 1 :
++ src_list_ptr = &src_nodes;
++ break;
++ case 2 :
++ src_list_ptr = &dst_nodes;
++ break;
++ case 3 :
++ src_list_ptr = &t_node;
++ break;
++ }
++
++ // get pointer to correct destination node list pointer
++ net_node_list_item** dst_list_ptr;
++ switch (dst_list) {
++ case 0 :
++ dst_list_ptr = &s_node;
++ break;
++ case 1 :
++ dst_list_ptr = &src_nodes;
++ break;
++ case 2 :
++ dst_list_ptr = &dst_nodes;
++ break;
++ case 3 :
++ dst_list_ptr = &t_node;
++ break;
++ }
++
++ // find source prefix node in the specified node list and insert such node
++ // if it does not exist
++ net_node_list_item* src_node = find_node(src_prefix, *src_list_ptr);
++ if (src_node == NULL) {
++ // node is inserted at the beginning of the list
++ insert_node(src_prefix, src_list_ptr);
++ src_node = *src_list_ptr;
++ }
++
++ // find destination prefix node in the specified node list and insert such node
++ // if it does not exist
++ net_node_list_item* dst_node = find_node(dst_prefix, *dst_list_ptr);
++ if (dst_node == NULL) {
++ // node is inserted at the beginning of the list
++ insert_node(dst_prefix, dst_list_ptr);
++ dst_node = *dst_list_ptr;
++ }
++
++ // find neighbour node corresponding to the filter and insert such node if
++ // it does not exist
++ net_neighbour_list_item* neighbour = find_neighbour(dst_node,
++ src_node->neighbours);
++ if (neighbour == NULL) {
++ // neighbour is inserted at the beginning of the list
++ insert_neighbour(src_node, dst_node, capacity, 0, orig_edge,
++ forward_edge, &(src_node->neighbours));
++ }
++} // end add_edge()
++
++
++/*
++ * Transforms the flow network into a level graph.
++ */
++void Flow_network::to_level_graph() {
++
++ // initialize auxiliary variables
++ queue q;
++ stack s;
++ if (s_node != NULL) {
++ s_node->visited = true;
++ q.push(s_node);
++ }
++
++ // do a breadth-first search
++ while (!q.empty()) {
++ // dequeue the front element of the queue
++ net_node_list_item* node = q.front();
++ q.pop();
++ // iterate through the list of node's neighbours
++ net_neighbour_list_item** neighbour_ptr = &(node->neighbours);
++ while ((*neighbour_ptr) != NULL) {
++ // push a pointer to a pointer to the edge on the stack
++ s.push(neighbour_ptr);
++ // if it has not been visited yet, enqueue a pointer to the target
++ // node of this edge
++ if ((*neighbour_ptr)->dst_node->visited == false) {
++ (*neighbour_ptr)->dst_node->visited = true;
++ q.push((*neighbour_ptr)->dst_node);
++ }
++ // move to the next neighbour
++ neighbour_ptr = &((*neighbour_ptr)->next);
++ }
++ }
++
++ // initialize a set of level graph nodes with the 't' node
++ list l;
++ l.push_front(t_node);
++
++ // do an inverse breadth-first search
++ while (!s.empty()) {
++ // pop the top element of the stack
++ net_neighbour_list_item** neighbour_ptr = s.top();
++ s.pop();
++ // check if the dst_node already belongs to the level graph
++ net_node_list_item* dst_node = (*neighbour_ptr)->dst_node;
++ list::iterator i;
++ for (i = l.begin(); i != l.end(); ++i) {
++ if (*i == dst_node) {
++ break;
++ }
++ }
++ if (i != l.end()) { // dst_node belongs to the level graph
++ // add also src_node to the level graph
++ l.push_front((*neighbour_ptr)->src_node);
++ } else { // dst_node does not belong to the level graph
++ // remove the edge from the flow network
++ net_neighbour_list_item* edge = (*neighbour_ptr);
++ *neighbour_ptr = (*neighbour_ptr)->next;
++ delete edge;
++ }
++ }
++} // end to_level_graph()
++
++
++/*
++ * Finds a blocking flow in the flow network, adds its value to the
++ * current flow in the corresponding filter graph, and also returns its
++ * value.
++ */
++int Flow_network::find_blocking_flow() {
++ // check if the flow network exists at all
++ if (s_node == NULL) {
++ return 0;
++ }
++
++ int blocking_flow = 0;
++ while (1) { // inifinite loop with return statement inside
++ // initialize auxiliary variables
++ stack s;
++ stack s_capacity;
++ net_node_list_item* node = s_node;
++ // do a depth-first search
++ while (node != t_node) {
++ while (node->neighbours == NULL) { // no output edges - traverse back
++ if (s.empty()) { // the stack is empty - no way to traverse back
++ return blocking_flow;
++ }
++ // traverse back along the edge on the top of the stack
++ net_neighbour_list_item** neighbour_ptr = s.top();
++ net_neighbour_list_item* neighbour = *neighbour_ptr;
++ node = neighbour->src_node;
++ // pop the top elements of both stacks
++ s.pop();
++ s_capacity.pop();
++ // remove back-traversed edge
++ *neighbour_ptr = neighbour->next;
++ delete neighbour;
++ }
++ // push the first edge of the current node and its remaining capacity on
++ // the respective stacks
++ s.push(&(node->neighbours));
++ s_capacity.push(node->neighbours->capacity - node->neighbours->flow);
++ // move forward along the edge - update the current node
++ node = node->neighbours->dst_node;
++ }
++
++ // determine the smallest capacity among edges of the found path
++ int min_capacity = s_capacity.top();
++ s_capacity.pop();
++ while (!s_capacity.empty()) {
++ // pop the top element of the capacity stack
++ int capacity = s_capacity.top();
++ s_capacity.pop();
++ if (capacity < min_capacity) { // update the smallest capacity
++ min_capacity = capacity;
++ }
++ }
++
++ // update the flow through the found path according to min_capacity value
++ while (!s.empty()) {
++ // pop the top element of the stack
++ net_neighbour_list_item** neighbour_ptr = s.top();
++ s.pop();
++ // update the flow in both the flow network and filter graph
++ net_neighbour_list_item* neighbour = *neighbour_ptr;
++ neighbour->flow += min_capacity;
++ if (neighbour->forward_edge) { // forward edge
++ neighbour->orig_edge->flow += min_capacity;
++ } else { // backward edge
++ neighbour->orig_edge->flow -= min_capacity;
++ }
++ // remove the edge if its remaining capacity decreases to 0
++ if (neighbour->capacity == neighbour->flow) {
++ *neighbour_ptr = neighbour->next;
++ delete neighbour;
++ }
++ }
++
++ // update the value of blocking flow through the flow network
++ blocking_flow += min_capacity;
++ }
++} // end find_blocking_flow()
++
++
++/*
++ * Prints the flow network.
++ */
++void Flow_network::print() {
++ cout << "S_NODE:" << endl;
++ print_node_list(s_node);
++ cout << "--------------------" << endl;
++
++ cout << "SRC_NODES:" << endl;
++ print_node_list(src_nodes);
++ cout << "--------------------" << endl;
++
++ cout << "DST_NODES:" << endl;
++ print_node_list(dst_nodes);
++ cout << "--------------------" << endl;
++
++ cout << "T_NODE:" << endl;
++ print_node_list(t_node);
++ cout << "--------------------" << endl;
++}
+diff --git a/flow_network.h b/flow_network.h
+new file mode 100644
+index 0000000..df76c3f
+--- /dev/null
++++ b/flow_network.h
+@@ -0,0 +1,362 @@
++// flow_network.h: header file for Flow_network class
++//
++// Jiri Matousek, 2017
++// imatousek@fit.vutbr.cz
++
++
++#ifndef FLOW_NETWORK_H
++#define FLOW_NETWORK_H
++
++
++// User includes
++#include "ip_prefix.h"
++#include "filter_graph.h"
++
++// Library includes
++
++// Default namespace
++using namespace std;
++
++
++// ****************************************************************************
++// Structures declaration
++// ****************************************************************************
++
++
++// Forward declarations (including typedef) to resolve circular dependency
++typedef struct net_node_list_item net_node_list_item;
++typedef struct net_neighbour_list_item net_neighbour_list_item;
++
++/*
++ * A structure representing an item in a list of network nodes.
++ */
++struct net_node_list_item {
++ // IP prefix represented by this node
++ IP_prefix prefix;
++
++ // Flag showing whether the node was already visited during a traversal
++ bool visited;
++
++ // List of neighbours
++ net_neighbour_list_item* neighbours;
++
++ // Next item in list of network nodes
++ net_node_list_item* next;
++};
++
++/*
++ * A structure representing an item in a list of neighbours of a network node.
++ */
++struct net_neighbour_list_item {
++ // Pointer to source node within list of network nodes
++ net_node_list_item* src_node;
++
++ // Pointer to destination node within list of network nodes
++ net_node_list_item* dst_node;
++
++ // Residual capacity of flow network's edge between neighbouring nodes
++ int capacity;
++
++ // Flow through flow network's edge between neighbouring nodes
++ int flow;
++
++ // Pointer to corresponding edge in filter graph
++ // (the pointer is the same for both forward and backward edges)
++ neighbour_list_item* orig_edge;
++
++ // Flag showing whether this node represents a forward edge
++ bool forward_edge;
++
++ // Next item in list of neighbours
++ net_neighbour_list_item* next;
++};
++
++
++// ****************************************************************************
++// Class declaration
++// ****************************************************************************
++
++/*
++ * Class for representation of a flow network -- i.e., weighted directed graph
++ * with special source (s) and terminate (t) nodes -- over a filter graph.
++ */
++class Flow_network {
++
++
++ // ***** Private members ***************************************************
++
++
++ private:
++ /*
++ * Lists of source and destination nodes of the flow network.
++ */
++ net_node_list_item* src_nodes;
++ net_node_list_item* dst_nodes;
++
++ /*
++ * Special nodes 's' and 't' of the flow network.
++ */
++ net_node_list_item* s_node;
++ net_node_list_item* t_node;
++
++ /*
++ * Private static function that creates a deep copy of the original list.
++ * The function creates new instances of all items from the original list
++ * (as well as their neighbours sublists) and sets the value of their
++ * components according to this original list. The only exception is a
++ * pointer to destination node, which is initialized to NULL (it points
++ * to different list, thus it cannot be initialized to correct value
++ * during copying).
++ * @param orig Pointer to the constant original node list.
++ * @return Pointer to the copy of the original node list.
++ */
++ static net_node_list_item* copy_node_list(const net_node_list_item* orig);
++
++ /*
++ * Private static function that correctly deallocates the whole node
++ * list.
++ * The function traverses the given list and all its sublists and
++ * starting from lists of neighbours it deallocates all the traversed
++ * list items.
++ * @ param list Pointer to node list that is to be deallocated.
++ */
++ static void remove_node_list(net_node_list_item* list);
++
++ /*
++ * Private static function that sets correct values of destination node
++ * pointers, which cannot be correctly initialized during copying.
++ * The function simultaneously traverses neighbours lists in the orig and
++ * copy node lists. For each destination node from the orig list it looks
++ * for a corresponding item in either prev (backward edges) or next
++ * (forward edges) node list and stores the pointer to this node to the
++ * current item in the copy list.
++ * @param orig Pointer to the constant original node list.
++ * @param copy Pointer to the copy of the original node list
++ * (destination node pointers of this list are set).
++ * @param prev Pointer to the list of destination nodes of backward
++ * edges.
++ * @param next Pointer to the list of destination nodes of forward
++ * edges.
++ */
++ static void set_destination_nodes(const net_node_list_item* orig,
++ net_node_list_item* copy,
++ net_node_list_item* prev,
++ net_node_list_item* next);
++
++// PROBABLY IT IS NOT NECESSARY TO HAVE THIS FUNCTION PRIVATE
++ /*
++ * Private static function that looks for a node with the given prefix in
++ * the given list of nodes.
++ * @param prefix Reference to a constant IP prefix of the node that the
++ * function looks for in the list.
++ * @param list Pointer to the list of nodes that is traversed during
++ * looking for the node with the given IP prefix.
++ * @return Pointer to the found node or NULL.
++ */
++ static net_node_list_item* find_node(const IP_prefix& prefix,
++ net_node_list_item* list);
++
++// PROBABLY IT IS NOT NECESSARY TO HAVE THIS FUNCTION PRIVATE
++ /*
++ * Private static function that looks for the corresponding neighbour
++ * node in the given list of neighbours.
++ * @param neighbour Pointer to a constant prefix node whose neighbour
++ * node the function looks for in the list.
++ * @param list Pointer to the list of neighbour nodes that is
++ * traversed during looking for the node corresponding
++ * to the given prefix node.
++ * @return Pointer to the found neighbour node or NULL.
++ */
++ static net_neighbour_list_item* find_neighbour(
++ const net_node_list_item* neighbour,
++ net_neighbour_list_item* list);
++
++ /*
++ * Private static function that inserts a node representing the given IP
++ * prefix at the beginning of the given list of nodes.
++ * @param prefix Reference to a constant IP prefix that is going to be
++ * represented by the inserted node.
++ * @param list Pointer to pointer to the list of nodes that is going
++ * to be extended by the inserted node.
++ */
++ static void insert_node(const IP_prefix& prefix,
++ net_node_list_item** list);
++
++ /*
++ * Private static function that inserts a neighbour node representing an
++ * edge between the given source and destination nodes at the beginning
++ * of the specified list of neighbour nodes.
++ * @param src_node Pointer to a source node of the represented
++ * edge.
++ * @param dst_node Pointer to a destination node of the represented
++ * edge.
++ * @param capacity Residual capacity of the represented edge.
++ * @param flow Flow through the represented edge.
++ * @param orig_edge Pointer to the corresponding edge in the filter
++ * graph.
++ * @param forward_edge Flag showing whether the inserted neighbour
++ * represents a forward edge.
++ * @param list Pointer to pointer to the list of neighbours
++ * that is going to be extended by the inserted
++ * node.
++ */
++ static void insert_neighbour(net_node_list_item* src_node,
++ net_node_list_item* dst_node,
++ int capacity,
++ int flow,
++ neighbour_list_item* orig_edge,
++ bool forward_edge,
++ net_neighbour_list_item** list);
++
++ /*
++ * Private static function that prints specified node list, including all
++ * sublists.
++ * @param nodes Pointer to the constant list of nodes that is to be
++ * printed.
++ */
++ static void print_node_list(const net_node_list_item* nodes);
++
++
++ // ***** Public members ****************************************************
++
++
++ public:
++ /*
++ * Default constructor.
++ * All pointers are initialized to NULL.
++ */
++ Flow_network();
++
++ /*
++ * Copy constructor.
++ * All pointers used by the filter graph are initialized to a deep copy
++ * of corresponding members of the original object.
++ * @param orig Reference to the constant original object.
++ */
++ Flow_network(const Flow_network& orig);
++
++ /*
++ * Copy assignment.
++ * The original object is destructed and its new content is constructed
++ * in a similar way as in the copy constructor.
++ * @param copy Reference to the constant copied object.
++ * @return Reference to the original object with new content.
++ */
++ Flow_network& operator= (const Flow_network& copy);
++
++ /*
++ * Destructor.
++ * Correctly deallocates all lists that are referenced by object members.
++ */
++ ~Flow_network();
++
++ /*
++ * Get function for the src_nodes member.
++ * @return Pointer to the constant list of source nodes.
++ */
++ inline const net_node_list_item* get_src_nodes() const {
++ return src_nodes;
++ } // end get_src_nodes()
++
++ /*
++ * Get function for the dst_nodes member.
++ * @return Pointer to the constant list of destination nodes.
++ */
++ inline const net_node_list_item* get_dst_nodes() const {
++ return dst_nodes;
++ } // end get_dst_nodes()
++
++ /*
++ * Get function for the s_node member.
++ * @return Pointer to the constant special 's' node.
++ */
++ inline const net_node_list_item* get_s_node() const {
++ return s_node;
++ } // end get_s_node()
++
++ /*
++ * Get function for the t_node member.
++ * @return Pointer to the constant special 't' node.
++ */
++ inline const net_node_list_item* get_t_node() const {
++ return t_node;
++ } // end get_t_node()
++
++ /*
++ * Adds an edge to the flow network.
++ * First of all, if they are not already present, the function inserts
++ * nodes representing src_prefix and dst_prefix into node lists src_list
++ * and dst_list, respectively. Next, if it is not already present, the
++ * function inserts a neighbour node representing the edge into the list
++ * of neighbours of the node representing the source prefix. Flow item of
++ * the neighbour node is set to 0, while other items within its structure
++ * are set according to the function's parameters.
++ * @param src_prefix Reference to the IP_prefix object that
++ * determines a source node of the added edge.
++ * @param src_list Specification of a node list that contains the
++ * source node of the added edge. Mapping of the
++ * four allowed values to the node lists is the
++ * following:
++ * 0 ... s_node
++ * 1 ... src_node
++ * 2 ... dst_node
++ * 3 ... t_node
++ * @param dst_prefix Reference to the IP_prefix object that
++ * determines a destination node of the added edge.
++ * @param dst_list Specification of a node list that contains the
++ * destination node of the added edge. Mapping of
++ * the four allowed values to the node lists is the
++ * following:
++ * 0 ... s_node
++ * 1 ... src_node
++ * 2 ... dst_node
++ * 3 ... t_node
++ * @param orig_edge Pointer to the corresponding edge in the filter
++ * graph.
++ * @param forward_edge Flag showing whether the added edge represents a
++ * forward edge.
++ * @param capacity Residual capacity of the added edge.
++ */
++ void add_edge(const IP_prefix& src_prefix, int src_list,
++ const IP_prefix& dst_prefix, int dst_list,
++ neighbour_list_item* orig_edge, bool forward_edge,
++ int capacity);
++
++ /*
++ * Transforms the flow network into a level graph.
++ * During inverse BFS of the flow network, starting from a set containing
++ * the 't' node, the function incrementally extends the set by start
++ * nodes of edges ending in one of the set nodes and removes from the
++ * flow network edges that do not meet this condition.
++ */
++ void to_level_graph();
++
++ /*
++ * Finds a blocking flow in the flow network, adds its value to the
++ * current flow in the corresponding filter graph, and also returns its
++ * value.
++ * The function repeatedly performs DST to find an s-t path in the flow
++ * network and the smallest capacity on this path. Once the function
++ * reaches the 't' node, it returns along the found path and increases
++ * the flow through the particular flow network edges as well as the
++ * corresponding filter graph edges. The function also updates capacities
++ * of backward-traversed flow network edges and removes them in case
++ * their capacity decreases to 0. In case of DFS ending in a node other
++ * than the 't' node, the function traverses back to the closest node
++ * with at least two output edges and removes all back-traversed edges.
++ * After each successful DFS and flow update on the found path, the
++ * function increases the value of the total flow through the flow
++ * network, which is returned at the end.
++ * The function expects that the flow network is in the form of a level
++ * graph at the time of invocation.
++ * @return The value of the blocking flow through the flow network.
++ */
++ int find_blocking_flow();
++
++ /*
++ * Prints the flow network.
++ */
++ void print();
++};
++
++#endif
+diff --git a/ip-address/COPYING b/ip-address/COPYING
+new file mode 100644
+index 0000000..03fc9f6
+--- /dev/null
++++ b/ip-address/COPYING
+@@ -0,0 +1,4 @@
++Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
++
++Distributed under the Boost Software License, Version 1.0. (See accompanying
++file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+diff --git a/ip-address/LICENSE_1_0.txt b/ip-address/LICENSE_1_0.txt
+new file mode 100644
+index 0000000..36b7cd9
+--- /dev/null
++++ b/ip-address/LICENSE_1_0.txt
+@@ -0,0 +1,23 @@
++Boost Software License - Version 1.0 - August 17th, 2003
++
++Permission is hereby granted, free of charge, to any person or organization
++obtaining a copy of the software and accompanying documentation covered by
++this license (the "Software") to use, reproduce, display, distribute,
++execute, and transmit the Software, and to prepare derivative works of the
++Software, and to permit third-parties to whom the Software is furnished to
++do so, all subject to the following:
++
++The copyright notices in the Software and this entire statement, including
++the above license grant, this restriction and the following disclaimer,
++must be included in all copies of the Software, in whole or in part, and
++all derivative works of the Software, unless such copies or derivative
++works are solely in the form of machine-executable object code generated by
++a source language processor.
++
++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
++SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
++FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
++ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++DEALINGS IN THE SOFTWARE.
+diff --git a/ip-address/README.md b/ip-address/README.md
+new file mode 100644
+index 0000000..08ff928
+--- /dev/null
++++ b/ip-address/README.md
+@@ -0,0 +1,18 @@
++IP Address Proposal
++===================
++
++Proposed IP addresses classes for the standard C++ library.
++
++What's Included
++---------------
++
++* `./include` - Reference implementation.
++
++Tested Platforms
++----------------
++
++* Mac OS 10.8 using g++ 4.7 (requires `-std=c++11`)
++* Mac OS 10.8 using clang++ from Xcode 4.6 (requires `-std=c++11` and `-stdlib=libc++`)
++* Linux (CentOS 6.2) using g++ 4.7 (requires `-std=c++11`)
++* Windows 7 32-bit using Visual Studio 2010
++* Windows 7 x64 using Visual Studio 2010
+diff --git a/ip-address/include/network b/ip-address/include/network
+new file mode 100644
+index 0000000..c97ea69
+--- /dev/null
++++ b/ip-address/include/network
+@@ -0,0 +1,25 @@
++//
++// network
++// ~~~~~~~
++//
++// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
++//
++// Distributed under the Boost Software License, Version 1.0. (See accompanying
++// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
++//
++
++#ifndef STDNET_NETWORK_HEADER_FILE
++#define STDNET_NETWORK_HEADER_FILE
++
++#if defined(_MSC_VER) && (_MSC_VER >= 1200)
++# pragma once
++#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
++
++#include "std/net/ip/fwd.hpp"
++#include "std/net/ip/address.hpp"
++#include "std/net/ip/address_v4.hpp"
++#include "std/net/ip/address_v6.hpp"
++#include "std/net/ip/address_cast.hpp"
++#include "std/net/literals.hpp"
++
++#endif // STDNET_NETWORK_HEADER_FILE
+diff --git a/ip-address/include/std/net/detail/config.hpp b/ip-address/include/std/net/detail/config.hpp
+new file mode 100644
+index 0000000..c797490
+--- /dev/null
++++ b/ip-address/include/std/net/detail/config.hpp
+@@ -0,0 +1,666 @@
++//
++// detail/config.hpp
++// ~~~~~~~~~~~~~~~~~
++//
++// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
++//
++// Distributed under the Boost Software License, Version 1.0. (See accompanying
++// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
++//
++
++#ifndef STDNET_DETAIL_CONFIG_HPP
++#define STDNET_DETAIL_CONFIG_HPP
++
++// Default to a header-only implementation. The user must specifically request
++// separate compilation by defining either STDNET_SEPARATE_COMPILATION or
++// STDNET_DYN_LINK (as a DLL/shared library implies separate compilation).
++#if !defined(STDNET_HEADER_ONLY)
++# if !defined(STDNET_SEPARATE_COMPILATION)
++# if !defined(STDNET_DYN_LINK)
++# define STDNET_HEADER_ONLY 1
++# endif // !defined(STDNET_DYN_LINK)
++# endif // !defined(STDNET_SEPARATE_COMPILATION)
++#endif // !defined(STDNET_HEADER_ONLY)
++
++#if defined(STDNET_HEADER_ONLY)
++# define STDNET_DECL inline
++#else // defined(STDNET_HEADER_ONLY)
++# if defined(_MSC_VER) || defined(__BORLANDC__) || defined(__CODEGEARC__)
++// We need to import/export our code only if the user has specifically asked
++// for it by defining STDNET_DYN_LINK.
++# if defined(STDNET_DYN_LINK)
++// Export if this is our own source, otherwise import.
++# if defined(STDNET_SOURCE)
++# define STDNET_DECL __declspec(dllexport)
++# else // defined(STDNET_SOURCE)
++# define STDNET_DECL __declspec(dllimport)
++# endif // defined(STDNET_SOURCE)
++# endif // defined(STDNET_DYN_LINK)
++# endif // defined(_MSC_VER) || defined(__BORLANDC__) || defined(__CODEGEARC__)
++#endif // defined(STDNET_HEADER_ONLY)
++
++// If STDNET_DECL isn't defined yet define it now.
++#if !defined(STDNET_DECL)
++# define STDNET_DECL
++#endif // !defined(STDNET_DECL)
++
++// Microsoft Visual C++ detection.
++#if !defined(STDNET_MSVC)
++# if defined(_MSC_VER) && !defined(__MWERKS__) && !defined(__EDG_VERSION__)
++# define STDNET_MSVC _MSC_VER
++# endif // defined(_MSC_VER) && !defined(__MWERKS__) && !defined(__EDG_VERSION__)
++#endif // defined(STDNET_MSVC)
++
++// Support move construction and assignment on compilers known to allow it.
++#if !defined(STDNET_HAS_MOVE)
++# if !defined(STDNET_DISABLE_MOVE)
++# if defined(__GNUC__)
++# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 5)) || (__GNUC__ > 4)
++# if defined(__GXX_EXPERIMENTAL_CXX0X__)
++# define STDNET_HAS_MOVE 1
++# endif // defined(__GXX_EXPERIMENTAL_CXX0X__)
++# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 5)) || (__GNUC__ > 4)
++# endif // defined(__GNUC__)
++# endif // !defined(STDNET_DISABLE_MOVE)
++#endif // !defined(STDNET_HAS_MOVE)
++
++// If STDNET_MOVE_CAST isn't defined, and move support is available, define
++// STDNET_MOVE_ARG and STDNET_MOVE_CAST to take advantage of rvalue
++// references and perfect forwarding.
++#if defined(STDNET_HAS_MOVE) && !defined(STDNET_MOVE_CAST)
++# define STDNET_MOVE_ARG(type) type&&
++# define STDNET_MOVE_CAST(type) static_cast
++#endif // defined(STDNET_HAS_MOVE) && !defined(STDNET_MOVE_CAST)
++
++// If STDNET_MOVE_CAST still isn't defined, default to a C++03-compatible
++// implementation. Note that older g++ and MSVC versions don't like it when you
++// pass a non-member function through a const reference, so for most compilers
++// we'll play it safe and stick with the old approach of passing the handler by
++// value.
++#if !defined(STDNET_MOVE_CAST)
++# if defined(__GNUC__)
++# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 1)) || (__GNUC__ > 4)
++# define STDNET_MOVE_ARG(type) const type&
++# else // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 1)) || (__GNUC__ > 4)
++# define STDNET_MOVE_ARG(type) type
++# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 1)) || (__GNUC__ > 4)
++# elif defined(STDNET_MSVC)
++# if (_MSC_VER >= 1400)
++# define STDNET_MOVE_ARG(type) const type&
++# else // (_MSC_VER >= 1400)
++# define STDNET_MOVE_ARG(type) type
++# endif // (_MSC_VER >= 1400)
++# else
++# define STDNET_MOVE_ARG(type) type
++# endif
++# define STDNET_MOVE_CAST(type) static_cast
++#endif // !defined_STDNET_MOVE_CAST
++
++// Support variadic templates on compilers known to allow it.
++#if !defined(STDNET_HAS_VARIADIC_TEMPLATES)
++# if !defined(STDNET_DISABLE_VARIADIC_TEMPLATES)
++# if defined(__GNUC__)
++# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 4)
++# if defined(__GXX_EXPERIMENTAL_CXX0X__)
++# define STDNET_HAS_VARIADIC_TEMPLATES 1
++# endif // defined(__GXX_EXPERIMENTAL_CXX0X__)
++# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 4)
++# endif // defined(__GNUC__)
++# if defined(__clang__)
++# if __has_feature(cxx_variadic_templates)
++# define STDNET_HAS_VARIADIC_TEMPLATES 1
++# endif // __has_feature(cxx_noexcept)
++# endif // defined(__clang__)
++# endif // !defined(STDNET_DISABLE_VARIADIC_TEMPLATES)
++#endif // !defined(STDNET_HAS_VARIADIC_TEMPLATES)
++
++// Support the noexcept specifier on compilers known to allow it.
++#if !defined(STDNET_NOEXCEPT)
++# if defined(__GNUC__)
++# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) || (__GNUC__ > 4)
++# if defined(__GXX_EXPERIMENTAL_CXX0X__)
++# define STDNET_NOEXCEPT noexcept
++# endif // defined(__GXX_EXPERIMENTAL_CXX0X__)
++# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) || (__GNUC__ > 4)
++# endif // defined(__GNUC__)
++# if defined(__clang__)
++# if __has_feature(cxx_noexcept)
++# define STDNET_NOEXCEPT noexcept
++# endif // __has_feature(cxx_noexcept)
++# endif // defined(__clang__)
++# if !defined(STDNET_NOEXCEPT)
++# define STDNET_NOEXCEPT
++# endif // !defined(STDNET_NOEXCEPT)
++#endif // !defined(STDNET_NOEXCEPT)
++
++// Support deleted functions on compilers known to allow it.
++#if !defined(STDNET_DELETED)
++# if defined(__GNUC__)
++# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) || (__GNUC__ > 4)
++# if defined(__GXX_EXPERIMENTAL_CXX0X__)
++# define STDNET_DELETED = delete
++# endif // defined(__GXX_EXPERIMENTAL_CXX0X__)
++# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) || (__GNUC__ > 4)
++# endif // defined(__GNUC__)
++# if defined(__clang__)
++# if __has_feature(cxx_deleted_functions)
++# define STDNET_DELETED = delete
++# endif // __has_feature(cxx_noexcept)
++# endif // defined(__clang__)
++# if !defined(STDNET_DELETED)
++# define STDNET_DELETED
++# endif // !defined(STDNET_DELETED)
++#endif // !defined(STDNET_DELETED)
++
++// Support relaxed constexpr on compilers known to allow it.
++#if !defined(STDNET_CONSTEXPR)
++# if defined(__GNUC__)
++# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) || (__GNUC__ > 4)
++# if defined(__GXX_EXPERIMENTAL_CXX0X__)
++# define STDNET_HAS_CONSTEXPR 1
++# define STDNET_CONSTEXPR constexpr
++# endif // defined(__GXX_EXPERIMENTAL_CXX0X__)
++# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) || (__GNUC__ > 4)
++# endif // defined(__GNUC__)
++# if defined(__clang__)
++# if __has_feature(cxx_constexpr)
++# define STDNET_HAS_CONSTEXPR 1
++# define STDNET_CONSTEXPR constexpr
++# endif // __has_feature(cxx_constexpr)
++# endif // defined(__clang__)
++# if !defined(STDNET_CONSTEXPR)
++# define STDNET_CONSTEXPR
++# endif // !defined(STDNET_CONSTEXPR)
++#endif // !defined(STDNET_CONSTEXPR)
++
++// Standard library support for system errors.
++#if !defined(STDNET_HAS_STD_SYSTEM_ERROR)
++# if !defined(STDNET_DISABLE_STD_SYSTEM_ERROR)
++# if defined(__GNUC__)
++# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 6)) || (__GNUC__ > 4)
++# if defined(__GXX_EXPERIMENTAL_CXX0X__)
++# define STDNET_HAS_STD_SYSTEM_ERROR 1
++# endif // defined(__GXX_EXPERIMENTAL_CXX0X__)
++# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 6)) || (__GNUC__ > 4)
++# endif // defined(__GNUC__)
++# endif // !defined(STDNET_DISABLE_STD_SYSTEM_ERROR)
++#endif // !defined(STDNET_HAS_STD_SYSTEM_ERROR)
++
++// Compliant C++11 compilers put noexcept specifiers on error_category members.
++#if !defined(STDNET_ERROR_CATEGORY_NOEXCEPT)
++# if defined(__GNUC__)
++# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) || (__GNUC__ > 4)
++# if defined(__GXX_EXPERIMENTAL_CXX0X__)
++# define STDNET_ERROR_CATEGORY_NOEXCEPT noexcept(true)
++# endif // defined(__GXX_EXPERIMENTAL_CXX0X__)
++# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) || (__GNUC__ > 4)
++# endif // defined(__GNUC__)
++# if defined(__clang__)
++# if __has_feature(cxx_noexcept)
++# define STDNET_ERROR_CATEGORY_NOEXCEPT noexcept(true)
++# endif // __has_feature(cxx_noexcept)
++# endif // defined(__clang__)
++# if !defined(STDNET_ERROR_CATEGORY_NOEXCEPT)
++# define STDNET_ERROR_CATEGORY_NOEXCEPT
++# endif // !defined(STDNET_ERROR_CATEGORY_NOEXCEPT)
++#endif // !defined(STDNET_ERROR_CATEGORY_NOEXCEPT)
++
++// Standard library support for arrays.
++#if !defined(STDNET_HAS_STD_ARRAY)
++# if !defined(STDNET_DISABLE_STD_ARRAY)
++# if defined(__GNUC__)
++# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 4)
++# if defined(__GXX_EXPERIMENTAL_CXX0X__)
++# define STDNET_HAS_STD_ARRAY 1
++# endif // defined(__GXX_EXPERIMENTAL_CXX0X__)
++# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 4)
++# endif // defined(__GNUC__)
++# if defined(STDNET_MSVC)
++# if (_MSC_VER >= 1600)
++# define STDNET_HAS_STD_ARRAY 1
++# endif // (_MSC_VER >= 1600)
++# endif // defined(STDNET_MSVC)
++# endif // !defined(STDNET_DISABLE_STD_ARRAY)
++#endif // !defined(STDNET_HAS_STD_ARRAY)
++
++// Standard library support for shared_ptr and weak_ptr.
++#if !defined(STDNET_HAS_STD_SHARED_PTR)
++# if !defined(STDNET_DISABLE_STD_SHARED_PTR)
++# if defined(__GNUC__)
++# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 4)
++# if defined(__GXX_EXPERIMENTAL_CXX0X__)
++# define STDNET_HAS_STD_SHARED_PTR 1
++# endif // defined(__GXX_EXPERIMENTAL_CXX0X__)
++# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 4)
++# endif // defined(__GNUC__)
++# if defined(STDNET_MSVC)
++# if (_MSC_VER >= 1600)
++# define STDNET_HAS_STD_SHARED_PTR 1
++# endif // (_MSC_VER >= 1600)
++# endif // defined(STDNET_MSVC)
++# endif // !defined(STDNET_DISABLE_STD_SHARED_PTR)
++#endif // !defined(STDNET_HAS_STD_SHARED_PTR)
++
++// Standard library support for atomic operations.
++#if !defined(STDNET_HAS_STD_ATOMIC)
++# if !defined(STDNET_DISABLE_STD_ATOMIC)
++# if defined(__GNUC__)
++# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 5)) || (__GNUC__ > 4)
++# if defined(__GXX_EXPERIMENTAL_CXX0X__)
++# define STDNET_HAS_STD_ATOMIC 1
++# endif // defined(__GXX_EXPERIMENTAL_CXX0X__)
++# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 5)) || (__GNUC__ > 4)
++# endif // defined(__GNUC__)
++# endif // !defined(STDNET_DISABLE_STD_ATOMIC)
++#endif // !defined(STDNET_HAS_STD_ATOMIC)
++
++// Standard library support for chrono. Some standard libraries (such as the
++// libstdc++ shipped with gcc 4.6) provide monotonic_clock as per early C++0x
++// drafts, rather than the eventually standardised name of steady_clock.
++#if !defined(STDNET_HAS_STD_CHRONO)
++# if !defined(STDNET_DISABLE_STD_CHRONO)
++# if defined(__GNUC__)
++# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 6)) || (__GNUC__ > 4)
++# if defined(__GXX_EXPERIMENTAL_CXX0X__)
++# define STDNET_HAS_STD_CHRONO 1
++# if ((__GNUC__ == 4) && (__GNUC_MINOR__ == 6))
++# define STDNET_HAS_STD_CHRONO_MONOTONIC_CLOCK 1
++# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ == 6))
++# endif // defined(__GXX_EXPERIMENTAL_CXX0X__)
++# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 6)) || (__GNUC__ > 4)
++# endif // defined(__GNUC__)
++# endif // !defined(STDNET_DISABLE_STD_CHRONO)
++#endif // !defined(STDNET_HAS_STD_CHRONO)
++
++// Standard library support for addressof.
++#if !defined(STDNET_HAS_STD_ADDRESSOF)
++# if !defined(STDNET_DISABLE_STD_ADDRESSOF)
++# if defined(__GNUC__)
++# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 5)) || (__GNUC__ > 4)
++# if defined(__GXX_EXPERIMENTAL_CXX0X__)
++# define STDNET_HAS_STD_ADDRESSOF 1
++# endif // defined(__GXX_EXPERIMENTAL_CXX0X__)
++# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 5)) || (__GNUC__ > 4)
++# endif // defined(__GNUC__)
++# endif // !defined(STDNET_DISABLE_STD_ADDRESSOF)
++#endif // !defined(STDNET_HAS_STD_ADDRESSOF)
++
++// Standard library support for the function class.
++#if !defined(STDNET_HAS_STD_FUNCTION)
++# if !defined(STDNET_DISABLE_STD_FUNCTION)
++# if defined(__GNUC__)
++# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 5)) || (__GNUC__ > 4)
++# if defined(__GXX_EXPERIMENTAL_CXX0X__)
++# define STDNET_HAS_STD_FUNCTION 1
++# endif // defined(__GXX_EXPERIMENTAL_CXX0X__)
++# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 5)) || (__GNUC__ > 4)
++# endif // defined(__GNUC__)
++# endif // !defined(STDNET_DISABLE_STD_FUNCTION)
++#endif // !defined(STDNET_HAS_STD_FUNCTION)
++
++// Standard library support for type traits.
++#if !defined(STDNET_HAS_STD_TYPE_TRAITS)
++# if !defined(STDNET_DISABLE_STD_TYPE_TRAITS)
++# if defined(__GNUC__)
++# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 5)) || (__GNUC__ > 4)
++# if defined(__GXX_EXPERIMENTAL_CXX0X__)
++# define STDNET_HAS_STD_TYPE_TRAITS 1
++# endif // defined(__GXX_EXPERIMENTAL_CXX0X__)
++# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 5)) || (__GNUC__ > 4)
++# endif // defined(__GNUC__)
++# endif // !defined(STDNET_DISABLE_STD_TYPE_TRAITS)
++#endif // !defined(STDNET_HAS_STD_TYPE_TRAITS)
++
++// Standard library support for the cstdint header.
++#if !defined(STDNET_HAS_CSTDINT)
++# if !defined(STDNET_DISABLE_CSTDINT)
++# if defined(__GNUC__)
++# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 5)) || (__GNUC__ > 4)
++# if defined(__GXX_EXPERIMENTAL_CXX0X__)
++# define STDNET_HAS_CSTDINT 1
++# endif // defined(__GXX_EXPERIMENTAL_CXX0X__)
++# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 5)) || (__GNUC__ > 4)
++# endif // defined(__GNUC__)
++# endif // !defined(STDNET_DISABLE_CSTDINT)
++#endif // !defined(STDNET_HAS_CSTDINT)
++
++// Windows target.
++#if !defined(STDNET_WINDOWS)
++# if defined(WIN32) || defined(_WIN32) || defined(__WIN32__)
++# define STDNET_WINDOWS 1
++# endif // defined(WIN32) || defined(_WIN32) || defined(__WIN32__)
++#endif // !defined(STDNET_WINDOWS)
++
++// Windows: target OS version.
++#if defined(STDNET_WINDOWS) || defined(__CYGWIN__)
++# if !defined(_WIN32_WINNT) && !defined(_WIN32_WINDOWS)
++# if defined(_MSC_VER) || defined(__BORLANDC__)
++# pragma message( \
++ "Please define _WIN32_WINNT or _WIN32_WINDOWS appropriately. For example:\n"\
++ "- add -D_WIN32_WINNT=0x0501 to the compiler command line; or\n"\
++ "- add _WIN32_WINNT=0x0501 to your project's Preprocessor Definitions.\n"\
++ "Assuming _WIN32_WINNT=0x0501 (i.e. Windows XP target).")
++# else // defined(_MSC_VER) || defined(__BORLANDC__)
++# warning Please define _WIN32_WINNT or _WIN32_WINDOWS appropriately.
++# warning For example, add -D_WIN32_WINNT=0x0501 to the compiler command line.
++# warning Assuming _WIN32_WINNT=0x0501 (i.e. Windows XP target).
++# endif // defined(_MSC_VER) || defined(__BORLANDC__)
++# define _WIN32_WINNT 0x0501
++# endif // !defined(_WIN32_WINNT) && !defined(_WIN32_WINDOWS)
++# if defined(_MSC_VER)
++# if defined(_WIN32) && !defined(WIN32)
++# if !defined(_WINSOCK2API_)
++# define WIN32 // Needed for correct types in winsock2.h
++# else // !defined(_WINSOCK2API_)
++# error Please define the macro WIN32 in your compiler options
++# endif // !defined(_WINSOCK2API_)
++# endif // defined(_WIN32) && !defined(WIN32)
++# endif // defined(_MSC_VER)
++# if defined(__BORLANDC__)
++# if defined(__WIN32__) && !defined(WIN32)
++# if !defined(_WINSOCK2API_)
++# define WIN32 // Needed for correct types in winsock2.h
++# else // !defined(_WINSOCK2API_)
++# error Please define the macro WIN32 in your compiler options
++# endif // !defined(_WINSOCK2API_)
++# endif // defined(__WIN32__) && !defined(WIN32)
++# endif // defined(__BORLANDC__)
++# if defined(__CYGWIN__)
++# if !defined(__USE_W32_SOCKETS)
++# error You must add -D__USE_W32_SOCKETS to your compiler options.
++# endif // !defined(__USE_W32_SOCKETS)
++# endif // defined(__CYGWIN__)
++#endif // defined(STDNET_WINDOWS) || defined(__CYGWIN__)
++
++// Windows: minimise header inclusion.
++#if defined(STDNET_WINDOWS) || defined(__CYGWIN__)
++# if !defined(STDNET_NO_WIN32_LEAN_AND_MEAN)
++# if !defined(WIN32_LEAN_AND_MEAN)
++# define WIN32_LEAN_AND_MEAN
++# endif // !defined(WIN32_LEAN_AND_MEAN)
++# endif // !defined(STDNET_NO_WIN32_LEAN_AND_MEAN)
++#endif // defined(STDNET_WINDOWS) || defined(__CYGWIN__)
++
++// Windows: suppress definition of "min" and "max" macros.
++#if defined(STDNET_WINDOWS) || defined(__CYGWIN__)
++# if !defined(STDNET_NO_NOMINMAX)
++# if !defined(NOMINMAX)
++# define NOMINMAX 1
++# endif // !defined(NOMINMAX)
++# endif // !defined(STDNET_NO_NOMINMAX)
++#endif // defined(STDNET_WINDOWS) || defined(__CYGWIN__)
++
++// Windows: No ANSI API calls.
++#if !defined(STDNET_NO_ANSI_APIS)
++# if defined(STDNET_WINDOWS) && defined(UNDER_CE)
++# define STDNET_NO_ANSI_APIS 1
++# endif // defined(STDNET_WINDOWS) && defined(UNDER_CE)
++#endif // !defined(STDNET_NO_ANSI_APIS)
++
++// Windows: IO Completion Ports.
++#if !defined(STDNET_HAS_IOCP)
++# if defined(STDNET_WINDOWS) || defined(__CYGWIN__)
++# if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0400)
++# if !defined(UNDER_CE)
++# if !defined(STDNET_DISABLE_IOCP)
++# define STDNET_HAS_IOCP 1
++# endif // !defined(STDNET_DISABLE_IOCP)
++# endif // !defined(UNDER_CE)
++# endif // defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0400)
++# endif // defined(STDNET_WINDOWS) || defined(__CYGWIN__)
++#endif // !defined(STDNET_HAS_IOCP)
++
++// Linux: epoll, eventfd and timerfd.
++#if defined(__linux__)
++# include
++# if !defined(STDNET_HAS_EPOLL)
++# if !defined(STDNET_DISABLE_EPOLL)
++# if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,45)
++# define STDNET_HAS_EPOLL 1
++# endif // LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,45)
++# endif // !defined(STDNET_DISABLE_EPOLL)
++# endif // !defined(STDNET_HAS_EPOLL)
++# if !defined(STDNET_HAS_EVENTFD)
++# if !defined(STDNET_DISABLE_EVENTFD)
++# if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
++# define STDNET_HAS_EVENTFD 1
++# endif // LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
++# endif // !defined(STDNET_DISABLE_EVENTFD)
++# endif // !defined(STDNET_HAS_EVENTFD)
++# if !defined(STDNET_HAS_TIMERFD)
++# if defined(STDNET_HAS_EPOLL)
++# if (__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 8)
++# define STDNET_HAS_TIMERFD 1
++# endif // (__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 8)
++# endif // defined(STDNET_HAS_EPOLL)
++# endif // !defined(STDNET_HAS_TIMERFD)
++#endif // defined(__linux__)
++
++// Mac OS X, FreeBSD, NetBSD, OpenBSD: kqueue.
++#if (defined(__MACH__) && defined(__APPLE__)) \
++ || defined(__FreeBSD__) \
++ || defined(__NetBSD__) \
++ || defined(__OpenBSD__)
++# if !defined(STDNET_HAS_KQUEUE)
++# if !defined(STDNET_DISABLE_KQUEUE)
++# define STDNET_HAS_KQUEUE 1
++# endif // !defined(STDNET_DISABLE_KQUEUE)
++# endif // !defined(STDNET_HAS_KQUEUE)
++#endif // (defined(__MACH__) && defined(__APPLE__))
++ // || defined(__FreeBSD__)
++ // || defined(__NetBSD__)
++ // || defined(__OpenBSD__)
++
++// Solaris: /dev/poll.
++#if defined(__sun)
++# if !defined(STDNET_HAS_DEV_POLL)
++# if !defined(STDNET_DISABLE_DEV_POLL)
++# define STDNET_HAS_DEV_POLL 1
++# endif // !defined(STDNET_DISABLE_DEV_POLL)
++# endif // !defined(STDNET_HAS_DEV_POLL)
++#endif // defined(__sun)
++
++// Serial ports.
++#if !defined(STDNET_HAS_SERIAL_PORT)
++# if defined(STDNET_HAS_IOCP) \
++ || !defined(STDNET_WINDOWS) && !defined(__CYGWIN__)
++# if !defined(__SYMBIAN32__)
++# if !defined(STDNET_DISABLE_SERIAL_PORT)
++# define STDNET_HAS_SERIAL_PORT 1
++# endif // !defined(STDNET_DISABLE_SERIAL_PORT)
++# endif // !defined(__SYMBIAN32__)
++# endif // defined(STDNET_HAS_IOCP)
++ // || !defined(STDNET_WINDOWS) && !defined(__CYGWIN__)
++#endif // !defined(STDNET_HAS_SERIAL_PORT)
++
++// Windows: stream handles.
++#if !defined(STDNET_HAS_WINDOWS_STREAM_HANDLE)
++# if !defined(STDNET_DISABLE_WINDOWS_STREAM_HANDLE)
++# if defined(STDNET_HAS_IOCP)
++# define STDNET_HAS_WINDOWS_STREAM_HANDLE 1
++# endif // defined(STDNET_HAS_IOCP)
++# endif // !defined(STDNET_DISABLE_WINDOWS_STREAM_HANDLE)
++#endif // !defined(STDNET_HAS_WINDOWS_STREAM_HANDLE)
++
++// Windows: random access handles.
++#if !defined(STDNET_HAS_WINDOWS_RANDOM_ACCESS_HANDLE)
++# if !defined(STDNET_DISABLE_WINDOWS_RANDOM_ACCESS_HANDLE)
++# if defined(STDNET_HAS_IOCP)
++# define STDNET_HAS_WINDOWS_RANDOM_ACCESS_HANDLE 1
++# endif // defined(STDNET_HAS_IOCP)
++# endif // !defined(STDNET_DISABLE_WINDOWS_RANDOM_ACCESS_HANDLE)
++#endif // !defined(STDNET_HAS_WINDOWS_RANDOM_ACCESS_HANDLE)
++
++// Windows: object handles.
++#if !defined(STDNET_HAS_WINDOWS_OBJECT_HANDLE)
++# if !defined(STDNET_DISABLE_WINDOWS_OBJECT_HANDLE)
++# if defined(STDNET_WINDOWS) || defined(__CYGWIN__)
++# if !defined(UNDER_CE)
++# define STDNET_HAS_WINDOWS_OBJECT_HANDLE 1
++# endif // !defined(UNDER_CE)
++# endif // defined(STDNET_WINDOWS) || defined(__CYGWIN__)
++# endif // !defined(STDNET_DISABLE_WINDOWS_OBJECT_HANDLE)
++#endif // !defined(STDNET_HAS_WINDOWS_OBJECT_HANDLE)
++
++// Windows: OVERLAPPED wrapper.
++#if !defined(STDNET_HAS_WINDOWS_OVERLAPPED_PTR)
++# if !defined(STDNET_DISABLE_WINDOWS_OVERLAPPED_PTR)
++# if defined(STDNET_HAS_IOCP)
++# define STDNET_HAS_WINDOWS_OVERLAPPED_PTR 1
++# endif // defined(STDNET_HAS_IOCP)
++# endif // !defined(STDNET_DISABLE_WINDOWS_OVERLAPPED_PTR)
++#endif // !defined(STDNET_HAS_WINDOWS_OVERLAPPED_PTR)
++
++// POSIX: stream-oriented file descriptors.
++#if !defined(STDNET_HAS_POSIX_STREAM_DESCRIPTOR)
++# if !defined(STDNET_DISABLE_POSIX_STREAM_DESCRIPTOR)
++# if !defined(STDNET_WINDOWS) && !defined(__CYGWIN__)
++# define STDNET_HAS_POSIX_STREAM_DESCRIPTOR 1
++# endif // !defined(STDNET_WINDOWS) && !defined(__CYGWIN__)
++# endif // !defined(STDNET_DISABLE_POSIX_STREAM_DESCRIPTOR)
++#endif // !defined(STDNET_HAS_POSIX_STREAM_DESCRIPTOR)
++
++// UNIX domain sockets.
++#if !defined(STDNET_HAS_LOCAL_SOCKETS)
++# if !defined(STDNET_DISABLE_LOCAL_SOCKETS)
++# if !defined(STDNET_WINDOWS) && !defined(__CYGWIN__)
++# define STDNET_HAS_LOCAL_SOCKETS 1
++# endif // !defined(STDNET_WINDOWS) && !defined(__CYGWIN__)
++# endif // !defined(STDNET_DISABLE_LOCAL_SOCKETS)
++#endif // !defined(STDNET_HAS_LOCAL_SOCKETS)
++
++// Can use sigaction() instead of signal().
++#if !defined(STDNET_HAS_SIGACTION)
++# if !defined(STDNET_DISABLE_SIGACTION)
++# if !defined(STDNET_WINDOWS) && !defined(__CYGWIN__)
++# define STDNET_HAS_SIGACTION 1
++# endif // !defined(STDNET_WINDOWS) && !defined(__CYGWIN__)
++# endif // !defined(STDNET_DISABLE_SIGACTION)
++#endif // !defined(STDNET_HAS_SIGACTION)
++
++// Can use signal().
++#if !defined(STDNET_HAS_SIGNAL)
++# if !defined(STDNET_DISABLE_SIGNAL)
++# if !defined(UNDER_CE)
++# define STDNET_HAS_SIGNAL 1
++# endif // !defined(UNDER_CE)
++# endif // !defined(STDNET_DISABLE_SIGNAL)
++#endif // !defined(STDNET_HAS_SIGNAL)
++
++// Whether standard iostreams are disabled.
++//#if !defined(STDNET_NO_IOSTREAM)
++//# define STDNET_NO_IOSTREAM 1
++//#endif // !defined(STDNET_NO_IOSTREAM)
++
++// Whether exception handling is disabled.
++//#if !defined(STDNET_NO_EXCEPTIONS)
++//# define STDNET_NO_EXCEPTIONS 1
++//#endif // !defined(STDNET_NO_EXCEPTIONS)
++
++// Whether the typeid operator is supported.
++//#if !defined(STDNET_NO_TYPEID)
++//# define STDNET_NO_TYPEID 1
++//#endif // !defined(STDNET_NO_TYPEID)
++
++// On POSIX (and POSIX-like) platforms we need to include unistd.h in order to
++// get access to the various platform feature macros, e.g. to be able to test
++// for threads support.
++#if !defined(STDNET_HAS_UNISTD_H)
++# if defined(unix) \
++ || defined(__unix) \
++ || defined(_XOPEN_SOURCE) \
++ || defined(_POSIX_SOURCE) \
++ || (defined(__MACH__) && defined(__APPLE__)) \
++ || defined(__FreeBSD__) \
++ || defined(__NetBSD__) \
++ || defined(__OpenBSD__) \
++ || defined(__linux__)
++# define STDNET_HAS_UNISTD_H 1
++# endif
++#endif // !defined(STDNET_HAS_UNISTD_H)
++#if defined(STDNET_HAS_UNISTD_H)
++# include
++#endif // defined(STDNET_HAS_UNISTD_H)
++
++// Threads.
++#if !defined(STDNET_HAS_THREADS)
++# if !defined(STDNET_DISABLE_THREADS)
++# if defined(_MSC_VER) && defined(_MT)
++# define STDNET_HAS_THREADS 1
++# elif defined(__BORLANDC__) && defined(__MT__)
++# define STDNET_HAS_THREADS 1
++# elif defined(_POSIX_THREADS)
++# define STDNET_HAS_THREADS 1
++# endif // defined(_MSC_VER) && defined(_MT)
++# endif // !defined(STDNET_DISABLE_THREADS)
++#endif // !defined(STDNET_HAS_THREADS)
++
++// POSIX threads.
++#if !defined(STDNET_HAS_PTHREADS)
++# if defined(STDNET_HAS_THREADS)
++# if defined(_POSIX_THREADS)
++# define STDNET_HAS_PTHREADS 1
++# endif // defined(_POSIX_THREADS)
++# endif // defined(STDNET_HAS_THREADS)
++#endif // !defined(STDNET_HAS_PTHREADS)
++
++// Helper to prevent macro expansion.
++#define STDNET_PREVENT_MACRO_SUBSTITUTION
++
++// Helper to define in-class constants.
++#if !defined(STDNET_STATIC_CONSTANT)
++# define STDNET_STATIC_CONSTANT(type, assignment) \
++ static const type assignment
++#endif // !defined(STDNET_STATIC_CONSTANT)
++
++// Microsoft Visual C++'s secure C runtime library.
++#if !defined(STDNET_HAS_SECURE_RTL)
++# if !defined(STDNET_DISABLE_SECURE_RTL)
++# if defined(STDNET_MSVC) \
++ && (STDNET_MSVC >= 1400) \
++ && !defined(UNDER_CE)
++# define STDNET_HAS_SECURE_RTL 1
++# endif // defined(STDNET_MSVC)
++ // && (STDNET_MSVC >= 1400)
++ // && !defined(UNDER_CE)
++# endif // !defined(STDNET_DISABLE_SECURE_RTL)
++#endif // !defined(STDNET_HAS_SECURE_RTL)
++
++// Handler hooking. Disabled for ancient Borland C++ and gcc compilers.
++#if !defined(STDNET_HAS_HANDLER_HOOKS)
++# if !defined(STDNET_DISABLE_HANDLER_HOOKS)
++# if defined(__GNUC__)
++# if (__GNUC__ >= 3)
++# define STDNET_HAS_HANDLER_HOOKS 1
++# endif // (__GNUC__ >= 3)
++# elif !defined(__BORLANDC__)
++# define STDNET_HAS_HANDLER_HOOKS 1
++# endif // !defined(__BORLANDC__)
++# endif // !defined(STDNET_DISABLE_HANDLER_HOOKS)
++#endif // !defined(STDNET_HAS_HANDLER_HOOKS)
++
++// Support for the __thread keyword extension.
++#if !defined(STDNET_DISABLE_THREAD_KEYWORD_EXTENSION)
++# if defined(__linux__)
++# if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
++# if ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3)
++# if !defined(__INTEL_COMPILER) && !defined(__ICL)
++# define STDNET_HAS_THREAD_KEYWORD_EXTENSION 1
++# elif defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 1100)
++# define STDNET_HAS_THREAD_KEYWORD_EXTENSION 1
++# endif // defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 1100)
++# endif // ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3)
++# endif // defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
++# endif // defined(__linux__)
++#endif // !defined(STDNET_DISABLE_THREAD_KEYWORD_EXTENSION)
++
++// Support for POSIX ssize_t typedef.
++#if !defined(STDNET_DISABLE_SSIZE_T)
++# if defined(__linux__) \
++ || (defined(__MACH__) && defined(__APPLE__))
++# define STDNET_HAS_SSIZE_T 1
++# endif // defined(__linux__)
++ // || (defined(__MACH__) && defined(__APPLE__))
++#endif // !defined(STDNET_DISABLE_SSIZE_T)
++
++#endif // STDNET_DETAIL_CONFIG_HPP
+diff --git a/ip-address/include/std/net/detail/impl/socket_ops.ipp b/ip-address/include/std/net/detail/impl/socket_ops.ipp
+new file mode 100644
+index 0000000..13b32a3
+--- /dev/null
++++ b/ip-address/include/std/net/detail/impl/socket_ops.ipp
+@@ -0,0 +1,260 @@
++//
++// detail/impl/socket_ops.ipp
++// ~~~~~~~~~~~~~~~~~~~~~~~~~~
++//
++// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
++//
++// Distributed under the Boost Software License, Version 1.0. (See accompanying
++// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
++//
++
++#ifndef STDNET_DETAIL_SOCKET_OPS_IPP
++#define STDNET_DETAIL_SOCKET_OPS_IPP
++
++#if defined(_MSC_VER) && (_MSC_VER >= 1200)
++# pragma once
++#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
++
++#include "std/net/detail/config.hpp"
++#include
++#include
++#include
++#include
++#include
++#include
++#include "std/net/detail/socket_ops.hpp"
++#include "std/net/detail/system_errors.hpp"
++
++#include "std/net/detail/push_options.hpp"
++
++namespace std {
++namespace experimental {
++namespace net {
++namespace detail {
++namespace socket_ops {
++
++#if defined(__hpux)
++// HP-UX doesn't declare these functions extern "C", so they are declared again
++// here to avoid linker errors about undefined symbols.
++extern "C" char* if_indextoname(unsigned int, char*);
++extern "C" unsigned int if_nametoindex(const char*);
++#endif // defined(__hpux)
++
++inline void clear_last_error()
++{
++#if defined(STDNET_WINDOWS) || defined(__CYGWIN__)
++ WSASetLastError(0);
++#else
++ errno = 0;
++#endif
++}
++
++template
++inline ReturnType error_wrapper(ReturnType return_value,
++ std::error_code& ec)
++{
++#if defined(STDNET_WINDOWS) || defined(__CYGWIN__)
++ ec = std::error_code(WSAGetLastError(),
++ std::experimental::net::detail::syserrc::system_category());
++#else
++ ec = std::error_code(errno,
++ std::experimental::net::detail::syserrc::system_category());
++#endif
++ return return_value;
++}
++
++const char* inet_ntop(int af, const void* src, char* dest, size_t length,
++ unsigned long scope_id, std::error_code& ec)
++{
++ clear_last_error();
++#if defined(STDNET_WINDOWS) || defined(__CYGWIN__)
++ using namespace std; // For memcpy.
++
++ if (af != AF_INET && af != AF_INET6)
++ {
++ ec = std::experimental::net::detail::syserrc::address_family_not_supported;
++ return 0;
++ }
++
++ union
++ {
++ socket_addr_type base;
++ sockaddr_storage_type storage;
++ sockaddr_in4_type v4;
++ sockaddr_in6_type v6;
++ } address;
++ DWORD address_length;
++ if (af == AF_INET)
++ {
++ address_length = sizeof(sockaddr_in4_type);
++ address.v4.sin_family = AF_INET;
++ address.v4.sin_port = 0;
++ memcpy(&address.v4.sin_addr, src, sizeof(in4_addr_type));
++ }
++ else // AF_INET6
++ {
++ address_length = sizeof(sockaddr_in6_type);
++ address.v6.sin6_family = AF_INET6;
++ address.v6.sin6_port = 0;
++ address.v6.sin6_flowinfo = 0;
++ address.v6.sin6_scope_id = scope_id;
++ memcpy(&address.v6.sin6_addr, src, sizeof(in6_addr_type));
++ }
++
++ DWORD string_length = static_cast(length);
++#if defined(STDNET_NO_ANSI_APIS)
++ LPWSTR string_buffer = (LPWSTR)_alloca(length * sizeof(WCHAR));
++ int result = error_wrapper(::WSAAddressToStringW(&address.base,
++ address_length, 0, string_buffer, &string_length), ec);
++ ::WideCharToMultiByte(CP_ACP, 0, string_buffer, -1, dest, length, 0, 0);
++#else
++ int result = error_wrapper(::WSAAddressToStringA(
++ &address.base, address_length, 0, dest, &string_length), ec);
++#endif
++
++ // Windows may set error code on success.
++ if (result != socket_error_retval)
++ ec = std::error_code();
++
++ // Windows may not set an error code on failure.
++ else if (result == socket_error_retval && !ec)
++ ec = std::experimental::net::detail::syserrc::invalid_argument;
++
++ return result == socket_error_retval ? 0 : dest;
++#else // defined(STDNET_WINDOWS) || defined(__CYGWIN__)
++ const char* result = error_wrapper(::inet_ntop(
++ af, src, dest, static_cast(length)), ec);
++ if (result == 0 && !ec)
++ ec = std::experimental::net::detail::syserrc::invalid_argument;
++ if (result != 0 && af == AF_INET6 && scope_id != 0)
++ {
++ using namespace std; // For strcat and sprintf.
++ char if_name[IF_NAMESIZE + 1] = "%";
++ const in6_addr_type* ipv6_address = static_cast(src);
++ bool is_link_local = ((ipv6_address->s6_addr[0] == 0xfe)
++ && ((ipv6_address->s6_addr[1] & 0xc0) == 0x80));
++ if (!is_link_local
++ || if_indextoname(static_cast(scope_id), if_name + 1) == 0)
++ sprintf(if_name + 1, "%lu", scope_id);
++ strcat(dest, if_name);
++ }
++ return result;
++#endif // defined(STDNET_WINDOWS) || defined(__CYGWIN__)
++}
++
++int inet_pton(int af, const char* src, void* dest,
++ unsigned long* scope_id, std::error_code& ec)
++{
++ clear_last_error();
++#if defined(STDNET_WINDOWS) || defined(__CYGWIN__)
++ using namespace std; // For memcpy and strcmp.
++
++ if (af != AF_INET && af != AF_INET6)
++ {
++ ec = std::experimental::net::detail::syserrc::address_family_not_supported;
++ return -1;
++ }
++
++ union
++ {
++ socket_addr_type base;
++ sockaddr_storage_type storage;
++ sockaddr_in4_type v4;
++ sockaddr_in6_type v6;
++ } address;
++ int address_length = sizeof(sockaddr_storage_type);
++#if defined(STDNET_NO_ANSI_APIS)
++ int num_wide_chars = strlen(src) + 1;
++ LPWSTR wide_buffer = (LPWSTR)_alloca(num_wide_chars * sizeof(WCHAR));
++ ::MultiByteToWideChar(CP_ACP, 0, src, -1, wide_buffer, num_wide_chars);
++ int result = error_wrapper(::WSAStringToAddressW(
++ wide_buffer, af, 0, &address.base, &address_length), ec);
++#else
++ int result = error_wrapper(::WSAStringToAddressA(
++ const_cast(src), af, 0, &address.base, &address_length), ec);
++#endif
++
++ if (af == AF_INET)
++ {
++ if (result != socket_error_retval)
++ {
++ memcpy(dest, &address.v4.sin_addr, sizeof(in4_addr_type));
++ ec = std::error_code();
++ }
++ else if (strcmp(src, "255.255.255.255") == 0)
++ {
++ static_cast(dest)->s_addr = INADDR_NONE;
++ ec = std::error_code();
++ }
++ }
++ else // AF_INET6
++ {
++ if (result != socket_error_retval)
++ {
++ memcpy(dest, &address.v6.sin6_addr, sizeof(in6_addr_type));
++ if (scope_id)
++ *scope_id = address.v6.sin6_scope_id;
++ ec = std::error_code();
++ }
++ }
++
++ // Windows may not set an error code on failure.
++ if (result == socket_error_retval && !ec)
++ ec = std::experimental::net::detail::syserrc::invalid_argument;
++
++ if (result != socket_error_retval)
++ ec = std::error_code();
++
++ return result == socket_error_retval ? -1 : 1;
++#else // defined(STDNET_WINDOWS) || defined(__CYGWIN__)
++ int result = error_wrapper(::inet_pton(af, src, dest), ec);
++ if (result <= 0 && !ec)
++ ec = std::experimental::net::detail::syserrc::invalid_argument;
++ if (result > 0 && af == AF_INET6 && scope_id)
++ {
++ using namespace std; // For strchr and atoi.
++ *scope_id = 0;
++ if (const char* if_name = strchr(src, '%'))
++ {
++ in6_addr_type* ipv6_address = static_cast(dest);
++ bool is_link_local = ((ipv6_address->s6_addr[0] == 0xfe)
++ && ((ipv6_address->s6_addr[1] & 0xc0) == 0x80));
++ if (is_link_local)
++ *scope_id = if_nametoindex(if_name + 1);
++ if (*scope_id == 0)
++ *scope_id = atoi(if_name + 1);
++ }
++ }
++ return result;
++#endif // defined(STDNET_WINDOWS) || defined(__CYGWIN__)
++}
++
++u_long_type network_to_host_long(u_long_type value)
++{
++ return ntohl(value);
++}
++
++u_long_type host_to_network_long(u_long_type value)
++{
++ return htonl(value);
++}
++
++u_short_type network_to_host_short(u_short_type value)
++{
++ return ntohs(value);
++}
++
++u_short_type host_to_network_short(u_short_type value)
++{
++ return htons(value);
++}
++
++} // namespace socket_ops
++} // namespace detail
++} // namespace net
++} // namespace experimental
++} // namespace std
++
++#include "std/net/detail/pop_options.hpp"
++
++#endif // STDNET_DETAIL_SOCKET_OPS_IPP
+diff --git a/ip-address/include/std/net/detail/impl/system_errors.ipp b/ip-address/include/std/net/detail/impl/system_errors.ipp
+new file mode 100644
+index 0000000..8b01e08
+--- /dev/null
++++ b/ip-address/include/std/net/detail/impl/system_errors.ipp
+@@ -0,0 +1,94 @@
++//
++// impl/system_errors.ipp
++// ~~~~~~~~~~~~~~~~~~~~~~
++//
++// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
++//
++// Distributed under the Boost Software License, Version 1.0. (See accompanying
++// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
++//
++
++#ifndef STDNET_DETAIL_IMPL_SYSTEM_ERRORS_IPP
++#define STDNET_DETAIL_IMPL_SYSTEM_ERRORS_IPP
++
++#if defined(_MSC_VER) && (_MSC_VER >= 1200)
++# pragma once
++#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
++
++#include "std/net/detail/config.hpp"
++#include "std/net/detail/local_free_on_block_exit.hpp"
++#include "std/net/detail/system_errors.hpp"
++
++#include "std/net/detail/push_options.hpp"
++
++namespace std {
++namespace experimental {
++namespace net {
++namespace detail {
++namespace syserrc {
++
++class system_category : public std::error_category
++{
++public:
++ const char* name() const STDNET_ERROR_CATEGORY_NOEXCEPT
++ {
++ return "std.net.system";
++ }
++
++ std::string message(int value) const
++ {
++#if defined(STDNET_WINDOWS) || defined(__CYGWIN__)
++ char* msg = 0;
++ DWORD length = ::FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER
++ | FORMAT_MESSAGE_FROM_SYSTEM
++ | FORMAT_MESSAGE_IGNORE_INSERTS, 0, value,
++ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (char*)&msg, 0, 0);
++ detail::local_free_on_block_exit local_free_obj(msg);
++ if (length && msg[length - 1] == '\n')
++ msg[--length] = '\0';
++ if (length && msg[length - 1] == '\r')
++ msg[--length] = '\0';
++ if (length)
++ return msg;
++ else
++ return "std.net.system error";
++#else // defined(STDNET_WINDOWS)
++#if !defined(__sun)
++ if (value == ECANCELED)
++ return "Operation aborted.";
++#endif // !defined(__sun)
++#if defined(__sun) || defined(__QNX__) || defined(__SYMBIAN32__)
++ using namespace std;
++ return strerror(value);
++#elif defined(__MACH__) && defined(__APPLE__) \
++ || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) \
++ || defined(_AIX) || defined(__hpux) || defined(__osf__) \
++ || defined(__ANDROID__)
++ char buf[256] = "";
++ using namespace std;
++ strerror_r(value, buf, sizeof(buf));
++ return buf;
++#else
++ char buf[256] = "";
++ return strerror_r(value, buf, sizeof(buf));
++#endif
++#endif // defined(STDNET_WINDOWS)
++ }
++};
++
++} // namespace syserrc
++
++const std::error_category& system_category()
++{
++ static syserrc::system_category instance;
++ return instance;
++}
++
++} // namespace detail
++} // namespace net
++} // namespace experimental
++} // namespace std
++
++#include "std/net/detail/pop_options.hpp"
++
++#endif // STDNET_DETAIL_IMPL_SYSTEM_ERRORS_IPP
+diff --git a/ip-address/include/std/net/detail/impl/throw_error.ipp b/ip-address/include/std/net/detail/impl/throw_error.ipp
+new file mode 100644
+index 0000000..92baf1c
+--- /dev/null
++++ b/ip-address/include/std/net/detail/impl/throw_error.ipp
+@@ -0,0 +1,49 @@
++//
++// detail/impl/throw_error.ipp
++// ~~~~~~~~~~~~~~~~~~~~~~~~~~~
++//
++// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
++//
++// Distributed under the Boost Software License, Version 1.0. (See accompanying
++// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
++//
++
++#ifndef STDNET_DETAIL_IMPL_THROW_ERROR_IPP
++#define STDNET_DETAIL_IMPL_THROW_ERROR_IPP
++
++#if defined(_MSC_VER) && (_MSC_VER >= 1200)
++# pragma once
++#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
++
++#include "std/net/detail/config.hpp"
++#include
++#include "std/net/detail/throw_error.hpp"
++#include "std/net/detail/throw_exception.hpp"
++
++#include "std/net/detail/push_options.hpp"
++
++namespace std {
++namespace experimental {
++namespace net {
++namespace detail {
++
++void do_throw_error(const std::error_code& err)
++{
++ std::system_error e(err);
++ std::experimental::net::detail::throw_exception(e);
++}
++
++void do_throw_error(const std::error_code& err, const char* location)
++{
++ std::system_error e(err, location);
++ std::experimental::net::detail::throw_exception(e);
++}
++
++} // namespace detail
++} // namespace net
++} // namespace experimental
++} // namespace std
++
++#include "std/net/detail/pop_options.hpp"
++
++#endif // STDNET_DETAIL_IMPL_THROW_ERROR_IPP
+diff --git a/ip-address/include/std/net/detail/impl/winsock_init.ipp b/ip-address/include/std/net/detail/impl/winsock_init.ipp
+new file mode 100644
+index 0000000..7114f6a
+--- /dev/null
++++ b/ip-address/include/std/net/detail/impl/winsock_init.ipp
+@@ -0,0 +1,73 @@
++//
++// detail/impl/winsock_init.ipp
++// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
++//
++// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
++//
++// Distributed under the Boost Software License, Version 1.0. (See accompanying
++// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
++//
++
++#ifndef STDNET_DETAIL_IMPL_WINSOCK_INIT_IPP
++#define STDNET_DETAIL_IMPL_WINSOCK_INIT_IPP
++
++#if defined(_MSC_VER) && (_MSC_VER >= 1200)
++# pragma once
++#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
++
++#include "std/net/detail/config.hpp"
++
++#if defined(STDNET_WINDOWS) || defined(__CYGWIN__)
++
++#include "std/net/detail/socket_types.hpp"
++#include "std/net/detail/system_errors.hpp"
++#include "std/net/detail/winsock_init.hpp"
++#include "std/net/detail/throw_error.hpp"
++
++#include "std/net/detail/push_options.hpp"
++
++namespace std {
++namespace experimental {
++namespace net {
++namespace detail {
++
++void winsock_init_base::startup(data& d,
++ unsigned char major, unsigned char minor)
++{
++ if (::InterlockedIncrement(&d.init_count_) == 1)
++ {
++ WSADATA wsa_data;
++ long result = ::WSAStartup(MAKEWORD(major, minor), &wsa_data);
++ ::InterlockedExchange(&d.result_, result);
++ }
++}
++
++void winsock_init_base::cleanup(data& d)
++{
++ if (::InterlockedDecrement(&d.init_count_) == 0)
++ {
++ ::WSACleanup();
++ }
++}
++
++void winsock_init_base::throw_on_error(data& d)
++{
++ long result = ::InterlockedExchangeAdd(&d.result_, 0);
++ if (result != 0)
++ {
++ std::error_code ec(result,
++ std::experimental::net::detail::system_category());
++ std::experimental::net::detail::throw_error(ec, "winsock");
++ }
++}
++
++} // namespace detail
++} // namespace net
++} // namespace experimental
++} // namespace std
++
++#include "std/net/detail/pop_options.hpp"
++
++#endif // defined(STDNET_WINDOWS) || defined(__CYGWIN__)
++
++#endif // STDNET_DETAIL_IMPL_WINSOCK_INIT_IPP
+diff --git a/ip-address/include/std/net/detail/local_free_on_block_exit.hpp b/ip-address/include/std/net/detail/local_free_on_block_exit.hpp
+new file mode 100644
+index 0000000..64c9137
+--- /dev/null
++++ b/ip-address/include/std/net/detail/local_free_on_block_exit.hpp
+@@ -0,0 +1,63 @@
++//
++// detail/local_free_on_block_exit.hpp
++// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
++//
++// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
++//
++// Distributed under the Boost Software License, Version 1.0. (See accompanying
++// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
++//
++
++#ifndef STDNET_DETAIL_LOCAL_FREE_ON_BLOCK_EXIT_HPP
++#define STDNET_DETAIL_LOCAL_FREE_ON_BLOCK_EXIT_HPP
++
++#if defined(_MSC_VER) && (_MSC_VER >= 1200)
++# pragma once
++#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
++
++#include "std/net/detail/config.hpp"
++
++#if defined(STDNET_WINDOWS) || defined(__CYGWIN__)
++
++#include "std/net/detail/socket_types.hpp"
++
++#include "std/net/detail/push_options.hpp"
++
++namespace std {
++namespace experimental {
++namespace net {
++namespace detail {
++
++class local_free_on_block_exit
++{
++public:
++ // Constructor blocks all signals for the calling thread.
++ explicit local_free_on_block_exit(void* p)
++ : p_(p)
++ {
++ }
++
++ // Destructor restores the previous signal mask.
++ ~local_free_on_block_exit()
++ {
++ ::LocalFree(p_);
++ }
++
++private:
++ // Disallow copying and assignemnt.
++ local_free_on_block_exit(const local_free_on_block_exit&);
++ local_free_on_block_exit& operator=(const local_free_on_block_exit&);
++
++ void* p_;
++};
++
++} // namespace detail
++} // namespace net
++} // namespace experimental
++} // namespace std
++
++#include "std/net/detail/pop_options.hpp"
++
++#endif // defined(STDNET_WINDOWS) || defined(__CYGWIN__)
++
++#endif // STDNET_DETAIL_LOCAL_FREE_ON_BLOCK_EXIT_HPP
+diff --git a/ip-address/include/std/net/detail/old_win_sdk_compat.hpp b/ip-address/include/std/net/detail/old_win_sdk_compat.hpp
+new file mode 100644
+index 0000000..3a3cdac
+--- /dev/null
++++ b/ip-address/include/std/net/detail/old_win_sdk_compat.hpp
+@@ -0,0 +1,218 @@
++//
++// detail/old_win_sdk_compat.hpp
++// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
++//
++// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
++//
++// Distributed under the Boost Software License, Version 1.0. (See accompanying
++// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
++//
++
++#ifndef STDNET_DETAIL_OLD_WIN_SDK_COMPAT_HPP
++#define STDNET_DETAIL_OLD_WIN_SDK_COMPAT_HPP
++
++#if defined(_MSC_VER) && (_MSC_VER >= 1200)
++# pragma once
++#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
++
++#include "std/net/detail/config.hpp"
++
++#if defined(STDNET_WINDOWS) || defined(__CYGWIN__)
++
++// Guess whether we are building against on old Platform SDK.
++#if !defined(IN6ADDR_ANY_INIT)
++#define STDNET_HAS_OLD_WIN_SDK 1
++#endif // !defined(IN6ADDR_ANY_INIT)
++
++#if defined(STDNET_HAS_OLD_WIN_SDK)
++
++// Emulation of types that are missing from old Platform SDKs.
++//
++// N.B. this emulation is also used if building for a Windows 2000 target with
++// a recent (i.e. Vista or later) SDK, as the SDK does not provide IPv6 support
++// in that case.
++
++#include "std/net/detail/push_options.hpp"
++
++namespace std {
++namespace experimental {
++namespace net {
++namespace detail {
++
++enum
++{
++ sockaddr_storage_maxsize = 128, // Maximum size.
++ sockaddr_storage_alignsize = (sizeof(__int64)), // Desired alignment.
++ sockaddr_storage_pad1size = (sockaddr_storage_alignsize - sizeof(short)),
++ sockaddr_storage_pad2size = (sockaddr_storage_maxsize -
++ (sizeof(short) + sockaddr_storage_pad1size + sockaddr_storage_alignsize))
++};
++
++struct sockaddr_storage_emulation
++{
++ short ss_family;
++ char __ss_pad1[sockaddr_storage_pad1size];
++ __int64 __ss_align;
++ char __ss_pad2[sockaddr_storage_pad2size];
++};
++
++struct in6_addr_emulation
++{
++ union
++ {
++ u_char Byte[16];
++ u_short Word[8];
++ } u;
++};
++
++#if !defined(s6_addr)
++# define _S6_un u
++# define _S6_u8 Byte
++# define s6_addr _S6_un._S6_u8
++#endif // !defined(s6_addr)
++
++struct sockaddr_in6_emulation
++{
++ short sin6_family;
++ u_short sin6_port;
++ u_long sin6_flowinfo;
++ in6_addr_emulation sin6_addr;
++ u_long sin6_scope_id;
++};
++
++struct ipv6_mreq_emulation
++{
++ in6_addr_emulation ipv6mr_multiaddr;
++ unsigned int ipv6mr_interface;
++};
++
++struct addrinfo_emulation
++{
++ int ai_flags;
++ int ai_family;
++ int ai_socktype;
++ int ai_protocol;
++ size_t ai_addrlen;
++ char* ai_canonname;
++ sockaddr* ai_addr;
++ addrinfo_emulation* ai_next;
++};
++
++#if !defined(AI_PASSIVE)
++# define AI_PASSIVE 0x1
++#endif
++
++#if !defined(AI_CANONNAME)
++# define AI_CANONNAME 0x2
++#endif
++
++#if !defined(AI_NUMERICHOST)
++# define AI_NUMERICHOST 0x4
++#endif
++
++#if !defined(EAI_AGAIN)
++# define EAI_AGAIN WSATRY_AGAIN
++#endif
++
++#if !defined(EAI_BADFLAGS)
++# define EAI_BADFLAGS WSAEINVAL
++#endif
++
++#if !defined(EAI_FAIL)
++# define EAI_FAIL WSANO_RECOVERY
++#endif
++
++#if !defined(EAI_FAMILY)
++# define EAI_FAMILY WSAEAFNOSUPPORT
++#endif
++
++#if !defined(EAI_MEMORY)
++# define EAI_MEMORY WSA_NOT_ENOUGH_MEMORY
++#endif
++
++#if !defined(EAI_NODATA)
++# define EAI_NODATA WSANO_DATA
++#endif
++
++#if !defined(EAI_NONAME)
++# define EAI_NONAME WSAHOST_NOT_FOUND
++#endif
++
++#if !defined(EAI_SERVICE)
++# define EAI_SERVICE WSATYPE_NOT_FOUND
++#endif
++
++#if !defined(EAI_SOCKTYPE)
++# define EAI_SOCKTYPE WSAESOCKTNOSUPPORT
++#endif
++
++#if !defined(NI_NOFQDN)
++# define NI_NOFQDN 0x01
++#endif
++
++#if !defined(NI_NUMERICHOST)
++# define NI_NUMERICHOST 0x02
++#endif
++
++#if !defined(NI_NAMEREQD)
++# define NI_NAMEREQD 0x04
++#endif
++
++#if !defined(NI_NUMERICSERV)
++# define NI_NUMERICSERV 0x08
++#endif
++
++#if !defined(NI_DGRAM)
++# define NI_DGRAM 0x10
++#endif
++
++#if !defined(IPPROTO_IPV6)
++# define IPPROTO_IPV6 41
++#endif
++
++#if !defined(IPV6_UNICAST_HOPS)
++# define IPV6_UNICAST_HOPS 4
++#endif
++
++#if !defined(IPV6_MULTICAST_IF)
++# define IPV6_MULTICAST_IF 9
++#endif
++
++#if !defined(IPV6_MULTICAST_HOPS)
++# define IPV6_MULTICAST_HOPS 10
++#endif
++
++#if !defined(IPV6_MULTICAST_LOOP)
++# define IPV6_MULTICAST_LOOP 11
++#endif
++
++#if !defined(IPV6_JOIN_GROUP)
++# define IPV6_JOIN_GROUP 12
++#endif
++
++#if !defined(IPV6_LEAVE_GROUP)
++# define IPV6_LEAVE_GROUP 13
++#endif
++
++} // namespace detail
++} // namespace net
++} // namespace experimental
++} // namespace std
++
++#include "std/net/detail/pop_options.hpp"
++
++#endif // defined(STDNET_HAS_OLD_WIN_SDK)
++
++// Even newer Platform SDKs that support IPv6 may not define IPV6_V6ONLY.
++#if !defined(IPV6_V6ONLY)
++# define IPV6_V6ONLY 27
++#endif
++
++// Some SDKs (e.g. Windows CE) don't define IPPROTO_ICMPV6.
++#if !defined(IPPROTO_ICMPV6)
++# define IPPROTO_ICMPV6 58
++#endif
++
++#endif // defined(STDNET_WINDOWS) || defined(__CYGWIN__)
++
++#endif // STDNET_DETAIL_OLD_WIN_SDK_COMPAT_HPP
+diff --git a/ip-address/include/std/net/detail/pop_options.hpp b/ip-address/include/std/net/detail/pop_options.hpp
+new file mode 100644
+index 0000000..8aa375e
+--- /dev/null
++++ b/ip-address/include/std/net/detail/pop_options.hpp
+@@ -0,0 +1,98 @@
++//
++// detail/pop_options.hpp
++// ~~~~~~~~~~~~~~~~~~~~~~
++//
++// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
++//
++// Distributed under the Boost Software License, Version 1.0. (See accompanying
++// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
++//
++
++// No header guard
++
++#if defined(__COMO__)
++
++// Comeau C++
++
++#elif defined(__DMC__)
++
++// Digital Mars C++
++
++#elif defined(__INTEL_COMPILER) || defined(__ICL) \
++ || defined(__ICC) || defined(__ECC)
++
++// Intel C++
++
++#elif defined(__GNUC__)
++
++// GNU C++
++
++# if defined(__MINGW32__) || defined(__CYGWIN__)
++# pragma pack (pop)
++# endif
++
++# if defined(__OBJC__)
++# if !defined(__APPLE_CC__) || (__APPLE_CC__ <= 1)
++# if defined(STDNET_OBJC_WORKAROUND)
++# undef Protocol
++# undef id
++# undef STDNET_OBJC_WORKAROUND
++# endif
++# endif
++# endif
++
++#elif defined(__KCC)
++
++// Kai C++
++
++#elif defined(__sgi)
++
++// SGI MIPSpro C++
++
++#elif defined(__DECCXX)
++
++// Compaq Tru64 Unix cxx
++
++#elif defined(__ghs)
++
++// Greenhills C++
++
++#elif defined(__BORLANDC__)
++
++// Borland C++
++
++# pragma option pop
++# pragma nopushoptwarn
++# pragma nopackwarning
++
++#elif defined(__MWERKS__)
++
++// Metrowerks CodeWarrior
++
++#elif defined(__SUNPRO_CC)
++
++// Sun Workshop Compiler C++
++
++#elif defined(__HP_aCC)
++
++// HP aCC
++
++#elif defined(__MRC__) || defined(__SC__)
++
++// MPW MrCpp or SCpp
++
++#elif defined(__IBMCPP__)
++
++// IBM Visual Age
++
++#elif defined(_MSC_VER)
++
++// Microsoft Visual C++
++//
++// Must remain the last #elif since some other vendors (Metrowerks, for example)
++// also #define _MSC_VER
++
++# pragma warning (pop)
++# pragma pack (pop)
++
++#endif
+diff --git a/ip-address/include/std/net/detail/push_options.hpp b/ip-address/include/std/net/detail/push_options.hpp
+new file mode 100644
+index 0000000..ec64373
+--- /dev/null
++++ b/ip-address/include/std/net/detail/push_options.hpp
+@@ -0,0 +1,127 @@
++//
++// detail/push_options.hpp
++// ~~~~~~~~~~~~~~~~~~~~~~~
++//
++// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
++//
++// Distributed under the Boost Software License, Version 1.0. (See accompanying
++// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
++//
++
++// No header guard
++
++#if defined(__COMO__)
++
++// Comeau C++
++
++#elif defined(__DMC__)
++
++// Digital Mars C++
++
++#elif defined(__INTEL_COMPILER) || defined(__ICL) \
++ || defined(__ICC) || defined(__ECC)
++
++// Intel C++
++
++#elif defined(__GNUC__)
++
++// GNU C++
++
++# if defined(__MINGW32__) || defined(__CYGWIN__)
++# pragma pack (push, 8)
++# endif
++
++# if defined(__OBJC__)
++# if !defined(__APPLE_CC__) || (__APPLE_CC__ <= 1)
++# if !defined(STDNET_DISABLE_OBJC_WORKAROUND)
++# if !defined(Protocol) && !defined(id)
++# define Protocol cpp_Protocol
++# define id cpp_id
++# define STDNET_OBJC_WORKAROUND
++# endif
++# endif
++# endif
++# endif
++
++#elif defined(__KCC)
++
++// Kai C++
++
++#elif defined(__sgi)
++
++// SGI MIPSpro C++
++
++#elif defined(__DECCXX)
++
++// Compaq Tru64 Unix cxx
++
++#elif defined(__ghs)
++
++// Greenhills C++
++
++#elif defined(__BORLANDC__)
++
++// Borland C++
++
++# pragma option push -a8 -b -Ve- -Vx- -w-inl -vi-
++# pragma nopushoptwarn
++# pragma nopackwarning
++# if !defined(__MT__)
++# error Multithreaded RTL must be selected.
++# endif // !defined(__MT__)
++
++#elif defined(__MWERKS__)
++
++// Metrowerks CodeWarrior
++
++#elif defined(__SUNPRO_CC)
++
++// Sun Workshop Compiler C++
++
++#elif defined(__HP_aCC)
++
++// HP aCC
++
++#elif defined(__MRC__) || defined(__SC__)
++
++// MPW MrCpp or SCpp
++
++#elif defined(__IBMCPP__)
++
++// IBM Visual Age
++
++#elif defined(_MSC_VER)
++
++// Microsoft Visual C++
++//
++// Must remain the last #elif since some other vendors (Metrowerks, for example)
++// also #define _MSC_VER
++
++# pragma warning (disable:4103)
++# pragma warning (push)
++# pragma warning (disable:4127)
++# pragma warning (disable:4180)
++# pragma warning (disable:4244)
++# pragma warning (disable:4355)
++# pragma warning (disable:4512)
++# pragma warning (disable:4675)
++# if defined(_M_IX86) && defined(_Wp64)
++// The /Wp64 option is broken. If you want to check 64 bit portability, use a
++// 64 bit compiler!
++# pragma warning (disable:4311)
++# pragma warning (disable:4312)
++# endif // defined(_M_IX86) && defined(_Wp64)
++# pragma pack (push, 8)
++// Note that if the /Og optimisation flag is enabled with MSVC6, the compiler
++// has a tendency to incorrectly optimise away some calls to member template
++// functions, even though those functions contain code that should not be
++// optimised away! Therefore we will always disable this optimisation option
++// for the MSVC6 compiler.
++# if (_MSC_VER < 1300)
++# pragma optimize ("g", off)
++# endif
++# if !defined(_MT)
++# error Multithreaded RTL must be selected.
++# endif // !defined(_MT)
++
++#endif
+diff --git a/ip-address/include/std/net/detail/socket_ops.hpp b/ip-address/include/std/net/detail/socket_ops.hpp
+new file mode 100644
+index 0000000..02cb8a4
+--- /dev/null
++++ b/ip-address/include/std/net/detail/socket_ops.hpp
+@@ -0,0 +1,57 @@
++//
++// detail/socket_ops.hpp
++// ~~~~~~~~~~~~~~~~~~~~~
++//
++// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
++//
++// Distributed under the Boost Software License, Version 1.0. (See accompanying
++// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
++//
++
++#ifndef STDNET_DETAIL_SOCKET_OPS_HPP
++#define STDNET_DETAIL_SOCKET_OPS_HPP
++
++#if defined(_MSC_VER) && (_MSC_VER >= 1200)
++# pragma once
++#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
++
++#include "std/net/detail/config.hpp"
++
++#include
++#include "std/net/detail/socket_types.hpp"
++
++#include "std/net/detail/push_options.hpp"
++
++namespace std {
++namespace experimental {
++namespace net {
++namespace detail {
++namespace socket_ops {
++
++STDNET_DECL const char* inet_ntop(int af, const void* src, char* dest,
++ size_t length, unsigned long scope_id, std::error_code& ec);
++
++STDNET_DECL int inet_pton(int af, const char* src, void* dest,
++ unsigned long* scope_id, std::error_code& ec);
++
++STDNET_DECL u_long_type network_to_host_long(u_long_type value);
++
++STDNET_DECL u_long_type host_to_network_long(u_long_type value);
++
++STDNET_DECL u_short_type network_to_host_short(u_short_type value);
++
++STDNET_DECL u_short_type host_to_network_short(u_short_type value);
++
++} // namespace socket_ops
++} // namespace detail
++} // namespace net
++} // namespace experimental
++} // namespace std
++
++#include "std/net/detail/pop_options.hpp"
++
++#if defined(STDNET_HEADER_ONLY)
++# include "std/net/detail/impl/socket_ops.ipp"
++#endif // defined(STDNET_HEADER_ONLY)
++
++#endif // STDNET_DETAIL_SOCKET_OPS_HPP
+diff --git a/ip-address/include/std/net/detail/socket_types.hpp b/ip-address/include/std/net/detail/socket_types.hpp
+new file mode 100644
+index 0000000..ee35521
+--- /dev/null
++++ b/ip-address/include/std/net/detail/socket_types.hpp
+@@ -0,0 +1,186 @@
++//
++// detail/socket_types.hpp
++// ~~~~~~~~~~~~~~~~~~~~~~~
++//
++// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
++//
++// Distributed under the Boost Software License, Version 1.0. (See accompanying
++// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
++//
++
++#ifndef STDNET_DETAIL_SOCKET_TYPES_HPP
++#define STDNET_DETAIL_SOCKET_TYPES_HPP
++
++#if defined(_MSC_VER) && (_MSC_VER >= 1200)
++# pragma once
++#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
++
++#include "std/net/detail/config.hpp"
++
++#if defined(STDNET_WINDOWS) || defined(__CYGWIN__)
++# if defined(_WINSOCKAPI_) && !defined(_WINSOCK2API_)
++# error WinSock.h has already been included
++# endif // defined(_WINSOCKAPI_) && !defined(_WINSOCK2API_)
++# if defined(__BORLANDC__)
++# include // Needed for __errno
++# if !defined(_WSPIAPI_H_)
++# define _WSPIAPI_H_
++# define STDNET_WSPIAPI_H_DEFINED
++# endif // !defined(_WSPIAPI_H_)
++# endif // defined(__BORLANDC__)
++# include
++# include
++# include
++# if defined(STDNET_WSPIAPI_H_DEFINED)
++# undef _WSPIAPI_H_
++# undef STDNET_WSPIAPI_H_DEFINED
++# endif // defined(STDNET_WSPIAPI_H_DEFINED)
++# if !defined(STDNET_NO_DEFAULT_LINKED_LIBS)
++# if defined(UNDER_CE)
++# pragma comment(lib, "ws2.lib")
++# elif defined(_MSC_VER) || defined(__BORLANDC__)
++# pragma comment(lib, "ws2_32.lib")
++# pragma comment(lib, "mswsock.lib")
++# endif // defined(_MSC_VER) || defined(__BORLANDC__)
++# endif // !defined(STDNET_NO_DEFAULT_LINKED_LIBS)
++# include "std/net/detail/old_win_sdk_compat.hpp"
++#else
++# include
++# if !defined(__SYMBIAN32__)
++# include
++# endif
++# include
++# include
++# include
++# if defined(__hpux)
++# include
++# endif
++# if !defined(__hpux) || defined(__SELECT)
++# include
++# endif
++# include
++# include
++# include
++# include
++# if !defined(__SYMBIAN32__)
++# include
++# endif
++# include
++# include
++# include
++# include
++# if defined(__sun)
++# include
++# include
++# endif
++#endif
++
++#include "std/net/detail/push_options.hpp"
++
++namespace std {
++namespace experimental {
++namespace net {
++namespace detail {
++
++#if defined(STDNET_WINDOWS) || defined(__CYGWIN__)
++typedef SOCKET socket_type;
++const SOCKET invalid_socket = INVALID_SOCKET;
++const int socket_error_retval = SOCKET_ERROR;
++const int max_addr_v4_str_len = 256;
++const int max_addr_v6_str_len = 256;
++typedef sockaddr socket_addr_type;
++typedef in_addr in4_addr_type;
++typedef ip_mreq in4_mreq_type;
++typedef sockaddr_in sockaddr_in4_type;
++# if defined(STDNET_HAS_OLD_WIN_SDK)
++typedef in6_addr_emulation in6_addr_type;
++typedef ipv6_mreq_emulation in6_mreq_type;
++typedef sockaddr_in6_emulation sockaddr_in6_type;
++typedef sockaddr_storage_emulation sockaddr_storage_type;
++typedef addrinfo_emulation addrinfo_type;
++# else
++typedef in6_addr in6_addr_type;
++typedef ipv6_mreq in6_mreq_type;
++typedef sockaddr_in6 sockaddr_in6_type;
++typedef sockaddr_storage sockaddr_storage_type;
++typedef addrinfo addrinfo_type;
++# endif
++typedef unsigned long ioctl_arg_type;
++typedef u_long u_long_type;
++typedef u_short u_short_type;
++typedef int signed_size_type;
++const int shutdown_receive = SD_RECEIVE;
++const int shutdown_send = SD_SEND;
++const int shutdown_both = SD_BOTH;
++const int message_peek = MSG_PEEK;
++const int message_out_of_band = MSG_OOB;
++const int message_do_not_route = MSG_DONTROUTE;
++const int message_end_of_record = 0; // Not supported on Windows.
++# if defined (_WIN32_WINNT)
++const int max_iov_len = 64;
++# else
++const int max_iov_len = 16;
++# endif
++#else
++typedef int socket_type;
++const int invalid_socket = -1;
++const int socket_error_retval = -1;
++const int max_addr_v4_str_len = INET_ADDRSTRLEN;
++#if defined(INET6_ADDRSTRLEN)
++const int max_addr_v6_str_len = INET6_ADDRSTRLEN + 1 + IF_NAMESIZE;
++#else // defined(INET6_ADDRSTRLEN)
++const int max_addr_v6_str_len = 256;
++#endif // defined(INET6_ADDRSTRLEN)
++typedef sockaddr socket_addr_type;
++typedef in_addr in4_addr_type;
++# if defined(__hpux)
++// HP-UX doesn't provide ip_mreq when _XOPEN_SOURCE_EXTENDED is defined.
++struct in4_mreq_type
++{
++ struct in_addr imr_multiaddr;
++ struct in_addr imr_interface;
++};
++# else
++typedef ip_mreq in4_mreq_type;
++# endif
++typedef sockaddr_in sockaddr_in4_type;
++typedef in6_addr in6_addr_type;
++typedef ipv6_mreq in6_mreq_type;
++typedef sockaddr_in6 sockaddr_in6_type;
++typedef sockaddr_storage sockaddr_storage_type;
++typedef sockaddr_un sockaddr_un_type;
++typedef addrinfo addrinfo_type;
++typedef int ioctl_arg_type;
++typedef uint32_t u_long_type;
++typedef uint16_t u_short_type;
++#if defined(STDNET_HAS_SSIZE_T)
++typedef ssize_t signed_size_type;
++#else // defined(STDNET_HAS_SSIZE_T)
++typedef int signed_size_type;
++#endif // defined(STDNET_HAS_SSIZE_T)
++const int shutdown_receive = SHUT_RD;
++const int shutdown_send = SHUT_WR;
++const int shutdown_both = SHUT_RDWR;
++const int message_peek = MSG_PEEK;
++const int message_out_of_band = MSG_OOB;
++const int message_do_not_route = MSG_DONTROUTE;
++const int message_end_of_record = MSG_EOR;
++# if defined(IOV_MAX)
++const int max_iov_len = IOV_MAX;
++# else
++// POSIX platforms are not required to define IOV_MAX.
++const int max_iov_len = 16;
++# endif
++#endif
++const int custom_socket_option_level = 0xA5100000;
++const int enable_connection_aborted_option = 1;
++const int always_fail_option = 2;
++
++} // namespace detail
++} // namespace net
++} // namespace experimental
++} // namespace std
++
++#include "std/net/detail/pop_options.hpp"
++
++#endif // STDNET_DETAIL_SOCKET_TYPES_HPP
+diff --git a/ip-address/include/std/net/detail/system_errors.hpp b/ip-address/include/std/net/detail/system_errors.hpp
+new file mode 100644
+index 0000000..4aa5c44
+--- /dev/null
++++ b/ip-address/include/std/net/detail/system_errors.hpp
+@@ -0,0 +1,221 @@
++//
++// system_errors.hpp
++// ~~~~~~~~~~~~~~~~~
++//
++// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
++//
++// Distributed under the Boost Software License, Version 1.0. (See accompanying
++// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
++//
++
++#ifndef STDNET_DETAIL_SYSTEM_ERRORS_HPP
++#define STDNET_DETAIL_SYSTEM_ERRORS_HPP
++
++#if defined(_MSC_VER) && (_MSC_VER >= 1200)
++# pragma once
++#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
++
++#include "std/net/detail/config.hpp"
++#include
++#if defined(STDNET_WINDOWS) || defined(__CYGWIN__)
++# include
++#else
++# include
++#endif
++
++#if defined(GENERATING_DOCUMENTATION)
++/// INTERNAL ONLY.
++# define STDNET_NATIVE_ERROR(e) implementation_defined
++/// INTERNAL ONLY.
++# define STDNET_SOCKET_ERROR(e) implementation_defined
++/// INTERNAL ONLY.
++# define STDNET_NETDB_ERROR(e) implementation_defined
++/// INTERNAL ONLY.
++# define STDNET_GETADDRINFO_ERROR(e) implementation_defined
++/// INTERNAL ONLY.
++# define STDNET_WIN_OR_POSIX(e_win, e_posix) implementation_defined
++#elif defined(STDNET_WINDOWS) || defined(__CYGWIN__)
++# define STDNET_NATIVE_ERROR(e) e
++# define STDNET_SOCKET_ERROR(e) WSA ## e
++# define STDNET_NETDB_ERROR(e) WSA ## e
++# define STDNET_GETADDRINFO_ERROR(e) WSA ## e
++# define STDNET_WIN_OR_POSIX(e_win, e_posix) e_win
++#else
++# define STDNET_NATIVE_ERROR(e) e
++# define STDNET_SOCKET_ERROR(e) e
++# define STDNET_NETDB_ERROR(e) e
++# define STDNET_GETADDRINFO_ERROR(e) e
++# define STDNET_WIN_OR_POSIX(e_win, e_posix) e_posix
++#endif
++
++#include "std/net/detail/push_options.hpp"
++
++namespace std {
++namespace experimental {
++namespace net {
++namespace detail {
++namespace syserrc {
++
++enum system_errors
++{
++ /// Permission denied.
++ access_denied = STDNET_SOCKET_ERROR(EACCES),
++
++ /// Address family not supported by protocol.
++ address_family_not_supported = STDNET_SOCKET_ERROR(EAFNOSUPPORT),
++
++ /// Address already in use.
++ address_in_use = STDNET_SOCKET_ERROR(EADDRINUSE),
++
++ /// Transport endpoint is already connected.
++ already_connected = STDNET_SOCKET_ERROR(EISCONN),
++
++ /// Operation already in progress.
++ already_started = STDNET_SOCKET_ERROR(EALREADY),
++
++ /// Broken pipe.
++ broken_pipe = STDNET_WIN_OR_POSIX(
++ STDNET_NATIVE_ERROR(ERROR_BROKEN_PIPE),
++ STDNET_NATIVE_ERROR(EPIPE)),
++
++ /// A connection has been aborted.
++ connection_aborted = STDNET_SOCKET_ERROR(ECONNABORTED),
++
++ /// Connection refused.
++ connection_refused = STDNET_SOCKET_ERROR(ECONNREFUSED),
++
++ /// Connection reset by peer.
++ connection_reset = STDNET_SOCKET_ERROR(ECONNRESET),
++
++ /// Bad file descriptor.
++ bad_descriptor = STDNET_SOCKET_ERROR(EBADF),
++
++ /// Bad address.
++ fault = STDNET_SOCKET_ERROR(EFAULT),
++
++ /// No route to host.
++ host_unreachable = STDNET_SOCKET_ERROR(EHOSTUNREACH),
++
++ /// Operation now in progress.
++ in_progress = STDNET_SOCKET_ERROR(EINPROGRESS),
++
++ /// Interrupted system call.
++ interrupted = STDNET_SOCKET_ERROR(EINTR),
++
++ /// Invalid argument.
++ invalid_argument = STDNET_SOCKET_ERROR(EINVAL),
++
++ /// Message too long.
++ message_size = STDNET_SOCKET_ERROR(EMSGSIZE),
++
++ /// The name was too long.
++ name_too_long = STDNET_SOCKET_ERROR(ENAMETOOLONG),
++
++ /// Network is down.
++ network_down = STDNET_SOCKET_ERROR(ENETDOWN),
++
++ /// Network dropped connection on reset.
++ network_reset = STDNET_SOCKET_ERROR(ENETRESET),
++
++ /// Network is unreachable.
++ network_unreachable = STDNET_SOCKET_ERROR(ENETUNREACH),
++
++ /// Too many open files.
++ no_descriptors = STDNET_SOCKET_ERROR(EMFILE),
++
++ /// No buffer space available.
++ no_buffer_space = STDNET_SOCKET_ERROR(ENOBUFS),
++
++ /// Cannot allocate memory.
++ no_memory = STDNET_WIN_OR_POSIX(
++ STDNET_NATIVE_ERROR(ERROR_OUTOFMEMORY),
++ STDNET_NATIVE_ERROR(ENOMEM)),
++
++ /// Operation not permitted.
++ no_permission = STDNET_WIN_OR_POSIX(
++ STDNET_NATIVE_ERROR(ERROR_ACCESS_DENIED),
++ STDNET_NATIVE_ERROR(EPERM)),
++
++ /// Protocol not available.
++ no_protocol_option = STDNET_SOCKET_ERROR(ENOPROTOOPT),
++
++ /// Transport endpoint is not connected.
++ not_connected = STDNET_SOCKET_ERROR(ENOTCONN),
++
++ /// Socket operation on non-socket.
++ not_socket = STDNET_SOCKET_ERROR(ENOTSOCK),
++
++ /// Operation cancelled.
++ operation_aborted = STDNET_WIN_OR_POSIX(
++ STDNET_NATIVE_ERROR(ERROR_OPERATION_ABORTED),
++ STDNET_NATIVE_ERROR(ECANCELED)),
++
++ /// Operation not supported.
++ operation_not_supported = STDNET_SOCKET_ERROR(EOPNOTSUPP),
++
++ /// Cannot send after transport endpoint shutdown.
++ shut_down = STDNET_SOCKET_ERROR(ESHUTDOWN),
++
++ /// Connection timed out.
++ timed_out = STDNET_SOCKET_ERROR(ETIMEDOUT),
++
++ /// Resource temporarily unavailable.
++ try_again = STDNET_WIN_OR_POSIX(
++ STDNET_NATIVE_ERROR(ERROR_RETRY),
++ STDNET_NATIVE_ERROR(EAGAIN)),
++
++ /// The socket is marked non-blocking and the requested operation would block.
++ would_block = STDNET_SOCKET_ERROR(EWOULDBLOCK)
++};
++
++} // namespace syserrc
++
++/// Returns the error category used for the system errors.
++extern STDNET_DECL const std::error_category& system_category();
++
++} // namespace detail
++} // namespace net
++} // namespace experimental
++} // namespace std
++
++namespace std {
++
++template<> struct is_error_code_enum<
++ std::experimental::net::detail::syserrc::system_errors>
++{
++ static const bool value = true;
++};
++
++} // namespace std
++
++namespace std {
++namespace experimental {
++namespace net {
++namespace detail {
++namespace syserrc {
++
++inline std::error_code make_error_code(system_errors e)
++{
++ return std::error_code(static_cast(e),
++ std::experimental::net::detail::system_category());
++}
++
++} // namespace syserrc
++} // namespace detail
++} // namespace net
++} // namespace experimental
++} // namespace std
++
++#include "std/net/detail/pop_options.hpp"
++
++#undef STDNET_NATIVE_ERROR
++#undef STDNET_SOCKET_ERROR
++#undef STDNET_NETDB_ERROR
++#undef STDNET_GETADDRINFO_ERROR
++#undef STDNET_WIN_OR_POSIX
++
++#if defined(STDNET_HEADER_ONLY)
++# include "std/net/detail/impl/system_errors.ipp"
++#endif // defined(STDNET_HEADER_ONLY)
++
++#endif // STDNET_DETAIL_SYSTEM_ERRORS_HPP
+diff --git a/ip-address/include/std/net/detail/throw_error.hpp b/ip-address/include/std/net/detail/throw_error.hpp
+new file mode 100644
+index 0000000..4067cb7
+--- /dev/null
++++ b/ip-address/include/std/net/detail/throw_error.hpp
+@@ -0,0 +1,57 @@
++//
++// detail/throw_error.hpp
++// ~~~~~~~~~~~~~~~~~~~~~~
++//
++// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
++//
++// Distributed under the Boost Software License, Version 1.0. (See accompanying
++// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
++//
++
++#ifndef STDNET_DETAIL_THROW_ERROR_HPP
++#define STDNET_DETAIL_THROW_ERROR_HPP
++
++#if defined(_MSC_VER) && (_MSC_VER >= 1200)
++# pragma once
++#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
++
++#include "std/net/detail/config.hpp"
++#include
++
++#include "std/net/detail/push_options.hpp"
++
++namespace std {
++namespace experimental {
++namespace net {
++namespace detail {
++
++STDNET_DECL void do_throw_error(const std::error_code& err);
++
++STDNET_DECL void do_throw_error(const std::error_code& err,
++ const char* location);
++
++inline void throw_error(const std::error_code& err)
++{
++ if (err)
++ do_throw_error(err);
++}
++
++inline void throw_error(const std::error_code& err,
++ const char* location)
++{
++ if (err)
++ do_throw_error(err, location);
++}
++
++} // namespace detail
++} // namespace net
++} // namespace experimental
++} // namespace std
++
++#include "std/net/detail/pop_options.hpp"
++
++#if defined(STDNET_HEADER_ONLY)
++# include "std/net/detail/impl/throw_error.ipp"
++#endif // defined(STDNET_HEADER_ONLY)
++
++#endif // STDNET_DETAIL_THROW_ERROR_HPP
+diff --git a/ip-address/include/std/net/detail/throw_exception.hpp b/ip-address/include/std/net/detail/throw_exception.hpp
+new file mode 100644
+index 0000000..0903df1
+--- /dev/null
++++ b/ip-address/include/std/net/detail/throw_exception.hpp
+@@ -0,0 +1,45 @@
++//
++// detail/throw_exception.hpp
++// ~~~~~~~~~~~~~~~~~~~~~~~~~~
++//
++// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
++//
++// Distributed under the Boost Software License, Version 1.0. (See accompanying
++// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
++//
++
++#ifndef STDNET_DETAIL_THROW_EXCEPTION_HPP
++#define STDNET_DETAIL_THROW_EXCEPTION_HPP
++
++#if defined(_MSC_VER) && (_MSC_VER >= 1200)
++# pragma once
++#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
++
++#include "std/net/detail/config.hpp"
++
++namespace std {
++namespace experimental {
++namespace net {
++namespace detail {
++
++// Declare the throw_exception function for all targets.
++template
++void throw_exception(const Exception& e);
++
++// Only define the throw_exception function when exceptions are enabled.
++// Otherwise, it is up to the application to provide a definition of this
++// function.
++# if !defined(STDNET_NO_EXCEPTIONS)
++template
++void throw_exception(const Exception& e)
++{
++ throw e;
++}
++# endif // !defined(STDNET_NO_EXCEPTIONS)
++
++} // namespace detail
++} // namespace net
++} // namespace experimental
++} // namespace std
++
++#endif // STDNET_DETAIL_THROW_EXCEPTION_HPP
+diff --git a/ip-address/include/std/net/detail/winsock_init.hpp b/ip-address/include/std/net/detail/winsock_init.hpp
+new file mode 100644
+index 0000000..fc008de
+--- /dev/null
++++ b/ip-address/include/std/net/detail/winsock_init.hpp
+@@ -0,0 +1,94 @@
++//
++// detail/winsock_init.hpp
++// ~~~~~~~~~~~~~~~~~~~~~~~
++//
++// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
++//
++// Distributed under the Boost Software License, Version 1.0. (See accompanying
++// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
++//
++
++#ifndef STDNET_DETAIL_WINSOCK_INIT_HPP
++#define STDNET_DETAIL_WINSOCK_INIT_HPP
++
++#if defined(_MSC_VER) && (_MSC_VER >= 1200)
++# pragma once
++#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
++
++#include "std/net/detail/config.hpp"
++
++#if defined(STDNET_WINDOWS) || defined(__CYGWIN__)
++
++#include "std/net/detail/push_options.hpp"
++
++namespace std {
++namespace experimental {
++namespace net {
++namespace detail {
++
++class winsock_init_base
++{
++protected:
++ // Structure to track result of initialisation and number of uses. POD is used
++ // to ensure that the values are zero-initialised prior to any code being run.
++ struct data
++ {
++ long init_count_;
++ long result_;
++ };
++
++ STDNET_DECL static void startup(data& d,
++ unsigned char major, unsigned char minor);
++
++ STDNET_DECL static void cleanup(data& d);
++
++ STDNET_DECL static void throw_on_error(data& d);
++};
++
++template
++class winsock_init : private winsock_init_base
++{
++public:
++ winsock_init(bool allow_throw = true)
++ {
++ startup(data_, Major, Minor);
++ if (allow_throw)
++ throw_on_error(data_);
++ }
++
++ winsock_init(const winsock_init&)
++ {
++ startup(data_, Major, Minor);
++ throw_on_error(data_);
++ }
++
++ ~winsock_init()
++ {
++ cleanup(data_);
++ }
++
++private:
++ static data data_;
++};
++
++template
++winsock_init_base::data winsock_init::data_;
++
++// Static variable to ensure that winsock is initialised before main, and
++// therefore before any other threads can get started.
++static const winsock_init<>& winsock_init_instance = winsock_init<>(false);
++
++} // namespace detail
++} // namespace net
++} // namespace experimental
++} // namespace std
++
++#include "std/net/detail/pop_options.hpp"
++
++#if defined(STDNET_HEADER_ONLY)
++# include "std/net/detail/impl/winsock_init.ipp"
++#endif // defined(STDNET_HEADER_ONLY)
++
++#endif // defined(STDNET_WINDOWS) || defined(__CYGWIN__)
++
++#endif // STDNET_DETAIL_WINSOCK_INIT_HPP
+diff --git a/ip-address/include/std/net/ip/address.hpp b/ip-address/include/std/net/ip/address.hpp
+new file mode 100644
+index 0000000..8a5e75a
+--- /dev/null
++++ b/ip-address/include/std/net/ip/address.hpp
+@@ -0,0 +1,328 @@
++//
++// ip/address.hpp
++// ~~~~~~~~~~~~~~
++//
++// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
++//
++// Distributed under the Boost Software License, Version 1.0. (See accompanying
++// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
++//
++
++#ifndef STDNET_IP_ADDRESS_HPP
++#define STDNET_IP_ADDRESS_HPP
++
++#if defined(_MSC_VER) && (_MSC_VER >= 1200)
++# pragma once
++#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
++
++#include "std/net/detail/config.hpp"
++#include
++#include
++#include
++#include "std/net/ip/fwd.hpp"
++#include "std/net/ip/address_v4.hpp"
++#include "std/net/ip/address_v6.hpp"
++
++#if !defined(STDNET_NO_IOSTREAM)
++# include
++#endif // !defined(STDNET_NO_IOSTREAM)
++
++#include "std/net/detail/push_options.hpp"
++
++namespace std {
++namespace experimental {
++namespace net {
++namespace ip {
++namespace detail {
++
++template
++struct is_convertible_to_address_1 : false_type {};
++
++template
++struct is_convertible_to_address_1(declval()))>::value>::type> : true_type {};
++
++template
++struct is_convertible_to_address : is_convertible_to_address_1 {};
++
++template <>
++struct is_convertible_to_address : false_type {};
++
++} // namespace detail
++
++//template <> struct is_convertible_to_address : false_type {};
++
++/// Implements version-independent IP addresses.
++/**
++ * The ip::address class provides the ability to use either IP version 4 or
++ * version 6 addresses.
++ *
++ * @par Thread Safety
++ * @e Distinct @e objects: Safe.@n
++ * @e Shared @e objects: Unsafe.
++ */
++class address
++{
++public:
++ /// Default constructor.
++ STDNET_CONSTEXPR address() STDNET_NOEXCEPT
++ : type_(invalid),
++ ipv4_address_(),
++ ipv6_address_()
++ {
++ }
++
++ /// Construct from another address type.
++ template ::value>::type>
++ STDNET_CONSTEXPR address(const T& t) STDNET_NOEXCEPT;
++
++#if defined(STDNET_HAS_VARIADIC_TEMPLATES)
++ /// Explicitly construct from a list of arguments.
++ template ()...))>::value>::type>
++ explicit STDNET_CONSTEXPR address(T&&... t);
++#else // defined(STDNET_HAS_VARIADIC_TEMPLATES)
++ template
++ explicit address(T1& t1, typename enable_if()))>::value>::type* = 0)
++ { *this = make_address(t1); }
++ template
++ explicit address(const T1& t1, typename enable_if()))>::value>::type* = 0)
++ { *this = make_address(t1); }
++ template
++ address(T1& t1, T2& t2, typename enable_if(), declval()))>::value>::type* = 0)
++ { *this = make_address(t1, t2); }
++ template
++ address(T1& t1, const T2& t2, typename enable_if(), declval()))>::value>::type* = 0)
++ { *this = make_address(t1, t2); }
++ template
++ address(const T1& t1, T2& t2, typename enable_if(), declval()))>::value>::type* = 0)
++ { *this = make_address(t1, t2); }
++ template
++ address(const T1& t1, const T2& t2, typename enable_if(), declval()))>::value>::type* = 0)
++ { *this = make_address(t1, t2); }
++#endif // defined(STDNET_HAS_VARIADIC_TEMPLATES)
++
++ /// Copy constructor.
++ STDNET_CONSTEXPR address(const address& other) STDNET_NOEXCEPT
++ : type_(other.type_),
++ ipv4_address_(other.ipv4_address_),
++ ipv6_address_(other.ipv6_address_)
++ {
++ }
++
++#if defined(STDNET_HAS_MOVE)
++ /// Move constructor.
++ address(address&& other) STDNET_NOEXCEPT
++ : type_(other.type_),
++ ipv4_address_(other.ipv4_address_),
++ ipv6_address_(other.ipv6_address_)
++ {
++ }
++#endif // defined(STDNET_HAS_MOVE)
++
++ /// Assign from another address.
++ address& operator=(const address& other) STDNET_NOEXCEPT
++ {
++ type_ = other.type_;
++ ipv4_address_ = other.ipv4_address_;
++ ipv6_address_ = other.ipv6_address_;
++ return *this;
++ }
++
++#if defined(STDNET_HAS_MOVE)
++ /// Move-assign from another address.
++ address& operator=(address&& other) STDNET_NOEXCEPT
++ {
++ type_ = other.type_;
++ ipv4_address_ = other.ipv4_address_;
++ ipv6_address_ = other.ipv6_address_;
++ return *this;
++ }
++#endif // defined(STDNET_HAS_MOVE)
++
++ /// Get whether the address is an IP version 4 address.
++ STDNET_CONSTEXPR bool is_v4() const STDNET_NOEXCEPT
++ {
++ return type_ == ipv4;
++ }
++
++ /// Get whether the address is an IP version 6 address.
++ STDNET_CONSTEXPR bool is_v6() const STDNET_NOEXCEPT
++ {
++ return type_ == ipv6;
++ }
++
++ /// Get the address as a string in dotted decimal format.
++ STDNET_DECL std::string to_string() const;
++
++ /// Get the address as a string in dotted decimal format.
++ STDNET_DECL std::string to_string(std::error_code& ec) const;
++
++ /// Determine whether the address is a loopback address.
++ STDNET_CONSTEXPR bool is_loopback() const STDNET_NOEXCEPT
++ {
++ return (type_ == ipv4) ? ipv4_address_.is_loopback() :
++ (type_ == ipv6) ? ipv6_address_.is_loopback() : false;
++ }
++
++ /// Determine whether the address is unspecified.
++ STDNET_CONSTEXPR bool is_unspecified() const STDNET_NOEXCEPT
++ {
++ return (type_ == ipv4) ? ipv4_address_.is_unspecified() :
++ (type_ == ipv6) ? ipv6_address_.is_unspecified() : false;
++ }
++
++ /// Determine whether the address is a multicast address.
++ STDNET_CONSTEXPR bool is_multicast() const STDNET_NOEXCEPT
++ {
++ return (type_ == ipv4) ? ipv4_address_.is_multicast() :
++ (type_ == ipv6) ? ipv6_address_.is_multicast() : false;
++ }
++
++ /// Compare two addresses for equality.
++ friend bool operator==(const address& a1,
++ const address& a2) STDNET_NOEXCEPT
++ {
++ if (a1.type_ != a2.type_)
++ return false;
++ if (a1.type_ == address::ipv4)
++ return a1.ipv4_address_ == a2.ipv4_address_;
++ if (a1.type_ == address::ipv6)
++ return a1.ipv4_address_ == a2.ipv4_address_;
++ return true;
++ }
++
++ /// Compare two addresses for inequality.
++ friend bool operator!=(const address& a1, const address& a2) STDNET_NOEXCEPT
++ {
++ return !(a1 == a2);
++ }
++
++ /// Compare addresses for ordering.
++ friend bool operator<(const address& a1,
++ const address& a2) STDNET_NOEXCEPT
++ {
++ if (a1.type_ < a2.type_)
++ return true;
++ if (a1.type_ > a2.type_)
++ return false;
++ if (a1.type_ == address::ipv4)
++ return a1.ipv4_address_ < a2.ipv4_address_;
++ if (a1.type_ == address::ipv6)
++ return a1.ipv6_address_ < a2.ipv6_address_;
++ return false;
++ }
++
++ /// Compare addresses for ordering.
++ friend bool operator>(const address& a1, const address& a2) STDNET_NOEXCEPT
++ {
++ return a2 < a1;
++ }
++
++ /// Compare addresses for ordering.
++ friend bool operator<=(const address& a1, const address& a2) STDNET_NOEXCEPT
++ {
++ return !(a2 < a1);
++ }
++
++ /// Compare addresses for ordering.
++ friend bool operator>=(const address& a1, const address& a2) STDNET_NOEXCEPT
++ {
++ return !(a1 < a2);
++ }
++
++private:
++ friend class address_v4;
++ friend class address_v6;
++
++ // The type of the address.
++ enum address_type { invalid, ipv4, ipv6 } type_;
++
++ // The underlying IPv4 address.
++ address_v4 ipv4_address_;
++
++ // The underlying IPv6 address.
++ address_v6 ipv6_address_;
++
++ // Helper constructor for address_cast.
++ STDNET_CONSTEXPR address(address_type type,
++ const address_v4& v4, const address_v6& v6)
++ : type_(type),
++ ipv4_address_(v4),
++ ipv6_address_(v6)
++ {
++ }
++
++ template friend STDNET_CONSTEXPR T address_cast(const address&,
++ typename enable_if::value>::type*);
++ template friend STDNET_CONSTEXPR T address_cast(const address&,
++ typename enable_if::value>::type*);
++ template friend STDNET_CONSTEXPR T address_cast(const address_v4&,
++ typename enable_if::value>::type*) STDNET_NOEXCEPT;
++ template friend STDNET_CONSTEXPR T address_cast(const address_v6&,
++ typename enable_if::value>::type*) STDNET_NOEXCEPT;
++};
++
++/// Create an address from an IPv4 address string in dotted decimal form,
++/// or from an IPv6 address in hexadecimal notation.
++STDNET_DECL address make_address(const char* str);
++
++/// Create an address from an IPv4 address string in dotted decimal form,
++/// or from an IPv6 address in hexadecimal notation.
++STDNET_DECL address make_address(const char* str,
++ std::error_code& ec) STDNET_NOEXCEPT;
++
++/// Create an address from an IPv4 address string in dotted decimal form,
++/// or from an IPv6 address in hexadecimal notation.
++STDNET_DECL address make_address(const std::string& str);
++
++/// Create an address from an IPv4 address string in dotted decimal form,
++/// or from an IPv6 address in hexadecimal notation.
++STDNET_DECL address make_address(const std::string& str,
++ std::error_code& ec) STDNET_NOEXCEPT;
++
++#if !defined(STDNET_NO_IOSTREAM)
++
++/// Output an address as a string.
++/**
++ * Used to output a human-readable string for a specified address.
++ *
++ * @param os The output stream to which the string will be written.
++ *
++ * @param addr The address to be written.
++ *
++ * @return The output stream.
++ *
++ * @relates std::experimental::net::ip::address
++ */
++template
++std::basic_ostream& operator<<(
++ std::basic_ostream& os, const address& addr);
++
++#endif // !defined(STDNET_NO_IOSTREAM)
++
++} // namespace ip
++} // namespace net
++} // namespace experimental
++} // namespace std
++
++#include "std/net/detail/pop_options.hpp"
++
++#include "std/net/ip/impl/address.hpp"
++#if defined(STDNET_HEADER_ONLY)
++# include "std/net/ip/impl/address.ipp"
++#endif // defined(STDNET_HEADER_ONLY)
++
++#include "std/net/ip/address_cast.hpp"
++
++#endif // STDNET_IP_ADDRESS_HPP
+diff --git a/ip-address/include/std/net/ip/address_cast.hpp b/ip-address/include/std/net/ip/address_cast.hpp
+new file mode 100644
+index 0000000..dae3d02
+--- /dev/null
++++ b/ip-address/include/std/net/ip/address_cast.hpp
+@@ -0,0 +1,106 @@
++//
++// ip/address_cast.hpp
++// ~~~~~~~~~~~~~~~~~~~
++//
++// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
++//
++// Distributed under the Boost Software License, Version 1.0. (See accompanying
++// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
++//
++
++#ifndef STDNET_IP_ADDRESS_CAST_HPP
++#define STDNET_IP_ADDRESS_CAST_HPP
++
++#if defined(_MSC_VER) && (_MSC_VER >= 1200)
++# pragma once
++#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
++
++#include "std/net/detail/config.hpp"
++#include "std/net/detail/throw_exception.hpp"
++#include "std/net/ip/fwd.hpp"
++#include "std/net/ip/address.hpp"
++#include "std/net/ip/address_v4.hpp"
++#include "std/net/ip/address_v6.hpp"
++#include "std/net/ip/bad_address_cast.hpp"
++
++#include "std/net/detail/push_options.hpp"
++
++namespace std {
++namespace experimental {
++namespace net {
++namespace ip {
++
++/// Cast a version-independent address to itself.
++template
++inline STDNET_CONSTEXPR T address_cast(const address& addr,
++ typename enable_if::value>::type*) STDNET_NOEXCEPT
++{
++ return addr;
++}
++
++/// Cast a version-independent address to an IPv4 address.
++/**
++ * @throws bad_address_cast if @c a does not represent an IPv4 address.
++ */
++template
++inline STDNET_CONSTEXPR T address_cast(const address& addr,
++ typename enable_if::value>::type*)
++{
++ return (addr.type_ != address::ipv4)
++ ? throw bad_address_cast()
++ : addr.ipv4_address_;
++}
++
++/// Cast a version-independent address to an IPv6 address.
++/**
++ * @throws bad_address_cast if @c a does not represent an IPv6 address.
++ */
++template
++inline STDNET_CONSTEXPR T address_cast(const address& addr,
++ typename enable_if::value>::type*)
++{
++ return (addr.type_ != address::ipv6)
++ ? throw bad_address_cast()
++ : addr.ipv6_address_;
++}
++
++/// Cast an IPv4 address to a version-independent address.
++template
++inline STDNET_CONSTEXPR T address_cast(const address_v4& addr,
++ typename enable_if::value>::type*) STDNET_NOEXCEPT
++{
++ return address(address::ipv4, addr, address_v6());
++}
++
++/// Cast an IPv4 address to itself.
++template
++inline STDNET_CONSTEXPR T address_cast(const address_v4& addr,
++ typename enable_if::value>::type*) STDNET_NOEXCEPT
++{
++ return addr;
++}
++
++/// Cast an IPv6 address to a version-independent address.
++template
++inline STDNET_CONSTEXPR T address_cast(const address_v6& addr,
++ typename enable_if::value>::type*) STDNET_NOEXCEPT
++{
++ return address(address::ipv6, address_v4(), addr);
++}
++
++/// Cast an IPv6 address to itself.
++template
++inline STDNET_CONSTEXPR T address_cast(const address_v6& addr,
++ typename enable_if::value>::type*) STDNET_NOEXCEPT
++{
++ return addr;
++}
++
++} // namespace ip
++} // namespace net
++} // namespace experimental
++} // namespace std
++
++#include "std/net/detail/pop_options.hpp"
++
++#endif // STDNET_IP_ADDRESS_CAST_HPP
+diff --git a/ip-address/include/std/net/ip/address_v4.hpp b/ip-address/include/std/net/ip/address_v4.hpp
+new file mode 100644
+index 0000000..a7b9234
+--- /dev/null
++++ b/ip-address/include/std/net/ip/address_v4.hpp
+@@ -0,0 +1,376 @@
++//
++// ip/address_v4.hpp
++// ~~~~~~~~~~~~~~~~~
++//
++// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
++//
++// Distributed under the Boost Software License, Version 1.0. (See accompanying
++// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
++//
++
++#ifndef STDNET_IP_ADDRESS_V4_HPP
++#define STDNET_IP_ADDRESS_V4_HPP
++
++#if defined(_MSC_VER) && (_MSC_VER >= 1200)
++# pragma once
++#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
++
++#include "std/net/detail/config.hpp"
++#include
++#include
++#include
++#include "std/net/ip/fwd.hpp"
++#include "std/net/detail/winsock_init.hpp"
++
++#if !defined(STDNET_NO_IOSTREAM)
++# include
++#endif // !defined(STDNET_NO_IOSTREAM)
++
++#include "std/net/detail/push_options.hpp"
++
++namespace std {
++namespace experimental {
++namespace net {
++namespace ip {
++
++class address;
++
++/// Implements IP version 4 style addresses.
++/**
++ * The ip::address_v4 class provides the ability to use and manipulate IP
++ * version 4 addresses.
++ *
++ * @par Thread Safety
++ * @e Distinct @e objects: Safe.@n
++ * @e Shared @e objects: Unsafe.
++ */
++class address_v4
++{
++public:
++ /// A standard-layout type used to represent an address as an array of bytes.
++ struct bytes_type : std::array
++ {
++#if defined(STDNET_HAS_VARIADIC_TEMPLATES)
++ template
++ explicit STDNET_CONSTEXPR bytes_type(T... t)
++ : std::array{{static_cast(t)...}}
++ {
++ }
++#else // defined(STDNET_HAS_VARIADIC_TEMPLATES)
++ explicit STDNET_CONSTEXPR bytes_type(unsigned char a = 0,
++ unsigned char b = 0, unsigned char c = 0, unsigned char d = 0)
++# if defined(STDNET_HAS_CONSTEXPR)
++ : std::array{{a, b, c, d}}
++ {
++ }
++# else // defined(STDNET_HAS_CONSTEXPR)
++ {
++ (*this)[0] = a, (*this)[1] = b, (*this)[2] = c, (*this)[3] = d;
++ }
++# endif // defined(STDNET_HAS_CONSTEXPR)
++#endif // defined(STDNET_HAS_VARIADIC_TEMPLATES)
++ };
++
++ /// Default constructor.
++ STDNET_CONSTEXPR address_v4() STDNET_NOEXCEPT
++ : bytes_(0, 0, 0, 0)
++ {
++ }
++
++ /// Implicit construction from bytes, in network byte order.
++ STDNET_CONSTEXPR address_v4(const bytes_type& bytes)
++ : bytes_(
++#if UCHAR_MAX > 0xFF
++ (bytes[0] > 0xFF || bytes[1] > 0xFF || bytes[2] > 0xFF || bytes[3] > 0xFF)
++ ? throw std::out_of_range("address_v4 from bytes_type") :
++#endif // UCHAR_MAX > 0xFF
++ bytes)
++ {
++ }
++
++#if defined(STDNET_HAS_VARIADIC_TEMPLATES)
++ /// Explicitly construct from a list of arguments.
++ template ()...))>::value>::type>
++ explicit STDNET_CONSTEXPR address_v4(T&&... t)
++ : address_v4(make_address_v4(static_cast(t)...))
++ {
++ }
++#else // defined(STDNET_HAS_VARIADIC_TEMPLATES)
++ template
++ explicit STDNET_CONSTEXPR address_v4(T1& t1,
++ typename enable_if()))>::value>::type* = 0)
++ : bytes_(make_address_v4(t1).to_bytes()) {}
++ template
++ explicit STDNET_CONSTEXPR address_v4(const T1& t1,
++ typename enable_if()))>::value>::type* = 0)
++ : bytes_(make_address_v4(t1).to_bytes()) {}
++ template
++ STDNET_CONSTEXPR address_v4(T1& t1, T2& t2,
++ typename enable_if(), declval()))>::value>::type* = 0)
++ : bytes_(make_address_v4(t1, t2).to_bytes()) {}
++ template
++ STDNET_CONSTEXPR address_v4(T1& t1, const T2& t2,
++ typename enable_if(), declval()))>::value>::type* = 0)
++ : bytes_(make_address_v4(t1, t2).to_bytes()) {}
++ template
++ STDNET_CONSTEXPR address_v4(const T1& t1, T2& t2,
++ typename enable_if(), declval()))>::value>::type* = 0)
++ : bytes_(make_address_v4(t1, t2).to_bytes()) {}
++ template
++ STDNET_CONSTEXPR address_v4(const T1& t1, const T2& t2,
++ typename enable_if(), declval()))>::value>::type* = 0)
++ : bytes_(make_address_v4(t1, t2).to_bytes()) {}
++#endif // defined(STDNET_HAS_VARIADIC_TEMPLATES)
++
++ /// Copy constructor.
++ STDNET_CONSTEXPR address_v4(const address_v4& other) STDNET_NOEXCEPT
++ : bytes_(other.bytes_)
++ {
++ }
++
++#if defined(STDNET_HAS_MOVE)
++ /// Move constructor.
++ address_v4(address_v4&& other) STDNET_NOEXCEPT
++ : bytes_(other.bytes_)
++ {
++ }
++#endif // defined(STDNET_HAS_MOVE)
++
++ /// Assign from another address.
++ address_v4& operator=(const address_v4& other) STDNET_NOEXCEPT
++ {
++ bytes_ = other.bytes_;
++ return *this;
++ }
++
++#if defined(STDNET_HAS_MOVE)
++ /// Move-assign from another address.
++ address_v4& operator=(address_v4&& other) STDNET_NOEXCEPT
++ {
++ bytes_ = other.bytes_;
++ return *this;
++ }
++#endif // defined(STDNET_HAS_MOVE)
++
++ /// Get the address in bytes, in network byte order.
++ STDNET_CONSTEXPR bytes_type to_bytes() const STDNET_NOEXCEPT
++ {
++ return bytes_;
++ }
++
++ /// Get the address as an unsigned long in host byte order
++ STDNET_CONSTEXPR unsigned long to_ulong() const STDNET_NOEXCEPT
++ {
++ return (static_cast(bytes_[0]) << 24)
++ | (static_cast(bytes_[1]) << 16)
++ | (static_cast(bytes_[2]) << 8)
++ | static_cast(bytes_[3]);
++ }
++
++ /// Get the address as a string in dotted decimal format.
++ STDNET_DECL std::string to_string() const;
++
++ /// Get the address as a string in dotted decimal format.
++ STDNET_DECL std::string to_string(std::error_code& ec) const;
++
++ /// Determine whether the address is a loopback address.
++ STDNET_CONSTEXPR bool is_loopback() const STDNET_NOEXCEPT
++ {
++ return (to_ulong() & 0xFF000000) == 0x7F000000;
++ }
++
++ /// Determine whether the address is unspecified.
++ STDNET_CONSTEXPR bool is_unspecified() const STDNET_NOEXCEPT
++ {
++ return to_ulong() == 0;
++ }
++
++ /// Determine whether the address is a class A address.
++ STDNET_CONSTEXPR bool is_class_a() const STDNET_NOEXCEPT
++ {
++ return (to_ulong() & 0x80000000) == 0;
++ }
++
++ /// Determine whether the address is a class B address.
++ STDNET_CONSTEXPR bool is_class_b() const STDNET_NOEXCEPT
++ {
++ return (to_ulong() & 0xC0000000) == 0x80000000;
++ }
++
++ /// Determine whether the address is a class C address.
++ STDNET_CONSTEXPR bool is_class_c() const STDNET_NOEXCEPT
++ {
++ return (to_ulong() & 0xE0000000) == 0xC0000000;
++ }
++
++ /// Determine whether the address is a multicast address.
++ STDNET_CONSTEXPR bool is_multicast() const STDNET_NOEXCEPT
++ {
++ return (to_ulong() & 0xF0000000) == 0xE0000000;
++ }
++
++ /// Compare two addresses for equality.
++ friend bool operator==(const address_v4& a1,
++ const address_v4& a2) STDNET_NOEXCEPT
++ {
++ return a1.bytes_ == a2.bytes_;
++ }
++
++ /// Compare two addresses for inequality.
++ friend bool operator!=(const address_v4& a1,
++ const address_v4& a2) STDNET_NOEXCEPT
++ {
++ return a1.bytes_ == a2.bytes_;
++ }
++
++ /// Compare addresses for ordering.
++ friend bool operator<(const address_v4& a1,
++ const address_v4& a2) STDNET_NOEXCEPT
++ {
++ return a1.to_ulong() < a2.to_ulong();
++ }
++
++ /// Compare addresses for ordering.
++ friend bool operator>(const address_v4& a1,
++ const address_v4& a2) STDNET_NOEXCEPT
++ {
++ return a1.to_ulong() > a2.to_ulong();
++ }
++
++ /// Compare addresses for ordering.
++ friend bool operator<=(const address_v4& a1,
++ const address_v4& a2) STDNET_NOEXCEPT
++ {
++ return a1.to_ulong() <= a2.to_ulong();
++ }
++
++ /// Compare addresses for ordering.
++ friend bool operator>=(const address_v4& a1,
++ const address_v4& a2) STDNET_NOEXCEPT
++ {
++ return a1.to_ulong() >= a2.to_ulong();
++ }
++
++ /// Obtain an address object that represents any address.
++ static STDNET_CONSTEXPR address_v4 any() STDNET_NOEXCEPT
++ {
++ return address_v4();
++ }
++
++ /// Obtain an address object that represents the loopback address.
++ static STDNET_CONSTEXPR address_v4 loopback() STDNET_NOEXCEPT
++ {
++ return address_v4(0x7F000001);
++ }
++
++ /// Obtain an address object that represents the broadcast address.
++ static STDNET_CONSTEXPR address_v4 broadcast() STDNET_NOEXCEPT
++ {
++ return address_v4(0xFFFFFFFF);
++ }
++
++ /// Obtain an address object that represents the broadcast address that
++ /// corresponds to the specified address and netmask.
++ static STDNET_CONSTEXPR address_v4 broadcast(const address_v4& addr,
++ const address_v4& mask) STDNET_NOEXCEPT
++ {
++ return address_v4(addr.to_ulong() | (mask.to_ulong() ^ 0xFFFFFFFF));
++ }
++
++private:
++ // The underlying IPv4 address.
++ bytes_type bytes_;
++};
++
++/// Construct an address_v4 from raw bytes.
++inline STDNET_CONSTEXPR address_v4 make_address_v4(const address_v4::bytes_type& bytes)
++{
++ return bytes;
++}
++
++/// Construct an address_v4 from a unsigned long in host byte order.
++inline STDNET_CONSTEXPR address_v4 make_address_v4(unsigned long addr)
++{
++ return
++#if ULONG_MAX > 0xFFFFFFFF
++ (addr > 0xFFFFFFFF)
++ ? throw std::out_of_range("address_v4 from unsigned long") :
++#endif // ULONG_MAX > 0xFFFFFFFF
++ address_v4::bytes_type((addr >> 24) & 0xFF,
++ (addr >> 16) & 0xFF, (addr >> 8) & 0xFF, addr & 0xFF);
++}
++
++/// Create an address_v4 from an IPv4 address string in dotted decimal form.
++STDNET_DECL address_v4 make_address_v4(const char* str);
++
++/// Create an address_v4 from an IPv4 address string in dotted decimal form.
++STDNET_DECL address_v4 make_address_v4(const char* str,
++ std::error_code& ec) STDNET_NOEXCEPT;
++
++/// Create an address_v4 from an IPv4 address string in dotted decimal form.
++STDNET_DECL address_v4 make_address_v4(const std::string& str);
++
++/// Create an address_v4 from an IPv4 address string in dotted decimal form.
++STDNET_DECL address_v4 make_address_v4(const std::string& str,
++ std::error_code& ec) STDNET_NOEXCEPT;
++
++#if defined(STDNET_HAS_CONSTEXPR)
++
++/// The IPv4 unspecified address.
++STDNET_CONSTEXPR address_v4 any_v4(0);
++
++/// The IPv4 loopback address.
++STDNET_CONSTEXPR address_v4 loopback_v4(0x7F000001);
++
++/// The IPv4 broadcast address.
++STDNET_CONSTEXPR address_v4 broadcast_v4(0xFFFFFFFF);
++
++#else // defined(STDNET_HAS_CONSTEXPR)
++
++static const address_v4 any_v4(0);
++static const address_v4 loopback_v4(0x7F000001);
++static const address_v4 broadcast_v4(0xFFFFFFFF);
++
++#endif // defined(STDNET_HAS_CONSTEXPR)
++
++#if !defined(STDNET_NO_IOSTREAM)
++
++/// Output an address as a string.
++/**
++ * Used to output a human-readable string for a specified address.
++ *
++ * @param os The output stream to which the string will be written.
++ *
++ * @param addr The address to be written.
++ *
++ * @return The output stream.
++ *
++ * @relates std::experimental::net::ip::address_v4
++ */
++template
++std::basic_ostream& operator<<(
++ std::basic_ostream& os, const address_v4& addr);
++
++#endif // !defined(STDNET_NO_IOSTREAM)
++
++} // namespace ip
++} // namespace net
++} // namespace experimental
++} // namespace std
++
++#include "std/net/detail/pop_options.hpp"
++
++#include "std/net/ip/impl/address_v4.hpp"
++#if defined(STDNET_HEADER_ONLY)
++# include "std/net/ip/impl/address_v4.ipp"
++#endif // defined(STDNET_HEADER_ONLY)
++
++#endif // STDNET_IP_ADDRESS_V4_HPP
+diff --git a/ip-address/include/std/net/ip/address_v6.hpp b/ip-address/include/std/net/ip/address_v6.hpp
+new file mode 100644
+index 0000000..3c4f440
+--- /dev/null
++++ b/ip-address/include/std/net/ip/address_v6.hpp
+@@ -0,0 +1,440 @@
++//
++// ip/address_v6.hpp
++// ~~~~~~~~~~~~~~~~~
++//
++// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
++//
++// Distributed under the Boost Software License, Version 1.0. (See accompanying
++// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
++//
++
++#ifndef STDNET_IP_ADDRESS_V6_HPP
++#define STDNET_IP_ADDRESS_V6_HPP
++
++#if defined(_MSC_VER) && (_MSC_VER >= 1200)
++# pragma once
++#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
++
++#include "std/net/detail/config.hpp"
++#include
++#include
++#include
++#include "std/net/ip/fwd.hpp"
++#include "std/net/ip/address_v4.hpp"
++#include "std/net/ip/bad_address_cast.hpp"
++#include "std/net/detail/winsock_init.hpp"
++
++#if !defined(STDNET_NO_IOSTREAM)
++# include
++#endif // !defined(STDNET_NO_IOSTREAM)
++
++#include "std/net/detail/push_options.hpp"
++
++namespace std {
++namespace experimental {
++namespace net {
++namespace ip {
++
++class address;
++class address_v4;
++
++/// Implements IP version 6 style addresses.
++/**
++ * The ip::address_v6 class provides the ability to use and manipulate IP
++ * version 6 addresses.
++ *
++ * @par Thread Safety
++ * @e Distinct @e objects: Safe.@n
++ * @e Shared @e objects: Unsafe.
++ */
++class address_v6
++{
++public:
++ /// A standard-layout type used to represent an address as an array of bytes.
++ struct bytes_type : std::array
++ {
++#if defined(STDNET_HAS_VARIADIC_TEMPLATES)
++ template
++ explicit STDNET_CONSTEXPR bytes_type(T... t)
++ : std::array{{static_cast(t)...}}
++ {
++ }
++#else // defined(STDNET_HAS_VARIADIC_TEMPLATES)
++ explicit STDNET_CONSTEXPR bytes_type(
++ unsigned char a = 0, unsigned char b = 0,
++ unsigned char c = 0, unsigned char d = 0,
++ unsigned char e = 0, unsigned char f = 0,
++ unsigned char g = 0, unsigned char h = 0,
++ unsigned char i = 0, unsigned char j = 0,
++ unsigned char k = 0, unsigned char l = 0,
++ unsigned char m = 0, unsigned char n = 0,
++ unsigned char o = 0, unsigned char p = 0)
++# if defined(STDNET_HAS_CONSTEXPR)
++ : std::array{{a, b, c, d, e, f, g, h, i, h, k, l, m, n, o, p}}
++ {
++ }
++# else // defined(STDNET_HAS_CONSTEXPR)
++ {
++ (*this)[0] = a, (*this)[1] = b, (*this)[2] = c, (*this)[3] = d;
++ (*this)[4] = e, (*this)[5] = f, (*this)[6] = g, (*this)[7] = h;
++ (*this)[8] = i, (*this)[9] = j, (*this)[10] = k, (*this)[11] = l;
++ (*this)[12] = m, (*this)[13] = n, (*this)[14] = o, (*this)[15] = p;
++ }
++# endif // defined(STDNET_HAS_CONSTEXPR)
++#endif // defined(STDNET_HAS_VARIADIC_TEMPLATES)
++ };
++
++ /// Default constructor.
++ STDNET_CONSTEXPR address_v6() STDNET_NOEXCEPT
++ : bytes_(0),
++ scope_id_(0)
++ {
++ }
++
++ /// Implicit construction from bytes, in network byte order.
++ STDNET_CONSTEXPR address_v6(const bytes_type& bytes, unsigned long scope = 0)
++ : bytes_(
++#if UCHAR_MAX > 0xFF
++ (bytes[0] > 0xFF || bytes[1] > 0xFF || bytes[2] > 0xFF || bytes[3] > 0xFF
++ || bytes[4] > 0xFF || bytes[5] > 0xFF || bytes[6] > 0xFF || bytes[7] > 0xFF
++ || bytes[8] > 0xFF || bytes[9] > 0xFF || bytes[10] > 0xFF || bytes[11] > 0xFF
++ || bytes[12] > 0xFF || bytes[13] > 0xFF || bytes[14] > 0xFF || bytes[15] > 0xFF)
++ ? throw std::out_of_range("address_v6 from bytes_type") :
++#endif // UCHAR_MAX > 0xFF
++ bytes),
++ scope_id_(scope)
++ {
++ }
++
++#if defined(STDNET_HAS_VARIADIC_TEMPLATES)
++ /// Explicitly construct from a list of arguments.
++ template ()...))>::value>::type>
++ explicit STDNET_CONSTEXPR address_v6(T&&... t)
++ : address_v6(make_address_v6(static_cast(t)...))
++ {
++ }
++#else // defined(STDNET_HAS_VARIADIC_TEMPLATES)
++ template
++ explicit STDNET_CONSTEXPR address_v6(T1& t1,
++ typename enable_if()))>::value>::type* = 0)
++ : bytes_(make_address_v6(t1).to_bytes()) {}
++ template
++ explicit STDNET_CONSTEXPR address_v6(const T1& t1,
++ typename enable_if()))>::value>::type* = 0)
++ : bytes_(make_address_v6(t1).to_bytes()) {}
++ template
++ STDNET_CONSTEXPR address_v6(T1& t1, T2& t2,
++ typename enable_if(), declval()))>::value>::type* = 0)
++ : bytes_(make_address_v6(t1, t2).to_bytes()) {}
++ template
++ STDNET_CONSTEXPR address_v6(T1& t1, const T2& t2,
++ typename enable_if(), declval()))>::value>::type* = 0)
++ : bytes_(make_address_v6(t1, t2).to_bytes()) {}
++ template
++ STDNET_CONSTEXPR address_v6(const T1& t1, T2& t2,
++ typename enable_if(), declval()))>::value>::type* = 0)
++ : bytes_(make_address_v6(t1, t2).to_bytes()) {}
++ template
++ STDNET_CONSTEXPR address_v6(const T1& t1, const T2& t2,
++ typename enable_if(), declval()))>::value>::type* = 0)
++ : bytes_(make_address_v6(t1, t2).to_bytes()) {}
++#endif // defined(STDNET_HAS_VARIADIC_TEMPLATES)
++
++ /// Copy constructor.
++ STDNET_CONSTEXPR address_v6(const address_v6& other) STDNET_NOEXCEPT
++ : bytes_(other.bytes_), scope_id_(other.scope_id_)
++ {
++ }
++
++#if defined(STDNET_HAS_MOVE)
++ /// Move constructor.
++ address_v6(address_v6&& other) STDNET_NOEXCEPT
++ : bytes_(other.bytes_), scope_id_(other.scope_id_)
++ {
++ }
++#endif // defined(STDNET_HAS_MOVE)
++
++ /// Assign from another address.
++ address_v6& operator=(const address_v6& other) STDNET_NOEXCEPT
++ {
++ bytes_ = other.bytes_;
++ scope_id_ = other.scope_id_;
++ return *this;
++ }
++
++#if defined(STDNET_HAS_MOVE)
++ /// Move-assign from another address.
++ address_v6& operator=(address_v6&& other) STDNET_NOEXCEPT
++ {
++ bytes_ = other.bytes_;
++ scope_id_ = other.scope_id_;
++ return *this;
++ }
++#endif // defined(STDNET_HAS_MOVE)
++
++ /// The scope ID of the address.
++ /**
++ * Returns the scope ID associated with the IPv6 address.
++ */
++ STDNET_CONSTEXPR unsigned long scope_id() const STDNET_NOEXCEPT
++ {
++ return scope_id_;
++ }
++
++ /// The scope ID of the address.
++ /**
++ * Modifies the scope ID associated with the IPv6 address.
++ */
++ void scope_id(unsigned long id) STDNET_NOEXCEPT
++ {
++ scope_id_ = id;
++ }
++
++ /// Get the address in bytes, in network byte order.
++ STDNET_CONSTEXPR bytes_type to_bytes() const STDNET_NOEXCEPT
++ {
++ return bytes_;
++ }
++
++ /// Get the address as a string.
++ STDNET_DECL std::string to_string() const;
++
++ /// Get the address as a string.
++ STDNET_DECL std::string to_string(std::error_code& ec) const;
++
++ /// Determine whether the address is a loopback address.
++ STDNET_CONSTEXPR bool is_loopback() const STDNET_NOEXCEPT
++ {
++ return ((bytes_[0] == 0) && (bytes_[1] == 0)
++ && (bytes_[2] == 0) && (bytes_[3] == 0)
++ && (bytes_[4] == 0) && (bytes_[5] == 0)
++ && (bytes_[6] == 0) && (bytes_[7] == 0)
++ && (bytes_[8] == 0) && (bytes_[9] == 0)
++ && (bytes_[10] == 0) && (bytes_[11] == 0)
++ && (bytes_[12] == 0) && (bytes_[13] == 0)
++ && (bytes_[14] == 0) && (bytes_[15] == 1));
++ }
++
++ /// Determine whether the address is unspecified.
++ STDNET_CONSTEXPR bool is_unspecified() const STDNET_NOEXCEPT
++ {
++ return ((bytes_[0] == 0) && (bytes_[1] == 0)
++ && (bytes_[2] == 0) && (bytes_[3] == 0)
++ && (bytes_[4] == 0) && (bytes_[5] == 0)
++ && (bytes_[6] == 0) && (bytes_[7] == 0)
++ && (bytes_[8] == 0) && (bytes_[9] == 0)
++ && (bytes_[10] == 0) && (bytes_[11] == 0)
++ && (bytes_[12] == 0) && (bytes_[13] == 0)
++ && (bytes_[14] == 0) && (bytes_[15] == 0));
++ }
++
++ /// Determine whether the address is link local.
++ STDNET_CONSTEXPR bool is_link_local() const STDNET_NOEXCEPT
++ {
++ return ((bytes_[0] == 0xfe) && ((bytes_[1] & 0xc0) == 0x80));
++ }
++
++ /// Determine whether the address is site local.
++ STDNET_CONSTEXPR bool is_site_local() const STDNET_NOEXCEPT
++ {
++ return ((bytes_[0] == 0xfe) && ((bytes_[1] & 0xc0) == 0xc0));
++ }
++
++ /// Determine whether the address is a mapped IPv4 address.
++ STDNET_CONSTEXPR bool is_v4_mapped() const STDNET_NOEXCEPT
++ {
++ return ((bytes_[0] == 0) && (bytes_[1] == 0)
++ && (bytes_[2] == 0) && (bytes_[3] == 0)
++ && (bytes_[4] == 0) && (bytes_[5] == 0)
++ && (bytes_[6] == 0) && (bytes_[7] == 0)
++ && (bytes_[8] == 0) && (bytes_[9] == 0)
++ && (bytes_[10] == 0xff) && (bytes_[11] == 0xff));
++ }
++
++ /// Determine whether the address is a multicast address.
++ STDNET_CONSTEXPR bool is_multicast() const STDNET_NOEXCEPT
++ {
++ return (bytes_[0] == 0xff);
++ }
++
++ /// Determine whether the address is a global multicast address.
++ STDNET_CONSTEXPR bool is_multicast_global() const STDNET_NOEXCEPT
++ {
++ return ((bytes_[0] == 0xff) && ((bytes_[1] & 0x0f) == 0x0e));
++ }
++
++ /// Determine whether the address is a link-local multicast address.
++ STDNET_CONSTEXPR bool is_multicast_link_local() const STDNET_NOEXCEPT
++ {
++ return ((bytes_[0] == 0xff) && ((bytes_[1] & 0x0f) == 0x02));
++ }
++
++ /// Determine whether the address is a node-local multicast address.
++ STDNET_CONSTEXPR bool is_multicast_node_local() const STDNET_NOEXCEPT
++ {
++ return ((bytes_[0] == 0xff) && ((bytes_[1] & 0x0f) == 0x01));
++ }
++
++ /// Determine whether the address is a org-local multicast address.
++ STDNET_CONSTEXPR bool is_multicast_org_local() const STDNET_NOEXCEPT
++ {
++ return ((bytes_[0] == 0xff) && ((bytes_[1] & 0x0f) == 0x08));
++ }
++
++ /// Determine whether the address is a site-local multicast address.
++ STDNET_CONSTEXPR bool is_multicast_site_local() const STDNET_NOEXCEPT
++ {
++ return ((bytes_[0] == 0xff) && ((bytes_[1] & 0x0f) == 0x05));
++ }
++
++ /// Compare two addresses for equality.
++ friend bool operator==(const address_v6& a1,
++ const address_v6& a2) STDNET_NOEXCEPT
++ {
++ return a1.bytes_ == a2.bytes_ && a1.scope_id_ == a2.scope_id_;
++ }
++
++ /// Compare two addresses for inequality.
++ friend bool operator!=(const address_v6& a1,
++ const address_v6& a2) STDNET_NOEXCEPT
++ {
++ return !(a1 == a2);
++ }
++
++ /// Compare addresses for ordering.
++ friend bool operator<(const address_v6& a1,
++ const address_v6& a2) STDNET_NOEXCEPT
++ {
++ if (a1.bytes_ < a2.bytes_)
++ return true;
++ if (a1.bytes_ > a2.bytes_)
++ return false;
++ return a1.scope_id_ < a2.scope_id_;
++ }
++
++ /// Compare addresses for ordering.
++ friend bool operator>(const address_v6& a1,
++ const address_v6& a2) STDNET_NOEXCEPT
++ {
++ return a2 < a1;
++ }
++
++ /// Compare addresses for ordering.
++ friend bool operator<=(const address_v6& a1,
++ const address_v6& a2) STDNET_NOEXCEPT
++ {
++ return !(a2 < a1);
++ }
++
++ /// Compare addresses for ordering.
++ friend bool operator>=(const address_v6& a1,
++ const address_v6& a2) STDNET_NOEXCEPT
++ {
++ return !(a1 < a2);
++ }
++
++ /// Obtain an address object that represents any address.
++ static STDNET_CONSTEXPR address_v6 any() STDNET_NOEXCEPT
++ {
++ return address_v6();
++ }
++
++ /// Obtain an address object that represents the loopback address.
++ static STDNET_CONSTEXPR address_v6 loopback() STDNET_NOEXCEPT
++ {
++ return bytes_type(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1);
++ }
++
++private:
++ friend STDNET_CONSTEXPR address_v4 make_address_v4(
++ v4_mapped_t, const address_v6&);
++
++ // The underlying IPv6 address.
++ bytes_type bytes_;
++
++ // The scope ID associated with the address.
++ unsigned long scope_id_;
++};
++
++/// Construct an address_v6 from raw bytes.
++inline STDNET_CONSTEXPR address_v6 make_address_v6(
++ const address_v6::bytes_type& bytes, unsigned long scope_id = 0)
++{
++ return address_v6(bytes, scope_id);
++}
++
++/// Create an address_v6 from an IPv6 address string.
++STDNET_DECL address_v6 make_address_v6(const char* str);
++
++/// Create an address_v6 from an IPv6 address string.
++STDNET_DECL address_v6 make_address_v6(const char* str,
++ std::error_code& ec) STDNET_NOEXCEPT;
++
++/// Create an address_v6 from an IPv6 address string.
++STDNET_DECL address_v6 make_address_v6(const std::string& str);
++
++/// Create an address_v6 from an IPv6 address string.
++STDNET_DECL address_v6 make_address_v6(const std::string& str,
++ std::error_code& ec) STDNET_NOEXCEPT;
++
++/// Create an IPv4-mapped address_v6 from an IPv4 address.
++inline STDNET_CONSTEXPR address_v6 make_address_v6(
++ v4_mapped_t, const address_v4& addr) STDNET_NOEXCEPT
++{
++ return address_v6::bytes_type(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF,
++ static_cast(addr.to_bytes())[0],
++ static_cast(addr.to_bytes())[1],
++ static_cast(addr.to_bytes())[2],
++ static_cast(addr.to_bytes())[3]);
++}
++
++/// Create an address_v4 from an IPv4-mapped address_v6.
++inline STDNET_CONSTEXPR address_v4 make_address_v4(
++ v4_mapped_t, const address_v6& addr)
++{
++ return !addr.is_v4_mapped() ? throw bad_address_cast() :
++ address_v4::bytes_type(addr.bytes_[12], addr.bytes_[13],
++ addr.bytes_[14], addr.bytes_[15]);
++}
++
++#if !defined(STDNET_NO_IOSTREAM)
++
++/// Output an address as a string.
++/**
++ * Used to output a human-readable string for a specified address.
++ *
++ * @param os The output stream to which the string will be written.
++ *
++ * @param addr The address to be written.
++ *
++ * @return The output stream.
++ *
++ * @relates std::experimental::net::ip::address_v6
++ */
++template
++std::basic_ostream& operator<<(
++ std::basic_ostream& os, const address_v6& addr);
++
++#endif // !defined(STDNET_NO_IOSTREAM)
++
++} // namespace ip
++} // namespace net
++} // namespace experimental
++} // namespace std
++
++#include "std/net/detail/pop_options.hpp"
++
++#include "std/net/ip/impl/address_v6.hpp"
++#if defined(STDNET_HEADER_ONLY)
++# include "std/net/ip/impl/address_v6.ipp"
++#endif // defined(STDNET_HEADER_ONLY)
++
++#endif // STDNET_IP_ADDRESS_V6_HPP
+diff --git a/ip-address/include/std/net/ip/bad_address_cast.hpp b/ip-address/include/std/net/ip/bad_address_cast.hpp
+new file mode 100644
+index 0000000..bc330c2
+--- /dev/null
++++ b/ip-address/include/std/net/ip/bad_address_cast.hpp
+@@ -0,0 +1,46 @@
++//
++// ip/bad_address_cast.hpp
++// ~~~~~~~~~~~~~~~~~~~~~~~
++//
++// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
++//
++// Distributed under the Boost Software License, Version 1.0. (See accompanying
++// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
++//
++
++#ifndef STDNET_IP_BAD_ADDRESS_CAST_HPP
++#define STDNET_IP_BAD_ADDRESS_CAST_HPP
++
++#if defined(_MSC_VER) && (_MSC_VER >= 1200)
++# pragma once
++#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
++
++#include "std/net/detail/config.hpp"
++#include
++
++#include "std/net/detail/push_options.hpp"
++
++namespace std {
++namespace experimental {
++namespace net {
++namespace ip {
++
++/// Exception thrown on a failed address_cast.
++class bad_address_cast
++ : public bad_cast
++{
++public:
++ virtual const char* what() const STDNET_NOEXCEPT
++ {
++ return "bad address cast";
++ }
++};
++
++} // namespace ip
++} // namespace net
++} // namespace experimental
++} // namespace std
++
++#include "std/net/detail/pop_options.hpp"
++
++#endif // STDNET_IP_BAD_ADDRESS_CAST_HPP
+diff --git a/ip-address/include/std/net/ip/fwd.hpp b/ip-address/include/std/net/ip/fwd.hpp
+new file mode 100644
+index 0000000..2c8f929
+--- /dev/null
++++ b/ip-address/include/std/net/ip/fwd.hpp
+@@ -0,0 +1,149 @@
++//
++// ip/fwd.hpp
++// ~~~~~~~~~~
++//
++// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
++//
++// Distributed under the Boost Software License, Version 1.0. (See accompanying
++// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
++//
++
++#ifndef STDNET_IP_FWD_HPP
++#define STDNET_IP_FWD_HPP
++
++#if defined(_MSC_VER) && (_MSC_VER >= 1200)
++# pragma once
++#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
++
++#include "std/net/detail/config.hpp"
++#include
++#include
++#include
++#include
++
++#if !defined(STDNET_NO_IOSTREAM)
++# include
++#endif // !defined(STDNET_NO_IOSTREAM)
++
++#include "std/net/detail/push_options.hpp"
++
++namespace std {
++namespace experimental {
++namespace net {
++namespace ip {
++
++#if defined(STDNET_HAS_CONSTEXPR)
++struct v4_mapped_t { STDNET_CONSTEXPR v4_mapped_t() {} };
++STDNET_CONSTEXPR v4_mapped_t v4_mapped;
++#else // !defined(STDNET_HAS_CONSTEXPR)
++enum v4_mapped_t { v4_mapped };
++#endif // !defined(STDNET_HAS_CONSTEXPR)
++
++class address;
++class address_v4;
++class address_v6;
++
++// address comparisons:
++bool operator==(const address&, const address&) STDNET_NOEXCEPT;
++bool operator!=(const address&, const address&) STDNET_NOEXCEPT;
++bool operator< (const address&, const address&) STDNET_NOEXCEPT;
++bool operator> (const address&, const address&) STDNET_NOEXCEPT;
++bool operator<=(const address&, const address&) STDNET_NOEXCEPT;
++bool operator>=(const address&, const address&) STDNET_NOEXCEPT;
++
++// address creation:
++address make_address(const char*);
++address make_address(const char*, std::error_code&) STDNET_NOEXCEPT;
++address make_address(const std::string&);
++address make_address(const std::string&, std::error_code&) STDNET_NOEXCEPT;
++
++#if !defined(STDNET_NO_IOSTREAM)
++
++// address I/O:
++template
++ basic_ostream& operator<<(
++ basic_ostream&, const address&);
++
++#endif // !defined(STDNET_NO_IOSTREAM)
++
++// address_v4 comparisons:
++bool operator==(const address_v4&, const address_v4&) STDNET_NOEXCEPT;
++bool operator!=(const address_v4&, const address_v4&) STDNET_NOEXCEPT;
++bool operator< (const address_v4&, const address_v4&) STDNET_NOEXCEPT;
++bool operator> (const address_v4&, const address_v4&) STDNET_NOEXCEPT;
++bool operator<=(const address_v4&, const address_v4&) STDNET_NOEXCEPT;
++bool operator>=(const address_v4&, const address_v4&) STDNET_NOEXCEPT;
++
++// address_v4 creation:
++//STDNET_CONSTEXPR address_v4 make_address_v4(const address_v4::octets&);
++STDNET_CONSTEXPR address_v4 make_address_v4(unsigned long);
++STDNET_CONSTEXPR address_v4 make_address_v4(v4_mapped_t, const address_v6&);
++address_v4 make_address_v4(const char*);
++address_v4 make_address_v4(const char*, error_code&) STDNET_NOEXCEPT;
++address_v4 make_address_v4(const std::string&);
++address_v4 make_address_v4(const std::string&, std::error_code&) STDNET_NOEXCEPT;
++
++#if !defined(STDNET_NO_IOSTREAM)
++
++// address_v4 I/O:
++template
++ basic_ostream& operator<<(
++ basic_ostream&, const address_v4&);
++
++#endif // !defined(STDNET_NO_IOSTREAM)
++
++// address_v6 comparisons:
++bool operator==(const address_v6&, const address_v6&) STDNET_NOEXCEPT;
++bool operator!=(const address_v6&, const address_v6&) STDNET_NOEXCEPT;
++bool operator< (const address_v6&, const address_v6&) STDNET_NOEXCEPT;
++bool operator> (const address_v6&, const address_v6&) STDNET_NOEXCEPT;
++bool operator<=(const address_v6&, const address_v6&) STDNET_NOEXCEPT;
++bool operator>=(const address_v6&, const address_v6&) STDNET_NOEXCEPT;
++
++// address_v6 creation:
++//STDNET_CONSTEXPR address_v6 make_address_v6(const address_v6::octets&, unsigned long = 0);
++STDNET_CONSTEXPR address_v6 make_address_v6(v4_mapped_t, const address_v4&) STDNET_NOEXCEPT;
++address_v6 make_address_v6(const char*);
++address_v6 make_address_v6(const char*, error_code&) STDNET_NOEXCEPT;
++address_v6 make_address_v6(const std::string&);
++address_v6 make_address_v6(const std::string&, error_code&) STDNET_NOEXCEPT;
++
++#if !defined(STDNET_NO_IOSTREAM)
++
++// address_v6 I/O:
++template
++ basic_ostream& operator<<(
++ basic_ostream&, const address_v6&);
++
++#endif // !defined(STDNET_NO_IOSTREAM)
++
++class bad_address_cast;
++
++// address conversion:
++template STDNET_CONSTEXPR T address_cast(const address&,
++ typename enable_if::value>::type* = 0) STDNET_NOEXCEPT;
++template STDNET_CONSTEXPR T address_cast(const address&,
++ typename enable_if::value>::type* = 0);
++template STDNET_CONSTEXPR T address_cast(const address&,
++ typename enable_if::value>::type* = 0);
++template STDNET_CONSTEXPR T address_cast(const address_v4&,
++ typename enable_if