diff --git a/COPYING b/COPYING
index 6e53833..ab5f370 100644
--- a/COPYING
+++ b/COPYING
@@ -1,5 +1,7 @@
-All source files are Copyright (c) 2008 The Board of Trustees of The
-Leland Stanford Junior University
+Source file copyrights are indicated at the top of each file.
+
+Files not in the datapath/ and vswitchd/ directories or associated
+subdirectories are covered under the OpenFlow license included below:
We are making the OpenFlow specification and associated documentation
(Software) available for public use and benefit with the expectation
@@ -372,3 +374,681 @@ 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 Library General
Public License instead of this License.
+
+Files in vswitchd/ and its sub-directories are covered under the GNU
+General Public License Version 3. Included below:
+
+ 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.
+
+
+ Copyright (C)
+
+ 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:
+
+ Copyright (C)
+ 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/ChangeLog b/ChangeLog
index 9cabd5c..2811ec1 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+v0.9.0-alpha4 - 08 May 2009
+---------------------------
+
+ - Changed binary forms name (BUG#40)
+ - Added MAC address rewrite manipulation in dpctl (BUG#42)
+ - Added proc file for NetFPGA to monitor statistical information (BUG#44)
+
v0.9.0-alpha3 - 28 Apr 2009
---------------------------
- Removed monolithic daemon called switch
diff --git a/INSTALL b/INSTALL
index fc7d3f7..4e3e90e 100644
--- a/INSTALL
+++ b/INSTALL
@@ -12,17 +12,17 @@ The OpenFlow reference implementation includes two OpenFlow switch
implementations:
- The "kernel-based switch": This divides the switch into a
- "datapath" Linux kernel module (openflow_mod.o for Linux 2.4
- or openflow_mod.ko for Linux 2.6) and a userspace program
- (secchan). The kernel-based switch is faster than either of
+ "datapath" Linux kernel module (ofdatapath.o for Linux 2.4
+ or ofdatapath.ko for Linux 2.6) and a userspace program
+ (ofprotocol). The kernel-based switch is faster than either of
the other two implementations but requires building and
installing a kernel module, which can sometimes be
challenging.
- The "userspace datapath-based switch": This divides the
switch into a userspace "datapath" (built as
- udatapath/udatapath) and the same userspace program used by
- the kernel-based switch (secchan). The userspace
+ udatapath/ofdatapath) and the same userspace program used by
+ the kernel-based switch (ofprotocol). The userspace
datapath-based switch does not require building a kernel
module, but it is not as fast as the kernel-based switch.
@@ -160,9 +160,9 @@ distribution in the ordinary way using "configure" and "make".
The following binaries will be built:
- - Userspace datapath: udatapath/udatapath.
+ - Userspace datapath: udatapath/ofdatapath.
- - Secure channel executable: secchan/secchan.
+ - Secure channel executable: secchan/ofprotocol.
- Controller executable: controller/controller.
@@ -187,17 +187,17 @@ distribution in the ordinary way using "configure" and "make".
If you passed --with-l26 to configure, "make" will also build the
following kernel modules:
- - datapath/linux-2.6/openflow_mod.ko
+ - datapath/linux-2.6/ofdatapath.ko
- - datapath/linux-2.6/hwtable__mod.ko for each
+ - datapath/linux-2.6/ofdatapath_.ko for each
specified on --enable-hw-tables (if any).
If you passed --with-l24 to configure, "make" will also build the
following kernel modules:
- - datapath/linux-2.4/openflow_mod.o
+ - datapath/linux-2.4/ofdatapath.o
- - datapath/linux-2.6/hwtable__mod.o for each
+ - datapath/linux-2.6/ofdatapath_.o for each
specified on --enable-hw-tables (if any).
3. Run "make install" to install the executables and manpages into the
@@ -206,11 +206,11 @@ distribution in the ordinary way using "configure" and "make".
4. If you built kernel modules, you may load them with "insmod", e.g.:
(Linux 2.6)
- % insmod datapath/linux-2.6/openflow_mod.ko
+ % insmod datapath/linux-2.6/ofdatapath.ko
(Linux 2.4)
% insmod datapath/linux-2.4/compat24_mod.o
- % insmod datapath/linux-2.4/openflow_mod.o
+ % insmod datapath/linux-2.4/ofdatapath.o
After you load the openflow module, you may load one hardware switch
table module (if any were built) to enable support for that hardware
@@ -218,7 +218,7 @@ distribution in the ordinary way using "configure" and "make".
The insmod program must be run as root. You may need to specify a
full path to insmod, e.g. /sbin/insmod. To verify that the modules
- have been loaded, run "/sbin/lsmod" and check that openflow_mod is
+ have been loaded, run "/sbin/lsmod" and check that ofdatapath is
listed.
4. Test the userspace programs, as described under Testing Userspace
@@ -359,7 +359,7 @@ installation instructions, refer to the Installation section below.
Userspace Datapath
------------------
-These instructions use the OpenFlow userspace datapath ("udatapath").
+These instructions use the OpenFlow userspace datapath ("ofdatapath").
1. Start the OpenFlow controller running in the background, by running
the "controller" program with a command like the following:
@@ -377,16 +377,16 @@ These instructions use the OpenFlow userspace datapath ("udatapath").
program such as "su" to become root temporarily.
3. Create a datapath instance running in the background. The command
- below creates a datapath that listens for connections from secchan
+ below creates a datapath that listens for connections from ofprotocol
on a Unix domain socket located in /var/run and services physical
ports eth1 and eth2:
- # udatapath punix:/var/run/dp0.sock -i eth1,eth2 &
+ # ofdatapath punix:/var/run/dp0.sock -i eth1,eth2 &
-4. Run secchan to start the secure channel connecting the datapath and
+4. Run ofprotocol to start the secure channel connecting the datapath and
the controller:
- # secchan unix:/var/run/controller.sock unix:/var/run/dp0.sock &
+ # ofprotocol unix:/var/run/controller.sock unix:/var/run/dp0.sock &
5. Devices plugged into the network ports specified in step 2 should
now be able to send packets to each other, as if they were plugged
@@ -409,7 +409,7 @@ each switch to reach the controller over the network:
control"). When in-band control is used, the location of the
controller may be configured manually or discovered
automatically. We will assume manual configuration here;
- please refer to secchan(8) for instructions on setting up
+ please refer to ofprotocol(8) for instructions on setting up
controller discovery.
Controller Setup
@@ -436,14 +436,14 @@ switch, follow the procedure below.
program such as "su" to become root temporarily.
1. Create a datapath instance running in the background. The command
- below creates a datapath that listens for connections from secchan
+ below creates a datapath that listens for connections from ofprotocol
on a Unix domain socket located in /var/run, services physical
ports eth1 and eth2, and creates a TAP network device named "tap0"
for use in in-band control:
- # udatapath punix:/var/run/dp0.sock -i eth1,eth2 --local-port=tap:tap0 &
+ # ofdatapath punix:/var/run/dp0.sock -i eth1,eth2 --local-port=tap:tap0 &
- (See udatapath(8) for details.)
+ (See ofdatapath(8) for details.)
If the switch will connect to the controller out-of-band, then the
--local-port option may be omitted, or --no-local-port may be
@@ -476,15 +476,15 @@ switch, follow the procedure below.
configuration is required at this point. You may proceed to
step 4.
-4. Run secchan to start the secure channel connecting the datapath to
+4. Run ofprotocol to start the secure channel connecting the datapath to
a remote controller. If the controller is running on host
- 192.168.1.2 port 6633 (the default port), the secchan invocation
+ 192.168.1.2 port 6633 (the default port), the ofprotocol invocation
would look like this:
- # secchan unix:/var/run/dp0.sock tcp:192.168.1.2
+ # ofprotocol unix:/var/run/dp0.sock tcp:192.168.1.2
- If you are using in-band control with controller discovery, omit
- the second argument to the secchan command.
+ the second argument to the ofprotocol command.
- If you are using out-of-band control, add --out-of-band to the
command line.
@@ -515,13 +515,13 @@ The OpenFlow kernel module must be loaded, as described under
# dpctl adddp nl:0
- (nl:0 is the first datapath within a host. openflow_mod supports
+ (nl:0 is the first datapath within a host. ofdatapath supports
multiple datapaths within the same host, which would be identified
as nl:1, nl:2, etc.)
Creating datapath nl:0 also creates a new network device named of0.
This network device, called the datapath's "local port", will be
- bridged to the physical switch ports by the secchan, for use in
+ bridged to the physical switch ports by the ofprotocol, for use in
in-band control.
If you built a support module for hardware accelerated OpenFlow
@@ -551,7 +551,7 @@ The OpenFlow kernel module must be loaded, as described under
- If you are using in-band control, then at this point you must
configure the of0 network device created in step 1. This
device is not yet bridged to any physical network (because
- secchan does that, and it is not yet running), so the next
+ ofprotocol does that, and it is not yet running), so the next
step depends on whether connectivity is required to configure
the device's IP address:
@@ -569,15 +569,15 @@ The OpenFlow kernel module must be loaded, as described under
configuration is required at this point. You may proceed to
step 4.
-4. Run secchan to start the secure channel connecting the datapath to
+4. Run ofprotocol to start the secure channel connecting the datapath to
a remote controller. If the controller is running on host
- 192.168.1.2 port 6633 (the default port), the secchan invocation
+ 192.168.1.2 port 6633 (the default port), the ofprotocol invocation
would look like this:
- # secchan nl:0 tcp:192.168.1.2
+ # ofprotocol nl:0 tcp:192.168.1.2
- If you are using in-band control with controller discovery, omit
- the second argument to the secchan command.
+ the second argument to the ofprotocol command.
- If you are using out-of-band control, add --out-of-band to the
command line.
@@ -627,9 +627,9 @@ with the instructions below, then the invocation would look like:
--certificate=ctl-cert.pem --ca-cert=pki/switchca/cacert.pem
To configure a switch to connect to a controller running on port 6633
-(the default) on host 192.168.1.2 over SSL, invoke secchan as follows:
+(the default) on host 192.168.1.2 over SSL, invoke ofprotocol as follows:
- # secchan -v DATAPATH ssl:192.168.1.2 --private-key=PRIVKEY \
+ # ofprotocol -v DATAPATH ssl:192.168.1.2 --private-key=PRIVKEY \
--certificate=CERT --ca-cert=CACERT
where DATAPATH is the datapath to connect to (e.g. nl:0 or
@@ -640,7 +640,7 @@ certificate for the controller CA. If, for example, your PKI was
created with the instructions below, then the invocation would look
like:
- # secchan -v DATAPATH ssl:192.168.1.2 --private-key=sc-privkey.pem \
+ # ofprotocol -v DATAPATH ssl:192.168.1.2 --private-key=sc-privkey.pem \
--certificate=sc-cert.pem --ca-cert=pki/controllerca/cacert.pem
[*] To be specific, OpenFlow uses TLS version 1.0 or later (TLSv1), as
@@ -664,7 +664,7 @@ controllerca subdirectory contains controller certificate authority
related files, including the following:
- cacert.pem: Root certificate for the controller certificate
- authority. This file must be provided to secchan with the
+ authority. This file must be provided to ofprotocol with the
--ca-cert option to enable it to authenticate valid controllers.
- private/cakey.pem: Private signing key for the controller
@@ -699,7 +699,7 @@ named sc-privkey.pem and sc-cert.pem, for example, you could run:
sc-privkey.pem and sc-cert.pem would need to be copied to the switch
for its use at runtime (they could then be deleted from their original
locations). The --private-key and --certificate options,
-respectively, of secchan would point to these files.
+respectively, of ofprotocol would point to these files.
Bug Reporting
-------------
diff --git a/Makefile.am b/Makefile.am
index 84fb6e7..e2cf2d1 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -45,20 +45,18 @@ noinst_LIBRARIES =
noinst_PROGRAMS =
noinst_SCRIPTS =
-EXTRA_DIST += README.hwtables subst
+EXTRA_DIST += README.hwtables soexpand.pl regress
-do_subst = ($(srcdir)/subst VLOG_OPTIONS $(srcdir)/lib/vlog.man | \
- sed -e 's,[@]LOGDIR[@],$(LOGDIR),g' \
- -e 's,[@]PKIDIR[@],$(PKIDIR),g' \
- -e 's,[@]RUNDIR[@],$(RUNDIR),g' \
- -e 's,[@]pkgdatadir[@],$(pkgdatadir),g' \
- -e 's,[@]PERL[@],$(PERL),g')
-ro_script = sed "`printf '1a\\' && printf '\\n\# -*- buffer-read-only: t -*-'`"
ro_c = echo '/* -*- mode: c; buffer-read-only: t -*- */'
SUFFIXES = .in
.in:
- $(do_subst) < $< > $@
+ $(PERL) $(srcdir)/soexpand.pl -I$(srcdir) < $< | \
+ sed -e 's,[@]LOGDIR[@],$(LOGDIR),g' \
+ -e 's,[@]PKIDIR[@],$(PKIDIR),g' \
+ -e 's,[@]RUNDIR[@],$(RUNDIR),g' \
+ -e 's,[@]pkgdatadir[@],$(pkgdatadir),g' \
+ -e 's,[@]PERL[@],$(PERL),g' > $@
include lib/automake.mk
include secchan/automake.mk
diff --git a/README b/README
index 2a2a1fb..a99bf65 100644
--- a/README
+++ b/README
@@ -38,7 +38,7 @@ following components:
- A Linux kernel module that implements the flow table and
OpenFlow protocol, in the datapath directory.
- - secchan, a program that implements the secure channel
+ - ofprotocol, a program that implements the secure channel
component of the reference switch.
- dpctl, a tool for configuring the kernel module.
@@ -47,10 +47,10 @@ following components:
The second implementation has the following components:
- - udatapath, which implements the same functionality as the
+ - ofdatapath, which implements the same functionality as the
Linux kernel module in userspace, at a cost in performance.
- - secchan, a program that implements the secure channel
+ - ofprotocol, a program that implements the secure channel
component of the reference switch (the same program used in
the kernel-based implementation).
@@ -61,7 +61,7 @@ This distribution includes some additional software as well:
learning switches.
- vlogconf, a utility that can adjust the logging levels of a
- running secchan or controller.
+ running ofprotocol or controller.
- ofp-pki, a utility for creating and managing the public-key
infrastructure for OpenFlow switches.
diff --git a/acinclude.m4 b/acinclude.m4
index 0036ac1..c9c9f7a 100644
--- a/acinclude.m4
+++ b/acinclude.m4
@@ -1,4 +1,6 @@
-# Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+# -*- autoconf -*-
+
+# Copyright (c) 2008, 2009 The Board of Trustees of The Leland Stanford
# Junior University
#
# We are making the OpenFlow specification and associated documentation
@@ -35,8 +37,8 @@ dnl
dnl Configure linux kernel source tree
AC_DEFUN([OFP_CHECK_LINUX], [
AC_ARG_WITH([$1],
- [AC_HELP_STRING([--with-$1=/path/to/linux-$3],
- [Specify the linux $3 kernel sources])],
+ [AC_HELP_STRING([--with-$1=/path/to/linux-$2],
+ [Specify the linux $2 kernel sources])],
[path="$withval"], [path=])dnl
if test -n "$path"; then
path=`eval echo "$path"`
@@ -44,25 +46,92 @@ AC_DEFUN([OFP_CHECK_LINUX], [
AC_MSG_CHECKING([for $path directory])
if test -d "$path"; then
AC_MSG_RESULT([yes])
- $4=$path
- AC_SUBST($4)
+ $3=$path
+ AC_SUBST($3)
else
AC_MSG_RESULT([no])
AC_ERROR([source dir $path doesn't exist])
fi
AC_MSG_CHECKING([for $path kernel version])
- version=`grep '^PATCHLEVEL = ' "$path/Makefile" | sed 's/PATCHLEVEL = '//`
- AC_MSG_RESULT([2.$version])
- if test "2.$version" != '$3'; then
- AC_ERROR([Linux kernel source in $path is not version $3])
+ patchlevel=`sed -n 's/^PATCHLEVEL = //p' "$path/Makefile"`
+ sublevel=`sed -n 's/^SUBLEVEL = //p' "$path/Makefile"`
+ AC_MSG_RESULT([2.$patchlevel.$sublevel])
+ if test "2.$patchlevel" != '$2'; then
+ AC_ERROR([Linux kernel source in $path is not version $2])
fi
if ! test -e "$path"/include/linux/version.h || \
! test -e "$path"/include/linux/autoconf.h; then
AC_MSG_ERROR([Linux kernel source in $path is not configured])
fi
+ m4_if($2, [2.6], [OFP_CHECK_LINUX26_COMPAT])
+ fi
+ AM_CONDITIONAL($4, test -n "$path")
+])
+
+dnl OFP_GREP_IFELSE(FILE, REGEX, IF-MATCH, IF-NO-MATCH)
+dnl
+dnl Greps FILE for REGEX. If it matches, runs IF-MATCH, otherwise IF-NO-MATCH.
+AC_DEFUN([OFP_GREP_IFELSE], [
+ AC_MSG_CHECKING([whether $2 matches in $1])
+ grep '$2' $1 >/dev/null 2>&1
+ status=$?
+ case $status in
+ 0)
+ AC_MSG_RESULT([yes])
+ $3
+ ;;
+ 1)
+ AC_MSG_RESULT([no])
+ $4
+ ;;
+ *)
+ AC_MSG_ERROR([grep exited with status $status])
+ ;;
+ esac
+])
+
+dnl OFP_DEFINE(NAME)
+dnl
+dnl Defines NAME to 1 in kcompat.h.
+AC_DEFUN([OFP_DEFINE], [
+ echo '#define $1 1' >> datapath/linux-2.6/kcompat.h.new
+])
+
+AC_DEFUN([OFP_CHECK_VETH], [
+ AC_MSG_CHECKING([whether to build veth module])
+ if test "$sublevel" = 18; then
+ AC_MSG_RESULT([yes])
+ AC_SUBST([BUILD_VETH], 1)
+ else
+ AC_MSG_RESULT([no])
+ fi
+])
+
+dnl OFP_CHECK_LINUX26_COMPAT
+dnl
+dnl Runs various Autoconf checks on the Linux 2.6 kernel source in
+dnl the directory in $KSRC26.
+AC_DEFUN([OFP_CHECK_LINUX26_COMPAT], [
+ rm -f datapath/linux-2.6/kcompat.h.new
+ mkdir -p datapath/linux-2.6
+ : > datapath/linux-2.6/kcompat.h.new
+ OFP_GREP_IFELSE([$KSRC26/include/linux/skbuff.h], [skb_transport_header],
+ [OFP_DEFINE([HAVE_SKBUFF_HEADER_HELPERS])])
+ OFP_GREP_IFELSE([$KSRC26/include/linux/skbuff.h], [raw],
+ [OFP_DEFINE([HAVE_MAC_RAW])])
+ OFP_GREP_IFELSE([$KSRC26/include/linux/skbuff.h],
+ [skb_copy_from_linear_data_offset],
+ [OFP_DEFINE([HAVE_SKB_COPY_FROM_LINEAR_DATA_OFFSET])])
+ OFP_GREP_IFELSE([$KSRC26/include/net/netlink.h], [NLA_NUL_STRING],
+ [OFP_DEFINE([HAVE_NLA_NUL_STRING])])
+ OFP_CHECK_VETH
+ if cmp -s datapath/linux-2.6/kcompat.h.new \
+ datapath/linux-2.6/kcompat.h >/dev/null 2>&1; then
+ rm datapath/linux-2.6/kcompat.h.new
+ else
+ mv datapath/linux-2.6/kcompat.h.new datapath/linux-2.6/kcompat.h
fi
- AM_CONDITIONAL($5, test -n "$path")
])
dnl Checks for --enable-hw-tables and substitutes HW_TABLES to any
@@ -142,4 +211,36 @@ AC_DEFUN([OFP_CHECK_DPKG_BUILDPACKAGE],
[AC_CHECK_PROG([HAVE_DPKG_BUILDPACKAGE], [dpkg-buildpackage], [yes], [no])
AM_CONDITIONAL([HAVE_DPKG_BUILDPACKAGE],
[test $HAVE_DPKG_BUILDPACKAGE = yes])])
-
+
+dnl ----------------------------------------------------------------------
+dnl These macros are from GNU PSPP, with the following original license:
+dnl Copyright (C) 2005, 2006, 2007 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+dnl OFP_CHECK_CC_OPTION([OPTION], [ACTION-IF-ACCEPTED], [ACTION-IF-REJECTED])
+dnl Check whether the given C compiler OPTION is accepted.
+dnl If so, execute ACTION-IF-ACCEPTED, otherwise ACTION-IF-REJECTED.
+AC_DEFUN([OFP_CHECK_CC_OPTION],
+[
+ m4_define([ofp_cv_name], [ofp_cv_[]m4_translit([$1], [-], [_])])dnl
+ AC_CACHE_CHECK([whether $CC accepts $1], [ofp_cv_name],
+ [ofp_save_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS $1"
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM(,)], [ofp_cv_name[]=yes], [ofp_cv_name[]=no])
+ CFLAGS="$ofp_save_CFLAGS"])
+ if test $ofp_cv_name = yes; then
+ m4_if([$2], [], [;], [$2])
+ else
+ m4_if([$3], [], [:], [$3])
+ fi
+])
+
+dnl OFP_ENABLE_OPTION([OPTION])
+dnl Check whether the given C compiler OPTION is accepted.
+dnl If so, add it to CFLAGS.
+dnl Example: OFP_ENABLE_OPTION([-Wdeclaration-after-statement])
+AC_DEFUN([OFP_ENABLE_OPTION],
+ [OFP_CHECK_CC_OPTION([$1], [CFLAGS="$CFLAGS $1"])])
+dnl ----------------------------------------------------------------------
diff --git a/configure.ac b/configure.ac
index 565f8a1..7d1d745 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,4 +1,4 @@
-# Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+# Copyright (c) 2008, 2009 The Board of Trustees of The Leland Stanford
# Junior University
#
# We are making the OpenFlow specification and associated documentation
@@ -31,7 +31,7 @@
# derivatives without specific, written prior permission.
AC_PREREQ(2.60)
-AC_INIT([openflow], [0.9.0-alpha3], [info@openflowswitch.org])
+AC_INIT([openflow], [0.9.0-alpha4], [info@openflowswitch.org])
NX_BUILDNR
AC_CONFIG_SRCDIR([README.hwtables])
AC_CONFIG_MACRO_DIR([m4])
@@ -54,17 +54,32 @@ fi
OFP_CHECK_LIBOPENFLOW
OFP_CHECK_IF_PACKET
OFP_CHECK_HWTABLES
+AC_SYS_LARGEFILE
AC_CHECK_FUNCS([strsignal])
AC_ARG_VAR(KARCH, [Kernel Architecture String])
AC_SUBST(KARCH)
-OFP_CHECK_LINUX(l26, 2.6, 2.6, KSRC26, L26_ENABLED)
-OFP_CHECK_LINUX(l24, 2.4, 2.4, KSRC24, L24_ENABLED)
+OFP_CHECK_LINUX(l26, 2.6, KSRC26, L26_ENABLED)
+OFP_CHECK_LINUX(l24, 2.4, KSRC24, L24_ENABLED)
OFP_CHECK_DPKG_BUILDPACKAGE
-CFLAGS="$CFLAGS -Wall -Wno-sign-compare -Wpointer-arith -Wdeclaration-after-statement"
+OFP_ENABLE_OPTION([-Wall])
+OFP_ENABLE_OPTION([-Wno-sign-compare])
+OFP_ENABLE_OPTION([-Wpointer-arith])
+OFP_ENABLE_OPTION([-Wdeclaration-after-statement])
+OFP_ENABLE_OPTION([-Wformat-security])
+OFP_ENABLE_OPTION([-Wswitch-enum])
+OFP_ENABLE_OPTION([-Wunused-parameter])
+OFP_ENABLE_OPTION([-Wstrict-aliasing])
+OFP_ENABLE_OPTION([-Wbad-function-cast])
+OFP_ENABLE_OPTION([-Wcast-align])
+OFP_ENABLE_OPTION([-Wstrict-prototypes])
+OFP_ENABLE_OPTION([-Wold-style-definition])
+OFP_ENABLE_OPTION([-Wmissing-prototypes])
+OFP_ENABLE_OPTION([-Wmissing-field-initializers])
+OFP_ENABLE_OPTION([-Wno-override-init])
OFP_ENABLE_EXT
m4_include([ext.m4])
diff --git a/controller/controller.8.in b/controller/controller.8.in
index a266252..c2b65cf 100644
--- a/controller/controller.8.in
+++ b/controller/controller.8.in
@@ -1,3 +1,5 @@
+.ds PN controller
+
.TH controller 8 "May 2008" "OpenFlow" "OpenFlow Manual"
.SH NAME
@@ -10,7 +12,7 @@ controller \- simple OpenFlow controller reference implementation
.SH DESCRIPTION
A sample OpenFlow controller which functions as an L2 MAC-learning
switch or hub. \fBcontroller\fR can manage a remote datapath through
-a secure channel (see \fBsecchan(8)\fR). It can also connect directly
+a secure channel (see \fBofprotocol(8)\fR). It can also connect directly
to a local datapath via Netlink.
\fBcontroller\fR controls one or more OpenFlow switches, specified as
@@ -84,7 +86,7 @@ already have the controller CA certificate for it to have any
confidence in the controller's identity. However, this option allows
a newly installed switch to obtain the controller CA certificate on
first boot using, e.g., the \fB--bootstrap-ca-cert\fR option to
-\fBsecchan\fR(8).
+\fBofprotocol\fR(8).
.TP
.BR \-n ", " \-\^\-noflow
@@ -106,7 +108,7 @@ recommended, flows will never expire. The default is 60 seconds.
This option affects only flows set up by the OpenFlow controller. In
some configurations, the OpenFlow secure channel can set up some flows
on its own. To set the idle time for those flows, pass
-\fB--max-idle\fR to \fBsecchan\fR(8).
+\fB--max-idle\fR to \fBofprotocol\fR(8).
This option has no effect when \fB-n\fR (or \fB--noflow\fR) is in use
(because the controller does not set up flows in that case).
@@ -124,37 +126,9 @@ through the controller and every packet is flooded.
This option is most useful for debugging. It reduces switching
performance, so it should not be used in production.
-.TP
-\fB-P\fR[\fIpidfile\fR], \fB--pidfile\fR[\fB=\fIpidfile\fR]
-Causes a file (by default, \fBcontroller.pid\fR) to be created indicating
-the PID of the running process. If \fIpidfile\fR is not specified, or
-if it does not begin with \fB/\fR, then it is created in
-\fB@RUNDIR@\fR.
-
-.TP
-\fB-f\fR, \fB--force\fR
-By default, when \fB-P\fR or \fB--pidfile\fR is specified and the
-specified pidfile already exists and is locked by a running process,
-\fBcontroller\fR refuses to start. Specify \fB-f\fR or \fB--force\fR
-to cause it to instead overwrite the pidfile.
-
-When \fB-P\fR or \fB--pidfile\fR is not specified, this option has no
-effect.
-
-.TP
-\fB-D\fR, \fB--detach\fR
-Causes \fBcontroller\fR to detach itself from the foreground session and
-run as a background process.
-
-.TP
-.BR \-h ", " \-\^\-help
-Prints a brief help message to the console.
-
-@VLOG_OPTIONS@
-
-.TP
-.BR \-V ", " \-\^\-version
-Prints version information to the console.
+.so lib/daemon.man
+.so lib/vlog.man
+.so lib/common.man
.SH EXAMPLES
@@ -171,6 +145,6 @@ To bind locally to port 6633 (the default) and wait for incoming connections fro
.SH "SEE ALSO"
.BR dpctl (8),
-.BR secchan (8),
-.BR udatapath (8),
+.BR ofprotocol (8),
+.BR ofdatapath (8),
.BR vlogconf (8)
diff --git a/datapath/Makefile.am b/datapath/Makefile.am
index eefc392..c466543 100644
--- a/datapath/Makefile.am
+++ b/datapath/Makefile.am
@@ -8,7 +8,20 @@ endif
EXTRA_DIST = $(dist_headers) $(dist_sources)
EXTRA_DIST += hwtable_dummy/Modules.mk hwtable_dummy/hwtable_dummy.c
-EXTRA_DIST += hwtable_nf2/Modules.mk hwtable_nf2/nf2_flowtable.c hwtable_nf2/nf2.h hwtable_nf2/nf2_lib.c hwtable_nf2/nf2_openflow.c hwtable_nf2/nf2_reg.h hwtable_nf2/nf2_flowtable.h hwtable_nf2/nf2_hwapi.h hwtable_nf2/nf2_lib.h hwtable_nf2/nf2_openflow.h
+EXTRA_DIST += \
+ hwtable_nf2/Modules.mk \
+ hwtable_nf2/nf2.h \
+ hwtable_nf2/nf2_hwapi.h \
+ hwtable_nf2/nf2_reg.h \
+ hwtable_nf2/nf2_flowtable.c \
+ hwtable_nf2/nf2_flowtable.h \
+ hwtable_nf2/nf2_lib.c \
+ hwtable_nf2/nf2_lib.h \
+ hwtable_nf2/nf2_procfs.c \
+ hwtable_nf2/nf2_procfs.h \
+ hwtable_nf2/nf2_openflow.c \
+ hwtable_nf2/nf2_openflow.h \
+ hwtable_nf2/openflow_switch.bit
# Suppress warnings about GNU extensions in Modules.mk files.
AUTOMAKE_OPTIONS = -Wno-portability
diff --git a/datapath/Modules.mk b/datapath/Modules.mk
index cbb52df..a02f0d7 100644
--- a/datapath/Modules.mk
+++ b/datapath/Modules.mk
@@ -1,7 +1,15 @@
-all_modules = $(dist_modules)
-dist_modules = openflow
+# Some modules should be built and distributed, e.g. openflow.
+#
+# Some modules should be distributed but not built, e.g. we do not build
+# veth if the kernel in question already has it.
+#
+# Some modules should be built but not distributed, e.g. third-party
+# hwtable modules.
+both_modules = ofdatapath
+build_modules = $(both_modules) # Modules to build
+dist_modules = $(both_modules) # Modules to distribute
-openflow_sources = \
+ofdatapath_sources = \
chain.c \
crc32.c \
datapath.c \
@@ -16,7 +24,7 @@ openflow_sources = \
table-hash.c \
table-linear.c
-openflow_headers = \
+ofdatapath_headers = \
chain.h \
compat.h \
crc32.h \
@@ -32,7 +40,7 @@ openflow_headers = \
dist_sources = $(foreach module,$(dist_modules),$($(module)_sources))
dist_headers = $(foreach module,$(dist_modules),$($(module)_headers))
-all_sources = $(foreach module,$(all_modules),$($(module)_sources))
-all_headers = $(foreach module,$(all_modules),$($(module)_headers))
-all_links = $(notdir $(all_sources))
-all_objects = $(notdir $(patsubst %.c,%.o,$(all_sources)))
+build_sources = $(foreach module,$(build_modules),$($(module)_sources))
+build_headers = $(foreach module,$(build_modules),$($(module)_headers))
+build_links = $(notdir $(build_sources))
+build_objects = $(notdir $(patsubst %.c,%.o,$(build_sources)))
diff --git a/datapath/brc_sysfs.h b/datapath/brc_sysfs.h
new file mode 100644
index 0000000..fa972b0
--- /dev/null
+++ b/datapath/brc_sysfs.h
@@ -0,0 +1,23 @@
+#ifndef BRC_SYSFS_H
+#define BRC_SYSFS_H 1
+
+struct datapath;
+
+#include
+#if LINUX_VERSION_CODE == KERNEL_VERSION(2,6,18)
+/* brc_sysfs_dp.c */
+int brc_sysfs_add_dp(struct datapath *dp);
+int brc_sysfs_del_dp(struct datapath *dp);
+
+/* brc_sysfs_if.c */
+int brc_sysfs_add_if(struct net_bridge_port *p);
+int brc_sysfs_del_if(struct net_bridge_port *p);
+#else
+static inline int brc_sysfs_add_dp(struct datapath *dp) { return 0; }
+static inline int brc_sysfs_del_dp(struct datapath *dp) { return 0; }
+static inline int brc_sysfs_add_if(struct net_bridge_port *p) { return 0; }
+static inline int brc_sysfs_del_if(struct net_bridge_port *p) { return 0; }
+#endif
+
+#endif /* brc_sysfs.h */
+
diff --git a/datapath/brc_sysfs_dp.c b/datapath/brc_sysfs_dp.c
new file mode 100644
index 0000000..b1b27ed
--- /dev/null
+++ b/datapath/brc_sysfs_dp.c
@@ -0,0 +1,519 @@
+#include
+#if LINUX_VERSION_CODE == KERNEL_VERSION(2,6,18)
+
+/*
+ * Sysfs attributes of bridge for OpenFlow
+ *
+ * This has been shamelessly copied from the kernel sources.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "brc_sysfs.h"
+#include "datapath.h"
+#include "dp_dev.h"
+
+#define to_dev(obj) container_of(obj, struct device, kobj)
+
+/* Hack to attempt to build on more platforms. */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21)
+#define to_kobj(d) &(d)->class_dev.kobj
+#define BRC_DEVICE_ATTR CLASS_DEVICE_ATTR
+#else
+#define to_kobj(d) &(d)->dev.kobj
+#define BRC_DEVICE_ATTR DEVICE_ATTR
+#endif
+
+
+/*
+ * Common code for storing bridge parameters.
+ */
+static ssize_t store_bridge_parm(struct class_device *d,
+ const char *buf, size_t len,
+ void (*set)(struct datapath *, unsigned long))
+{
+ struct datapath *dp = dp_dev_get_dp(to_net_dev(d));
+ char *endp;
+ unsigned long val;
+
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ val = simple_strtoul(buf, &endp, 0);
+ if (endp == buf)
+ return -EINVAL;
+
+#if 0
+ spin_lock_bh(&br->lock);
+ (*set)(br, val);
+ spin_unlock_bh(&br->lock);
+#else
+ printk("%s: xxx writing dp parms not supported yet!\n",
+ dp->netdev->name);
+#endif
+ return len;
+}
+
+
+static ssize_t show_forward_delay(struct class_device *d,
+ char *buf)
+{
+#if 0
+ struct datapath *dp = dp_dev_get_dp(to_net_dev(d));
+ return sprintf(buf, "%lu\n", jiffies_to_clock_t(br->forward_delay));
+#else
+ return sprintf(buf, "%d\n", 0);
+#endif
+}
+
+static void set_forward_delay(struct datapath *dp, unsigned long val)
+{
+#if 0
+ unsigned long delay = clock_t_to_jiffies(val);
+ br->forward_delay = delay;
+ if (br_is_root_bridge(br))
+ br->bridge_forward_delay = delay;
+#else
+ printk("%s: xxx attempt to set_forward_delay()\n", dp->netdev->name);
+#endif
+}
+
+static ssize_t store_forward_delay(struct class_device *d,
+ const char *buf, size_t len)
+{
+ return store_bridge_parm(d, buf, len, set_forward_delay);
+}
+static BRC_DEVICE_ATTR(forward_delay, S_IRUGO | S_IWUSR,
+ show_forward_delay, store_forward_delay);
+
+static ssize_t show_hello_time(struct class_device *d, char *buf)
+{
+#if 0
+ return sprintf(buf, "%lu\n",
+ jiffies_to_clock_t(to_bridge(d)->hello_time));
+#else
+ return sprintf(buf, "%d\n", 0);
+#endif
+}
+
+static void set_hello_time(struct datapath *dp, unsigned long val)
+{
+#if 0
+ unsigned long t = clock_t_to_jiffies(val);
+ br->hello_time = t;
+ if (br_is_root_bridge(br))
+ br->bridge_hello_time = t;
+#else
+ printk("%s: xxx attempt to set_hello_time()\n", dp->netdev->name);
+#endif
+}
+
+static ssize_t store_hello_time(struct class_device *d,
+ const char *buf,
+ size_t len)
+{
+ return store_bridge_parm(d, buf, len, set_hello_time);
+}
+static BRC_DEVICE_ATTR(hello_time, S_IRUGO | S_IWUSR, show_hello_time,
+ store_hello_time);
+
+static ssize_t show_max_age(struct class_device *d,
+ char *buf)
+{
+#if 0
+ return sprintf(buf, "%lu\n",
+ jiffies_to_clock_t(to_bridge(d)->max_age));
+#else
+ return sprintf(buf, "%d\n", 0);
+#endif
+}
+
+static void set_max_age(struct datapath *dp, unsigned long val)
+{
+#if 0
+ unsigned long t = clock_t_to_jiffies(val);
+ br->max_age = t;
+ if (br_is_root_bridge(br))
+ br->bridge_max_age = t;
+#else
+ printk("%s: xxx attempt to set_max_age()\n", dp->netdev->name);
+#endif
+}
+
+static ssize_t store_max_age(struct class_device *d,
+ const char *buf, size_t len)
+{
+ return store_bridge_parm(d, buf, len, set_max_age);
+}
+static BRC_DEVICE_ATTR(max_age, S_IRUGO | S_IWUSR, show_max_age, store_max_age);
+
+static ssize_t show_ageing_time(struct class_device *d,
+ char *buf)
+{
+#if 0
+ struct datapath *dp = dp_dev_get_dp(to_net_dev(d));
+ return sprintf(buf, "%lu\n", jiffies_to_clock_t(br->ageing_time));
+#else
+ return sprintf(buf, "%d\n", 0);
+#endif
+}
+
+static void set_ageing_time(struct datapath *dp, unsigned long val)
+{
+#if 0
+ br->ageing_time = clock_t_to_jiffies(val);
+#else
+ printk("%s: xxx attempt to set_ageing_time()\n", dp->netdev->name);
+#endif
+}
+
+static ssize_t store_ageing_time(struct class_device *d,
+ const char *buf, size_t len)
+{
+ return store_bridge_parm(d, buf, len, set_ageing_time);
+}
+static BRC_DEVICE_ATTR(ageing_time, S_IRUGO | S_IWUSR, show_ageing_time,
+ store_ageing_time);
+
+static ssize_t show_stp_state(struct class_device *d,
+ char *buf)
+{
+#if 0
+ struct datapath *dp = dp_dev_get_dp(to_net_dev(d));
+ return sprintf(buf, "%d\n", br->stp_enabled);
+#else
+ return sprintf(buf, "%d\n", 0);
+#endif
+}
+
+
+static ssize_t store_stp_state(struct class_device *d,
+ const char *buf,
+ size_t len)
+{
+ struct datapath *dp = dp_dev_get_dp(to_net_dev(d));
+#if 0
+ char *endp;
+ unsigned long val;
+
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ val = simple_strtoul(buf, &endp, 0);
+ if (endp == buf)
+ return -EINVAL;
+
+ rtnl_lock();
+ br_stp_set_enabled(br, val);
+ rtnl_unlock();
+#else
+ printk("%s: xxx attempt to set_stp_state()\n", dp->netdev->name);
+#endif
+
+ return len;
+}
+static BRC_DEVICE_ATTR(stp_state, S_IRUGO | S_IWUSR, show_stp_state,
+ store_stp_state);
+
+static ssize_t show_priority(struct class_device *d,
+ char *buf)
+{
+#if 0
+ struct datapath *dp = dp_dev_get_dp(to_net_dev(d));
+ return sprintf(buf, "%d\n",
+ (br->bridge_id.prio[0] << 8) | br->bridge_id.prio[1]);
+#else
+ return sprintf(buf, "%d\n", 0);
+#endif
+}
+
+static void set_priority(struct datapath *dp, unsigned long val)
+{
+#if 0
+ br_stp_set_bridge_priority(br, (u16) val);
+#else
+ printk("%s: xxx attempt to set_priority()\n", dp->netdev->name);
+#endif
+}
+
+static ssize_t store_priority(struct class_device *d,
+ const char *buf, size_t len)
+{
+ return store_bridge_parm(d, buf, len, set_priority);
+}
+static BRC_DEVICE_ATTR(priority, S_IRUGO | S_IWUSR, show_priority, store_priority);
+
+static ssize_t show_root_id(struct class_device *d,
+ char *buf)
+{
+#if 0
+ return br_show_bridge_id(buf, &to_bridge(d)->designated_root);
+#else
+ return sprintf(buf, "0000.010203040506\n");
+#endif
+}
+static BRC_DEVICE_ATTR(root_id, S_IRUGO, show_root_id, NULL);
+
+static ssize_t show_bridge_id(struct class_device *d,
+ char *buf)
+{
+ struct datapath *dp = dp_dev_get_dp(to_net_dev(d));
+ const unsigned char *addr = dp->netdev->dev_addr;
+
+ /* xxx Do we need a lock of some sort? */
+ return sprintf(buf, "%.2x%.2x.%.2x%.2x%.2x%.2x%.2x%.2x\n",
+ 0, 0, addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
+}
+static BRC_DEVICE_ATTR(bridge_id, S_IRUGO, show_bridge_id, NULL);
+
+static ssize_t show_root_port(struct class_device *d,
+ char *buf)
+{
+#if 0
+ return sprintf(buf, "%d\n", to_bridge(d)->root_port);
+#else
+ return sprintf(buf, "%d\n", 0);
+#endif
+}
+static BRC_DEVICE_ATTR(root_port, S_IRUGO, show_root_port, NULL);
+
+static ssize_t show_root_path_cost(struct class_device *d,
+ char *buf)
+{
+#if 0
+ return sprintf(buf, "%d\n", to_bridge(d)->root_path_cost);
+#else
+ return sprintf(buf, "%d\n", 0);
+#endif
+}
+static BRC_DEVICE_ATTR(root_path_cost, S_IRUGO, show_root_path_cost, NULL);
+
+static ssize_t show_topology_change(struct class_device *d,
+ char *buf)
+{
+#if 0
+ return sprintf(buf, "%d\n", to_bridge(d)->topology_change);
+#else
+ return sprintf(buf, "%d\n", 0);
+#endif
+}
+static BRC_DEVICE_ATTR(topology_change, S_IRUGO, show_topology_change, NULL);
+
+static ssize_t show_topology_change_detected(struct class_device *d,
+ char *buf)
+{
+#if 0
+ struct datapath *dp = dp_dev_get_dp(to_net_dev(d));
+ return sprintf(buf, "%d\n", br->topology_change_detected);
+#else
+ return sprintf(buf, "%d\n", 0);
+#endif
+}
+static BRC_DEVICE_ATTR(topology_change_detected, S_IRUGO,
+ show_topology_change_detected, NULL);
+
+static ssize_t show_hello_timer(struct class_device *d,
+ char *buf)
+{
+#if 0
+ struct datapath *dp = dp_dev_get_dp(to_net_dev(d));
+ return sprintf(buf, "%ld\n", br_timer_value(&br->hello_timer));
+#else
+ return sprintf(buf, "%d\n", 0);
+#endif
+}
+static BRC_DEVICE_ATTR(hello_timer, S_IRUGO, show_hello_timer, NULL);
+
+static ssize_t show_tcn_timer(struct class_device *d,
+ char *buf)
+{
+#if 0
+ struct datapath *dp = dp_dev_get_dp(to_net_dev(d));
+ return sprintf(buf, "%ld\n", br_timer_value(&br->tcn_timer));
+#else
+ return sprintf(buf, "%d\n", 0);
+#endif
+}
+static BRC_DEVICE_ATTR(tcn_timer, S_IRUGO, show_tcn_timer, NULL);
+
+static ssize_t show_topology_change_timer(struct class_device *d,
+ char *buf)
+{
+#if 0
+ struct datapath *dp = dp_dev_get_dp(to_net_dev(d));
+ return sprintf(buf, "%ld\n", br_timer_value(&br->topology_change_timer));
+#else
+ return sprintf(buf, "%d\n", 0);
+#endif
+}
+static BRC_DEVICE_ATTR(topology_change_timer, S_IRUGO, show_topology_change_timer,
+ NULL);
+
+static ssize_t show_gc_timer(struct class_device *d,
+ char *buf)
+{
+#if 0
+ struct datapath *dp = dp_dev_get_dp(to_net_dev(d));
+ return sprintf(buf, "%ld\n", br_timer_value(&br->gc_timer));
+#else
+ return sprintf(buf, "%d\n", 0);
+#endif
+}
+static BRC_DEVICE_ATTR(gc_timer, S_IRUGO, show_gc_timer, NULL);
+
+static ssize_t show_group_addr(struct class_device *d,
+ char *buf)
+{
+#if 0
+ struct datapath *dp = dp_dev_get_dp(to_net_dev(d));
+ return sprintf(buf, "%x:%x:%x:%x:%x:%x\n",
+ br->group_addr[0], br->group_addr[1],
+ br->group_addr[2], br->group_addr[3],
+ br->group_addr[4], br->group_addr[5]);
+#else
+ return sprintf(buf, "00:01:02:03:04:05\n");
+#endif
+}
+
+static ssize_t store_group_addr(struct class_device *d,
+ const char *buf, size_t len)
+{
+ struct datapath *dp = dp_dev_get_dp(to_net_dev(d));
+#if 0
+ unsigned new_addr[6];
+ int i;
+
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ if (sscanf(buf, "%x:%x:%x:%x:%x:%x",
+ &new_addr[0], &new_addr[1], &new_addr[2],
+ &new_addr[3], &new_addr[4], &new_addr[5]) != 6)
+ return -EINVAL;
+
+ /* Must be 01:80:c2:00:00:0X */
+ for (i = 0; i < 5; i++)
+ if (new_addr[i] != br_group_address[i])
+ return -EINVAL;
+
+ if (new_addr[5] & ~0xf)
+ return -EINVAL;
+
+ if (new_addr[5] == 1 /* 802.3x Pause address */
+ || new_addr[5] == 2 /* 802.3ad Slow protocols */
+ || new_addr[5] == 3) /* 802.1X PAE address */
+ return -EINVAL;
+
+ spin_lock_bh(&br->lock);
+ for (i = 0; i < 6; i++)
+ br->group_addr[i] = new_addr[i];
+ spin_unlock_bh(&br->lock);
+#else
+ printk("%s: xxx attempt to store_group_addr()\n", dp->netdev->name);
+#endif
+ return len;
+}
+
+static BRC_DEVICE_ATTR(group_addr, S_IRUGO | S_IWUSR,
+ show_group_addr, store_group_addr);
+
+static struct attribute *bridge_attrs[] = {
+ &class_device_attr_forward_delay.attr,
+ &class_device_attr_hello_time.attr,
+ &class_device_attr_max_age.attr,
+ &class_device_attr_ageing_time.attr,
+ &class_device_attr_stp_state.attr,
+ &class_device_attr_priority.attr,
+ &class_device_attr_bridge_id.attr,
+ &class_device_attr_root_id.attr,
+ &class_device_attr_root_path_cost.attr,
+ &class_device_attr_root_port.attr,
+ &class_device_attr_topology_change.attr,
+ &class_device_attr_topology_change_detected.attr,
+ &class_device_attr_hello_timer.attr,
+ &class_device_attr_tcn_timer.attr,
+ &class_device_attr_topology_change_timer.attr,
+ &class_device_attr_gc_timer.attr,
+ &class_device_attr_group_addr.attr,
+ NULL
+};
+
+static struct attribute_group bridge_group = {
+ .name = SYSFS_BRIDGE_ATTR,
+ .attrs = bridge_attrs,
+};
+
+/*
+ * Add entries in sysfs onto the existing network class device
+ * for the bridge.
+ * Adds a attribute group "bridge" containing tuning parameters.
+ * Sub directory to hold links to interfaces.
+ *
+ * Note: the ifobj exists only to be a subdirectory
+ * to hold links. The ifobj exists in the same data structure
+ * as its parent the bridge so reference counting works.
+ */
+int brc_sysfs_add_dp(struct datapath *dp)
+{
+ struct net_device *dev = dp->netdev;
+ struct kobject *kobj = to_kobj(dev);
+ int err;
+
+ err = sysfs_create_group(kobj, &bridge_group);
+ if (err) {
+ pr_info("%s: can't create group %s/%s\n",
+ __func__, dev->name, bridge_group.name);
+ goto out1;
+ }
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
+ kobject_set_name(&dp->ifobj, SYSFS_BRIDGE_PORT_SUBDIR);
+ dp->ifobj.ktype = NULL;
+ dp->ifobj.kset = NULL;
+ dp->ifobj.parent = kobj;
+
+ err = kobject_register(&dp->ifobj);
+ if (err) {
+ pr_info("%s: can't add kobject (directory) %s/%s\n",
+ __FUNCTION__, dev->name, dp->ifobj.name);
+ goto out2;
+ }
+#else
+ br->ifobj = kobject_create_and_add(SYSFS_BRIDGE_PORT_SUBDIR, kobj);
+ if (!br->ifobj) {
+ pr_info("%s: can't add kobject (directory) %s/%s\n",
+ __func__, dev->name, SYSFS_BRIDGE_PORT_SUBDIR);
+ goto out2;
+ }
+#endif
+ return 0;
+
+ out2:
+ sysfs_remove_group(to_kobj(dev), &bridge_group);
+ out1:
+ return err;
+}
+
+int brc_sysfs_del_dp(struct datapath *dp)
+{
+ struct net_device *dev = dp->netdev;
+ struct kobject *kobj = to_kobj(dev);
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
+ kobject_unregister(&dp->ifobj);
+#else
+ kobject_put(dp->ifobj);
+#endif
+ sysfs_remove_group(kobj, &bridge_group);
+
+ return 0;
+}
+#endif /* Only support 2.6.18 */
diff --git a/datapath/brc_sysfs_if.c b/datapath/brc_sysfs_if.c
new file mode 100644
index 0000000..cf1412f
--- /dev/null
+++ b/datapath/brc_sysfs_if.c
@@ -0,0 +1,335 @@
+#include
+#if LINUX_VERSION_CODE == KERNEL_VERSION(2,6,18)
+
+/*
+ * Sysfs attributes of bridge ports for OpenFlow
+ *
+ * This has been shamelessly copied from the kernel sources.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "brc_sysfs.h"
+#include "datapath.h"
+
+struct brport_attribute {
+ struct attribute attr;
+ ssize_t (*show)(struct net_bridge_port *, char *);
+ ssize_t (*store)(struct net_bridge_port *, unsigned long);
+};
+
+#define BRPORT_ATTR(_name,_mode,_show,_store) \
+struct brport_attribute brport_attr_##_name = { \
+ .attr = {.name = __stringify(_name), \
+ .mode = _mode, \
+ .owner = THIS_MODULE, }, \
+ .show = _show, \
+ .store = _store, \
+};
+
+static ssize_t show_path_cost(struct net_bridge_port *p, char *buf)
+{
+#if 0
+ return sprintf(buf, "%d\n", p->path_cost);
+#else
+ return sprintf(buf, "%d\n", 0);
+#endif
+}
+static ssize_t store_path_cost(struct net_bridge_port *p, unsigned long v)
+{
+#if 0
+ br_stp_set_path_cost(p, v);
+#endif
+ return 0;
+}
+static BRPORT_ATTR(path_cost, S_IRUGO | S_IWUSR,
+ show_path_cost, store_path_cost);
+
+static ssize_t show_priority(struct net_bridge_port *p, char *buf)
+{
+#if 0
+ return sprintf(buf, "%d\n", p->priority);
+#else
+ return sprintf(buf, "%d\n", 0);
+#endif
+}
+static ssize_t store_priority(struct net_bridge_port *p, unsigned long v)
+{
+#if 0
+ if (v >= (1<<(16-BR_PORT_BITS)))
+ return -ERANGE;
+ br_stp_set_port_priority(p, v);
+#endif
+ return 0;
+}
+static BRPORT_ATTR(priority, S_IRUGO | S_IWUSR,
+ show_priority, store_priority);
+
+static ssize_t show_designated_root(struct net_bridge_port *p, char *buf)
+{
+#if 0
+ return br_show_bridge_id(buf, &p->designated_root);
+#else
+ return sprintf(buf, "0000.010203040506\n");
+#endif
+}
+static BRPORT_ATTR(designated_root, S_IRUGO, show_designated_root, NULL);
+
+static ssize_t show_designated_bridge(struct net_bridge_port *p, char *buf)
+{
+#if 0
+ return br_show_bridge_id(buf, &p->designated_bridge);
+#else
+ return sprintf(buf, "0000.060504030201\n");
+#endif
+}
+static BRPORT_ATTR(designated_bridge, S_IRUGO, show_designated_bridge, NULL);
+
+static ssize_t show_designated_port(struct net_bridge_port *p, char *buf)
+{
+#if 0
+ return sprintf(buf, "%d\n", p->designated_port);
+#else
+ return sprintf(buf, "%d\n", 0);
+#endif
+}
+static BRPORT_ATTR(designated_port, S_IRUGO, show_designated_port, NULL);
+
+static ssize_t show_designated_cost(struct net_bridge_port *p, char *buf)
+{
+#if 0
+ return sprintf(buf, "%d\n", p->designated_cost);
+#else
+ return sprintf(buf, "%d\n", 0);
+#endif
+}
+static BRPORT_ATTR(designated_cost, S_IRUGO, show_designated_cost, NULL);
+
+static ssize_t show_port_id(struct net_bridge_port *p, char *buf)
+{
+#if 0
+ return sprintf(buf, "0x%x\n", p->port_id);
+#else
+ return sprintf(buf, "0x%x\n", 0);
+#endif
+}
+static BRPORT_ATTR(port_id, S_IRUGO, show_port_id, NULL);
+
+static ssize_t show_port_no(struct net_bridge_port *p, char *buf)
+{
+ return sprintf(buf, "0x%x\n", p->port_no);
+}
+
+static BRPORT_ATTR(port_no, S_IRUGO, show_port_no, NULL);
+
+static ssize_t show_change_ack(struct net_bridge_port *p, char *buf)
+{
+#if 0
+ return sprintf(buf, "%d\n", p->topology_change_ack);
+#else
+ return sprintf(buf, "%d\n", 0);
+#endif
+}
+static BRPORT_ATTR(change_ack, S_IRUGO, show_change_ack, NULL);
+
+static ssize_t show_config_pending(struct net_bridge_port *p, char *buf)
+{
+#if 0
+ return sprintf(buf, "%d\n", p->config_pending);
+#else
+ return sprintf(buf, "%d\n", 0);
+#endif
+}
+static BRPORT_ATTR(config_pending, S_IRUGO, show_config_pending, NULL);
+
+static ssize_t show_port_state(struct net_bridge_port *p, char *buf)
+{
+#if 0
+ return sprintf(buf, "%d\n", p->state);
+#else
+ return sprintf(buf, "%d\n", 0);
+#endif
+}
+static BRPORT_ATTR(state, S_IRUGO, show_port_state, NULL);
+
+static ssize_t show_message_age_timer(struct net_bridge_port *p,
+ char *buf)
+{
+#if 0
+ return sprintf(buf, "%ld\n", br_timer_value(&p->message_age_timer));
+#else
+ return sprintf(buf, "%d\n", 0);
+#endif
+}
+static BRPORT_ATTR(message_age_timer, S_IRUGO, show_message_age_timer, NULL);
+
+static ssize_t show_forward_delay_timer(struct net_bridge_port *p,
+ char *buf)
+{
+#if 0
+ return sprintf(buf, "%ld\n", br_timer_value(&p->forward_delay_timer));
+#else
+ return sprintf(buf, "%d\n", 0);
+#endif
+}
+static BRPORT_ATTR(forward_delay_timer, S_IRUGO, show_forward_delay_timer, NULL);
+
+static ssize_t show_hold_timer(struct net_bridge_port *p,
+ char *buf)
+{
+#if 0
+ return sprintf(buf, "%ld\n", br_timer_value(&p->hold_timer));
+#else
+ return sprintf(buf, "%d\n", 0);
+#endif
+}
+static BRPORT_ATTR(hold_timer, S_IRUGO, show_hold_timer, NULL);
+
+static struct brport_attribute *brport_attrs[] = {
+ &brport_attr_path_cost,
+ &brport_attr_priority,
+ &brport_attr_port_id,
+ &brport_attr_port_no,
+ &brport_attr_designated_root,
+ &brport_attr_designated_bridge,
+ &brport_attr_designated_port,
+ &brport_attr_designated_cost,
+ &brport_attr_state,
+ &brport_attr_change_ack,
+ &brport_attr_config_pending,
+ &brport_attr_message_age_timer,
+ &brport_attr_forward_delay_timer,
+ &brport_attr_hold_timer,
+ NULL
+};
+
+#define to_brport_attr(_at) container_of(_at, struct brport_attribute, attr)
+#define to_brport(obj) container_of(obj, struct net_bridge_port, kobj)
+
+static ssize_t brport_show(struct kobject * kobj,
+ struct attribute * attr, char * buf)
+{
+ struct brport_attribute * brport_attr = to_brport_attr(attr);
+ struct net_bridge_port * p = to_brport(kobj);
+
+ return brport_attr->show(p, buf);
+}
+
+static ssize_t brport_store(struct kobject * kobj,
+ struct attribute * attr,
+ const char * buf, size_t count)
+{
+ struct net_bridge_port * p = to_brport(kobj);
+#if 0
+ struct brport_attribute * brport_attr = to_brport_attr(attr);
+ char *endp;
+ unsigned long val;
+#endif
+ ssize_t ret = -EINVAL;
+
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+#if 0
+ val = simple_strtoul(buf, &endp, 0);
+ if (endp != buf) {
+ rtnl_lock();
+ if (p->dev && p->br && brport_attr->store) {
+ spin_lock_bh(&p->br->lock);
+ ret = brport_attr->store(p, val);
+ spin_unlock_bh(&p->br->lock);
+ if (ret == 0)
+ ret = count;
+ }
+ rtnl_unlock();
+ }
+#else
+ printk("%s: xxx writing port parms not supported yet!\n",
+ p->dp->netdev->name);
+#endif
+ return ret;
+}
+
+struct sysfs_ops brport_sysfs_ops = {
+ .show = brport_show,
+ .store = brport_store,
+};
+
+static void release_nbp(struct kobject *kobj)
+{
+ struct net_bridge_port *p
+ = container_of(kobj, struct net_bridge_port, kobj);
+ kfree(p);
+}
+
+struct kobj_type brport_ktype = {
+ .sysfs_ops = &brport_sysfs_ops,
+ .release = release_nbp
+};
+
+/*
+ * Add sysfs entries to ethernet device added to a bridge.
+ * Creates a brport subdirectory with bridge attributes.
+ * Puts symlink in bridge's brport subdirectory
+ */
+int brc_sysfs_add_if(struct net_bridge_port *p)
+{
+ struct datapath *dp = p->dp;
+ struct brport_attribute **a;
+ int err;
+
+ kobject_init(&p->kobj);
+ kobject_set_name(&p->kobj, SYSFS_BRIDGE_PORT_ATTR);
+ p->kobj.ktype = &brport_ktype;
+ p->kobj.kset = NULL;
+ p->kobj.parent = &(p->dev->class_dev.kobj);
+
+ err = kobject_add(&p->kobj);
+ if (err)
+ goto err_put;
+
+ err = sysfs_create_link(&p->kobj, &dp->netdev->class_dev.kobj,
+ SYSFS_BRIDGE_PORT_LINK);
+ if (err)
+ goto err_del;
+
+ for (a = brport_attrs; *a; ++a) {
+ err = sysfs_create_file(&p->kobj, &((*a)->attr));
+ if (err)
+ goto err_del;
+ }
+
+ err = sysfs_create_link(&dp->ifobj, &p->kobj, p->dev->name);
+ if (err)
+ goto err_del;
+
+ kobject_uevent(&p->kobj, KOBJ_ADD);
+
+ return err;
+
+err_del:
+ kobject_del(&p->kobj);
+err_put:
+ kobject_put(&p->kobj);
+ return err;
+}
+
+int brc_sysfs_del_if(struct net_bridge_port *p)
+{
+ struct net_device *dev = p->dev;
+
+ kobject_uevent(&p->kobj, KOBJ_REMOVE);
+ kobject_del(&p->kobj);
+
+ dev_put(dev);
+
+ kobject_put(&p->kobj);
+
+ return 0;
+}
+#endif /* Only support 2.6.18 */
diff --git a/datapath/brcompat.c b/datapath/brcompat.c
new file mode 100644
index 0000000..5b86a70
--- /dev/null
+++ b/datapath/brcompat.c
@@ -0,0 +1,504 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "compat.h"
+#include "openflow/openflow-netlink.h"
+#include "openflow/brcompat-netlink.h"
+#include "brc_sysfs.h"
+#include "datapath.h"
+#include "dp_dev.h"
+
+static struct genl_family brc_genl_family;
+static struct genl_multicast_group brc_mc_group;
+
+/* Completion for vswitchd to notify the ioctl that the operation
+ * completed. */
+static DECLARE_COMPLETION(dp_act_done);
+
+/* Time to wait for vswitchd to respond to a datapath action (in
+ * milliseconds) */
+#define DP_ACT_TIMEOUT 5000
+
+/* Positive errno as a result of a datapath action. Calls that make
+ * use of this variable are serialized by the br_ioctl_mutex. */
+static int dp_act_err;
+
+int brc_send_dp_add_del(const char *dp_name, int add);
+int brc_send_port_add_del(struct net_device *dev, struct net_device *port,
+ int add);
+
+
+static int
+get_dp_ifindices(int *indices, int num)
+{
+ int i, index = 0;
+
+ rcu_read_lock();
+ for (i=0; i < DP_MAX && index < num; i++) {
+ struct datapath *dp = dp_get_by_idx(i);
+ if (!dp)
+ continue;
+ indices[index++] = dp->netdev->ifindex;
+ }
+ rcu_read_unlock();
+
+ return index;
+}
+
+static void
+get_port_ifindices(struct datapath *dp, int *ifindices, int num)
+{
+ struct net_bridge_port *p;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu (p, &dp->port_list, node) {
+ if (p->port_no < num)
+ ifindices[p->port_no] = p->dev->ifindex;
+ }
+ rcu_read_unlock();
+}
+
+/* Legacy deviceless bridge ioctl's. Called with br_ioctl_mutex. */
+static int
+old_deviceless(void __user *uarg)
+{
+ unsigned long args[3];
+
+ if (copy_from_user(args, uarg, sizeof(args)))
+ return -EFAULT;
+
+ switch (args[0]) {
+ case BRCTL_GET_BRIDGES: {
+ int *indices;
+ int ret = 0;
+
+ if (args[2] >= 2048)
+ return -ENOMEM;
+
+ indices = kcalloc(args[2], sizeof(int), GFP_KERNEL);
+ if (indices == NULL)
+ return -ENOMEM;
+
+ args[2] = get_dp_ifindices(indices, args[2]);
+
+ ret = copy_to_user((void __user *)args[1], indices,
+ args[2]*sizeof(int)) ? -EFAULT : args[2];
+
+ kfree(indices);
+ return ret;
+ }
+
+ case BRCTL_DEL_BRIDGE:
+ case BRCTL_ADD_BRIDGE: {
+ char dp_name[IFNAMSIZ];
+
+ if (copy_from_user(dp_name, (void __user *)args[1], IFNAMSIZ))
+ return -EFAULT;
+
+ dp_name[IFNAMSIZ-1] = 0;
+ if (args[0] == BRCTL_ADD_BRIDGE)
+ return brc_send_dp_add_del(dp_name, 1);
+
+ return brc_send_dp_add_del(dp_name, 0);
+ }
+ }
+
+ return -EOPNOTSUPP;
+}
+
+/* Called with the br_ioctl_mutex. */
+static int
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,23)
+brc_ioctl_deviceless_stub(unsigned int cmd, void __user *uarg)
+#else
+brc_ioctl_deviceless_stub(struct net *net, unsigned int cmd, void __user *uarg)
+#endif
+{
+ switch (cmd) {
+ case SIOCGIFBR:
+ case SIOCSIFBR:
+ return old_deviceless(uarg);
+
+ case SIOCBRADDBR:
+ case SIOCBRDELBR: {
+ char dp_name[IFNAMSIZ];
+
+ if (copy_from_user(dp_name, uarg, IFNAMSIZ))
+ return -EFAULT;
+
+ dp_name[IFNAMSIZ-1] = 0;
+ return brc_send_dp_add_del(dp_name, cmd == SIOCBRADDBR);
+ }
+ }
+
+ return -EOPNOTSUPP;
+}
+
+/* Legacy ioctl's through SIOCDEVPRIVATE. Called with rtnl_lock. */
+static int
+old_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+ struct dp_dev *dp_dev = netdev_priv(dev);
+ struct datapath *dp = dp_dev->dp;
+ unsigned long args[4];
+
+ if (copy_from_user(args, rq->ifr_data, sizeof(args)))
+ return -EFAULT;
+
+ switch (args[0]) {
+ case BRCTL_ADD_IF:
+ case BRCTL_DEL_IF: {
+ struct net_device *port;
+ int err;
+
+ port = dev_get_by_index(&init_net, args[1]);
+ if (!port)
+ return -EINVAL;
+
+ err = brc_send_port_add_del(dev, port, args[0] == BRCTL_ADD_IF);
+ dev_put(port);
+ return err;
+ }
+
+ case BRCTL_GET_BRIDGE_INFO: {
+ struct __bridge_info b;
+ uint64_t id = 0;
+ int i;
+
+ memset(&b, 0, sizeof(struct __bridge_info));
+
+ for (i=0; idev_addr[i] << (8*(ETH_ALEN-1 - i));
+ b.bridge_id = cpu_to_be64(id);
+ b.stp_enabled = 0;
+
+ if (copy_to_user((void __user *)args[1], &b, sizeof(b)))
+ return -EFAULT;
+
+ return 0;
+ }
+
+ case BRCTL_GET_PORT_LIST: {
+ int num, *indices;
+
+ num = args[2];
+ if (num < 0)
+ return -EINVAL;
+ if (num == 0)
+ num = 256;
+ if (num > OFPP_MAX)
+ num = OFPP_MAX;
+
+ indices = kcalloc(num, sizeof(int), GFP_KERNEL);
+ if (indices == NULL)
+ return -ENOMEM;
+
+ get_port_ifindices(dp, indices, num);
+ if (copy_to_user((void __user *)args[1], indices, num*sizeof(int)))
+ num = -EFAULT;
+ kfree(indices);
+ return num;
+ }
+ }
+
+ return -EOPNOTSUPP;
+}
+
+/* Called with the rtnl_lock. */
+static int
+brc_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+ int err;
+
+ switch (cmd) {
+ case SIOCDEVPRIVATE:
+ err = old_dev_ioctl(dev, rq, cmd);
+ break;
+
+ case SIOCBRADDIF:
+ case SIOCBRDELIF: {
+ struct net_device *port;
+
+ port = dev_get_by_index(&init_net, rq->ifr_ifindex);
+ if (!port)
+ return -EINVAL;
+
+ err = brc_send_port_add_del(dev, port, cmd == SIOCBRADDIF);
+ dev_put(port);
+ break;
+ }
+
+ default:
+ err = -EOPNOTSUPP;
+ break;
+ }
+
+ return err;
+}
+
+
+static struct genl_family brc_genl_family = {
+ .id = GENL_ID_GENERATE,
+ .hdrsize = 0,
+ .name = BRC_GENL_FAMILY_NAME,
+ .version = 1,
+ .maxattr = BRC_GENL_A_MAX,
+};
+
+static int brc_genl_query(struct sk_buff *skb, struct genl_info *info)
+{
+ int err = -EINVAL;
+ struct sk_buff *ans_skb;
+ void *data;
+
+ ans_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!ans_skb)
+ return -ENOMEM;
+
+ data = genlmsg_put_reply(ans_skb, info, &brc_genl_family,
+ 0, BRC_GENL_C_QUERY_MC);
+ if (data == NULL) {
+ err = -ENOMEM;
+ goto err;
+ }
+ NLA_PUT_U32(ans_skb, BRC_GENL_A_MC_GROUP, brc_mc_group.id);
+
+ genlmsg_end(ans_skb, data);
+ return genlmsg_reply(ans_skb, info);
+
+err:
+nla_put_failure:
+ kfree_skb(ans_skb);
+ return err;
+}
+
+static struct genl_ops brc_genl_ops_query_dp = {
+ .cmd = BRC_GENL_C_QUERY_MC,
+ .flags = GENL_ADMIN_PERM, /* Requires CAP_NET_ADMIN privelege. */
+ .policy = NULL,
+ .doit = brc_genl_query,
+ .dumpit = NULL
+};
+
+/* Attribute policy: what each attribute may contain. */
+static struct nla_policy brc_genl_policy[BRC_GENL_A_MAX + 1] = {
+ [BRC_GENL_A_ERR_CODE] = { .type = NLA_U32 }
+};
+
+static int
+brc_genl_dp_result(struct sk_buff *skb, struct genl_info *info)
+{
+ dp_act_err = nla_get_u32(info->attrs[BRC_GENL_A_ERR_CODE]);
+ complete(&dp_act_done);
+
+ return 0;
+}
+
+static struct genl_ops brc_genl_ops_dp_result = {
+ .cmd = BRC_GENL_C_DP_RESULT,
+ .flags = GENL_ADMIN_PERM, /* Requires CAP_NET_ADMIN privelege. */
+ .policy = brc_genl_policy,
+ .doit = brc_genl_dp_result,
+ .dumpit = NULL
+};
+
+int brc_send_dp_add_del(const char *dp_name, int add)
+{
+ struct sk_buff *skb;
+ void *data;
+ int retval;
+
+ skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+ if (skb == NULL)
+ return -ENOMEM;
+
+ if (add)
+ data = genlmsg_put(skb, 0, 0, &brc_genl_family, 0,
+ BRC_GENL_C_DP_ADD);
+ else
+ data = genlmsg_put(skb, 0, 0, &brc_genl_family, 0,
+ BRC_GENL_C_DP_DEL);
+ if (!data)
+ goto err;
+
+ NLA_PUT_STRING(skb, BRC_GENL_A_DP_NAME, dp_name);
+
+ init_completion(&dp_act_done);
+
+ genlmsg_end(skb, data);
+ retval = genlmsg_multicast(skb, 0, brc_mc_group.id, GFP_KERNEL);
+ if (retval < 0)
+ return retval;
+
+ if (!wait_for_completion_timeout(&dp_act_done,
+ msecs_to_jiffies(DP_ACT_TIMEOUT)))
+ return -EIO;
+
+ /* The value is returned as a positive errno, so make it negative */
+ if (dp_act_err)
+ return -dp_act_err;
+
+ return 0;
+
+nla_put_failure:
+err:
+ kfree_skb(skb);
+ return -ENOMEM;
+}
+
+int brc_send_port_add_del(struct net_device *dev, struct net_device *port,
+ int add)
+{
+ struct dp_dev *dp_dev = netdev_priv(dev);
+ struct datapath *dp = dp_dev->dp;
+ struct sk_buff *skb;
+ void *data;
+
+ skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+ if (skb == NULL)
+ return -ENOMEM;
+
+ if (add) {
+ /* Only add the port if it's not attached to a datapath. */
+ if (port->br_port != NULL) {
+ kfree_skb(skb);
+ return -EBUSY;
+ }
+ data = genlmsg_put(skb, 0, 0, &brc_genl_family, 0,
+ BRC_GENL_C_PORT_ADD);
+ } else {
+ /* Only delete the port if it's attached to this datapath. */
+ if (port->br_port == NULL || port->br_port->dp != dp) {
+ kfree_skb(skb);
+ return -ENOENT;
+ }
+ data = genlmsg_put(skb, 0, 0, &brc_genl_family, 0,
+ BRC_GENL_C_PORT_DEL);
+ }
+ if (!data)
+ goto err;
+
+ NLA_PUT_STRING(skb, BRC_GENL_A_DP_NAME, dev->name);
+ NLA_PUT_STRING(skb, BRC_GENL_A_PORT_NAME, port->name);
+
+ genlmsg_end(skb, data);
+ return genlmsg_multicast(skb, 0, brc_mc_group.id, GFP_KERNEL);
+
+nla_put_failure:
+err:
+ kfree_skb(skb);
+ return -EINVAL;
+}
+
+int brc_add_dp(struct datapath *dp)
+{
+ if (!try_module_get(THIS_MODULE))
+ return -ENODEV;
+#if CONFIG_SYSFS
+ brc_sysfs_add_dp(dp);
+#endif
+
+ return 0;
+}
+
+int brc_del_dp(struct datapath *dp)
+{
+#if CONFIG_SYSFS
+ brc_sysfs_del_dp(dp);
+#endif
+ module_put(THIS_MODULE);
+
+ return 0;
+}
+
+static int
+__init brc_init(void)
+{
+ int i;
+ int err;
+
+ printk("OpenFlow Bridge Compatibility, built "__DATE__" "__TIME__"\n");
+
+ rcu_read_lock();
+ for (i=0; i
@@ -24,7 +25,8 @@ static int add_table(struct sw_chain *chain, struct sw_table *table)
if (table == NULL)
return -ENOMEM;
if (chain->n_tables >= CHAIN_MAX_TABLES) {
- printk("too many tables in chain\n");
+ printk(KERN_EMERG "%s: too many tables in chain\n",
+ chain->dp->netdev->name);
table->destroy(table);
return -ENOBUFS;
}
diff --git a/datapath/datapath.c b/datapath/datapath.c
index 28f6c4f..7dc55b7 100644
--- a/datapath/datapath.c
+++ b/datapath/datapath.c
@@ -1,6 +1,6 @@
/*
* Distributed under the terms of the GNU GPL version 2.
- * Copyright (c) 2007, 2008 The Board of Trustees of The Leland
+ * Copyright (c) 2007, 2008, 2009 The Board of Trustees of The Leland
* Stanford Junior University
*/
@@ -50,8 +50,8 @@
/* Strings to describe the manufacturer, hardware, and software. This data
* is queriable through the switch description stats message. */
-static char mfr_desc[DESC_STR_LEN] = "Nicira Networks, Inc.";
-static char hw_desc[DESC_STR_LEN] = "Reference Linux Kernel Module";
+static char mfr_desc[DESC_STR_LEN] = "Stanford University (developed by Nicira Networks, Inc.)";
+static char hw_desc[DESC_STR_LEN] = "Reference Data plane Kernel Extension for Linux";
static char sw_desc[DESC_STR_LEN] = VERSION BUILDNR;
static char serial_num[SERIAL_NUM_LEN] = "None";
@@ -68,6 +68,21 @@ MODULE_PARM(serial_num, "s");
#endif
+int (*dp_ioctl_hook)(struct net_device *dev, struct ifreq *rq, int cmd);
+EXPORT_SYMBOL(dp_ioctl_hook);
+
+int (*dp_add_dp_hook)(struct datapath *dp);
+EXPORT_SYMBOL(dp_add_dp_hook);
+
+int (*dp_del_dp_hook)(struct datapath *dp);
+EXPORT_SYMBOL(dp_del_dp_hook);
+
+int (*dp_add_if_hook)(struct net_bridge_port *p);
+EXPORT_SYMBOL(dp_add_if_hook);
+
+int (*dp_del_if_hook)(struct net_bridge_port *p);
+EXPORT_SYMBOL(dp_del_if_hook);
+
/* Number of milliseconds between runs of the maintenance thread. */
#define MAINT_SLEEP_MSECS 1000
@@ -76,16 +91,33 @@ MODULE_PARM(serial_num, "s");
#define MAX(X, Y) ((X) > (Y) ? (X) : (Y))
static struct genl_family dp_genl_family;
-static struct genl_multicast_group mc_group;
-/* It's hard to imagine wanting more than one datapath, but... */
-#define DP_MAX 32
+/*
+ * Datapath multicast groups.
+ *
+ * Really we want one multicast group per in-use datapath (or even more than
+ * one). Locking issues, however, mean that we can't allocate a multicast
+ * group at the point in the code where we we actually create a datapath[*], so
+ * we have to pre-allocate them. It's massive overkill to allocate DP_MAX of
+ * them in advance, since we will hardly ever actually create DP_MAX datapaths,
+ * so instead we allocate a few multicast groups at startup and choose one for
+ * each datapath by hashing its datapath index.
+ *
+ * [*] dp_genl_add, to add a new datapath, is called under the genl_lock
+ * mutex, and genl_register_mc_group, called to acquire a new multicast
+ * group ID, also acquires genl_lock, thus deadlock.
+ */
+#define N_MC_GROUPS 16 /* Must be power of 2. */
+static struct genl_multicast_group mc_groups[N_MC_GROUPS];
/* Datapaths. Protected on the read side by rcu_read_lock, on the write side
* by dp_mutex. dp_mutex is almost completely redundant with genl_mutex
* maintained by the Generic Netlink code, but the timeout path needs mutual
* exclusion too.
*
+ * dp_mutex nests inside the RTNL lock: if you need both you must take the RTNL
+ * lock first.
+ *
* It is safe to access the datapath and net_bridge_port structures with just
* dp_mutex.
*/
@@ -192,8 +224,9 @@ alloc_openflow_skb(struct datapath *dp, size_t openflow_len, uint8_t type,
if ((openflow_len + sizeof(struct ofp_header)) > UINT16_MAX) {
if (net_ratelimit())
- printk("alloc_openflow_skb: openflow message too large: %zu\n",
- openflow_len);
+ printk(KERN_ERR "%s: alloc_openflow_skb: openflow "
+ "message too large: %zu\n",
+ dp->netdev->name, openflow_len);
return NULL;
}
@@ -202,8 +235,6 @@ alloc_openflow_skb(struct datapath *dp, size_t openflow_len, uint8_t type,
genl_len += nla_total_size(openflow_len); /* DP_GENL_A_OPENFLOW */
skb = *pskb = genlmsg_new(genl_len, GFP_ATOMIC);
if (!skb) {
- if (net_ratelimit())
- printk("alloc_openflow_skb: genlmsg_new failed\n");
return NULL;
}
@@ -214,14 +245,22 @@ alloc_openflow_skb(struct datapath *dp, size_t openflow_len, uint8_t type,
return oh;
}
+/* Returns the ID of the multicast group used by datapath 'dp'. */
+static u32
+dp_mc_group(const struct datapath *dp)
+{
+ return mc_groups[dp->dp_idx & (N_MC_GROUPS - 1)].id;
+}
+
/* Sends 'skb' to 'sender' if it is nonnull, otherwise multicasts 'skb' to all
* listeners. */
static int
-send_openflow_skb(struct sk_buff *skb, const struct sender *sender)
+send_openflow_skb(const struct datapath *dp,
+ struct sk_buff *skb, const struct sender *sender)
{
return (sender
? genlmsg_unicast(skb, sender->pid)
- : genlmsg_multicast(skb, 0, mc_group.id, GFP_ATOMIC));
+ : genlmsg_multicast(skb, 0, dp_mc_group(dp), GFP_ATOMIC));
}
/* Retrieves the datapath id, which is the MAC address of the "of" device. */
@@ -237,34 +276,56 @@ uint64_t get_datapath_id(struct net_device *dev)
return id;
}
-/* Creates a new datapath numbered 'dp_idx'. Returns 0 for success or a
- * negative error code. */
-static int new_dp(int dp_idx)
+/* Find the first free datapath index. Return the index or -1 if a free
+ * index could not be found. */
+int gen_dp_idx(void)
+{
+ int i;
+
+ for (i=0; i= DP_MAX)
- return -EINVAL;
+ rtnl_lock();
+ mutex_lock(&dp_mutex);
+ if (dp_idx == -1)
+ dp_idx = gen_dp_idx();
+ err = -EINVAL;
+ if (dp_idx < 0 || dp_idx >= DP_MAX)
+ goto err_unlock;
+
+ err = -ENODEV;
if (!try_module_get(THIS_MODULE))
- return -ENODEV;
+ goto err_unlock;
/* Exit early if a datapath with that number already exists. */
- if (dps[dp_idx]) {
- err = -EEXIST;
- goto err_unlock;
- }
+ err = -EEXIST;
+ if (dps[dp_idx])
+ goto err_put;
err = -ENOMEM;
dp = kzalloc(sizeof *dp, GFP_KERNEL);
if (dp == NULL)
- goto err_unlock;
+ goto err_put;
dp->dp_idx = dp_idx;
- /* Setup our "of" device */
- err = dp_dev_setup(dp);
+ /* Setup our datapath device */
+ err = dp_dev_setup(dp, dp_name);
if (err)
goto err_free_dp;
@@ -287,6 +348,11 @@ static int new_dp(int dp_idx)
goto err_destroy_chain;
dps[dp_idx] = dp;
+ mutex_unlock(&dp_mutex);
+ rtnl_unlock();
+
+ if (dp_add_dp_hook)
+ dp_add_dp_hook(dp);
return 0;
@@ -298,9 +364,12 @@ err_destroy_dp_dev:
dp_dev_destroy(dp);
err_free_dp:
kfree(dp);
-err_unlock:
+err_put:
module_put(THIS_MODULE);
- return err;
+err_unlock:
+ mutex_unlock(&dp_mutex);
+ rtnl_unlock();
+ return err;
}
/* Find and return a free port number under 'dp'. */
@@ -313,6 +382,7 @@ static int find_portno(struct datapath *dp)
return -EXFULL;
}
+/* Called with RTNL lock and dp_mutex. */
static struct net_bridge_port *new_nbp(struct datapath *dp,
struct net_device *dev, int port_no)
{
@@ -325,15 +395,12 @@ static struct net_bridge_port *new_nbp(struct datapath *dp,
if (p == NULL)
return ERR_PTR(-ENOMEM);
- rtnl_lock();
dev_set_promiscuity(dev, 1);
- rtnl_unlock();
dev_hold(dev);
p->dp = dp;
p->dev = dev;
p->port_no = port_no;
spin_lock_init(&p->lock);
- INIT_WORK(&p->port_task, NULL);
if (port_no != OFPP_LOCAL)
rcu_assign_pointer(dev->br_port, p);
if (port_no < DP_MAX_PORTS)
@@ -343,6 +410,7 @@ static struct net_bridge_port *new_nbp(struct datapath *dp,
return p;
}
+/* Called with RTNL lock and dp_mutex. */
int add_switch_port(struct datapath *dp, struct net_device *dev)
{
struct net_bridge_port *p;
@@ -362,24 +430,30 @@ int add_switch_port(struct datapath *dp, struct net_device *dev)
init_port_status(p);
+ if (dp_add_if_hook)
+ dp_add_if_hook(p);
+
/* Notify the ctlpath that this port has been added */
dp_send_port_status(p, OFPPR_ADD);
return 0;
}
-/* Delete 'p' from switch. */
+/* Delete 'p' from switch.
+ * Called with RTNL lock and dp_mutex. */
int dp_del_switch_port(struct net_bridge_port *p)
{
#ifdef SUPPORT_SNAT
unsigned long flags;
#endif
+#if CONFIG_SYSFS
+ if ((p->port_no != OFPP_LOCAL) && dp_del_if_hook)
+ sysfs_remove_link(&p->dp->ifobj, p->dev->name);
+#endif
+
/* First drop references to device. */
- cancel_work_sync(&p->port_task);
- rtnl_lock();
dev_set_promiscuity(p->dev, -1);
- rtnl_unlock();
list_del_rcu(&p->node);
if (p->port_no != OFPP_LOCAL)
rcu_assign_pointer(p->dp->ports[p->port_no], NULL);
@@ -398,8 +472,12 @@ int dp_del_switch_port(struct net_bridge_port *p)
/* Notify the ctlpath that this port no longer exists */
dp_send_port_status(p, OFPPR_DELETE);
- dev_put(p->dev);
- kfree(p);
+ if ((p->port_no != OFPP_LOCAL) && dp_del_if_hook) {
+ dp_del_if_hook(p);
+ } else {
+ dev_put(p->dev);
+ kfree(p);
+ }
return 0;
}
@@ -408,11 +486,16 @@ static void del_dp(struct datapath *dp)
{
struct net_bridge_port *p, *n;
+ send_sig(SIGKILL, dp->dp_task, 0);
kthread_stop(dp->dp_task);
/* Drop references to DP. */
list_for_each_entry_safe (p, n, &dp->port_list, node)
dp_del_switch_port(p);
+
+ if (dp_del_dp_hook)
+ dp_del_dp_hook(dp);
+
rcu_assign_pointer(dps[dp->dp_idx], NULL);
/* Kill off local_port dev references from buffered packets that have
@@ -435,7 +518,8 @@ static int dp_maint_func(void *data)
{
struct datapath *dp = (struct datapath *) data;
- while (!kthread_should_stop()) {
+ allow_signal(SIGKILL);
+ while (!signal_pending(current)) {
#ifdef SUPPORT_SNAT
struct net_bridge_port *p;
@@ -450,7 +534,10 @@ static int dp_maint_func(void *data)
chain_timeout(dp->chain);
msleep_interruptible(MAINT_SLEEP_MSECS);
}
-
+ while (!kthread_should_stop()) {
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule();
+ }
return 0;
}
@@ -514,7 +601,7 @@ static void dp_frame_hook(struct sk_buff *skb)
static inline unsigned packet_length(const struct sk_buff *skb)
{
- int length = skb->len - ETH_HLEN;
+ unsigned length = skb->len - ETH_HLEN;
if (skb->protocol == htons(ETH_P_8021Q))
length -= VLAN_HLEN;
return length;
@@ -570,10 +657,8 @@ void dp_set_origin(struct datapath *dp, uint16_t in_port,
static int
dp_xmit_skb_finish(struct sk_buff *skb)
{
- /* The ip_fragment function does not copy the Ethernet header into
- * the newly generated frames, so put back the values stowed
- * earlier. */
- if (snat_copy_header(skb)) {
+ /* Copy back the Ethernet header that was stowed earlier. */
+ if (skb->protocol == htons(ETH_P_IP) && snat_copy_header(skb)) {
kfree_skb(skb);
return -EINVAL;
}
@@ -600,6 +685,11 @@ dp_xmit_skb(struct sk_buff *skb)
skb_pull(skb, ETH_HLEN);
+ /* The ip_fragment function does not copy the Ethernet header into
+ * the newly generated frames, so stow the original. */
+ if (skb->protocol == htons(ETH_P_IP))
+ snat_save_header(skb);
+
if (skb->protocol == htons(ETH_P_IP) &&
skb->len > skb->dev->mtu &&
!skb_is_gso(skb)) {
@@ -616,10 +706,12 @@ dp_xmit_skb(struct sk_buff *skb)
int
dp_xmit_skb(struct sk_buff *skb)
{
+ struct datapath *dp = skb->dev->br_port->dp;
int len = skb->len;
+
if (packet_length(skb) > skb->dev->mtu && !skb_is_gso(skb)) {
- printk("dropped over-mtu packet: %d > %d\n",
- packet_length(skb), skb->dev->mtu);
+ printk(KERN_WARNING "%s: dropped over-mtu packet: %d > %d\n",
+ dp->netdev->name, packet_length(skb), skb->dev->mtu);
kfree_skb(skb);
return -E2BIG;
}
@@ -642,7 +734,9 @@ int dp_output_port(struct datapath *dp, struct sk_buff *skb, int out_port,
* the skb. */
if (!skb->dev) {
if (net_ratelimit())
- printk("skb device not set forwarding to in_port\n");
+ printk(KERN_NOTICE "%s: skb device not set "
+ "forwarding to in_port\n",
+ dp->netdev->name);
kfree_skb(skb);
return -ESRCH;
}
@@ -663,8 +757,7 @@ int dp_output_port(struct datapath *dp, struct sk_buff *skb, int out_port,
return output_all(dp, skb, 0);
case OFPP_CONTROLLER:
- return dp_output_control(dp, skb, fwd_save_skb(skb), 0,
- OFPR_ACTION);
+ return dp_output_control(dp, skb, 0, OFPR_ACTION);
case OFPP_LOCAL: {
struct net_device *dev = dp->netdev;
@@ -682,7 +775,9 @@ int dp_output_port(struct datapath *dp, struct sk_buff *skb, int out_port,
/* To send to the input port, must use OFPP_IN_PORT */
kfree_skb(skb);
if (net_ratelimit())
- printk("can't directly forward to input port\n");
+ printk(KERN_NOTICE "%s: can't directly "
+ "forward to input port\n",
+ dp->netdev->name);
return -EINVAL;
}
if (p->config & OFPPC_NO_FWD && !ignore_no_fwd) {
@@ -700,27 +795,94 @@ int dp_output_port(struct datapath *dp, struct sk_buff *skb, int out_port,
bad_port:
kfree_skb(skb);
if (net_ratelimit())
- printk("can't forward to bad port %d\n", out_port);
+ printk(KERN_NOTICE "%s: can't forward to bad port %d\n",
+ dp->netdev->name, out_port);
return -ENOENT;
}
-/* Takes ownership of 'skb' and transmits it to 'dp''s control path. If
- * 'buffer_id' != -1, then only the first 64 bytes of 'skb' are sent;
- * otherwise, all of 'skb' is sent. 'reason' indicates why 'skb' is being
- * sent. 'max_len' sets the maximum number of bytes that the caller
- * wants to be sent; a value of 0 indicates the entire packet should be
- * sent. */
+#ifdef CONFIG_XEN
+/* This code is copied verbatim from net/dev/core.c in Xen's
+ * linux-2.6.18-92.1.10.el5.xs5.0.0.394.644. We can't call those functions
+ * directly because they aren't exported. */
+static int skb_pull_up_to(struct sk_buff *skb, void *ptr)
+{
+ if (ptr < (void *)skb->tail)
+ return 1;
+ if (__pskb_pull_tail(skb,
+ ptr - (void *)skb->data - skb_headlen(skb))) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+inline int skb_checksum_setup(struct sk_buff *skb)
+{
+ if (skb->proto_csum_blank) {
+ if (skb->protocol != htons(ETH_P_IP))
+ goto out;
+ if (!skb_pull_up_to(skb, skb->nh.iph + 1))
+ goto out;
+ skb->h.raw = (unsigned char *)skb->nh.iph + 4*skb->nh.iph->ihl;
+ switch (skb->nh.iph->protocol) {
+ case IPPROTO_TCP:
+ skb->csum = offsetof(struct tcphdr, check);
+ break;
+ case IPPROTO_UDP:
+ skb->csum = offsetof(struct udphdr, check);
+ break;
+ default:
+ if (net_ratelimit())
+ printk(KERN_ERR "Attempting to checksum a non-"
+ "TCP/UDP packet, dropping a protocol"
+ " %d packet", skb->nh.iph->protocol);
+ goto out;
+ }
+ if (!skb_pull_up_to(skb, skb->h.raw + skb->csum + 2))
+ goto out;
+ skb->ip_summed = CHECKSUM_HW;
+ skb->proto_csum_blank = 0;
+ }
+ return 0;
+out:
+ return -EPROTO;
+}
+#endif
+
+/* Takes ownership of 'skb' and transmits it to 'dp''s control path. 'reason'
+ * indicates why 'skb' is being sent. 'max_len' sets the maximum number of
+ * bytes that the caller wants to be sent; a value of 0 indicates the entire
+ * packet should be sent. */
int
dp_output_control(struct datapath *dp, struct sk_buff *skb,
- uint32_t buffer_id, size_t max_len, int reason)
+ size_t max_len, int reason)
{
/* FIXME? Can we avoid creating a new skbuff in the case where we
* forward the whole packet? */
struct sk_buff *f_skb;
struct ofp_packet_in *opi;
size_t fwd_len, opi_len;
+ uint32_t buffer_id;
int err;
+ WARN_ON_ONCE(skb_shared(skb));
+
+#ifdef CONFIG_XEN
+ /* If a checksum-deferred packet is forwarded to the controller,
+ * correct the pointers and checksum.
+ */
+ err = skb_checksum_setup(skb);
+ if (err)
+ goto out;
+ if (skb->ip_summed == CHECKSUM_HW) {
+ err = skb_checksum_help(skb, 0);
+ if (err)
+ goto out;
+ }
+#endif
+
+ buffer_id = fwd_save_skb(skb);
+
fwd_len = skb->len;
if ((buffer_id != (uint32_t) -1) && max_len)
fwd_len = min(fwd_len, max_len);
@@ -739,7 +901,7 @@ dp_output_control(struct datapath *dp, struct sk_buff *skb,
opi->reason = reason;
opi->pad = 0;
skb_copy_bits(skb, 0, opi->data, fwd_len);
- err = send_openflow_skb(f_skb, NULL);
+ err = send_openflow_skb(dp, f_skb, NULL);
out:
kfree_skb(skb);
@@ -894,7 +1056,7 @@ dp_send_features_reply(struct datapath *dp, const struct sender *sender)
/* Shrink to fit. */
ofr_len = sizeof(*ofr) + (sizeof(struct ofp_phy_port) * port_count);
resize_openflow_skb(skb, &ofr->header, ofr_len);
- return send_openflow_skb(skb, sender);
+ return send_openflow_skb(dp, skb, sender);
}
int
@@ -911,7 +1073,7 @@ dp_send_config_reply(struct datapath *dp, const struct sender *sender)
osc->flags = htons(dp->flags);
osc->miss_send_len = htons(dp->miss_send_len);
- return send_openflow_skb(skb, sender);
+ return send_openflow_skb(dp, skb, sender);
}
int
@@ -933,40 +1095,10 @@ dp_send_hello(struct datapath *dp, const struct sender *sender,
if (!reply)
return -ENOMEM;
- return send_openflow_skb(skb, sender);
+ return send_openflow_skb(dp, skb, sender);
}
}
-/* Callback function for a workqueue to disable an interface */
-static void
-down_port_cb(struct work_struct *work)
-{
- struct net_bridge_port *p = container_of(work, struct net_bridge_port,
- port_task);
-
- rtnl_lock();
- if (dev_change_flags(p->dev, p->dev->flags & ~IFF_UP) < 0)
- if (net_ratelimit())
- printk("problem bringing up port %s\n", p->dev->name);
- rtnl_unlock();
- p->config |= OFPPC_PORT_DOWN;
-}
-
-/* Callback function for a workqueue to enable an interface */
-static void
-up_port_cb(struct work_struct *work)
-{
- struct net_bridge_port *p = container_of(work, struct net_bridge_port,
- port_task);
-
- rtnl_lock();
- if (dev_change_flags(p->dev, p->dev->flags | IFF_UP) < 0)
- if (net_ratelimit())
- printk("problem bringing down port %s\n", p->dev->name);
- rtnl_unlock();
- p->config &= ~OFPPC_PORT_DOWN;
-}
-
int
dp_update_port_flags(struct datapath *dp, const struct ofp_port_mod *opm)
{
@@ -987,22 +1119,6 @@ dp_update_port_flags(struct datapath *dp, const struct ofp_port_mod *opm)
p->config &= ~config_mask;
p->config |= ntohl(opm->config) & config_mask;
}
-
- /* Modifying the status of an interface requires taking a lock
- * that cannot be done from here. For this reason, we use a shared
- * workqueue, which will cause it to be executed from a safer
- * context. */
- if (opm->mask & htonl(OFPPC_PORT_DOWN)) {
- if ((opm->config & htonl(OFPPC_PORT_DOWN))
- && (p->config & OFPPC_PORT_DOWN) == 0) {
- PREPARE_WORK(&p->port_task, down_port_cb);
- schedule_work(&p->port_task);
- } else if ((opm->config & htonl(OFPPC_PORT_DOWN)) == 0
- && (p->config & OFPPC_PORT_DOWN)) {
- PREPARE_WORK(&p->port_task, up_port_cb);
- schedule_work(&p->port_task);
- }
- }
spin_unlock_irqrestore(&p->lock, flags);
return 0;
@@ -1043,7 +1159,7 @@ dp_send_port_status(struct net_bridge_port *p, uint8_t status)
memset(ops->pad, 0, sizeof ops->pad);
fill_port_desc(p, &ops->desc);
- return send_openflow_skb(skb, NULL);
+ return send_openflow_skb(p->dp, skb, NULL);
}
/* Convert jiffies_64 to milliseconds. */
@@ -1092,7 +1208,7 @@ dp_send_flow_end(struct datapath *dp, struct sw_flow *flow,
nfe->packet_count = cpu_to_be64(flow->packet_count);
nfe->byte_count = cpu_to_be64(flow->byte_count);
- return send_openflow_skb(skb, NULL);
+ return send_openflow_skb(dp, skb, NULL);
}
EXPORT_SYMBOL(dp_send_flow_end);
@@ -1113,7 +1229,7 @@ dp_send_error_msg(struct datapath *dp, const struct sender *sender,
oem->code = htons(code);
memcpy(oem->data, data, len);
- return send_openflow_skb(skb, sender);
+ return send_openflow_skb(dp, skb, sender);
}
int
@@ -1129,7 +1245,7 @@ dp_send_echo_reply(struct datapath *dp, const struct sender *sender,
return -ENOMEM;
memcpy(reply + 1, rq + 1, ntohs(rq->length) - sizeof *rq);
- return send_openflow_skb(skb, sender);
+ return send_openflow_skb(dp, skb, sender);
}
/* Generic Netlink interface.
@@ -1150,16 +1266,25 @@ static struct genl_family dp_genl_family = {
/* Attribute policy: what each attribute may contain. */
static struct nla_policy dp_genl_policy[DP_GENL_A_MAX + 1] = {
[DP_GENL_A_DP_IDX] = { .type = NLA_U32 },
+ [DP_GENL_A_DP_NAME] = { .type = NLA_NUL_STRING },
[DP_GENL_A_MC_GROUP] = { .type = NLA_U32 },
- [DP_GENL_A_PORTNAME] = { .type = NLA_STRING }
+ [DP_GENL_A_PORTNAME] = { .type = NLA_NUL_STRING }
};
static int dp_genl_add(struct sk_buff *skb, struct genl_info *info)
{
- if (!info->attrs[DP_GENL_A_DP_IDX])
+ int dp_idx = info->attrs[DP_GENL_A_DP_IDX] ?
+ nla_get_u32(info->attrs[DP_GENL_A_DP_IDX]) : -1;
+ const char *dp_name = info->attrs[DP_GENL_A_DP_NAME] ?
+ nla_data(info->attrs[DP_GENL_A_DP_NAME]) : NULL;
+
+ if (VERIFY_NUL_STRING(info->attrs[DP_GENL_A_DP_NAME]))
return -EINVAL;
- return new_dp(nla_get_u32(info->attrs[DP_GENL_A_DP_IDX]));
+ if ((dp_idx == -1) && (!dp_name))
+ return -EINVAL;
+
+ return new_dp(dp_idx, dp_name);
}
static struct genl_ops dp_genl_ops_add_dp = {
@@ -1170,28 +1295,75 @@ static struct genl_ops dp_genl_ops_add_dp = {
.dumpit = NULL,
};
-struct datapath *dp_get(int dp_idx)
+/* Must be called with rcu_read_lock or dp_mutex. */
+struct datapath *dp_get_by_idx(int dp_idx)
{
if (dp_idx < 0 || dp_idx >= DP_MAX)
return NULL;
return rcu_dereference(dps[dp_idx]);
}
+EXPORT_SYMBOL(dp_get_by_idx);
+
+/* Must be called with rcu_read_lock or dp_mutex. */
+struct datapath *dp_get_by_name(const char *dp_name)
+{
+ int i;
+ for (i=0; inetdev->name, dp_name))
+ return dp;
+ }
+ return NULL;
+}
+
+/* Must be called with rcu_read_lock or dp_mutex. */
+static struct datapath *
+lookup_dp(struct genl_info *info)
+{
+ int dp_idx = info->attrs[DP_GENL_A_DP_IDX] ?
+ nla_get_u32(info->attrs[DP_GENL_A_DP_IDX]) : -1;
+ const char *dp_name = info->attrs[DP_GENL_A_DP_NAME] ?
+ nla_data(info->attrs[DP_GENL_A_DP_NAME]) : NULL;
+
+ if (VERIFY_NUL_STRING(info->attrs[DP_GENL_A_DP_NAME]))
+ return ERR_PTR(-EINVAL);
+
+ if (dp_idx != -1) {
+ struct datapath *dp = dp_get_by_idx(dp_idx);
+ if (!dp)
+ return ERR_PTR(-ENOENT);
+ else if (dp_name && strcmp(dp->netdev->name, dp_name))
+ return ERR_PTR(-EINVAL);
+ else
+ return dp;
+ } else if (dp_name) {
+ struct datapath *dp = dp_get_by_name(dp_name);
+ return dp ? dp : ERR_PTR(-ENOENT);
+ } else {
+ return ERR_PTR(-EINVAL);
+ }
+}
static int dp_genl_del(struct sk_buff *skb, struct genl_info *info)
{
+ struct net_device *dev = NULL;
struct datapath *dp;
int err;
- if (!info->attrs[DP_GENL_A_DP_IDX])
- return -EINVAL;
-
- dp = dp_get(nla_get_u32(info->attrs[DP_GENL_A_DP_IDX]));
- if (!dp)
- err = -ENOENT;
+ rtnl_lock();
+ mutex_lock(&dp_mutex);
+ dp = lookup_dp(info);
+ if (IS_ERR(dp))
+ err = PTR_ERR(dp);
else {
+ dev = dp->netdev;
del_dp(dp);
err = 0;
}
+ mutex_unlock(&dp_mutex);
+ rtnl_unlock();
+ if (dev)
+ free_netdev(dev);
return err;
}
@@ -1204,30 +1376,18 @@ static struct genl_ops dp_genl_ops_del_dp = {
};
/* Queries a datapath for related information. Currently the only relevant
- * information is the datapath's multicast group ID. Really we want one
- * multicast group per datapath, but because of locking issues[*] we can't
- * easily get one. Thus, every datapath will currently return the same
- * global multicast group ID, but in the future it would be nice to fix that.
- *
- * [*] dp_genl_add, to add a new datapath, is called under the genl_lock
- * mutex, and genl_register_mc_group, called to acquire a new multicast
- * group ID, also acquires genl_lock, thus deadlock.
- */
+ * information is the datapath's multicast group ID, datapath ID, and
+ * datapath device name. */
static int dp_genl_query(struct sk_buff *skb, struct genl_info *info)
{
struct datapath *dp;
struct sk_buff *ans_skb = NULL;
- int dp_idx;
- int err = -ENOMEM;
-
- if (!info->attrs[DP_GENL_A_DP_IDX])
- return -EINVAL;
+ int err;
rcu_read_lock();
- dp_idx = nla_get_u32((info->attrs[DP_GENL_A_DP_IDX]));
- dp = dp_get(dp_idx);
- if (!dp)
- err = -ENOENT;
+ dp = lookup_dp(info);
+ if (IS_ERR(dp))
+ err = PTR_ERR(dp);
else {
void *data;
ans_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
@@ -1235,14 +1395,14 @@ static int dp_genl_query(struct sk_buff *skb, struct genl_info *info)
err = -ENOMEM;
goto err;
}
+ err = -ENOMEM;
data = genlmsg_put_reply(ans_skb, info, &dp_genl_family,
0, DP_GENL_C_QUERY_DP);
- if (data == NULL) {
- err = -ENOMEM;
+ if (data == NULL)
goto err;
- }
- NLA_PUT_U32(ans_skb, DP_GENL_A_DP_IDX, dp_idx);
- NLA_PUT_U32(ans_skb, DP_GENL_A_MC_GROUP, mc_group.id);
+ NLA_PUT_U32(ans_skb, DP_GENL_A_DP_IDX, dp->dp_idx);
+ NLA_PUT_STRING(ans_skb, DP_GENL_A_DP_NAME, dp->netdev->name);
+ NLA_PUT_U32(ans_skb, DP_GENL_A_MC_GROUP, dp_mc_group(dp));
genlmsg_end(ans_skb, data);
err = genlmsg_reply(ans_skb, info);
@@ -1269,14 +1429,18 @@ static int dp_genl_add_del_port(struct sk_buff *skb, struct genl_info *info)
struct net_device *port;
int err;
- if (!info->attrs[DP_GENL_A_DP_IDX] || !info->attrs[DP_GENL_A_PORTNAME])
+ if (!info->attrs[DP_GENL_A_PORTNAME] ||
+ VERIFY_NUL_STRING(info->attrs[DP_GENL_A_PORTNAME]))
return -EINVAL;
+ rtnl_lock();
+ mutex_lock(&dp_mutex);
+
/* Get datapath. */
- dp = dp_get(nla_get_u32(info->attrs[DP_GENL_A_DP_IDX]));
- if (!dp) {
- err = -ENOENT;
- goto out;
+ dp = lookup_dp(info);
+ if (IS_ERR(dp)) {
+ err = PTR_ERR(dp);
+ goto out_unlock;
}
/* Get interface to add/remove. */
@@ -1284,7 +1448,7 @@ static int dp_genl_add_del_port(struct sk_buff *skb, struct genl_info *info)
nla_data(info->attrs[DP_GENL_A_PORTNAME]));
if (!port) {
err = -ENOENT;
- goto out;
+ goto out_unlock;
}
/* Execute operation. */
@@ -1300,7 +1464,9 @@ static int dp_genl_add_del_port(struct sk_buff *skb, struct genl_info *info)
out_put:
dev_put(port);
-out:
+out_unlock:
+ mutex_unlock(&dp_mutex);
+ rtnl_unlock();
return err;
}
@@ -1331,7 +1497,7 @@ static int dp_genl_openflow(struct sk_buff *skb, struct genl_info *info)
if (!info->attrs[DP_GENL_A_DP_IDX] || !va)
return -EINVAL;
- dp = dp_get(nla_get_u32(info->attrs[DP_GENL_A_DP_IDX]));
+ dp = dp_get_by_idx(nla_get_u32(info->attrs[DP_GENL_A_DP_IDX]));
if (!dp)
return -ENOENT;
@@ -1724,7 +1890,7 @@ dp_genl_openflow_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
if (!attrs[DP_GENL_A_DP_IDX])
return -EINVAL;
dp_idx = nla_get_u16(attrs[DP_GENL_A_DP_IDX]);
- dp = dp_get(dp_idx);
+ dp = dp_get_by_idx(dp_idx);
if (!dp)
return -ENOENT;
@@ -1772,7 +1938,7 @@ dp_genl_openflow_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
dp_idx = cb->args[1];
s = &stats[cb->args[2]];
- dp = dp_get(dp_idx);
+ dp = dp_get_by_idx(dp_idx);
if (!dp)
return -ENOENT;
} else {
@@ -1851,16 +2017,19 @@ static int dp_init_netlink(void)
goto err_unregister;
}
- strcpy(mc_group.name, "openflow");
- err = genl_register_mc_group(&dp_genl_family, &mc_group);
- if (err < 0)
- goto err_unregister;
+ for (i = 0; i < N_MC_GROUPS; i++) {
+ snprintf(mc_groups[i].name, sizeof mc_groups[i].name,
+ "openflow%d", i);
+ err = genl_register_mc_group(&dp_genl_family, &mc_groups[i]);
+ if (err < 0)
+ goto err_unregister;
+ }
return 0;
err_unregister:
genl_unregister_family(&dp_genl_family);
- return err;
+ return err;
}
static void dp_uninit_netlink(void)
@@ -1921,6 +2090,12 @@ static int __init dp_init(void)
if (err)
goto error_unreg_notifier;
+ dp_ioctl_hook = NULL;
+ dp_add_dp_hook = NULL;
+ dp_del_dp_hook = NULL;
+ dp_add_if_hook = NULL;
+ dp_del_if_hook = NULL;
+
/* Check if better descriptions of the switch are available than the
* defaults. */
set_desc();
diff --git a/datapath/datapath.h b/datapath/datapath.h
index 867e502..babf10e 100644
--- a/datapath/datapath.h
+++ b/datapath/datapath.h
@@ -16,12 +16,6 @@
#define NL_FLOWS_PER_MESSAGE 100
-#ifdef NDEBUG
-#define dprintk(x...)
-#else
-#define dprintk(x...) printk(x)
-#endif
-
/* Capabilities supported by this implementation. */
#define OFP_SUPPORTED_CAPABILITIES ( OFPC_FLOW_STATS \
| OFPC_TABLE_STATS \
@@ -61,6 +55,8 @@ struct datapath {
/* Flag controlling whether Flow End messages are generated. */
uint8_t send_flow_end;
+ struct kobject ifobj;
+
/* Switch ports. */
struct net_bridge_port *ports[DP_MAX_PORTS];
struct net_bridge_port *local_port; /* OFPP_LOCAL port. */
@@ -79,22 +75,26 @@ struct net_bridge_port {
u32 config; /* Some subset of OFPPC_* flags. */
u32 state; /* Some subset of OFPPS_* flags. */
spinlock_t lock;
- struct work_struct port_task;
struct datapath *dp;
struct net_device *dev;
+ struct kobject kobj;
struct snat_conf *snat; /* Only set if SNAT is configured for this port. */
struct list_head node; /* Element in datapath.ports. */
};
extern struct mutex dp_mutex;
extern struct notifier_block dp_device_notifier;
+extern int (*dp_ioctl_hook)(struct net_device *dev, struct ifreq *rq, int cmd);
+extern int (*dp_add_dp_hook)(struct datapath *dp);
+extern int (*dp_del_dp_hook)(struct datapath *dp);
+extern int (*dp_add_if_hook)(struct net_bridge_port *p);
+extern int (*dp_del_if_hook)(struct net_bridge_port *p);
int dp_del_switch_port(struct net_bridge_port *);
int dp_xmit_skb(struct sk_buff *skb);
int dp_output_port(struct datapath *, struct sk_buff *, int out_port,
int ignore_no_fwd);
-int dp_output_control(struct datapath *, struct sk_buff *, uint32_t,
- size_t, int);
+int dp_output_control(struct datapath *, struct sk_buff *, size_t, int);
void dp_set_origin(struct datapath *, uint16_t, struct sk_buff *);
int dp_send_features_reply(struct datapath *, const struct sender *);
int dp_send_config_reply(struct datapath *, const struct sender *);
@@ -110,6 +110,7 @@ int dp_send_hello(struct datapath *, const struct sender *,
const struct ofp_header *);
/* Should hold at least RCU read lock when calling */
-struct datapath *dp_get(int dp_idx);
+struct datapath *dp_get_by_idx(int dp_idx);
+struct datapath *dp_get_by_name(const char *dp_name);
#endif /* datapath.h */
diff --git a/datapath/dp_act.c b/datapath/dp_act.c
index cd2cb56..dcc93b9 100644
--- a/datapath/dp_act.c
+++ b/datapath/dp_act.c
@@ -1,12 +1,11 @@
/*
* Distributed under the terms of the GNU GPL version 2.
- * Copyright (c) 2007, 2008 The Board of Trustees of The Leland
+ * Copyright (c) 2007, 2008, 2009 The Board of Trustees of The Leland
* Stanford Junior University
*/
/* Functions for executing OpenFlow actions. */
-#include "compat.h"
#include
#include
#include
@@ -42,8 +41,7 @@ do_output(struct datapath *dp, struct sk_buff *skb, size_t max_len,
return -ENOMEM;
return (likely(out_port != OFPP_CONTROLLER)
? dp_output_port(dp, skb, out_port, ignore_no_fwd)
- : dp_output_control(dp, skb, fwd_save_skb(skb),
- max_len, OFPR_ACTION));
+ : dp_output_control(dp, skb, max_len, OFPR_ACTION));
}
@@ -313,7 +311,7 @@ static uint16_t
validate_ofpat(struct datapath *dp, const struct sw_flow_key *key,
const struct ofp_action_header *ah, uint16_t type, uint16_t len)
{
- int ret = ACT_VALIDATION_OK;
+ uint16_t ret = ACT_VALIDATION_OK;
const struct openflow_action *act = &of_actions[type];
if ((len < act->min_size) || (len > act->max_size))
@@ -400,16 +398,8 @@ execute_ofpat(struct sk_buff *skb, struct sw_flow_key *key,
const struct ofp_action_header *ah, uint16_t type)
{
const struct openflow_action *act = &of_actions[type];
-
- if (act->execute) {
- if (!make_writable(&skb)) {
- if (net_ratelimit())
- printk("make_writable failed\n");
- return skb;
- }
+ if (act->execute && make_writable(&skb))
skb = act->execute(skb, key, ah);
- }
-
return skb;
}
@@ -420,6 +410,7 @@ execute_vendor(struct sk_buff *skb, const struct sw_flow_key *key,
{
struct ofp_action_vendor_header *avh
= (struct ofp_action_vendor_header *)ah;
+ struct datapath *dp = skb->dev->br_port->dp;
/* NB: If changes need to be made to the packet, a call should be
* made to make_writable or its equivalent first. */
@@ -432,8 +423,9 @@ execute_vendor(struct sk_buff *skb, const struct sw_flow_key *key,
default:
/* This should not be possible due to prior validation. */
if (net_ratelimit())
- printk("attempt to execute action with unknown vendor: %#x\n",
- ntohl(avh->vendor));
+ printk(KERN_WARNING "%s: attempt to execute action "
+ "with unknown vendor: %#x\n",
+ dp->netdev->name, ntohl(avh->vendor));
break;
}
@@ -483,7 +475,9 @@ void execute_actions(struct datapath *dp, struct sk_buff *skb,
if (!skb) {
if (net_ratelimit())
- printk("execute_actions lost skb\n");
+ printk(KERN_WARNING "%s: "
+ "execute_actions lost skb\n",
+ dp->netdev->name);
return;
}
}
diff --git a/datapath/dp_dev.c b/datapath/dp_dev.c
index ec36361..58a2469 100644
--- a/datapath/dp_dev.c
+++ b/datapath/dp_dev.c
@@ -1,27 +1,27 @@
#include
#include
#include
+#include
#include
#include
#include
-#include
#include "datapath.h"
+#include "dp_dev.h"
#include "forward.h"
-struct dp_dev {
- struct net_device_stats stats;
- struct datapath *dp;
- struct sk_buff_head xmit_queue;
- struct work_struct xmit_work;
-};
-
static struct dp_dev *dp_dev_priv(struct net_device *netdev)
{
return netdev_priv(netdev);
}
+struct datapath *dp_dev_get_dp(struct net_device *netdev)
+{
+ return dp_dev_priv(netdev)->dp;
+}
+EXPORT_SYMBOL(dp_dev_get_dp);
+
static struct net_device_stats *dp_dev_get_stats(struct net_device *netdev)
{
struct dp_dev *dp_dev = dp_dev_priv(netdev);
@@ -122,53 +122,32 @@ static int dp_dev_stop(struct net_device *netdev)
return 0;
}
-/* Check if the DMI UUID contains a Nicira mac address that should be
- * used for this interface. The UUID is assumed to be RFC 4122
- * compliant. */
-static void
-set_uuid_mac(struct net_device *netdev)
+static void dp_getinfo(struct net_device *dev, struct ethtool_drvinfo *info)
{
- const char *uuid = dmi_get_system_info(DMI_PRODUCT_UUID);
- const char *uptr;
- uint8_t mac[ETH_ALEN];
- int i;
-
- if (!uuid || *uuid == '\0' || strlen(uuid) != 36)
- return;
-
- /* We are only interested version 1 UUIDs, since the last six bytes
- * are an IEEE 802 MAC address. */
- if (uuid[14] != '1')
- return;
-
- /* Pull out the embedded MAC address. The kernel's sscanf doesn't
- * support field widths on hex digits, so we use this hack. */
- uptr = uuid + 24;
- for (i=0; idev_addr, mac, ETH_ALEN);
+ strcpy(info->driver, "openflow");
+ sprintf(info->version, "0x%d", OFP_VERSION);
+ strcpy(info->fw_version, "N/A");
+ strcpy(info->bus_info, "N/A");
}
+static struct ethtool_ops dp_ethtool_ops = {
+ .get_drvinfo = dp_getinfo,
+ .get_link = ethtool_op_get_link,
+ .get_sg = ethtool_op_get_sg,
+ .get_tx_csum = ethtool_op_get_tx_csum,
+ .get_tso = ethtool_op_get_tso,
+};
+
static void
do_setup(struct net_device *netdev)
{
ether_setup(netdev);
+ netdev->do_ioctl = dp_ioctl_hook;
netdev->get_stats = dp_dev_get_stats;
netdev->hard_start_xmit = dp_dev_xmit;
netdev->open = dp_dev_open;
+ SET_ETHTOOL_OPS(netdev, &dp_ethtool_ops);
netdev->stop = dp_dev_stop;
netdev->tx_queue_len = 100;
netdev->set_mac_address = dp_dev_mac_addr;
@@ -186,30 +165,34 @@ do_setup(struct net_device *netdev)
netdev->dev_addr[3] |= 0xc0;
}
-
-int dp_dev_setup(struct datapath *dp)
+/* Create a datapath device associated with 'dp'. If 'dp_name' is null,
+ * the device name will be of the form 'of'.
+ *
+ * Called with RTNL lock and dp_mutex.*/
+int dp_dev_setup(struct datapath *dp, const char *dp_name)
{
struct dp_dev *dp_dev;
struct net_device *netdev;
- char of_name[8];
+ char dev_name[IFNAMSIZ];
int err;
- snprintf(of_name, sizeof of_name, "of%d", dp->dp_idx);
- netdev = alloc_netdev(sizeof(struct dp_dev), of_name, do_setup);
+ if (dp_name) {
+ if (strlen(dp_name) >= IFNAMSIZ)
+ return -EINVAL;
+ strncpy(dev_name, dp_name, sizeof(dev_name));
+ } else
+ snprintf(dev_name, sizeof dev_name, "of%d", dp->dp_idx);
+
+ netdev = alloc_netdev(sizeof(struct dp_dev), dev_name, do_setup);
if (!netdev)
return -ENOMEM;
- err = register_netdev(netdev);
+ err = register_netdevice(netdev);
if (err) {
free_netdev(netdev);
return err;
}
- /* For "of0", we check the DMI UUID to see if a Nicira mac address
- * is available to use instead of the random one just generated. */
- if (dp->dp_idx == 0)
- set_uuid_mac(netdev);
-
dp_dev = dp_dev_priv(netdev);
dp_dev->dp = dp;
skb_queue_head_init(&dp_dev->xmit_queue);
@@ -218,6 +201,7 @@ int dp_dev_setup(struct datapath *dp)
return 0;
}
+/* Called with RTNL lock and dp_mutex.*/
void dp_dev_destroy(struct datapath *dp)
{
struct dp_dev *dp_dev = dp_dev_priv(dp->netdev);
@@ -225,8 +209,7 @@ void dp_dev_destroy(struct datapath *dp)
netif_tx_disable(dp->netdev);
synchronize_net();
skb_queue_purge(&dp_dev->xmit_queue);
- unregister_netdev(dp->netdev);
- free_netdev(dp->netdev);
+ unregister_netdevice(dp->netdev);
}
int is_dp_dev(struct net_device *netdev)
diff --git a/datapath/dp_dev.h b/datapath/dp_dev.h
index 2d4453b..26f7248 100644
--- a/datapath/dp_dev.h
+++ b/datapath/dp_dev.h
@@ -1,9 +1,17 @@
#ifndef DP_DEV_H
#define DP_DEV_H 1
-int dp_dev_setup(struct datapath *);
+struct dp_dev {
+ struct net_device_stats stats;
+ struct datapath *dp;
+ struct sk_buff_head xmit_queue;
+ struct work_struct xmit_work;
+};
+
+int dp_dev_setup(struct datapath *, const char *);
void dp_dev_destroy(struct datapath *);
int dp_dev_recv(struct net_device *, struct sk_buff *);
int is_dp_dev(struct net_device *);
+struct datapath *dp_dev_get_dp(struct net_device *);
#endif /* dp_dev.h */
diff --git a/datapath/dp_notify.c b/datapath/dp_notify.c
index 9bf491d..54c8840 100644
--- a/datapath/dp_notify.c
+++ b/datapath/dp_notify.c
@@ -1,6 +1,6 @@
/*
* Distributed under the terms of the GNU GPL version 2.
- * Copyright (c) 2007, 2008 The Board of Trustees of The Leland
+ * Copyright (c) 2007, 2008, 2009 The Board of Trustees of The Leland
* Stanford Junior University
*/
@@ -17,7 +17,6 @@ static int dp_device_event(struct notifier_block *unused, unsigned long event,
struct net_device *dev = ptr;
struct net_bridge_port *p = dev->br_port;
unsigned long int flags;
- uint32_t orig_state, orig_config;
/* Check if monitored port */
@@ -25,36 +24,17 @@ static int dp_device_event(struct notifier_block *unused, unsigned long event,
return NOTIFY_DONE;
spin_lock_irqsave(&p->lock, flags);
- orig_state = p->state;
- orig_config = p->config;
-
switch (event) {
- case NETDEV_CHANGE:
- if (netif_carrier_ok(p->dev))
- p->state &= ~OFPPS_LINK_DOWN;
- else
- p->state |= OFPPS_LINK_DOWN;
- break;
-
- case NETDEV_DOWN:
- p->config |= OFPPC_PORT_DOWN;
- break;
-
- case NETDEV_UP:
- p->config &= ~OFPPC_PORT_DOWN;
- break;
-
case NETDEV_UNREGISTER:
spin_unlock_irqrestore(&p->lock, flags);
+ mutex_lock(&dp_mutex);
dp_del_switch_port(p);
+ mutex_unlock(&dp_mutex);
return NOTIFY_DONE;
break;
}
spin_unlock_irqrestore(&p->lock, flags);
- if ((orig_state != p->state) || (orig_config != p->config))
- dp_send_port_status(p, OFPPR_MODIFY);
-
return NOTIFY_DONE;
}
diff --git a/datapath/forward.c b/datapath/forward.c
index 8b9b15a..14b8897 100644
--- a/datapath/forward.c
+++ b/datapath/forward.c
@@ -1,6 +1,6 @@
/*
* Distributed under the terms of the GNU GPL version 2.
- * Copyright (c) 2007, 2008 The Board of Trustees of The Leland
+ * Copyright (c) 2007, 2008, 2009 The Board of Trustees of The Leland
* Stanford Junior University
*/
@@ -71,8 +71,7 @@ void fwd_port_input(struct sw_chain *chain, struct sk_buff *skb,
WARN_ON_ONCE(skb_shared(skb));
WARN_ON_ONCE(skb->destructor);
if (run_flow_through_tables(chain, skb, p))
- dp_output_control(chain->dp, skb, fwd_save_skb(skb),
- chain->dp->miss_send_len,
+ dp_output_control(chain->dp, skb, chain->dp->miss_send_len,
OFPR_NO_MATCH);
}
@@ -128,7 +127,8 @@ recv_packet_out(struct sw_chain *chain, const struct sender *sender,
if (actions_len > (ntohs(opo->header.length) - sizeof *opo)) {
if (net_ratelimit())
- printk("message too short for number of actions\n");
+ printk(KERN_NOTICE "%s: message too short for number "
+ "of actions\n", chain->dp->netdev->name);
return -EINVAL;
}
@@ -348,7 +348,8 @@ recv_vendor(struct sw_chain *chain, const struct sender *sender,
return nx_recv_msg(chain, sender, msg);
default:
if (net_ratelimit())
- printk("unknown vendor: 0x%x\n", ntohl(ovh->vendor));
+ printk(KERN_NOTICE "%s: unknown vendor: 0x%x\n",
+ chain->dp->netdev->name, ntohl(ovh->vendor));
dp_send_error_msg(chain->dp, sender, OFPET_BAD_REQUEST,
OFPBRC_BAD_VENDOR, msg, ntohs(ovh->header.length));
return -EINVAL;
@@ -427,8 +428,9 @@ fwd_control_input(struct sw_chain *chain, const struct sender *sender,
}
if (ntohs(oh->length) != length) {
if (net_ratelimit())
- printk("received message length wrong: %d/%d\n",
- ntohs(oh->length), length);
+ printk(KERN_NOTICE "%s: received message length "
+ "wrong: %d/%d\n", chain->dp->netdev->name,
+ ntohs(oh->length), length);
return -EINVAL;
}
@@ -517,8 +519,9 @@ static struct sk_buff *retrieve_skb(uint32_t id)
skb = p->skb;
p->skb = NULL;
} else {
- printk("cookie mismatch: %x != %x\n",
- id >> PKT_BUFFER_BITS, p->cookie);
+ if (net_ratelimit())
+ printk(KERN_NOTICE "cookie mismatch: %x != %x\n",
+ id >> PKT_BUFFER_BITS, p->cookie);
}
spin_unlock_irqrestore(&buffer_lock, flags);
diff --git a/datapath/hwtable_dummy/Modules.mk b/datapath/hwtable_dummy/Modules.mk
index 2a3014d..08e56b1 100644
--- a/datapath/hwtable_dummy/Modules.mk
+++ b/datapath/hwtable_dummy/Modules.mk
@@ -1,7 +1,7 @@
# Specify the module to build.
-all_modules += hwtable_dummy
+build_modules += ofdatapath_dummy
+dist_modules += ofdatapath_dummy
# Specify the source files that comprise the module.
-hwtable_dummy_sources = \
+ofdatapath_dummy_sources = \
hwtable_dummy/hwtable_dummy.c
-
diff --git a/datapath/hwtable_nf2/Modules.mk b/datapath/hwtable_nf2/Modules.mk
index bf47aab..3567491 100644
--- a/datapath/hwtable_nf2/Modules.mk
+++ b/datapath/hwtable_nf2/Modules.mk
@@ -1,16 +1,19 @@
# Specify the module to build.
-all_modules += hwtable_nf2
+build_modules += ofdatapath_netfpga
+dist_modules += ofdatapath_netfpga
# Specify the source files that comprise the module.
-hwtable_nf2_sources = \
+ofdatapath_netfpga_sources = \
hwtable_nf2/nf2_flowtable.c \
+ hwtable_nf2/nf2_procfs.c \
hwtable_nf2/nf2_openflow.c \
- hwtable_nf2/nf2_lib.c
+ hwtable_nf2/nf2_lib.c
-hwtable_nf2_headers = \
+ofdatapath_netfpga_headers = \
hwtable_nf2/nf2.h \
hwtable_nf2/nf2_reg.h \
hwtable_nf2/nf2_hwapi.h \
hwtable_nf2/nf2_flowtable.h \
+ hwtable_nf2/nf2_procfs.h \
hwtable_nf2/nf2_openflow.h \
hwtable_nf2/nf2_lib.h
diff --git a/datapath/hwtable_nf2/README b/datapath/hwtable_nf2/README
index b1c0008..8f2671a 100644
--- a/datapath/hwtable_nf2/README
+++ b/datapath/hwtable_nf2/README
@@ -12,7 +12,7 @@ Installation
----------------------------------------
First build OpenFlow ensuring you include the directive to build the
-hwtable_nf2 hardware table in your configure statement:
+openflow_netfpga2 hardware table in your configure statement:
% ./configure --with-l26=/lib/modules/`uname -r`/build
--enable-hw-tables=nf2
@@ -45,7 +45,7 @@ Install the OpenFlow kernel module:
Install the NetFPGA Hardware Table kernel module:
- % insmod /datapath/linux-2.6/hwtable_nf2_mod.ko
+ % insmod /datapath/linux-2.6/openflow_netfpga2_mod.ko
Create an OpenFlow datapath:
diff --git a/datapath/hwtable_nf2/nf2_flowtable.c b/datapath/hwtable_nf2/nf2_flowtable.c
index 5df470b..03183c1 100644
--- a/datapath/hwtable_nf2/nf2_flowtable.c
+++ b/datapath/hwtable_nf2/nf2_flowtable.c
@@ -47,6 +47,7 @@
#include "hwtable_nf2/nf2_flowtable.h"
#include "hwtable_nf2/nf2_openflow.h"
#include "hwtable_nf2/nf2_lib.h"
+#include "hwtable_nf2/nf2_procfs.h"
struct nf2_flowtable {
struct sw_table flowtab;
@@ -390,12 +391,14 @@ nf2_create_flowtable(void)
static int __init
nf2_startup(void)
{
+ nf2_create_procfs();
return chain_set_hw_hook(nf2_create_flowtable, THIS_MODULE);
}
static void
nf2_cleanup(void)
{
+ nf2_remove_procfs();
chain_clear_hw_hook();
}
diff --git a/datapath/hwtable_nf2/nf2_lib.c b/datapath/hwtable_nf2/nf2_lib.c
index c2c37b4..cf79d55 100644
--- a/datapath/hwtable_nf2/nf2_lib.c
+++ b/datapath/hwtable_nf2/nf2_lib.c
@@ -35,6 +35,7 @@
#include "crc32.h"
#include "flow.h"
#include "table.h"
+#include "compat26.h"
#include "hwtable_nf2/nf2.h"
#include "hwtable_nf2/nf2_reg.h"
diff --git a/datapath/hwtable_nf2/nf2_openflow.c b/datapath/hwtable_nf2/nf2_openflow.c
index 00b9597..952665b 100644
--- a/datapath/hwtable_nf2/nf2_openflow.c
+++ b/datapath/hwtable_nf2/nf2_openflow.c
@@ -50,6 +50,7 @@ static void log_mask(nf2_of_mask_wrap *);
static void log_mask_raw(nf2_of_mask_wrap *);
static void log_action(nf2_of_action_wrap *);
static void log_action_raw(nf2_of_action_wrap *);
+static struct nf2_all_ports_info_addr *nf2_get_all_ports_info_addr(void);
static void
log_entry(nf2_of_entry_wrap *entry)
@@ -600,3 +601,208 @@ nf2_get_missed_count(struct net_device *dev)
return ((unsigned long int)(val_wild + val_exact));
}
+
+struct nf2_device_info *
+nf2_get_device_info(struct net_device *dev)
+{
+ struct nf2_device_info *nf2devinfo;
+ int i;
+
+ nf2devinfo = kzalloc(sizeof(struct nf2_device_info), GFP_KERNEL);
+ if (nf2devinfo == NULL)
+ return NULL;
+
+ // Read the version and revision
+ nf2k_reg_read(dev, DEVICE_ID_REG, &(nf2devinfo->nf2_device_id));
+ nf2k_reg_read(dev, DEVICE_REVISION_REG, &(nf2devinfo->nf2_device_rev));
+
+ // Read the design name string
+ for (i = 0; i < (DEVICE_STR_LEN / 4) - 2; i++) {
+ nf2k_reg_read(dev, DEVICE_STR_REG + i * 4,
+ (uint32_t *)(nf2devinfo->nf2_device_str + i * 4));
+ *(uint32_t *)(nf2devinfo->nf2_device_str + i * 4)
+ = ntohl(*(uint32_t *)
+ (nf2devinfo->nf2_device_str + i * 4));
+ }
+ nf2devinfo->nf2_device_str[DEVICE_STR_LEN - 1] = '\0';
+
+ return nf2devinfo;
+}
+
+struct nf2_match_info *
+nf2_get_match_info(struct net_device *dev)
+{
+ struct nf2_match_info *nf2matchinfo;
+
+ nf2matchinfo = kzalloc(sizeof(struct nf2_match_info), GFP_KERNEL);
+ if (nf2matchinfo == NULL)
+ return NULL;
+
+ nf2k_reg_read(dev, OPENFLOW_LOOKUP_WILDCARD_MISSES_REG,
+ &(nf2matchinfo->wildcard_misses));
+ nf2k_reg_read(dev, OPENFLOW_LOOKUP_WILDCARD_HITS_REG,
+ &(nf2matchinfo->wildcard_hits));
+ nf2k_reg_read(dev, OPENFLOW_LOOKUP_EXACT_MISSES_REG,
+ &(nf2matchinfo->exact_misses));
+ nf2k_reg_read(dev, OPENFLOW_LOOKUP_EXACT_HITS_REG,
+ &(nf2matchinfo->exact_hits));
+
+ return nf2matchinfo;
+}
+
+static struct nf2_all_ports_info_addr *
+nf2_get_all_ports_info_addr(void)
+{
+ struct nf2_all_ports_info_addr *nf2addr;
+
+ nf2addr = kzalloc(sizeof(struct nf2_all_ports_info_addr), GFP_KERNEL);
+ if (nf2addr == NULL)
+ return NULL;
+
+ nf2addr->rx_q_num_pkts_stored_reg[0]
+ = RX_QUEUE_0_NUM_PKTS_STORED_REG;
+ nf2addr->rx_q_num_pkts_dropped_full_reg[0]
+ = RX_QUEUE_0_NUM_PKTS_DROPPED_FULL_REG;
+ nf2addr->rx_q_num_pkts_dropped_bad_reg[0]
+ = RX_QUEUE_0_NUM_PKTS_DROPPED_BAD_REG;
+ nf2addr->rx_q_num_words_pushed_reg[0]
+ = RX_QUEUE_0_NUM_WORDS_PUSHED_REG;
+ nf2addr->rx_q_num_bytes_pushed_reg[0]
+ = RX_QUEUE_0_NUM_BYTES_PUSHED_REG;
+ nf2addr->rx_q_num_pkts_dequeued_reg[0]
+ = RX_QUEUE_0_NUM_PKTS_DEQUEUED_REG;
+ nf2addr->rx_q_num_pkts_in_queue_reg[0]
+ = RX_QUEUE_0_NUM_PKTS_IN_QUEUE_REG;
+ nf2addr->tx_q_num_pkts_in_queue_reg[0]
+ = TX_QUEUE_0_NUM_PKTS_IN_QUEUE_REG;
+ nf2addr->tx_q_num_pkts_sent_reg[0]
+ = TX_QUEUE_0_NUM_PKTS_SENT_REG;
+ nf2addr->tx_q_num_words_pushed_reg[0]
+ = TX_QUEUE_0_NUM_WORDS_PUSHED_REG;
+ nf2addr->tx_q_num_bytes_pushed_reg[0]
+ = TX_QUEUE_0_NUM_BYTES_PUSHED_REG;
+ nf2addr->tx_q_num_pkts_enqueued_reg[0]
+ = TX_QUEUE_0_NUM_PKTS_ENQUEUED_REG;
+
+ nf2addr->rx_q_num_pkts_stored_reg[1]
+ = RX_QUEUE_1_NUM_PKTS_STORED_REG;
+ nf2addr->rx_q_num_pkts_dropped_full_reg[1]
+ = RX_QUEUE_1_NUM_PKTS_DROPPED_FULL_REG;
+ nf2addr->rx_q_num_pkts_dropped_bad_reg[1]
+ = RX_QUEUE_1_NUM_PKTS_DROPPED_BAD_REG;
+ nf2addr->rx_q_num_words_pushed_reg[1]
+ = RX_QUEUE_1_NUM_WORDS_PUSHED_REG;
+ nf2addr->rx_q_num_bytes_pushed_reg[1]
+ = RX_QUEUE_1_NUM_BYTES_PUSHED_REG;
+ nf2addr->rx_q_num_pkts_dequeued_reg[1]
+ = RX_QUEUE_1_NUM_PKTS_DEQUEUED_REG;
+ nf2addr->rx_q_num_pkts_in_queue_reg[1]
+ = RX_QUEUE_1_NUM_PKTS_IN_QUEUE_REG;
+ nf2addr->tx_q_num_pkts_in_queue_reg[1]
+ = TX_QUEUE_1_NUM_PKTS_IN_QUEUE_REG;
+ nf2addr->tx_q_num_pkts_sent_reg[1]
+ = TX_QUEUE_1_NUM_PKTS_SENT_REG;
+ nf2addr->tx_q_num_words_pushed_reg[1]
+ = TX_QUEUE_1_NUM_WORDS_PUSHED_REG;
+ nf2addr->tx_q_num_bytes_pushed_reg[1]
+ = TX_QUEUE_1_NUM_BYTES_PUSHED_REG;
+ nf2addr->tx_q_num_pkts_enqueued_reg[1]
+ = TX_QUEUE_1_NUM_PKTS_ENQUEUED_REG;
+
+ nf2addr->rx_q_num_pkts_stored_reg[2]
+ = RX_QUEUE_2_NUM_PKTS_STORED_REG;
+ nf2addr->rx_q_num_pkts_dropped_full_reg[2]
+ = RX_QUEUE_2_NUM_PKTS_DROPPED_FULL_REG;
+ nf2addr->rx_q_num_pkts_dropped_bad_reg[2]
+ = RX_QUEUE_2_NUM_PKTS_DROPPED_BAD_REG;
+ nf2addr->rx_q_num_words_pushed_reg[2]
+ = RX_QUEUE_2_NUM_WORDS_PUSHED_REG;
+ nf2addr->rx_q_num_bytes_pushed_reg[2]
+ = RX_QUEUE_2_NUM_BYTES_PUSHED_REG;
+ nf2addr->rx_q_num_pkts_dequeued_reg[2]
+ = RX_QUEUE_2_NUM_PKTS_DEQUEUED_REG;
+ nf2addr->rx_q_num_pkts_in_queue_reg[2]
+ = RX_QUEUE_2_NUM_PKTS_IN_QUEUE_REG;
+ nf2addr->tx_q_num_pkts_in_queue_reg[2]
+ = TX_QUEUE_2_NUM_PKTS_IN_QUEUE_REG;
+ nf2addr->tx_q_num_pkts_sent_reg[2]
+ = TX_QUEUE_2_NUM_PKTS_SENT_REG;
+ nf2addr->tx_q_num_words_pushed_reg[2]
+ = TX_QUEUE_2_NUM_WORDS_PUSHED_REG;
+ nf2addr->tx_q_num_bytes_pushed_reg[2]
+ = TX_QUEUE_2_NUM_BYTES_PUSHED_REG;
+ nf2addr->tx_q_num_pkts_enqueued_reg[2]
+ = TX_QUEUE_2_NUM_PKTS_ENQUEUED_REG;
+
+ nf2addr->rx_q_num_pkts_stored_reg[3]
+ = RX_QUEUE_3_NUM_PKTS_STORED_REG;
+ nf2addr->rx_q_num_pkts_dropped_full_reg[3]
+ = RX_QUEUE_3_NUM_PKTS_DROPPED_FULL_REG;
+ nf2addr->rx_q_num_pkts_dropped_bad_reg[3]
+ = RX_QUEUE_3_NUM_PKTS_DROPPED_BAD_REG;
+ nf2addr->rx_q_num_words_pushed_reg[3]
+ = RX_QUEUE_3_NUM_WORDS_PUSHED_REG;
+ nf2addr->rx_q_num_bytes_pushed_reg[3]
+ = RX_QUEUE_3_NUM_BYTES_PUSHED_REG;
+ nf2addr->rx_q_num_pkts_dequeued_reg[3]
+ = RX_QUEUE_3_NUM_PKTS_DEQUEUED_REG;
+ nf2addr->rx_q_num_pkts_in_queue_reg[3]
+ = RX_QUEUE_3_NUM_PKTS_IN_QUEUE_REG;
+ nf2addr->tx_q_num_pkts_in_queue_reg[3]
+ = TX_QUEUE_3_NUM_PKTS_IN_QUEUE_REG;
+ nf2addr->tx_q_num_pkts_sent_reg[3]
+ = TX_QUEUE_3_NUM_PKTS_SENT_REG;
+ nf2addr->tx_q_num_words_pushed_reg[3]
+ = TX_QUEUE_3_NUM_WORDS_PUSHED_REG;
+ nf2addr->tx_q_num_bytes_pushed_reg[3]
+ = TX_QUEUE_3_NUM_BYTES_PUSHED_REG;
+ nf2addr->tx_q_num_pkts_enqueued_reg[3]
+ = TX_QUEUE_3_NUM_PKTS_ENQUEUED_REG;
+
+ return nf2addr;
+}
+
+struct nf2_all_ports_info *
+nf2_get_all_ports_info(struct net_device *dev)
+{
+ struct nf2_all_ports_info *nf2portinfo;
+ struct nf2_all_ports_info_addr *nf2addr;
+ int i;
+
+ nf2portinfo = kzalloc(sizeof(struct nf2_all_ports_info), GFP_KERNEL);
+ if (nf2portinfo == NULL)
+ return NULL;
+ nf2addr = nf2_get_all_ports_info_addr();
+ if (nf2addr == NULL)
+ return NULL;
+
+ for (i = 0; i < NF2_PORT_NUM; i++) {
+ nf2k_reg_read(dev, nf2addr->rx_q_num_pkts_stored_reg[i],
+ &(nf2portinfo->port[i].rx_q_num_pkts_stored));
+ nf2k_reg_read(dev, nf2addr->rx_q_num_pkts_dropped_full_reg[i],
+ &(nf2portinfo->port[i].
+ rx_q_num_pkts_dropped_full));
+ nf2k_reg_read(dev, nf2addr->rx_q_num_pkts_dropped_bad_reg[i],
+ &(nf2portinfo->port[i].
+ rx_q_num_pkts_dropped_bad));
+ nf2k_reg_read(dev, nf2addr->rx_q_num_words_pushed_reg[i],
+ &(nf2portinfo->port[i].rx_q_num_words_pushed));
+ nf2k_reg_read(dev, nf2addr->rx_q_num_bytes_pushed_reg[i],
+ &(nf2portinfo->port[i].rx_q_num_bytes_pushed));
+ nf2k_reg_read(dev, nf2addr->rx_q_num_pkts_dequeued_reg[i],
+ &(nf2portinfo->port[i].rx_q_num_pkts_dequeued));
+ nf2k_reg_read(dev, nf2addr->rx_q_num_pkts_in_queue_reg[i],
+ &(nf2portinfo->port[i].rx_q_num_pkts_in_queue));
+ nf2k_reg_read(dev, nf2addr->tx_q_num_pkts_in_queue_reg[i],
+ &(nf2portinfo->port[i].tx_q_num_pkts_in_queue));
+ nf2k_reg_read(dev, nf2addr->tx_q_num_pkts_sent_reg[i],
+ &(nf2portinfo->port[i].tx_q_num_pkts_sent));
+ nf2k_reg_read(dev, nf2addr->tx_q_num_words_pushed_reg[i],
+ &(nf2portinfo->port[i].tx_q_num_words_pushed));
+ nf2k_reg_read(dev, nf2addr->tx_q_num_bytes_pushed_reg[i],
+ &(nf2portinfo->port[i].tx_q_num_bytes_pushed));
+ nf2k_reg_read(dev, nf2addr->tx_q_num_pkts_enqueued_reg[i],
+ &(nf2portinfo->port[i].tx_q_num_pkts_enqueued));
+ }
+ return nf2portinfo;
+}
diff --git a/datapath/hwtable_nf2/nf2_openflow.h b/datapath/hwtable_nf2/nf2_openflow.h
index d1b2e8e..f570fe5 100644
--- a/datapath/hwtable_nf2/nf2_openflow.h
+++ b/datapath/hwtable_nf2/nf2_openflow.h
@@ -93,6 +93,55 @@ typedef union nf2_of_exact_counters_wrap {
uint32_t raw[NF2_OF_EXACT_COUNTERS_WORD_LEN];
} nf2_of_exact_counters_wrap;
+#define DEVICE_STR_LEN 100
+struct nf2_device_info {
+ uint32_t nf2_device_id;
+ uint32_t nf2_device_rev;
+ char nf2_device_str[DEVICE_STR_LEN];
+};
+
+#define NF2_PORT_NUM 4
+struct nf2_all_ports_info_addr {
+ unsigned int rx_q_num_pkts_stored_reg[NF2_PORT_NUM];
+ unsigned int rx_q_num_pkts_dropped_full_reg[NF2_PORT_NUM];
+ unsigned int rx_q_num_pkts_dropped_bad_reg[NF2_PORT_NUM];
+ unsigned int rx_q_num_words_pushed_reg[NF2_PORT_NUM];
+ unsigned int rx_q_num_bytes_pushed_reg[NF2_PORT_NUM];
+ unsigned int rx_q_num_pkts_dequeued_reg[NF2_PORT_NUM];
+ unsigned int rx_q_num_pkts_in_queue_reg[NF2_PORT_NUM];
+ unsigned int tx_q_num_pkts_in_queue_reg[NF2_PORT_NUM];
+ unsigned int tx_q_num_pkts_sent_reg[NF2_PORT_NUM];
+ unsigned int tx_q_num_words_pushed_reg[NF2_PORT_NUM];
+ unsigned int tx_q_num_bytes_pushed_reg[NF2_PORT_NUM];
+ unsigned int tx_q_num_pkts_enqueued_reg[NF2_PORT_NUM];
+};
+
+struct nf2_port_info {
+ uint32_t rx_q_num_pkts_stored;
+ uint32_t rx_q_num_pkts_dropped_full;
+ uint32_t rx_q_num_pkts_dropped_bad;
+ uint32_t rx_q_num_words_pushed;
+ uint32_t rx_q_num_bytes_pushed;
+ uint32_t rx_q_num_pkts_dequeued;
+ uint32_t rx_q_num_pkts_in_queue;
+ uint32_t tx_q_num_pkts_in_queue;
+ uint32_t tx_q_num_pkts_sent;
+ uint32_t tx_q_num_words_pushed;
+ uint32_t tx_q_num_bytes_pushed;
+ uint32_t tx_q_num_pkts_enqueued;
+};
+
+struct nf2_all_ports_info {
+ struct nf2_port_info port[NF2_PORT_NUM];
+};
+
+struct nf2_match_info {
+ uint32_t wildcard_misses;
+ uint32_t wildcard_hits;
+ uint32_t exact_misses;
+ uint32_t exact_hits;
+};
+
#pragma pack(pop) /* XXX: Restore original alignment from stack */
void nf2_reset_card(struct net_device *);
@@ -109,5 +158,8 @@ unsigned int nf2_get_wildcard_packet_count(struct net_device *, int);
unsigned int nf2_get_wildcard_byte_count(struct net_device *, int);
unsigned long int nf2_get_matched_count(struct net_device *);
unsigned long int nf2_get_missed_count(struct net_device *);
+struct nf2_device_info *nf2_get_device_info(struct net_device *);
+struct nf2_all_ports_info *nf2_get_all_ports_info(struct net_device *);
+struct nf2_match_info *nf2_get_match_info(struct net_device *);
#endif
diff --git a/datapath/hwtable_nf2/nf2_procfs.c b/datapath/hwtable_nf2/nf2_procfs.c
new file mode 100644
index 0000000..4fdd27a
--- /dev/null
+++ b/datapath/hwtable_nf2/nf2_procfs.c
@@ -0,0 +1,209 @@
+/*-
+ * Copyright (c) 2008, 2009
+ * The Board of Trustees of The Leland Stanford Junior University
+ *
+ * We are making the OpenFlow specification and associated documentation
+ * (Software) available for public use and benefit with the expectation that
+ * others will use, modify and enhance the Software and contribute those
+ * enhancements back to the community. However, since we would like to make the
+ * Software available for broadest use, with as few restrictions as possible
+ * permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this Software to deal in the Software under the copyrights without
+ * restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * 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 AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ * The name and trademarks of copyright holder(s) may NOT be used in
+ * advertising or publicity pertaining to the Software or any derivatives
+ * without specific, written prior permission.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "chain.h"
+#include "table.h"
+#include "flow.h"
+#include "datapath.h"
+
+#include "hwtable_nf2/nf2_reg.h"
+#include "hwtable_nf2/nf2_flowtable.h"
+#include "hwtable_nf2/nf2_openflow.h"
+#include "hwtable_nf2/nf2_lib.h"
+#include "hwtable_nf2/nf2_procfs.h"
+
+#define NF2_PROCFS_NAME "net/openflow-netfpga"
+
+static struct semaphore proc_sem;
+
+static int disp_dev_info(char *);
+static int disp_port_info(char *);
+static int disp_match_info(char *);
+static int proc_read(char *, char **, off_t, int, int *, void *);
+
+static int
+disp_dev_info(char *page)
+{
+ struct net_device *netdev;
+ struct nf2_device_info *nf2devinfo;
+ int len = 0;
+
+ netdev = nf2_get_net_device();
+ if (netdev == NULL)
+ return 0;
+ nf2devinfo = nf2_get_device_info(netdev);
+ if (nf2devinfo == NULL) {
+ nf2_free_net_device(netdev);
+ return 0;
+ }
+ nf2_free_net_device(netdev);
+
+ len += sprintf(page + len,
+ "NetFPGA: "
+ "design name: %s, device ID: %d, device revision: %d\n",
+ nf2devinfo->nf2_device_str,
+ nf2devinfo->nf2_device_id, nf2devinfo->nf2_device_rev);
+ len += sprintf(page + len, "\n");
+
+ return len;
+}
+
+static int
+disp_port_info(char *page)
+{
+ struct net_device *netdev;
+ struct nf2_all_ports_info *nf2portinfo;
+ int len = 0;
+ int i;
+
+ netdev = nf2_get_net_device();
+ if (netdev == NULL)
+ return 0;
+ nf2portinfo = nf2_get_all_ports_info(netdev);
+ if (nf2portinfo == NULL) {
+ nf2_free_net_device(netdev);
+ return 0;
+ }
+ nf2_free_net_device(netdev);
+
+ for (i = 0; i < NF2_PORT_NUM; i++) {
+ len += sprintf(page + len, "Interface nf2c%d\n", i);
+ len += sprintf(page + len,
+ " Input queue: %u/%u (current/queued)\n",
+ nf2portinfo->port[i].rx_q_num_pkts_in_queue,
+ nf2portinfo->port[i].rx_q_num_pkts_dequeued);
+ len += sprintf(page + len,
+ " %u packets input, %u dropped "
+ "(%u buffer exhausted, %u bad packets)\n"
+ " %u pushed words, %u pushed bytes\n",
+ nf2portinfo->port[i].rx_q_num_pkts_stored,
+ nf2portinfo->port[i].rx_q_num_pkts_dropped_full
+ + nf2portinfo->port[i].rx_q_num_pkts_dropped_bad,
+ nf2portinfo->port[i].rx_q_num_pkts_dropped_full,
+ nf2portinfo->port[i].rx_q_num_pkts_dropped_bad,
+ nf2portinfo->port[i].rx_q_num_words_pushed,
+ nf2portinfo->port[i].rx_q_num_bytes_pushed);
+ len += sprintf(page + len,
+ " Output queue: %u/%u (current/queued)\n",
+ nf2portinfo->port[i].tx_q_num_pkts_in_queue,
+ nf2portinfo->port[i].tx_q_num_pkts_enqueued);
+ len += sprintf(page + len,
+ " %u packets output, "
+ "%u pushed words, %u pushed bytes\n",
+ nf2portinfo->port[i].tx_q_num_pkts_sent,
+ nf2portinfo->port[i].tx_q_num_words_pushed,
+ nf2portinfo->port[i].tx_q_num_bytes_pushed);
+ }
+ len += sprintf(page + len, "\n");
+
+ return len;
+}
+
+static int
+disp_match_info(char *page)
+{
+ struct net_device *netdev;
+ struct nf2_match_info *nf2matchinfo;
+ int len = 0;
+
+ netdev = nf2_get_net_device();
+ if (netdev == NULL)
+ return 0;
+ nf2matchinfo = nf2_get_match_info(netdev);
+ if (nf2matchinfo == NULL) {
+ nf2_free_net_device(netdev);
+ return 0;
+ }
+ nf2_free_net_device(netdev);
+
+ len += sprintf(page + len,
+ "WILDCARD match table lookup: %u/%u (hits/misses)\n",
+ nf2matchinfo->wildcard_hits,
+ nf2matchinfo->wildcard_misses);
+ len += sprintf(page + len,
+ "EXACT match table lookup: %u/%u (hits/misses)\n",
+ nf2matchinfo->exact_hits, nf2matchinfo->exact_misses);
+ len += sprintf(page + len, "\n");
+
+ return len;
+}
+
+static int
+proc_read(char *page, char **start, off_t offset, int count, int *eof,
+ void *data)
+{
+ int len = 0;
+ int buf_pos = 1;
+
+ if (down_interruptible(&proc_sem))
+ return -ERESTARTSYS;
+
+ if (buf_pos != 0) {
+ len += disp_dev_info(page + len);
+ len += disp_port_info(page + len);
+ len += disp_match_info(page + len);
+ buf_pos = 0;
+ }
+ up(&proc_sem);
+
+ *eof = 1;
+ return len;
+}
+
+void
+nf2_create_procfs(void)
+{
+ struct proc_dir_entry *entry;
+
+ entry = create_proc_entry(NF2_PROCFS_NAME,
+ S_IFREG | S_IRUGO | S_IWUGO, NULL);
+ if (entry == NULL)
+ return;
+
+ entry->read_proc = proc_read;
+ sema_init(&proc_sem, 1);
+}
+
+void
+nf2_remove_procfs(void)
+{
+ remove_proc_entry(NF2_PROCFS_NAME, NULL);
+}
diff --git a/datapath/hwtable_nf2/nf2_procfs.h b/datapath/hwtable_nf2/nf2_procfs.h
new file mode 100644
index 0000000..011f680
--- /dev/null
+++ b/datapath/hwtable_nf2/nf2_procfs.h
@@ -0,0 +1,39 @@
+/*-
+ * Copyright (c) 2008, 2009
+ * The Board of Trustees of The Leland Stanford Junior University
+ *
+ * We are making the OpenFlow specification and associated documentation
+ * (Software) available for public use and benefit with the expectation that
+ * others will use, modify and enhance the Software and contribute those
+ * enhancements back to the community. However, since we would like to make the
+ * Software available for broadest use, with as few restrictions as possible
+ * permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this Software to deal in the Software under the copyrights without
+ * restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * 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 AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ * The name and trademarks of copyright holder(s) may NOT be used in
+ * advertising or publicity pertaining to the Software or any derivatives
+ * without specific, written prior permission.
+ */
+
+#ifndef HWTABLE_NF2_NF2_PROCFS_
+#define HWTABLE_NF2_NF2_PROCFS_
+
+void nf2_create_procfs(void);
+void nf2_remove_procfs(void);
+
+#endif
diff --git a/datapath/hwtable_nf2/nf2_reg.h b/datapath/hwtable_nf2/nf2_reg.h
index c8e4ec3..3518962 100644
--- a/datapath/hwtable_nf2/nf2_reg.h
+++ b/datapath/hwtable_nf2/nf2_reg.h
@@ -18,9 +18,9 @@
#define MAC_DIS_CRC_CHECK 7
#define MAC_DIS_CRC_GEN 8
-#define DEVICE_ID_REG 0x0400000
-#define DEVICE_REVISION_REG 0x0400004
-#define DEVICE_STR_REG 0x0400008
+#define DEVICE_ID_REG 0x0400010
+#define DEVICE_REVISION_REG 0x0400014
+#define DEVICE_STR_REG 0x040001c
#define SRAM_BASE_ADDR_REG 0x1000000
diff --git a/datapath/linux-2.4/.gitignore b/datapath/linux-2.4/.gitignore
index ad178b1..28483d4 100644
--- a/datapath/linux-2.4/.gitignore
+++ b/datapath/linux-2.4/.gitignore
@@ -3,6 +3,7 @@
/Makefile
/Makefile.main
/attr.c
+/brcompat.c
/chain.c
/datapath.c
/compat24.c
diff --git a/datapath/linux-2.4/Kbuild.in b/datapath/linux-2.4/Kbuild.in
index 86bfa6b..047da49 100644
--- a/datapath/linux-2.4/Kbuild.in
+++ b/datapath/linux-2.4/Kbuild.in
@@ -30,7 +30,7 @@ endif
override CFLAGS := $(OF_CFLAGS)
# Multipart objects
-list-multi := $(patsubst %,%_mod.o,$(all_modules))
+list-multi := $(patsubst %,%_mod.o,$(build_modules))
# Module objects that we can build
obj-m += $(list-multi)
@@ -39,10 +39,10 @@ obj-m += $(list-multi)
define parts_template
$(1)_mod-objs = $$(patsubst %.c,%.o,$(notdir $($(1)_sources)))
endef
-$(foreach module,$(all_modules),$(eval $(call parts_template,$(module))))
+$(foreach module,$(build_modules),$(eval $(call parts_template,$(module))))
# Objects that export symbols
-export-objs = $(all_objects)
+export-objs = $(build_objects)
# Include the kernel's global Rules.make
include $(TOPDIR)/Rules.make
@@ -52,7 +52,7 @@ define link_template
$(1)_mod.o: $($(1)_mod-objs)
$(LD) -r -o $$@ $($(1)_mod-objs)
endef
-$(foreach module,$(all_modules),$(eval $(call link_template,$(module))))
+$(foreach module,$(build_modules),$(eval $(call link_template,$(module))))
# Build system rewrite May 2008: Bobby Holley
#
diff --git a/datapath/linux-2.4/Makefile.main.in b/datapath/linux-2.4/Makefile.main.in
index 08848c5..1647f60 100644
--- a/datapath/linux-2.4/Makefile.main.in
+++ b/datapath/linux-2.4/Makefile.main.in
@@ -12,8 +12,8 @@ include $(srcdir)/../Modules.mk
include $(srcdir)/Modules.mk
include @HW_TABLES@
-default: $(all_links)
-$(foreach m,$(all_modules), \
+default: $(build_links)
+$(foreach m,$(build_modules), \
$(foreach s,$($(m)_sources), \
$(eval $(notdir $(s)): ; ln -s $(srcdir)/../$(s) .)))
@@ -24,7 +24,7 @@ all: default
check: all
clean:
rm -f *.o *_mod.o .*.o.flags
- for d in $(all_links); do if test -h $$d; then rm $$d; fi; done
+ for d in $(build_links); do if test -h $$d; then rm $$d; fi; done
ifneq (,$(KSRC))
diff --git a/datapath/linux-2.4/Modules.mk b/datapath/linux-2.4/Modules.mk
index 802face..5aa1a39 100644
--- a/datapath/linux-2.4/Modules.mk
+++ b/datapath/linux-2.4/Modules.mk
@@ -1,4 +1,4 @@
-dist_modules += compat24
+both_modules += compat24
compat24_sources = \
linux-2.4/compat-2.4/attr.c \
@@ -21,12 +21,15 @@ compat24_headers = \
linux-2.4/compat-2.4/include-mips/asm/break.h \
linux-2.4/compat-2.4/include-mips/asm/page.h \
linux-2.4/compat-2.4/include-mips/asm/system.h \
+ linux-2.4/compat-2.4/include/asm/bug.h \
linux-2.4/compat-2.4/include/asm/system.h \
linux-2.4/compat-2.4/include/linux/compiler.h \
linux-2.4/compat-2.4/include/linux/delay.h \
+ linux-2.4/compat-2.4/include/linux/dmi.h \
linux-2.4/compat-2.4/include/linux/etherdevice.h \
linux-2.4/compat-2.4/include/linux/genetlink.h \
linux-2.4/compat-2.4/include/linux/gfp.h \
+ linux-2.4/compat-2.4/include/linux/icmp.h \
linux-2.4/compat-2.4/include/linux/if_ether.h \
linux-2.4/compat-2.4/include/linux/if_vlan.h \
linux-2.4/compat-2.4/include/linux/ip.h \
@@ -54,6 +57,7 @@ compat24_headers = \
linux-2.4/compat-2.4/include/linux/timer.h \
linux-2.4/compat-2.4/include/linux/types.h \
linux-2.4/compat-2.4/include/linux/udp.h \
+ linux-2.4/compat-2.4/include/linux/workqueue.h \
linux-2.4/compat-2.4/include/net/checksum.h \
linux-2.4/compat-2.4/include/net/genetlink.h \
linux-2.4/compat-2.4/include/net/llc_pdu.h \
diff --git a/datapath/linux-2.4/compat-2.4/include/linux/netdevice.h b/datapath/linux-2.4/compat-2.4/include/linux/netdevice.h
index 60c81b0..9decde2 100644
--- a/datapath/linux-2.4/compat-2.4/include/linux/netdevice.h
+++ b/datapath/linux-2.4/compat-2.4/include/linux/netdevice.h
@@ -8,8 +8,6 @@
/*----------------------------------------------------------------------------
* In 2.6.24, a namespace argument became required for dev_get_by_name.
*/
-#define net_init NULL
-
#ifdef dev_get_by_name
#undef dev_get_by_name
#define dev_get_by_name(net, name) \
@@ -23,6 +21,19 @@ static inline struct net_device *compat_dev_get_by_name(const char *name)
dev_get_by_name((name))
#endif /* dev_get_by_name */
+#ifdef dev_get_by_index
+#undef dev_get_by_index
+#define dev_get_by_index(net, ifindex) \
+ compat_dev_get_by_index((ifindex))
+static inline struct net_device *compat_dev_get_by_index(int ifindex)
+{
+ return (_set_ver(dev_get_by_index))(ifindex);
+}
+#else
+#define dev_get_by_index(net, ifindex) \
+ dev_get_by_index((ifindex))
+#endif /* dev_get_by_index */
+
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,27)
static inline void *netdev_priv(struct net_device *dev)
{
diff --git a/datapath/linux-2.4/compat-2.4/include/net/netlink.h b/datapath/linux-2.4/compat-2.4/include/net/netlink.h
index 46cdafd..f4e3155 100644
--- a/datapath/linux-2.4/compat-2.4/include/net/netlink.h
+++ b/datapath/linux-2.4/compat-2.4/include/net/netlink.h
@@ -1011,4 +1011,9 @@ static inline int nla_validate_nested(struct nlattr *start, int maxtype,
#define nla_for_each_nested(pos, nla, rem) \
nla_for_each_attr(pos, nla_data(nla), nla_len(nla), rem)
+static inline int VERIFY_NUL_STRING(struct nlattr *attr)
+{
+ return 0;
+}
+
#endif
diff --git a/datapath/linux-2.6/.gitignore b/datapath/linux-2.6/.gitignore
index 8e8a3b5..fc42261 100644
--- a/datapath/linux-2.6/.gitignore
+++ b/datapath/linux-2.6/.gitignore
@@ -1,6 +1,9 @@
/Kbuild
/Makefile
/Makefile.main
+/brcompat.c
+/brc_sysfs_dp.c
+/brc_sysfs_if.c
/chain.c
/crc32.c
/crc_t.c
@@ -11,7 +14,8 @@
/flow.c
/forward.c
/forward_t.c
-/genetlink.c
+/genetlink-brcompat.c
+/genetlink-openflow.c
/hwtable_dummy.c
/nx_act.c
/nx_act_snat.c
diff --git a/datapath/linux-2.6/Kbuild.in b/datapath/linux-2.6/Kbuild.in
index 7864786..25f9e78 100644
--- a/datapath/linux-2.6/Kbuild.in
+++ b/datapath/linux-2.6/Kbuild.in
@@ -13,7 +13,6 @@ EXTRA_CFLAGS := -DVERSION=\"$(VERSION)\"
EXTRA_CFLAGS += -I$(srcdir)/..
EXTRA_CFLAGS += -I$(builddir)/..
EXTRA_CFLAGS += -I$(top_srcdir)/include
-EXTRA_CFLAGS += -I$(top_srcdir)/datapath
EXTRA_CFLAGS += @SUPPORT_SNAT@
ifeq '$(BUILDNR)' '0'
EXTRA_CFLAGS += -DBUILDNR=\"\"
@@ -21,16 +20,17 @@ else
EXTRA_CFLAGS += -DBUILDNR=\"+build$(BUILDNR)\"
endif
EXTRA_CFLAGS += -g
+EXTRA_CFLAGS += -include $(builddir)/kcompat.h
# These include directories have to go before -I$(KSRC)/include.
# NOSTDINC_FLAGS just happens to be a variable that goes in the
# right place, even though it's conceptually incorrect.
NOSTDINC_FLAGS += -I$(srcdir)/compat-2.6 -I$(srcdir)/compat-2.6/include
-obj-m := $(patsubst %,%_mod.o,$(all_modules))
+obj-m := $(patsubst %,%.o,$(build_modules))
define module_template
-$(1)_mod-y = $$(notdir $$(patsubst %.c,%.o,$($(1)_sources)))
+$(1)-y = $$(notdir $$(patsubst %.c,%.o,$($(1)_sources)))
endef
-$(foreach module,$(all_modules),$(eval $(call module_template,$(module))))
+$(foreach module,$(build_modules),$(eval $(call module_template,$(module))))
diff --git a/datapath/linux-2.6/Makefile.main.in b/datapath/linux-2.6/Makefile.main.in
index a93b86e..ddf32ab 100644
--- a/datapath/linux-2.6/Makefile.main.in
+++ b/datapath/linux-2.6/Makefile.main.in
@@ -4,25 +4,26 @@ export srcdir = @abs_srcdir@
export top_srcdir = @abs_top_srcdir@
export KSRC = @KSRC26@
export VERSION = @VERSION@
+export BUILD_VETH = @BUILD_VETH@
include $(srcdir)/../Modules.mk
include $(srcdir)/Modules.mk
include @HW_TABLES@
-default: $(all_links)
+default: $(build_links)
-$(foreach m,$(all_modules), \
- $(foreach s,$($(m)_sources), \
- $(eval $(notdir $(s)): ; ln -s $(srcdir)/../$(s) $@)))
+$(foreach s,$(sort $(foreach m,$(build_modules),$($(m)_sources))), \
+ $(eval $(notdir $(s)): ; ln -s $(srcdir)/../$(s) $@))
distclean: clean
+ rm -f kcompat.h
distdir: clean
install:
all: default
check: all
clean:
- rm -f *.o *.ko *_mod.* Module.symvers *.cmd
- for d in $(all_links); do if test -h $$d; then rm $$d; fi; done
+ rm -f *.o *.ko *_mod.* Module.symvers *.cmd kcompat.h.new
+ for d in $(build_links); do if test -h $$d; then rm $$d; fi; done
ifneq ($(KSRC),)
diff --git a/datapath/linux-2.6/Modules.mk b/datapath/linux-2.6/Modules.mk
index b950355..01f0586 100644
--- a/datapath/linux-2.6/Modules.mk
+++ b/datapath/linux-2.6/Modules.mk
@@ -1,13 +1,18 @@
-openflow_sources += \
- linux-2.6/compat-2.6/genetlink.c \
+ofdatapath_sources += \
+ linux-2.6/compat-2.6/genetlink-openflow.c \
linux-2.6/compat-2.6/random32.c
-
-openflow_headers += \
+ofdatapath_headers += \
linux-2.6/compat-2.6/compat26.h \
+ linux-2.6/compat-2.6/include/asm-generic/bug.h \
+ linux-2.6/compat-2.6/include/linux/dmi.h \
+ linux-2.6/compat-2.6/include/linux/icmp.h \
+ linux-2.6/compat-2.6/include/linux/if_arp.h \
linux-2.6/compat-2.6/include/linux/ip.h \
linux-2.6/compat-2.6/include/linux/ipv6.h \
+ linux-2.6/compat-2.6/include/linux/jiffies.h \
linux-2.6/compat-2.6/include/linux/lockdep.h \
linux-2.6/compat-2.6/include/linux/mutex.h \
+ linux-2.6/compat-2.6/include/linux/netfilter_ipv4.h \
linux-2.6/compat-2.6/include/linux/netlink.h \
linux-2.6/compat-2.6/include/linux/random.h \
linux-2.6/compat-2.6/include/linux/rculist.h \
@@ -16,5 +21,21 @@ openflow_headers += \
linux-2.6/compat-2.6/include/linux/timer.h \
linux-2.6/compat-2.6/include/linux/types.h \
linux-2.6/compat-2.6/include/linux/udp.h \
+ linux-2.6/compat-2.6/include/linux/workqueue.h \
linux-2.6/compat-2.6/include/net/checksum.h \
- linux-2.6/compat-2.6/include/net/genetlink.h
+ linux-2.6/compat-2.6/include/net/genetlink.h \
+ linux-2.6/compat-2.6/include/net/netlink.h
+
+#both_modules += brcompat
+brcompat_sources = \
+ linux-2.6/compat-2.6/genetlink-brcompat.c \
+ brcompat.c \
+ brc_sysfs_dp.c \
+ brc_sysfs_if.c
+brcompat_headers = \
+ brc_sysfs.h
+
+#dist_modules += veth
+#build_modules += $(if $(BUILD_VETH),veth)
+veth_sources = linux-2.6/compat-2.6/veth.c
+veth_headers =
diff --git a/datapath/linux-2.6/compat-2.6/compat26.h b/datapath/linux-2.6/compat-2.6/compat26.h
index 8159517..37f6a4f 100644
--- a/datapath/linux-2.6/compat-2.6/compat26.h
+++ b/datapath/linux-2.6/compat-2.6/compat26.h
@@ -10,11 +10,13 @@
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,23)
/*----------------------------------------------------------------------------
* In 2.6.24, a namespace argument became required for dev_get_by_name. */
-#define net_init NULL
#define dev_get_by_name(net, name) \
dev_get_by_name((name))
+#define dev_get_by_index(net, ifindex) \
+ dev_get_by_index((ifindex))
+
#endif /* linux kernel <= 2.6.23 */
@@ -26,9 +28,4 @@
#endif /* linux kernel <= 2.6.22 */
-/* For CentOS / RHEL kernel compatibility */
-#ifndef RHEL_RELEASE_VERSION
-#define RHEL_RELEASE_VERSION(X,Y) ( 0 )
-#endif
-
#endif /* compat26.h */
diff --git a/datapath/linux-2.6/compat-2.6/genetlink-brcompat.c b/datapath/linux-2.6/compat-2.6/genetlink-brcompat.c
new file mode 100644
index 0000000..f30996c
--- /dev/null
+++ b/datapath/linux-2.6/compat-2.6/genetlink-brcompat.c
@@ -0,0 +1,20 @@
+#include "net/genetlink.h"
+
+#include
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+
+/* We fix grp->id to 32 so that it doesn't collide with any of the multicast
+ * groups selected by openflow_mod, which uses groups 16 through 31. Collision
+ * isn't fatal--multicast listeners should check that the family is the one
+ * that they want and discard others--but it wastes time and memory to receive
+ * unwanted messages. */
+int genl_register_mc_group(struct genl_family *family,
+ struct genl_multicast_group *grp)
+{
+ grp->id = 32;
+ grp->family = family;
+
+ return 0;
+}
+
+#endif /* kernel < 2.6.23 */
diff --git a/datapath/linux-2.6/compat-2.6/genetlink-openflow.c b/datapath/linux-2.6/compat-2.6/genetlink-openflow.c
new file mode 100644
index 0000000..9e09215
--- /dev/null
+++ b/datapath/linux-2.6/compat-2.6/genetlink-openflow.c
@@ -0,0 +1,22 @@
+#include "net/genetlink.h"
+
+#include
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+
+/* We use multicast groups 16 through 31 to avoid colliding with the multicast
+ * group selected by brcompat_mod, which uses groups 32. Collision isn't
+ * fatal--multicast listeners should check that the family is the one that they
+ * want and discard others--but it wastes time and memory to receive unwanted
+ * messages. */
+int genl_register_mc_group(struct genl_family *family,
+ struct genl_multicast_group *grp)
+{
+ /* This code is called single-threaded. */
+ static unsigned int next_id = 0;
+ grp->id = next_id++ % 16 + 16;
+ grp->family = family;
+
+ return 0;
+}
+
+#endif /* kernel < 2.6.23 */
diff --git a/datapath/linux-2.6/compat-2.6/genetlink.c b/datapath/linux-2.6/compat-2.6/genetlink.c
deleted file mode 100644
index c0e6ae9..0000000
--- a/datapath/linux-2.6/compat-2.6/genetlink.c
+++ /dev/null
@@ -1,15 +0,0 @@
-#include "net/genetlink.h"
-
-#include
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
-
-int genl_register_mc_group(struct genl_family *family,
- struct genl_multicast_group *grp)
-{
- grp->id = 1;
- grp->family = family;
-
- return 0;
-}
-
-#endif /* kernel < 2.6.23 */
diff --git a/datapath/linux-2.6/compat-2.6/include/asm-generic/bug.h b/datapath/linux-2.6/compat-2.6/include/asm-generic/bug.h
index 13f23cd..bd0a171 100644
--- a/datapath/linux-2.6/compat-2.6/include/asm-generic/bug.h
+++ b/datapath/linux-2.6/compat-2.6/include/asm-generic/bug.h
@@ -3,12 +3,7 @@
#include_next
-#include
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
-
#ifndef WARN_ON_ONCE
-
#define WARN_ON_ONCE(condition) ({ \
static int __warned; \
int __ret_warn_once = !!(condition); \
@@ -20,8 +15,6 @@
unlikely(__ret_warn_once); \
})
-#endif /* ifndef WARN_ON_ONCE */
-
-#endif /* linux kernel < 2.6.19 */
+#endif
#endif
diff --git a/datapath/linux-2.6/compat-2.6/include/linux/icmp.h b/datapath/linux-2.6/compat-2.6/include/linux/icmp.h
index 0674443..89b354e 100644
--- a/datapath/linux-2.6/compat-2.6/include/linux/icmp.h
+++ b/datapath/linux-2.6/compat-2.6/include/linux/icmp.h
@@ -3,17 +3,11 @@
#include_next
-#include
-#if ((LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22)) && \
- (RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(5,2)))
-
-#ifdef __KERNEL__
+#ifndef HAVE_SKBUFF_HEADER_HELPERS
static inline struct icmphdr *icmp_hdr(const struct sk_buff *skb)
{
return (struct icmphdr *)skb_transport_header(skb);
}
-#endif /* __KERNEL__ */
-
-#endif /* linux kernel < 2.6.22 */
+#endif
#endif
diff --git a/datapath/linux-2.6/compat-2.6/include/linux/if_arp.h b/datapath/linux-2.6/compat-2.6/include/linux/if_arp.h
index 05ae870..3ec9ea7 100644
--- a/datapath/linux-2.6/compat-2.6/include/linux/if_arp.h
+++ b/datapath/linux-2.6/compat-2.6/include/linux/if_arp.h
@@ -4,19 +4,13 @@
#include_next
-#include
-#if ((LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22)) && \
- (RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(5,2)))
-
-#ifdef __KERNEL__
+#ifndef HAVE_SKBUFF_HEADER_HELPERS
#include
static inline struct arphdr *arp_hdr(const struct sk_buff *skb)
{
return (struct arphdr *)skb_network_header(skb);
}
-#endif /* __KERNEL__ */
-
-#endif /* linux kernel < 2.6.22 && RHEL release code < 5.2*/
+#endif /* !HAVE_SKBUFF_HEADER_HELPERS */
#endif
diff --git a/datapath/linux-2.6/compat-2.6/include/linux/ip.h b/datapath/linux-2.6/compat-2.6/include/linux/ip.h
index 82e8402..3676539 100644
--- a/datapath/linux-2.6/compat-2.6/include/linux/ip.h
+++ b/datapath/linux-2.6/compat-2.6/include/linux/ip.h
@@ -3,13 +3,7 @@
#include_next
-#include
-#if ((LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22)) && \
- (RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(5,2)))
-
-#ifdef __KERNEL__
-#include
-
+#ifndef HAVE_SKBUFF_HEADER_HELPERS
static inline struct iphdr *ip_hdr(const struct sk_buff *skb)
{
return (struct iphdr *)skb_network_header(skb);
@@ -19,8 +13,6 @@ static inline unsigned int ip_hdrlen(const struct sk_buff *skb)
{
return ip_hdr(skb)->ihl * 4;
}
-#endif /* __KERNEL__ */
-
-#endif /* linux kernel < 2.6.22 */
+#endif /* !HAVE_SKBUFF_HEADER_HELPERS */
#endif
diff --git a/datapath/linux-2.6/compat-2.6/include/linux/ipv6.h b/datapath/linux-2.6/compat-2.6/include/linux/ipv6.h
index 69bac73..25a5431 100644
--- a/datapath/linux-2.6/compat-2.6/include/linux/ipv6.h
+++ b/datapath/linux-2.6/compat-2.6/include/linux/ipv6.h
@@ -3,19 +3,11 @@
#include_next
-#include
-#if ((LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22)) && \
- (RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(5,2)))
-
-#ifdef __KERNEL__
-#include
-
+#ifndef HAVE_SKBUFF_HEADER_HELPERS
static inline struct ipv6hdr *ipv6_hdr(const struct sk_buff *skb)
{
return (struct ipv6hdr *)skb_network_header(skb);
}
-#endif /* __KERNEL__ */
-
-#endif /* linux kernel < 2.6.22 */
+#endif
#endif
diff --git a/datapath/linux-2.6/compat-2.6/include/linux/jiffies.h b/datapath/linux-2.6/compat-2.6/include/linux/jiffies.h
new file mode 100644
index 0000000..3286e63
--- /dev/null
+++ b/datapath/linux-2.6/compat-2.6/include/linux/jiffies.h
@@ -0,0 +1,26 @@
+#ifndef __LINUX_JIFFIES_WRAPPER_H
+#define __LINUX_JIFFIES_WRAPPER_H 1
+
+#include_next
+
+#include
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
+
+/* Same as above, but does so with platform independent 64bit types.
+ * These must be used when utilizing jiffies_64 (i.e. return value of
+ * get_jiffies_64() */
+#define time_after64(a,b) \
+ (typecheck(__u64, a) && \
+ typecheck(__u64, b) && \
+ ((__s64)(b) - (__s64)(a) < 0))
+#define time_before64(a,b) time_after64(b,a)
+
+#define time_after_eq64(a,b) \
+ (typecheck(__u64, a) && \
+ typecheck(__u64, b) && \
+ ((__s64)(a) - (__s64)(b) >= 0))
+#define time_before_eq64(a,b) time_after_eq64(b,a)
+
+#endif /* linux kernel < 2.6.19 */
+
+#endif
diff --git a/datapath/linux-2.6/compat-2.6/include/linux/netdevice.h b/datapath/linux-2.6/compat-2.6/include/linux/netdevice.h
new file mode 100644
index 0000000..7abeb3b
--- /dev/null
+++ b/datapath/linux-2.6/compat-2.6/include/linux/netdevice.h
@@ -0,0 +1,10 @@
+#ifndef __LINUX_NETDEVICE_WRAPPER_H
+#define __LINUX_NETDEVICE_WRAPPER_H 1
+
+#include_next
+
+#ifndef to_net_dev
+#define to_net_dev(class) container_of(class, struct net_device, class_dev)
+#endif
+
+#endif
diff --git a/datapath/linux-2.6/compat-2.6/include/linux/netfilter_bridge.h b/datapath/linux-2.6/compat-2.6/include/linux/netfilter_bridge.h
new file mode 100644
index 0000000..1c8183c
--- /dev/null
+++ b/datapath/linux-2.6/compat-2.6/include/linux/netfilter_bridge.h
@@ -0,0 +1,24 @@
+#ifndef __LINUX_NETFILTER_BRIDGE_WRAPPER_H
+#define __LINUX_NETFILTER_BRIDGE_WRAPPER_H
+
+#include_next
+
+#include
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22)
+
+#include
+#include
+
+static inline unsigned int nf_bridge_encap_header_len(const struct sk_buff *skb)
+{
+ switch (skb->protocol) {
+ case __constant_htons(ETH_P_8021Q):
+ return VLAN_HLEN;
+ default:
+ return 0;
+ }
+}
+
+#endif /* linux version < 2.6.22 */
+
+#endif
diff --git a/datapath/linux-2.6/compat-2.6/include/linux/skbuff.h b/datapath/linux-2.6/compat-2.6/include/linux/skbuff.h
index bfa3ca5..55d32eb 100644
--- a/datapath/linux-2.6/compat-2.6/include/linux/skbuff.h
+++ b/datapath/linux-2.6/compat-2.6/include/linux/skbuff.h
@@ -1,24 +1,55 @@
#ifndef __LINUX_SKBUFF_WRAPPER_H
#define __LINUX_SKBUFF_WRAPPER_H 1
-#include
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22)
-#undef kmem_cache_create
-#endif /* linux kernel <= 2.6.22 */
-
#include_next
-#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,22)
-/*----------------------------------------------------------------------------
- * In 2.6.23, the last argument was dropped from kmem_cache_create. */
-#define kmem_cache_create(n, s, a, f, c) \
- kmem_cache_create((n), (s), (a), (f), (c), NULL)
+#include
+
+#ifndef HAVE_SKB_COPY_FROM_LINEAR_DATA_OFFSET
+static inline void skb_copy_from_linear_data_offset(const struct sk_buff *skb,
+ const int offset, void *to,
+ const unsigned int len)
+{
+ memcpy(to, skb->data + offset, len);
+}
+
+static inline void skb_copy_to_linear_data_offset(struct sk_buff *skb,
+ const int offset,
+ const void *from,
+ const unsigned int len)
+{
+ memcpy(skb->data + offset, from, len);
+}
+
+#endif /* !HAVE_SKB_COPY_FROM_LINEAR_DATA_OFFSET */
+
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+static inline int __skb_cow(struct sk_buff *skb, unsigned int headroom,
+ int cloned)
+{
+ int delta = 0;
+
+ if (headroom < NET_SKB_PAD)
+ headroom = NET_SKB_PAD;
+ if (headroom > skb_headroom(skb))
+ delta = headroom - skb_headroom(skb);
+
+ if (delta || cloned)
+ return pskb_expand_head(skb, ALIGN(delta, NET_SKB_PAD), 0,
+ GFP_ATOMIC);
+ return 0;
+}
+
+static inline int skb_cow_head(struct sk_buff *skb, unsigned int headroom)
+{
+ return __skb_cow(skb, headroom, skb_header_cloned(skb));
+}
+#endif /* linux < 2.6.23 */
-#endif /* linux kernel <= 2.6.22 */
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17)
-/* Emulate Linux 2.6.17 and later behavior, in which kfree_skb silently ignores
+/* Emulate Linux 2.6.17 and later behavior, in which kfree_skb silently ignores
* null pointer arguments. */
#define kfree_skb(skb) kfree_skb_maybe_null(skb)
static inline void kfree_skb_maybe_null(struct sk_buff *skb)
@@ -29,22 +60,21 @@ static inline void kfree_skb_maybe_null(struct sk_buff *skb)
#endif
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
+#ifndef CHECKSUM_PARTIAL
/* Note that CHECKSUM_PARTIAL is not implemented, but this allows us to at
* least test against it: see update_csum() in forward.c. */
-#undef CHECKSUM_PARTIAL
#define CHECKSUM_PARTIAL 3
+#endif
+#ifndef CHECKSUM_COMPLETE
#define CHECKSUM_COMPLETE CHECKSUM_HW
-#endif /* linux kernel < 2.6.19 */
-
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22)
+#endif
+#ifdef HAVE_MAC_RAW
#define mac_header mac.raw
#define network_header nh.raw
+#endif
-#if RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(5,2)
-
+#ifndef HAVE_SKBUFF_HEADER_HELPERS
static inline unsigned char *skb_transport_header(const struct sk_buff *skb)
{
return skb->h.raw;
@@ -102,9 +132,6 @@ static inline void skb_copy_to_linear_data(struct sk_buff *skb,
{
memcpy(skb->data, from, len);
}
-
-#endif /* rhel version < 5.2 */
-
-#endif /* linux kernel < 2.6.22 */
+#endif /* !HAVE_SKBUFF_HEADER_HELPERS */
#endif
diff --git a/datapath/linux-2.6/compat-2.6/include/linux/tcp.h b/datapath/linux-2.6/compat-2.6/include/linux/tcp.h
index 362f56f..6fad193 100644
--- a/datapath/linux-2.6/compat-2.6/include/linux/tcp.h
+++ b/datapath/linux-2.6/compat-2.6/include/linux/tcp.h
@@ -1,27 +1,9 @@
#ifndef __LINUX_TCP_WRAPPER_H
#define __LINUX_TCP_WRAPPER_H 1
-#include
-#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,23)
-#undef dev_get_by_name
-#endif
-
#include_next
-#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,23)
-/*----------------------------------------------------------------------------
- * In 2.6.24, a namespace argument became required for dev_get_by_name. */
-#define net_init NULL
-
-#define dev_get_by_name(net, name) \
- dev_get_by_name((name))
-
-#endif /* linux kernel <= 2.6.23 */
-
-#if ((LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22)) && \
- (RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(5,2)))
-
-#ifdef __KERNEL__
+#ifndef HAVE_SKBUFF_HEADER_HELPERS
static inline struct tcphdr *tcp_hdr(const struct sk_buff *skb)
{
return (struct tcphdr *)skb_transport_header(skb);
@@ -31,8 +13,6 @@ static inline unsigned int tcp_hdrlen(const struct sk_buff *skb)
{
return tcp_hdr(skb)->doff * 4;
}
-#endif /* __KERNEL__ */
-
-#endif /* linux kernel < 2.6.22 */
+#endif /* !HAVE_SKBUFF_HEADER_HELPERS */
#endif
diff --git a/datapath/linux-2.6/compat-2.6/include/linux/udp.h b/datapath/linux-2.6/compat-2.6/include/linux/udp.h
index 52cc4ca..6fe4721 100644
--- a/datapath/linux-2.6/compat-2.6/include/linux/udp.h
+++ b/datapath/linux-2.6/compat-2.6/include/linux/udp.h
@@ -3,18 +3,11 @@
#include_next
-#include
-#if ((LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22)) && \
- (RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(5,2)))
-
-#ifdef __KERNEL__
+#ifndef HAVE_SKBUFF_HEADER_HELPERS
static inline struct udphdr *udp_hdr(const struct sk_buff *skb)
{
return (struct udphdr *)skb_transport_header(skb);
}
-#endif /* __KERNEL__ */
-
-
-#endif /* linux kernel < 2.6.22 */
+#endif /* HAVE_SKBUFF_HEADER_HELPERS */
#endif
diff --git a/datapath/linux-2.6/compat-2.6/include/net/netlink.h b/datapath/linux-2.6/compat-2.6/include/net/netlink.h
new file mode 100644
index 0000000..e0d594d
--- /dev/null
+++ b/datapath/linux-2.6/compat-2.6/include/net/netlink.h
@@ -0,0 +1,22 @@
+#ifndef __NET_NETLINK_WRAPPER_H
+#define __NET_NETLINK_WRAPPER_H 1
+
+#include_next
+
+#ifndef HAVE_NLA_NUL_STRING
+#define NLA_NUL_STRING NLA_STRING
+
+static inline int VERIFY_NUL_STRING(struct nlattr *attr)
+{
+ return (!attr || (nla_len(attr)
+ && memchr(nla_data(attr), '\0', nla_len(attr)))
+ ? 0 : EINVAL);
+}
+#else
+static inline int VERIFY_NUL_STRING(struct nlattr *attr)
+{
+ return 0;
+}
+#endif /* !HAVE_NLA_NUL_STRING */
+
+#endif /* net/netlink.h */
diff --git a/datapath/linux-2.6/compat-2.6/veth.c b/datapath/linux-2.6/compat-2.6/veth.c
new file mode 100644
index 0000000..3cda336
--- /dev/null
+++ b/datapath/linux-2.6/compat-2.6/veth.c
@@ -0,0 +1,537 @@
+/* veth driver port to Linux 2.6.18 */
+
+/*
+ * drivers/net/veth.c
+ *
+ * Copyright (C) 2007, 2009 OpenVZ http://openvz.org, SWsoft Inc
+ *
+ * Author: Pavel Emelianov
+ * Ethtool interface from: Eric W. Biederman
+ *
+ */
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#define DRV_NAME "veth"
+#define DRV_VERSION "1.0"
+
+struct veth_net_stats {
+ unsigned long rx_packets;
+ unsigned long tx_packets;
+ unsigned long rx_bytes;
+ unsigned long tx_bytes;
+ unsigned long tx_dropped;
+};
+
+struct veth_priv {
+ struct net_device *peer;
+ struct net_device *dev;
+ struct list_head list;
+ struct veth_net_stats *stats;
+ unsigned ip_summed;
+ struct net_device_stats dev_stats;
+};
+
+static LIST_HEAD(veth_list);
+
+/*
+ * ethtool interface
+ */
+
+static struct {
+ const char string[ETH_GSTRING_LEN];
+} ethtool_stats_keys[] = {
+ { "peer_ifindex" },
+};
+
+static int veth_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ cmd->supported = 0;
+ cmd->advertising = 0;
+ cmd->speed = SPEED_10000;
+ cmd->duplex = DUPLEX_FULL;
+ cmd->port = PORT_TP;
+ cmd->phy_address = 0;
+ cmd->transceiver = XCVR_INTERNAL;
+ cmd->autoneg = AUTONEG_DISABLE;
+ cmd->maxtxpkt = 0;
+ cmd->maxrxpkt = 0;
+ return 0;
+}
+
+static void veth_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
+{
+ strcpy(info->driver, DRV_NAME);
+ strcpy(info->version, DRV_VERSION);
+ strcpy(info->fw_version, "N/A");
+}
+
+static void veth_get_strings(struct net_device *dev, u32 stringset, u8 *buf)
+{
+ switch(stringset) {
+ case ETH_SS_STATS:
+ memcpy(buf, ðtool_stats_keys, sizeof(ethtool_stats_keys));
+ break;
+ }
+}
+
+static void veth_get_ethtool_stats(struct net_device *dev,
+ struct ethtool_stats *stats, u64 *data)
+{
+ struct veth_priv *priv;
+
+ priv = netdev_priv(dev);
+ data[0] = priv->peer->ifindex;
+}
+
+static u32 veth_get_rx_csum(struct net_device *dev)
+{
+ struct veth_priv *priv;
+
+ priv = netdev_priv(dev);
+ return priv->ip_summed == CHECKSUM_UNNECESSARY;
+}
+
+static int veth_set_rx_csum(struct net_device *dev, u32 data)
+{
+ struct veth_priv *priv;
+
+ priv = netdev_priv(dev);
+ priv->ip_summed = data ? CHECKSUM_UNNECESSARY : CHECKSUM_NONE;
+ return 0;
+}
+
+static u32 veth_get_tx_csum(struct net_device *dev)
+{
+ return (dev->features & NETIF_F_NO_CSUM) != 0;
+}
+
+static int veth_set_tx_csum(struct net_device *dev, u32 data)
+{
+ if (data)
+ dev->features |= NETIF_F_NO_CSUM;
+ else
+ dev->features &= ~NETIF_F_NO_CSUM;
+ return 0;
+}
+
+static struct ethtool_ops veth_ethtool_ops = {
+ .get_settings = veth_get_settings,
+ .get_drvinfo = veth_get_drvinfo,
+ .get_link = ethtool_op_get_link,
+ .get_rx_csum = veth_get_rx_csum,
+ .set_rx_csum = veth_set_rx_csum,
+ .get_tx_csum = veth_get_tx_csum,
+ .set_tx_csum = veth_set_tx_csum,
+ .get_sg = ethtool_op_get_sg,
+ .set_sg = ethtool_op_set_sg,
+ .get_strings = veth_get_strings,
+ .get_ethtool_stats = veth_get_ethtool_stats,
+};
+
+/*
+ * xmit
+ */
+
+static int veth_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct net_device *rcv = NULL;
+ struct veth_priv *priv, *rcv_priv;
+ struct veth_net_stats *stats;
+ int length, cpu;
+
+ skb_orphan(skb);
+
+ priv = netdev_priv(dev);
+ rcv = priv->peer;
+ rcv_priv = netdev_priv(rcv);
+
+ cpu = smp_processor_id();
+ stats = per_cpu_ptr(priv->stats, cpu);
+
+ if (!(rcv->flags & IFF_UP))
+ goto outf;
+
+ skb->dev = rcv;
+ skb->pkt_type = PACKET_HOST;
+ skb->protocol = eth_type_trans(skb, rcv);
+ if (dev->features & NETIF_F_NO_CSUM)
+ skb->ip_summed = rcv_priv->ip_summed;
+
+ dst_release(skb->dst);
+ skb->dst = NULL;
+ secpath_reset(skb);
+ nf_reset(skb);
+
+ length = skb->len;
+
+ stats->tx_bytes += length;
+ stats->tx_packets++;
+
+ stats = per_cpu_ptr(rcv_priv->stats, cpu);
+ stats->rx_bytes += length;
+ stats->rx_packets++;
+
+ netif_rx(skb);
+ return 0;
+
+outf:
+ kfree_skb(skb);
+ stats->tx_dropped++;
+ return 0;
+}
+
+/*
+ * general routines
+ */
+
+static struct net_device_stats *veth_get_stats(struct net_device *dev)
+{
+ struct veth_priv *priv;
+ struct net_device_stats *dev_stats;
+ int cpu;
+ struct veth_net_stats *stats;
+
+ priv = netdev_priv(dev);
+ dev_stats = &priv->dev_stats;
+
+ dev_stats->rx_packets = 0;
+ dev_stats->tx_packets = 0;
+ dev_stats->rx_bytes = 0;
+ dev_stats->tx_bytes = 0;
+ dev_stats->tx_dropped = 0;
+
+ for_each_online_cpu(cpu) {
+ stats = per_cpu_ptr(priv->stats, cpu);
+
+ dev_stats->rx_packets += stats->rx_packets;
+ dev_stats->tx_packets += stats->tx_packets;
+ dev_stats->rx_bytes += stats->rx_bytes;
+ dev_stats->tx_bytes += stats->tx_bytes;
+ dev_stats->tx_dropped += stats->tx_dropped;
+ }
+
+ return dev_stats;
+}
+
+static int veth_open(struct net_device *dev)
+{
+ struct veth_priv *priv;
+
+ priv = netdev_priv(dev);
+ if (priv->peer == NULL)
+ return -ENOTCONN;
+
+ if (priv->peer->flags & IFF_UP) {
+ netif_carrier_on(dev);
+ netif_carrier_on(priv->peer);
+ }
+ return 0;
+}
+
+static int veth_dev_init(struct net_device *dev)
+{
+ struct veth_net_stats *stats;
+ struct veth_priv *priv;
+
+ stats = alloc_percpu(struct veth_net_stats);
+ if (stats == NULL)
+ return -ENOMEM;
+
+ priv = netdev_priv(dev);
+ priv->stats = stats;
+ return 0;
+}
+
+static void veth_dev_free(struct net_device *dev)
+{
+ struct veth_priv *priv;
+
+ priv = netdev_priv(dev);
+ free_percpu(priv->stats);
+ free_netdev(dev);
+}
+
+static void veth_setup(struct net_device *dev)
+{
+ ether_setup(dev);
+
+ dev->hard_start_xmit = veth_xmit;
+ dev->get_stats = veth_get_stats;
+ dev->open = veth_open;
+ dev->ethtool_ops = &veth_ethtool_ops;
+ dev->features |= NETIF_F_LLTX;
+ dev->init = veth_dev_init;
+ dev->destructor = veth_dev_free;
+}
+
+static void veth_change_state(struct net_device *dev)
+{
+ struct net_device *peer;
+ struct veth_priv *priv;
+
+ priv = netdev_priv(dev);
+ peer = priv->peer;
+
+ if (netif_carrier_ok(peer)) {
+ if (!netif_carrier_ok(dev))
+ netif_carrier_on(dev);
+ } else {
+ if (netif_carrier_ok(dev))
+ netif_carrier_off(dev);
+ }
+}
+
+static int veth_device_event(struct notifier_block *unused,
+ unsigned long event, void *ptr)
+{
+ struct net_device *dev = ptr;
+
+ if (dev->open != veth_open)
+ goto out;
+
+ switch (event) {
+ case NETDEV_CHANGE:
+ veth_change_state(dev);
+ break;
+ }
+out:
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block veth_notifier_block __read_mostly = {
+ .notifier_call = veth_device_event,
+};
+
+/*
+ * netlink interface
+ */
+
+static int veth_newlink(const char *devname, const char *peername)
+{
+ int err;
+ const char *names[2];
+ struct net_device *devs[2];
+ int i;
+
+ names[0] = devname;
+ names[1] = peername;
+ devs[0] = devs[1] = NULL;
+
+ for (i = 0; i < 2; i++) {
+ struct net_device *dev;
+
+ err = -ENOMEM;
+ devs[i] = alloc_netdev(sizeof(struct veth_priv),
+ names[i], veth_setup);
+ if (!devs[i]) {
+ goto err;
+ }
+
+ dev = devs[i];
+
+ if (strchr(dev->name, '%')) {
+ err = dev_alloc_name(dev, dev->name);
+ if (err < 0)
+ goto err;
+ }
+ random_ether_addr(dev->dev_addr);
+
+ err = register_netdevice(dev);
+ if (err < 0)
+ goto err;
+
+ netif_carrier_off(dev);
+ }
+
+ /*
+ * tie the devices together
+ */
+
+ for (i = 0; i < 2; i++) {
+ struct veth_priv *priv = netdev_priv(devs[i]);
+ priv->dev = devs[i];
+ priv->peer = devs[!i];
+ if (!i)
+ list_add(&priv->list, &veth_list);
+ else
+ INIT_LIST_HEAD(&priv->list);
+ }
+ return 0;
+
+err:
+ for (i = 0; i < 2; i++) {
+ if (devs[i]) {
+ if (devs[i]->reg_state != NETREG_UNINITIALIZED)
+ unregister_netdevice(devs[i]);
+ else
+ free_netdev(devs[i]);
+ }
+ }
+ return err;
+}
+
+static void veth_dellink(struct net_device *dev)
+{
+ struct veth_priv *priv;
+ struct net_device *peer;
+
+ priv = netdev_priv(dev);
+ peer = priv->peer;
+
+ if (!list_empty(&priv->list))
+ list_del(&priv->list);
+
+ priv = netdev_priv(peer);
+ if (!list_empty(&priv->list))
+ list_del(&priv->list);
+
+ unregister_netdevice(dev);
+ unregister_netdevice(peer);
+}
+
+/*
+ * sysfs
+ */
+
+/*
+ * "show" function for the veth_pairs attribute.
+ * The class parameter is ignored.
+ */
+static ssize_t veth_show_veth_pairs(struct class *cls, char *buffer)
+{
+ int res = 0;
+ struct veth_priv *priv;
+
+ list_for_each_entry(priv, &veth_list, list) {
+ if (res > (PAGE_SIZE - (IFNAMSIZ * 2 + 1))) {
+ /* not enough space for another interface name */
+ if ((PAGE_SIZE - res) > 10)
+ res = PAGE_SIZE - 10;
+ res += sprintf(buffer + res, "++more++");
+ break;
+ }
+ res += sprintf(buffer + res, "%s,%s ",
+ priv->dev->name, priv->peer->name);
+ }
+ res += sprintf(buffer + res, "\n");
+ res++;
+ return res;
+}
+
+/*
+ * "store" function for the veth_pairs attribute. This is what
+ * creates and deletes veth pairs.
+ *
+ * The class parameter is ignored.
+ *
+ */
+static ssize_t veth_store_veth_pairs(struct class *cls, const char *buffer,
+ size_t count)
+{
+ int c = *buffer++;
+ int retval;
+ printk("1\n");
+ if (c == '+') {
+ char devname[IFNAMSIZ + 1] = "";
+ char peername[IFNAMSIZ + 1] = "";
+ char *comma = strchr(buffer, ',');
+ printk("2\n");
+ if (!comma)
+ goto err_no_cmd;
+ strncat(devname, buffer,
+ min_t(int, sizeof devname, comma - buffer));
+ strncat(peername, comma + 1,
+ min_t(int, sizeof peername, strcspn(comma + 1, "\n")));
+ printk("3 '%s' '%s'\n", devname, peername);
+ if (!dev_valid_name(devname) || !dev_valid_name(peername))
+ goto err_no_cmd;
+ printk("4\n");
+ rtnl_lock();
+ retval = veth_newlink(devname, peername);
+ rtnl_unlock();
+ return retval ? retval : count;
+ } else if (c == '-') {
+ struct net_device *dev;
+
+ rtnl_lock();
+ dev = dev_get_by_name(buffer);
+ if (!dev)
+ retval = -ENODEV;
+ else if (dev->init != veth_dev_init)
+ retval = -EINVAL;
+ else {
+ veth_dellink(dev);
+ retval = count;
+ }
+ rtnl_unlock();
+
+ return retval;
+ }
+
+err_no_cmd:
+ printk(KERN_ERR DRV_NAME ": no command found in veth_pairs. Use +ifname,peername or -ifname.\n");
+ return -EPERM;
+}
+
+/* class attribute for veth_pairs file. This ends up in /sys/class/net */
+static CLASS_ATTR(veth_pairs, S_IWUSR | S_IRUGO,
+ veth_show_veth_pairs, veth_store_veth_pairs);
+
+static struct class *netdev_class;
+
+/*
+ * Initialize sysfs. This sets up the veth_pairs file in
+ * /sys/class/net.
+ */
+int veth_create_sysfs(void)
+{
+ struct net_device *dev = dev_get_by_name("lo");
+ if (!dev)
+ return -ESRCH;
+ netdev_class = dev->class_dev.class;
+ if (!netdev_class)
+ return -ENODEV;
+
+ return class_create_file(netdev_class, &class_attr_veth_pairs);
+}
+
+/*
+ * Remove /sys/class/net/veth_pairs.
+ */
+void veth_destroy_sysfs(void)
+{
+ class_remove_file(netdev_class, &class_attr_veth_pairs);
+}
+
+
+
+/*
+ * init/fini
+ */
+
+static __init int veth_init(void)
+{
+ int retval = veth_create_sysfs();
+ if (retval)
+ return retval;
+ register_netdevice_notifier(&veth_notifier_block);
+ return 0;
+}
+
+static __exit void veth_exit(void)
+{
+ unregister_netdevice_notifier(&veth_notifier_block);
+}
+
+module_init(veth_init);
+module_exit(veth_exit);
+
+MODULE_DESCRIPTION("Virtual Ethernet Tunnel");
+MODULE_LICENSE("GPL v2");
diff --git a/datapath/nx_act_snat.c b/datapath/nx_act_snat.c
index 127714f..c7705d4 100644
--- a/datapath/nx_act_snat.c
+++ b/datapath/nx_act_snat.c
@@ -1,7 +1,7 @@
#ifdef SUPPORT_SNAT
/*
* Distributed under the terms of the GNU GPL version 2.
- * Copyright (c) 2008 Nicira Networks
+ * Copyright (c) 2008, 2009 Nicira Networks
*/
#include
@@ -70,10 +70,13 @@ static inline struct nf_bridge_info *nf_bridge_alloc(struct sk_buff *skb)
}
/* Save a copy of the original Ethernet header. */
-static inline void snat_save_header(struct sk_buff *skb)
+void snat_save_header(struct sk_buff *skb)
{
int header_size = ETH_HLEN + nf_bridge_encap_header_len(skb);
+ if (!skb->nf_bridge)
+ return;
+
skb_copy_from_linear_data_offset(skb, -header_size,
skb->nf_bridge->data, header_size);
}
@@ -165,11 +168,8 @@ dnat_mac(struct net_bridge_port *p, struct sk_buff *skb)
list_for_each_entry (m, &sc->mappings, node) {
if (m->ip_addr == iph->daddr){
/* Found it! */
- if (!make_writable(&skb)) {
- if (net_ratelimit())
- printk("make_writable failed\n");
+ if (!make_writable(&skb))
return -EINVAL;
- }
m->used = jiffies;
memcpy(eh->h_dest, m->hw_addr, ETH_ALEN);
break;
@@ -242,7 +242,6 @@ snat_pre_route_finish(struct sk_buff *skb)
/* Pass the translated packet as input to the OpenFlow stack, which
* consumes it. */
- snat_save_header(skb);
skb_push(skb, ETH_HLEN);
skb_reset_mac_header(skb);
fwd_port_input(p->dp->chain, skb, p);
@@ -332,11 +331,8 @@ handle_icmp_snat(struct sk_buff *skb)
/* Send an echo reply in response */
nskb = skb_copy(skb, GFP_ATOMIC);
- if (!nskb) {
- if (net_ratelimit())
- printk("skb copy failed for icmp reply\n");
+ if (!nskb)
return -1;
- }
/* Update Ethernet header. */
eh = eth_hdr(nskb);
@@ -355,14 +351,14 @@ handle_icmp_snat(struct sk_buff *skb)
tmp_ip = iph->daddr;
iph->daddr = iph->saddr;
iph->saddr = tmp_ip;
- iph->check = ip_fast_csum(iph, iph->ihl);
+ iph->check = ip_fast_csum((void *)iph, iph->ihl);
/* Update ICMP header. */
icmph = icmp_hdr(nskb);
icmph->type = ICMP_ECHOREPLY;
icmph->checksum = 0;
- icmph->checksum = ip_compute_csum(icmph,
- nskb->tail - nskb->transport_header);
+ icmph->checksum = ip_compute_csum((void *)icmph,
+ nskb->tail - skb_transport_header(nskb));
dp_xmit_skb_push(nskb);
@@ -409,7 +405,7 @@ snat_pre_route(struct sk_buff *skb)
goto consume;
iph = ip_hdr(skb);
- if (unlikely(ip_fast_csum(iph, iph->ihl)))
+ if (unlikely(ip_fast_csum((void *)iph, iph->ihl)))
goto consume;
len = ntohs(iph->tot_len);
@@ -554,7 +550,9 @@ snat_del_port(struct datapath *dp, const struct nx_snat_config *nsc)
if (!p) {
if (net_ratelimit())
- printk("Attempt to remove snat on non-existent port: %d\n", port);
+ printk(KERN_NOTICE "%s: attempt to remove snat on "
+ "non-existent port: %d\n",
+ dp->netdev->name, port);
return -EINVAL;
}
@@ -563,7 +561,8 @@ snat_del_port(struct datapath *dp, const struct nx_snat_config *nsc)
/* SNAT not configured on this port */
spin_unlock_irqrestore(&p->lock, flags);
if (net_ratelimit())
- printk("Attempt to remove snat on non-snat port: %d\n", port);
+ printk(KERN_NOTICE "%s: attempt to remove snat on "
+ "non-snat port: %d\n", dp->netdev->name, port);
return -EINVAL;
}
@@ -587,7 +586,9 @@ snat_add_port(struct datapath *dp, const struct nx_snat_config *nsc)
if (!p) {
if (net_ratelimit())
- printk("Attempt to add snat on non-existent port: %d\n", port);
+ printk(KERN_NOTICE "%s: attempt to add snat on "
+ "non-existent port: %d\n",
+ dp->netdev->name, port);
return -EINVAL;
}
diff --git a/datapath/nx_act_snat.h b/datapath/nx_act_snat.h
index 7570aa0..fc86d38 100644
--- a/datapath/nx_act_snat.h
+++ b/datapath/nx_act_snat.h
@@ -34,6 +34,7 @@ struct snat_conf {
void snat_local_in(struct sk_buff *skb);
int snat_pre_route(struct sk_buff *skb);
void snat_skb(struct datapath *dp, const struct sk_buff *skb, int out_port);
+void snat_save_header(struct sk_buff *skb);
int snat_copy_header(struct sk_buff *skb);
void snat_maint(struct net_bridge_port *p);
int snat_mod_config(struct datapath *, const struct nx_act_config *);
diff --git a/datapath/table-hash.c b/datapath/table-hash.c
index 0e1692f..9e27b0c 100644
--- a/datapath/table-hash.c
+++ b/datapath/table-hash.c
@@ -1,6 +1,6 @@
/*
* Distributed under the terms of the GNU GPL version 2.
- * Copyright (c) 2007, 2008 The Board of Trustees of The Leland
+ * Copyright (c) 2007, 2008, 2009 The Board of Trustees of The Leland
* Stanford Junior University
*/
@@ -148,7 +148,8 @@ static int table_hash_timeout(struct datapath *dp, struct sw_table *swt)
unsigned int i;
int count = 0;
- mutex_lock(&dp_mutex);
+ if (mutex_lock_interruptible(&dp_mutex))
+ return 0;
for (i = 0; i <= th->bucket_mask; i++) {
struct sw_flow **bucket = &th->buckets[i];
struct sw_flow *flow = *bucket;
@@ -241,7 +242,8 @@ struct sw_table *table_hash_create(unsigned int polynomial,
BUG_ON(n_buckets & (n_buckets - 1));
th->buckets = kmem_zalloc(n_buckets * sizeof *th->buckets);
if (th->buckets == NULL) {
- printk("failed to allocate %u buckets\n", n_buckets);
+ printk(KERN_EMERG "failed to allocate %u buckets\n",
+ n_buckets);
kfree(th);
return NULL;
}
@@ -421,8 +423,8 @@ kmem_alloc(size_t size)
if (!ptr) {
ptr = vmalloc(size);
if (ptr)
- printk("openflow: used vmalloc for %lu bytes\n",
- (unsigned long)size);
+ printk(KERN_NOTICE "openflow: used vmalloc for %lu "
+ "bytes\n", (unsigned long)size);
}
return ptr;
}
diff --git a/datapath/table-linear.c b/datapath/table-linear.c
index 1d5e186..bef58a6 100644
--- a/datapath/table-linear.c
+++ b/datapath/table-linear.c
@@ -1,6 +1,6 @@
/*
* Distributed under the terms of the GNU GPL version 2.
- * Copyright (c) 2007, 2008 The Board of Trustees of The Leland
+ * Copyright (c) 2007, 2008, 2009 The Board of Trustees of The Leland
* Stanford Junior University
*/
@@ -124,7 +124,8 @@ static int table_linear_timeout(struct datapath *dp, struct sw_table *swt)
struct sw_flow *flow;
int count = 0;
- mutex_lock(&dp_mutex);
+ if (mutex_lock_interruptible(&dp_mutex))
+ return 0;
list_for_each_entry (flow, &tl->flows, node) {
int reason = flow_timeout(flow);
if (reason >= 0) {
diff --git a/debian/.gitignore b/debian/.gitignore
index ef6b801..1df6fba 100644
--- a/debian/.gitignore
+++ b/debian/.gitignore
@@ -5,6 +5,7 @@
/control
/corekeeper
/files
+/nicira-switch
/openflow
/openflow-common
/openflow-common.copyright
diff --git a/debian/control.in b/debian/control.in
index 608800d..c5da704 100644
--- a/debian/control.in
+++ b/debian/control.in
@@ -2,7 +2,7 @@ Source: openflow
Section: net
Priority: extra
Maintainer: OpenFlow Team
-Build-Depends: debhelper (>= 5), autoconf (>= 2.60), automake1.10, libssl-dev, pkg-config (>= 0.21), po-debconf, bzip2, openssl, libncurses5-dev
+Build-Depends: debhelper (>= 5), autoconf (>= 2.60), automake1.10, libssl-dev, pkg-config (>= 0.21), po-debconf, bzip2, openssl, libncurses5-dev, libpcre3-dev
Standards-Version: 3.7.3
Package: openflow-datapath-source
diff --git a/debian/openflow-common.install b/debian/openflow-common.install
index 624bc34..eed7413 100644
--- a/debian/openflow-common.install
+++ b/debian/openflow-common.install
@@ -1,2 +1,3 @@
+_debian/utilities/ofp-parse-leaks usr/bin
_debian/utilities/ofp-pki usr/sbin
_debian/utilities/vlogconf usr/sbin
diff --git a/debian/openflow-switch.init b/debian/openflow-switch.init
index 8ee19cd..cd518a7 100755
--- a/debian/openflow-switch.init
+++ b/debian/openflow-switch.init
@@ -25,6 +25,8 @@ DESC=secchan
test -x $DAEMON || exit 0
+NICIRA_OUI="002320"
+
LOGDIR=/var/log/openflow
PIDFILE=/var/run/$NAME.pid
DHCLIENT_PIDFILE=/var/run/dhclient.of0.pid
@@ -271,6 +273,20 @@ case "$1" in
xx='[0-9abcdefABCDEF][0-9abcdefABCDEF]'
case $DATAPATH_ID in
'')
+ # Check if the DMI System UUID contains a Nicira mac address
+ # that should be used for this datapath. The UUID is assumed
+ # to be RFC 4122 compliant.
+ DMIDECODE=`which dmidecode`
+ if [ -n $DMIDECODE ]; then
+ UUID_MAC=`$DMIDECODE -s system-uuid | cut -d'-' -f 5`
+ case $UUID_MAC in
+ $NICIRA_OUI*)
+ ifconfig of0 down
+ must_succeed "Setting of0 MAC address to $UUID_MAC" ifconfig of0 hw ether $UUID_MAC
+ ifconfig of0 up
+ ;;
+ esac
+ fi
;;
$xx:$xx:$xx:$xx:$xx:$xx)
ifconfig of0 down
diff --git a/debian/openflow-switch.template b/debian/openflow-switch.template
index 2b78b6b..f3f641e 100644
--- a/debian/openflow-switch.template
+++ b/debian/openflow-switch.template
@@ -159,9 +159,10 @@ DAEMON_OPTS=""
# DATAPATH_ID: Identifier for this switch.
#
-# By default, the switch generates a new, random datapath ID every time
-# it starts up. By setting this value, the datapath ID will be consistent
-# from one run to the next.
+# By default, the switch checks if the DMI System UUID contains a Nicira
+# mac address to use as a datapath ID. If not, then the switch generates
+# a new, random datapath ID every time it starts up. By setting this
+# value, the supplied datapath ID will always be used.
#
# Set DATAPATH_ID to a MAC address in the form XX:XX:XX:XX:XX:XX where each
# X is a hexadecimal digit (0-9 or a-f).
diff --git a/doc/ofmp-spec/ofmp-spec.tex b/doc/ofmp-spec/ofmp-spec.tex
new file mode 100644
index 0000000..90a73ab
--- /dev/null
+++ b/doc/ofmp-spec/ofmp-spec.tex
@@ -0,0 +1,640 @@
+% Created 2009-02-02 Mon 17:09
+\documentclass[11pt,a4paper]{article}
+\usepackage[utf8]{inputenc}
+\usepackage[T1]{fontenc}
+\usepackage{graphicx}
+\usepackage{longtable}
+\usepackage{hyperref}
+
+
+\title{OpenFlow Management Protocol}
+\author{Version 0.0.1 (Wire Protocol 0x80)}
+\date{04 February 2009}
+
+\begin{document}
+
+\maketitle
+
+\setcounter{tocdepth}{3}
+\tableofcontents
+\vspace*{1cm}
+
+\section{Goals}
+\label{sec-1}
+
+
+The high-level design goals for the protocol are described in this
+section.
+
+\begin{itemize}
+\item Rapid implementation
+
+ Most importantly, Nicira must implement the protocol quickly to
+ meet current business objectives. However, the ability to implement
+ the protocol rapidly should be a benefit to anyone implementing the
+ protocol.
+\item Support ``all'' switch features
+
+ The controller should be able to learn about all available switch
+ features and retrieve and modify their values through the protocol.
+\item ``Multi-master''
+
+ It must be possible to make changes at both the controller and the
+ switch. When changes are made and connectivity exists between the
+ switch and the controller, both sides should be resynchronized
+ within a short period of time (< 1 sec??).
+\item Extensible
+
+ To support future enhancements, the protocol must be capable of
+ accommodating additional configuration settings and
+ upgrading/downgrading configuration appropriately.
+\item Scalable
+
+ The protocol should be able to handle any size switch, from a few
+ ports to hundreds of ports.
+\item Minimal cost of implementation on the switch
+
+ One of the selling points of OpenFlow is the ability to take cost
+ out of switches by implementing complex network-wide functionality
+ in the controller. It is thus preferable to minimize the cost of
+ implementation on the switch.
+\end{itemize}
+\section{Specification}
+\label{sec-2}
+
+
+ The OpenFlow Management Protocol is based on the OpenFlow
+ protocol, with which it is often expected to be used. It augments
+ the flow table management provided by the OpenFlow protocol with
+ more general switch management functions required in a full-featured
+ switch.
+
+\subsection{Connection Maintenance}
+\label{sec-2.1}
+
+
+ As with OpenFlow, the switch is responsible for initiating the
+ connection to the controller at a user configurable IP address and
+ TCP port. The default destination port for the connection is 6632,
+ one less than the default OpenFlow destination port 6633. The IP
+ address of the controller may be discovered through the same
+ discovery protocol defined for OpenFlow.
+
+ The switch must attempt to maintain an open connection to the
+ controller at all times while in an operational state, although the
+ exact details for detecting connection failure and opening a new
+ connection are left to individual implementations.
+
+ After connection establishment, both sides must send \hyperref[sec-2.6.1]{hello messages}
+ to each other before any other messages are sent.
+
+ While the default ports for OpenFlow and the OpenFlow Management
+ Protocol are different, the latter is deliberately designed to be
+ able to coexist on the same port, in which case the appropriate
+ connection type can be determined by examining the initial hello
+ message sent after connection establishment.
+
+\subsection{Encryption}
+\label{sec-2.2}
+
+
+ The OpenFlow Management Protocol uses the same encryption
+ mechanism defined in the analogous section of the OpenFlow
+ specification. It is expected, but not required, that the
+ Management Protocol will use the same keys as the OpenFlow
+ protocol for authentication of either end of the connection.
+
+\subsection{Framing}
+\label{sec-2.3}
+
+
+ The Management Protocol uses the same basic header as
+ the OpenFlow protocol, defined as \verb|ofp_header| in the OpenFlow
+ specification. The fields of this header are used in the same way
+ as they are in the OpenFlow protocol except that the type IDs
+ generally have different meanings. However, a few message types
+ are shared among the two protocols, in which case they have the
+ same message type code in both protocols.
+
+ Each message type is further described in subsequent sections of
+ this document. The message types IDs are defined by:
+
+{ \footnotesize
+
+\begin{verbatim}
+ enum ofmp_type {
+ OFMP_HELLO, /* Message Types Shared with OpenFlow. */
+ OFMP_ERROR, /* Message Types Shared with OpenFlow. */
+ OFMP_ECHO_REQUEST, /* Message Types Shared with OpenFlow. */
+ OFMP_ECHO_REPLY, /* Message Types Shared with OpenFlow. */
+ OFMP_VENDOR, /* Message Types Shared with OpenFlow. */
+ OFMP_RESERVED_0, /* Reserved Message Types. */
+ OFMP_RESERVED_1, /* Reserved Message Types. */
+ OFMP_EXTENDED_DATA, /* Extended Data Message */
+ OFMP_CAPABILITY_REQUEST,/* Management Protocol-specific. */
+ OFMP_CAPABILITY_REPLY, /* Management Protocol-specific. */
+ OFMP_SWITCH_RESOURCES, /* Management Protocol-specific. */
+ OFMP_CONFIG_UPDATE, /* Management Protocol-specific. */
+ OFMP_CONFIG_UPDATE_ACK, /* Management Protocol-specific. */
+ OFMP_HOST_INFO /* Management Protocol-specific. */
+ };
+
+\end{verbatim}
+
+
+}
+
+\subsection{Type/Length/Value Encoding in Messages}
+\label{sec-2.4}
+
+
+ Some messages use a type/length/value (TLV) encoding scheme for
+ data transferred in the message body. This is done for primarily
+ for flexibility and extensibility.
+
+ When present, TLVs occur in sequence until a TLV is encountered
+ with type \verb|OFMPTLV_END|. This TLV must have a length of zero. A
+ single TLV of type \verb|OFMPTLV_END| would represent an empty list of
+ TLVs.
+
+ The TLV message format is defined by:
+
+{ \footnotesize
+
+\begin{verbatim}
+ enum ofmp_tlv_type {
+ OFMPTLV_END = 0,
+ },
+
+ struct ofmp_tlv {
+ uint16_t type; /* Type of value. */
+ uint16_t len; /* Length of value (excluding type & len). */
+ data[0]; /* Value data as defined by type & len. */
+ };
+
+\end{verbatim}
+
+
+}
+
+ The meaning of the \verb|type| field is unique to each message type
+ that uses the TLV encoding except that type code 0 must always
+ indicate the end of the TLV encoded data. Implementations must
+ skip unknown type values and continue processing to enhance
+ forward compatibility.
+
+\subsection{Reserved Message Types}
+\label{sec-2.5}
+
+
+ Because of the use the same basic message framing, distinguishing
+ the OpenFlow Management Protocol from OpenFlow could be
+ difficult. The definition and required use of the Management
+ Protocol \hyperref[sec-2.6.1]{hello message} is intended to be compatible with
+ OpenFlow while facilitating this differentiation.
+
+ To further assist in differentiation, the message type IDs for the
+ feature request and feature reply messages defined in the OpenFlow
+ specification are reserved in the Management Protocol.
+ If either the switch or the controller receives one of these
+ message type IDs on a connection expected to be a Management
+ Protocol connection, it must reply with an \hyperref[sec-2.6.5]{error message}
+ specifying reserved message type code and then terminate the
+ connection.
+
+\subsection{Messages Types Shared with OpenFlow}
+\label{sec-2.6}
+
+
+ There are a few message types used by the Management
+ Protocol that are used for essentially the same purposes in it and
+ the OpenFlow protocol and have identical or compatible message
+ formats. These message types use the same message type ID in both
+ protocols.
+
+\subsubsection{Hello}
+\label{sec-2.6.1}
+
+
+ This message is the same as the OpenFlow message except that
+ after the OpenFlow header the data of the message includes the
+ UTF-8 encoded string ``OPENFLOW MANAGEMENT PROTOCOL''. This
+ simplifies debugging and allows automatic detection of the
+ protocol.
+
+\subsubsection{Echo Request}
+\label{sec-2.6.2}
+
+
+ This message works exactly as the corresponding message in
+ OpenFlow.
+
+\subsubsection{Echo Reply}
+\label{sec-2.6.3}
+
+
+ This message works exactly as the corresponding message in
+ OpenFlow.
+
+\subsubsection{Vendor}
+\label{sec-2.6.4}
+
+
+ This message works exactly as the corresponding message in
+ OpenFlow.
+
+\subsubsection{Error}
+\label{sec-2.6.5}
+
+
+ This message works the same as the corresponding message in
+ OpenFlow with the exception that all the meaning of the type and
+ code fields are unique to each protocol.
+
+ For the Management Protocol, the type values are defined by:
+
+{ \footnotesize
+
+\begin{verbatim}
+ enum ofmp_error_type {
+ OFMPET_HELLO_FAILED, /* Hello failed. */
+ OFMPET_BAD_REQUEST, /* Request was not understood. */
+ OFMPET_UNKNOWN_FORMAT, /* Format of typed data not understood. */
+ OFMPET_CONFIG_ERROR /* Error processing configuration update. */
+ };
+
+\end{verbatim}
+
+
+}
+
+ The \verb|OFMPET_HELLO_FAILED| type is exactly analogous to the
+ \verb|OFPET_HELLO_FAILED| type in OpenFlow. A single code is currently
+ defined:
+
+{ \footnotesize
+
+\begin{verbatim}
+ enum ofmp_hello_failed_code {
+ OFMPHFC_INCOMPATIBLE, /* Hello failed */
+ };
+
+\end{verbatim}
+
+
+}
+
+ The \verb|OFMPET_BAD_REQUEST| type is also similar to the
+ \verb|OFPET_HELLO_FAILED| type in OpenFlow. The subtypes are:
+
+{ \footnotesize
+
+\begin{verbatim}
+ enum ofmp_bad_request_code {
+ OFMPBRC_BAD_VERSION, /* ofp_header.version not supported. */
+ OFMPBRC_BAD_TYPE, /* ofp_header.type not supported. */
+ OFMPBRC_RESERVED_0, /* For ID alignment w/compatible codes
+ * in OpenFlow */
+ OFMPBRC_BAD_VENDOR, /* Vendor not supported. */
+ OFMPBRC_BAD_SUBTYPE /* Vendor subtype not supported. */
+ };
+
+\end{verbatim}
+
+
+}
+
+ The \verb|data| field contains at least 64 bytes of the failed request.
+
+ The \verb|OFMPET_UNKNOWN_FORMAT| type is used when the format specified
+ for a message such as \hyperref[sec-2.8.2]{capabilities request} or
+ \hyperref[sec-2.8.4]{configuration update} is unknown. The \verb|code| field is not used in
+ this error. The \verb|data| field contains a sequence of 32-bit
+ unsigned integers representing acceptable format types in order
+ from most preferred to least.
+
+\subsection{Extended Data Message}
+\label{sec-2.7}
+
+
+ Because the OpenFlow message header only has a 16-bit length field,
+ the largest possible message size is 64kB. This may be
+ insufficient for some message types such as the configuration
+ update message. The extended data message removes this restriction
+ by encapsulating the message body of the original message (without
+ the base OpenFlow header) in more than one extended data messages.
+
+ The extended data message format is:
+
+{ \footnotesize
+
+\begin{verbatim}
+ enum ofmped_flags {
+ MORE_DATA = 1
+ };
+
+ struct ofmp_extended_data {
+ uint8_t type, /* Type code of the encapsulated message */
+ uint8_t flags,
+ uint8_t pad[6], /* Maintain same alignment as regular body */
+ uint8_t data[0]; /* Message body fragment */
+ };
+
+\end{verbatim}
+
+
+}
+
+ The MORE\_{}DATA flag must be set in every extended data message
+ sequence encapsulating a message body too long to fit in a single
+ standard message except the last message, in which the it must be
+ cleared. All but the final messages should typically be maximum
+ length, although this is not required.
+
+ To simplify implementations, messages MUST NOT be sent encapsulated
+ in extended data messages if they will fit in a single regular
+ message.
+
+\subsection{OpenFlow Management Protocol-specific Messages}
+\label{sec-2.8}
+
+
+ The remaining message types are specific to the Management Protocol.
+
+\subsubsection{Switch Resources}
+\label{sec-2.8.1}
+
+\begin{itemize}
+\item TODO Describe reply in detail
+\end{itemize}
+ The switch resources message describes the resources available on
+ the switch. It must be sent by the switch to the controller
+ immediately after the initial hello exchange at connection
+ establishment and any time thereafter when resources change on the
+ switch.
+
+\subsubsection{Capabilities Request}
+\label{sec-2.8.2}
+
+
+ The capabilities request message is sent from the controller to
+ the switch, which replies with a \hyperref[sec-2.8.3]{capabilities reply} message.
+ While not required, the controller will typically send a
+ capabilities request message after receiving the hello message on
+ connection establishment to determine switch capabilities. In
+ this way, it is very similar to the feature request and reply
+ messages in the OpenFlow specification. However, because the
+ encoding of the capabilities is different, the messages have
+ different names.
+
+ The capabilities request message format is:
+
+{ \footnotesize
+
+\begin{verbatim}
+ struct ofmp_capabilities_request {
+ uint32_t format; /* Format in which to send capabilities */
+ };
+
+\end{verbatim}
+
+
+}
+
+ The only field in this message, \verb|format|, specifies the format
+ the controller expects for the reply. The only capabilities
+ format current specified is the \hyperref[*==Simple==Capabilities==Format]{Simple Capabilities Format}.
+
+ On receipt of a capabilities request message, the switch must
+ send a capabilities reply in the specified format to the
+ controller or an \hyperref[*==Unknown==capabilities==format]{unknown capabilities format error message} in
+ reply.
+
+\subsubsection{Capabilities Reply}
+\label{sec-2.8.3}
+
+
+ The capabilities reply message is sent from the switch to the
+ controller in response to a \hyperref[sec-2.8.2]{capabilities request} message from the
+ controller. The capabilities reply message format is:
+
+{ \footnotesize
+
+\begin{verbatim}
+ struct ofmp_capabilities_reply {
+ uint32_t format; /* Format of capabilities info */
+ uint8_t data[0]; /* Capabilities data */
+ };
+
+\end{verbatim}
+
+
+}
+
+ The \verb|format| specifies the format of the capabilities data. It
+ must be the same as the \verb|format| field in the
+ \hyperref[sec-2.8.2]{capabilities request}.
+
+\paragraph{Simple Capability Format}
+\label{sec-2.8.3.1}
+
+\begin{itemize}
+\item Basically, same as vswitchd configuration syntax
+\item Each key/value pair is contained on a single line. The line
+ separator is a single newline character.
+\item Keys have hierarchy based on controlling entities domain
+ name. Initially all keys will be `com.nicira'\ldots{}
+\item Values can just be true/false to indicate the presence/absence
+ of a feature (or just not present for the absences).
+\item Values can also be strings and/or numbers indicating limits on
+ specific capabilities, etc.
+\item It should be possible to use a capabilities reply to determine
+ whether additional OpenFlow Management Protocol message
+ types are supported or not to minimize the need to change the
+ protocol version in the framing header.
+\end{itemize}
+\subsubsection{Configuration Update}
+\label{sec-2.8.4}
+
+
+ The configuration update message can be sent asynchronously in
+ either direction. It's purpose is to inform the peer that a
+ configuration value has changed. On receipt of a configuration
+ update message, the message must be processed and a corresponding
+ configuration update reply message describing the results of that
+ processing sent back to the peer.
+
+ The format of the configuration update message is:
+
+{ \footnotesize
+
+\begin{verbatim}
+ struct ofmp_config_update {
+ uint32_t format; /* Format of capabilities info */
+ uint8_t cookie[20]; /* Collision detect cookie */
+ uint8_t data[0]; /* Capabilities data */
+ };
+
+\end{verbatim}
+
+
+}
+
+ The \verb|format| field specifies the format in which the
+ configuration data is presented. The only format currently
+ supported is the \hyperref[sec-2.8.4.1]{Simple Configuration Format}.
+
+ The \verb|cookie| field is used to detect simultaneous configuration
+ changes on both peers. The use of the field is described in the
+ \hyperref[sec-2.8.4.2]{detection of simultaneous changes section}.
+
+ On receipt of a configuration update message the message must be
+ processed and either a \hyperref[*==Configuration==Update==Reply]{configuration update reply} or an
+ \hyperref[sec-2.6.5]{error message}.
+
+\paragraph{Simple Configuration Format}
+\label{sec-2.8.4.1}
+
+\begin{itemize}
+\item TODO complete this section more fully.
+\item Basically, the current vswitchd configuration file format
+\item Keys in ``fully specified form'' (no sections)
+\item Keys sorted as by implementation now
+\end{itemize}
+\paragraph{Detection of simultaneous changes}
+\label{sec-2.8.4.2}
+
+\begin{itemize}
+\item TODO Describe simultaneous change detection more completely.
+\item Collision detect cookie values are a SHA-1 hash of last
+ configuration accepted from peer, over sorted keys \& values in
+ memory.
+\item To prevent inadvertent overwriting of data, the switch is
+ treated as the master for all updates. It should refuse
+ updates with an incorrect cookie. Controller is responsible
+ for dealing with recovering from simultaneous changes by
+ merging configurations or whatever other means necessary.
+\end{itemize}
+\subsubsection{Configuration Update Acknowledgment}
+\label{sec-2.8.5}
+
+
+ The configuration update acknowledgment is sent in response to a
+ \hyperref[sec-2.8.4]{configuration update} message. It indicates the update has been
+ successfully accepted and processed. The message body consists
+ of an updated collision detect cookie:
+
+{ \footnotesize
+
+\begin{verbatim}
+ struct ofmp_config_update_ack {
+ uint8_t cookie[20]; /* Collision detect cookie */
+ };
+
+\end{verbatim}
+
+
+}
+
+\subsubsection{Host Info}
+\label{sec-2.8.6}
+
+
+ In some cases, the switch may definitively know additional
+ information about one or more hosts present on a given datapath
+ and port. In these cases the switch may send the host info
+ message to the controller to provide this information.
+
+ The format of the host info message is:
+
+{ \footnotesize
+
+\begin{verbatim}
+ struct ofmp_host_info {
+ uint64_t datapath_id; /* Referenced datapath. */
+ uint16_t port_id; /* Referenced port. */
+ data[0]; /* TLV encoded data about host. */
+ };
+
+\end{verbatim}
+
+
+}
+
+ The data in the message is encoded as described in the
+ \hyperref[sec-2.4]{Type/Length/Value Encoding in Messages} section.
+
+{ \footnotesize
+
+\begin{verbatim}
+ enum ofmp_host_info_tlv_type {
+ OFMPHITLV_END = 0, /* End of TLV records. */
+ OFMPHITLV_HOST_DLADDR , /* Host MAC address. */
+ OFMPHITLV_HOST_UUID /* Host unique identifier. */
+ };
+
+\end{verbatim}
+
+
+}
+
+ The value for \verb|OFMPHITLV_HOST_DLADDR| is a six-byte MAC address.
+ The value for \verb|OFMPHITLV_HOST_UUID| is a variable length
+ universally unique identifier for the host.
+
+\section{Design Rationale}
+\label{sec-3}
+
+
+\subsection{Why specify an additional protocol}
+\label{sec-3.1}
+
+
+ The core purpose of OpenFlow is to manage the flow table in the
+ switch. There are many other activities required to fully manage a
+ switch, configuring ports, updating stats, etc. Some of these
+ require significant data transfer that could interfere with the
+ primary purpose of OpenFlow.
+
+ Switches from existing vendors typically already have their own
+ methods for accomplishing these tasks. A vendor considering
+ adopting OpenFlow may not wish to re-implement this support. If
+ the core OpenFlow specification required such a reimplementation,
+ it could likely reduce the adoption of OpenFlow or considerably
+ slow the process of standardization of new protocol messages to
+ accomplish these tasks, extending the time before implementations
+ appeared on the market.
+
+\subsection{Use of a non-standard protocol}
+\label{sec-3.2}
+
+
+ The requirements for the protocol, including bidirectional
+ asynchronous messaging are not well supported by any widely
+ implemented protocol used for similar purposes. It is expected
+ most implementers of the remote switch protocol will also be
+ implementers of the OpenFlow protocol and thus specifying a similar
+ protocol will increase code reuse.
+
+\subsection{Reuse of some OpenFlow messages}
+\label{sec-3.3}
+
+
+ The messaging requirements of OpenFlow and the OpenFlow Management
+ Protocol are similar in a few areas. Reusing the same messages in
+ those areas should further contribute to code reuse.
+
+\subsection{Connection direction from switch to controller}
+\label{sec-3.4}
+
+
+\subsection{Use of vswitchd configuration file format for config documents}
+\label{sec-3.5}
+
+
+ Simply put, it is available to Nicira already and matches the
+ initial requirements for switch configuration management.
+
+
+\end{document}
+
diff --git a/include/openflow/automake.mk b/include/openflow/automake.mk
index 5bf7585..26490ec 100644
--- a/include/openflow/automake.mk
+++ b/include/openflow/automake.mk
@@ -1,4 +1,5 @@
noinst_HEADERS += \
+ include/openflow/brcompat-netlink.h \
include/openflow/nicira-ext.h \
include/openflow/openflow.h \
include/openflow/openflow-netlink.h
diff --git a/include/openflow/brcompat-netlink.h b/include/openflow/brcompat-netlink.h
new file mode 100644
index 0000000..34560a3
--- /dev/null
+++ b/include/openflow/brcompat-netlink.h
@@ -0,0 +1,64 @@
+/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+ * Junior University
+ *
+ * We are making the OpenFlow specification and associated documentation
+ * (Software) available for public use and benefit with the expectation
+ * that others will use, modify and enhance the Software and contribute
+ * those enhancements back to the community. However, since we would
+ * like to make the Software available for broadest use, with as few
+ * restrictions as possible permission is hereby granted, free of
+ * charge, to any person obtaining a copy of this Software to deal in
+ * the Software under the copyrights without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * 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 AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * The name and trademarks of copyright holder(s) may NOT be used in
+ * advertising or publicity pertaining to the Software or any
+ * derivatives without specific, written prior permission.
+ */
+
+#ifndef OPENFLOW_BRCOMPAT_NETLINK_H
+#define OPENFLOW_BRCOMPAT_NETLINK_H 1
+
+#define BRC_GENL_FAMILY_NAME "brcompat"
+
+/* Attributes that can be attached to the datapath's netlink messages. */
+enum {
+ BRC_GENL_A_UNSPEC,
+ BRC_GENL_A_DP_NAME, /* Datapath name. */
+ BRC_GENL_A_PORT_NAME, /* Interface name. */
+ BRC_GENL_A_ERR_CODE, /* Positive error code. */
+ BRC_GENL_A_MC_GROUP, /* Generic netlink multicast group. */
+
+ __BRC_GENL_A_MAX,
+ BRC_GENL_A_MAX = __BRC_GENL_A_MAX - 1
+};
+
+/* Commands that can be executed on the datapath's netlink interface. */
+enum brc_genl_command {
+ BRC_GENL_C_UNSPEC,
+ BRC_GENL_C_DP_ADD, /* Datapath created. */
+ BRC_GENL_C_DP_DEL, /* Datapath destroyed. */
+ BRC_GENL_C_DP_RESULT, /* Result of datapath command from vswitchd. */
+ BRC_GENL_C_PORT_ADD, /* Port added to datapath. */
+ BRC_GENL_C_PORT_DEL, /* Port removed from datapath. */
+ BRC_GENL_C_QUERY_MC, /* Get multicast group for brcompat. */
+
+ __BRC_GENL_C_MAX,
+ BRC_GENL_C_MAX = __BRC_GENL_C_MAX - 1
+};
+#endif /* openflow/brcompat-netlink.h */
diff --git a/include/openflow/openflow-netlink.h b/include/openflow/openflow-netlink.h
index 895fad5..931e697 100644
--- a/include/openflow/openflow-netlink.h
+++ b/include/openflow/openflow-netlink.h
@@ -39,10 +39,11 @@
/* Attributes that can be attached to the datapath's netlink messages. */
enum {
DP_GENL_A_UNSPEC,
- DP_GENL_A_DP_IDX, /* Datapath Ethernet device name. */
+ DP_GENL_A_DP_IDX, /* Datapath device index. */
DP_GENL_A_PORTNAME, /* Device name for datapath port. */
DP_GENL_A_MC_GROUP, /* Generic netlink multicast group. */
DP_GENL_A_OPENFLOW, /* OpenFlow packet. */
+ DP_GENL_A_DP_NAME, /* Datapath device name. */
__DP_GENL_A_MAX,
DP_GENL_A_MAX = __DP_GENL_A_MAX - 1
@@ -62,4 +63,7 @@ enum dp_genl_command {
DP_GENL_C_MAX = __DP_GENL_C_MAX - 1
};
+/* Maximum number of datapaths. */
+#define DP_MAX 256
+
#endif /* openflow/openflow-netlink.h */
diff --git a/lib/automake.mk b/lib/automake.mk
index 9c45a01..d1cccd9 100644
--- a/lib/automake.mk
+++ b/lib/automake.mk
@@ -1,6 +1,10 @@
noinst_LIBRARIES += lib/libopenflow.a
lib_libopenflow_a_SOURCES = \
+ lib/backtrace.c \
+ lib/backtrace.h \
+ lib/bitmap.c \
+ lib/bitmap.h \
lib/command-line.c \
lib/command-line.h \
lib/compiler.h \
@@ -25,6 +29,10 @@ lib_libopenflow_a_SOURCES = \
lib/flow.h \
lib/hash.c \
lib/hash.h \
+ lib/hmap.c \
+ lib/hmap.h \
+ lib/leak-checker.c \
+ lib/leak-checker.h \
lib/learning-switch.c \
lib/learning-switch.h \
lib/list.c \
@@ -38,6 +46,8 @@ lib_libopenflow_a_SOURCES = \
lib/ofpbuf.c \
lib/ofpbuf.h \
lib/packets.h \
+ lib/pcap.c \
+ lib/pcap.h \
lib/poll-loop.c \
lib/poll-loop.h \
lib/port-array.c \
@@ -51,17 +61,24 @@ lib_libopenflow_a_SOURCES = \
lib/rconn.c \
lib/rconn.h \
lib/sat-math.h \
+ lib/shash.c \
+ lib/shash.h \
+ lib/signals.c \
+ lib/signals.h \
lib/socket-util.c \
lib/socket-util.h \
lib/stp.c \
lib/stp.h \
lib/svec.c \
lib/svec.h \
+ lib/tag.c \
+ lib/tag.h \
lib/timeval.c \
lib/timeval.h \
lib/type-props.h \
lib/util.c \
lib/util.h \
+ lib/vconn-fd.c \
lib/vconn-provider.h \
lib/vconn-ssl.h \
lib/vconn-stream.c \
diff --git a/lib/backtrace.c b/lib/backtrace.c
new file mode 100644
index 0000000..067b196
--- /dev/null
+++ b/lib/backtrace.c
@@ -0,0 +1,123 @@
+/* Copyright (c) 2008, 2009 The Board of Trustees of The Leland Stanford
+ * Junior University
+ *
+ * We are making the OpenFlow specification and associated documentation
+ * (Software) available for public use and benefit with the expectation
+ * that others will use, modify and enhance the Software and contribute
+ * those enhancements back to the community. However, since we would
+ * like to make the Software available for broadest use, with as few
+ * restrictions as possible permission is hereby granted, free of
+ * charge, to any person obtaining a copy of this Software to deal in
+ * the Software under the copyrights without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * 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 AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * The name and trademarks of copyright holder(s) may NOT be used in
+ * advertising or publicity pertaining to the Software or any
+ * derivatives without specific, written prior permission.
+ */
+
+#include
+#include "backtrace.h"
+#include
+#include
+#include
+#include
+#include "compiler.h"
+
+#define THIS_MODULE VLM_backtrace
+#include "vlog.h"
+
+static uintptr_t UNUSED
+get_max_stack(void)
+{
+ static const char file_name[] = "/proc/self/maps";
+ char line[1024];
+ int line_number;
+ FILE *f;
+
+ f = fopen(file_name, "r");
+ if (f == NULL) {
+ VLOG_WARN("opening %s failed: %s", file_name, strerror(errno));
+ return -1;
+ }
+
+ for (line_number = 1; fgets(line, sizeof line, f); line_number++) {
+ if (strstr(line, "[stack]")) {
+ uintptr_t end;
+ if (sscanf(line, "%*"SCNxPTR"-%"SCNxPTR, &end) != 1) {
+ VLOG_WARN("%s:%d: parse error", file_name, line_number);
+ continue;
+ }
+ fclose(f);
+ return end;
+ }
+ }
+ fclose(f);
+
+ VLOG_WARN("%s: no stack found", file_name);
+ return -1;
+}
+
+static uintptr_t
+stack_high(void)
+{
+ static uintptr_t high;
+ if (!high) {
+ high = get_max_stack();
+ }
+ return high;
+}
+
+static uintptr_t
+stack_low(void)
+{
+#ifdef __i386__
+ uintptr_t low;
+ asm("movl %%esp,%0" : "=g" (low));
+ return low;
+#else
+ /* This causes a warning in GCC that cannot be disabled, so use it only on
+ * non-x86. */
+ int dummy;
+ return (uintptr_t) &dummy;
+#endif
+}
+
+static bool
+in_stack(void *p)
+{
+ uintptr_t address = (uintptr_t) p;
+ return address >= stack_low() && address < stack_high();
+}
+
+void
+backtrace_capture(struct backtrace *backtrace)
+{
+ void **frame;
+ size_t n;
+
+ n = 0;
+ for (frame = __builtin_frame_address(1);
+ frame != NULL && in_stack(frame) && frame[0] != NULL
+ && n < BACKTRACE_MAX_FRAMES;
+ frame = frame[0])
+ {
+ backtrace->frames[n++] = (uintptr_t) frame[1];
+ }
+ backtrace->n_frames = n;
+}
diff --git a/lib/backtrace.h b/lib/backtrace.h
new file mode 100644
index 0000000..bd2e0b4
--- /dev/null
+++ b/lib/backtrace.h
@@ -0,0 +1,48 @@
+/* Copyright (c) 2009 The Board of Trustees of The Leland Stanford
+ * Junior University
+ *
+ * We are making the OpenFlow specification and associated documentation
+ * (Software) available for public use and benefit with the expectation
+ * that others will use, modify and enhance the Software and contribute
+ * those enhancements back to the community. However, since we would
+ * like to make the Software available for broadest use, with as few
+ * restrictions as possible permission is hereby granted, free of
+ * charge, to any person obtaining a copy of this Software to deal in
+ * the Software under the copyrights without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * 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 AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * The name and trademarks of copyright holder(s) may NOT be used in
+ * advertising or publicity pertaining to the Software or any
+ * derivatives without specific, written prior permission.
+ */
+
+#ifndef BACKTRACE_H
+#define BACKTRACE_H 1
+
+#include
+
+#define BACKTRACE_MAX_FRAMES 31
+
+struct backtrace {
+ int n_frames;
+ uintptr_t frames[BACKTRACE_MAX_FRAMES];
+};
+
+void backtrace_capture(struct backtrace *);
+
+#endif /* backtrace.h */
diff --git a/lib/bitmap.c b/lib/bitmap.c
new file mode 100644
index 0000000..b2881db
--- /dev/null
+++ b/lib/bitmap.c
@@ -0,0 +1,72 @@
+/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+ * Junior University
+ *
+ * We are making the OpenFlow specification and associated documentation
+ * (Software) available for public use and benefit with the expectation
+ * that others will use, modify and enhance the Software and contribute
+ * those enhancements back to the community. However, since we would
+ * like to make the Software available for broadest use, with as few
+ * restrictions as possible permission is hereby granted, free of
+ * charge, to any person obtaining a copy of this Software to deal in
+ * the Software under the copyrights without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * 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 AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * The name and trademarks of copyright holder(s) may NOT be used in
+ * advertising or publicity pertaining to the Software or any
+ * derivatives without specific, written prior permission.
+ */
+
+#include
+#include "bitmap.h"
+#include
+
+/* Sets 'count' consecutive bits in 'bitmap', starting at bit offset 'start',
+ * to 'value'. */
+void
+bitmap_set_multiple(unsigned long *bitmap, size_t start, size_t count,
+ bool value)
+{
+ for (; count && start % BITMAP_ULONG_BITS; count--) {
+ bitmap_set(bitmap, start++, value);
+ }
+ for (; count >= BITMAP_ULONG_BITS; count -= BITMAP_ULONG_BITS) {
+ *bitmap_unit__(bitmap, start) = -(unsigned long) value;
+ start += BITMAP_ULONG_BITS;
+ }
+ for (; count; count--) {
+ bitmap_set(bitmap, start++, value);
+ }
+}
+
+/* Compares the 'n' bits in bitmaps 'a' and 'b'. Returns true if all bits are
+ * equal, false otherwise. */
+bool
+bitmap_equal(const unsigned long *a, const unsigned long *b, size_t n)
+{
+ size_t i;
+
+ if (memcmp(a, b, n / BITMAP_ULONG_BITS * sizeof(unsigned long))) {
+ return false;
+ }
+ for (i = ROUND_DOWN(n, BITMAP_ULONG_BITS); i < n; i++) {
+ if (bitmap_is_set(a, i) != bitmap_is_set(b, i)) {
+ return false;
+ }
+ }
+ return true;
+}
diff --git a/lib/bitmap.h b/lib/bitmap.h
new file mode 100644
index 0000000..7d37b57
--- /dev/null
+++ b/lib/bitmap.h
@@ -0,0 +1,99 @@
+/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+ * Junior University
+ *
+ * We are making the OpenFlow specification and associated documentation
+ * (Software) available for public use and benefit with the expectation
+ * that others will use, modify and enhance the Software and contribute
+ * those enhancements back to the community. However, since we would
+ * like to make the Software available for broadest use, with as few
+ * restrictions as possible permission is hereby granted, free of
+ * charge, to any person obtaining a copy of this Software to deal in
+ * the Software under the copyrights without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * 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 AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * The name and trademarks of copyright holder(s) may NOT be used in
+ * advertising or publicity pertaining to the Software or any
+ * derivatives without specific, written prior permission.
+ */
+
+#ifndef BITMAP_H
+#define BITMAP_H 1
+
+#include
+#include
+#include "util.h"
+
+#define BITMAP_ULONG_BITS (sizeof(unsigned long) * CHAR_BIT)
+
+static inline unsigned long *
+bitmap_unit__(const unsigned long *bitmap, size_t offset)
+{
+ return (unsigned long *) &bitmap[offset / BITMAP_ULONG_BITS];
+}
+
+static inline unsigned long
+bitmap_bit__(size_t offset)
+{
+ return 1UL << (offset % BITMAP_ULONG_BITS);
+}
+
+static inline unsigned long *
+bitmap_allocate(size_t n_bits)
+{
+ return xcalloc(1, ROUND_UP(n_bits, BITMAP_ULONG_BITS));
+}
+
+static inline void
+bitmap_free(unsigned long *bitmap)
+{
+ free(bitmap);
+}
+
+static inline bool
+bitmap_is_set(const unsigned long *bitmap, size_t offset)
+{
+ return (*bitmap_unit__(bitmap, offset) & bitmap_bit__(offset)) != 0;
+}
+
+static inline void
+bitmap_set1(unsigned long *bitmap, size_t offset)
+{
+ *bitmap_unit__(bitmap, offset) |= bitmap_bit__(offset);
+}
+
+static inline void
+bitmap_set0(unsigned long *bitmap, size_t offset)
+{
+ *bitmap_unit__(bitmap, offset) &= ~bitmap_bit__(offset);
+}
+
+static inline void
+bitmap_set(unsigned long *bitmap, size_t offset, bool value)
+{
+ if (value) {
+ bitmap_set1(bitmap, offset);
+ } else {
+ bitmap_set0(bitmap, offset);
+ }
+}
+
+void bitmap_set_multiple(unsigned long *, size_t start, size_t count,
+ bool value);
+bool bitmap_equal(const unsigned long *, const unsigned long *, size_t n);
+
+#endif /* bitmap.h */
diff --git a/lib/common.man b/lib/common.man
new file mode 100644
index 0000000..1f12bb7
--- /dev/null
+++ b/lib/common.man
@@ -0,0 +1,7 @@
+.TP
+.BR \-h ", " \-\^\-help
+Prints a brief help message to the console.
+
+.TP
+.BR \-V ", " \-\^\-version
+Prints version information to the console.
diff --git a/lib/compiler.h b/lib/compiler.h
index 2e185bf..c42c6e4 100644
--- a/lib/compiler.h
+++ b/lib/compiler.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+/* Copyright (c) 2008, 2009 The Board of Trustees of The Leland Stanford
* Junior University
*
* We are making the OpenFlow specification and associated documentation
@@ -39,6 +39,7 @@
#define PACKED __attribute__((__packed__))
#define PRINTF_FORMAT(FMT, ARG1) __attribute__((__format__(printf, FMT, ARG1)))
#define STRFTIME_FORMAT(FMT) __attribute__((__format__(__strftime__, FMT, 0)))
+#define MALLOC_LIKE __attribute__((__malloc__))
#define likely(x) __builtin_expect((x),1)
#define unlikely(x) __builtin_expect((x),0)
diff --git a/lib/daemon.c b/lib/daemon.c
index 3b766b5..8346549 100644
--- a/lib/daemon.c
+++ b/lib/daemon.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+/* Copyright (c) 2008, 2009 The Board of Trustees of The Leland Stanford
* Junior University
*
* We are making the OpenFlow specification and associated documentation
@@ -178,6 +178,7 @@ make_pidfile(void)
} else {
/* Keep 'fd' open to retain the lock. */
}
+ free(text);
} else {
VLOG_ERR("%s: write failed: %s", tmpfile, strerror(errno));
close(fd);
diff --git a/lib/daemon.man b/lib/daemon.man
new file mode 100644
index 0000000..4ab6568
--- /dev/null
+++ b/lib/daemon.man
@@ -0,0 +1,21 @@
+.TP
+\fB-P\fR[\fIpidfile\fR], \fB--pidfile\fR[\fB=\fIpidfile\fR]
+Causes a file (by default, \fB\*(PN.pid\fR) to be created indicating
+the PID of the running process. If \fIpidfile\fR is not specified, or
+if it does not begin with \fB/\fR, then it is created in
+\fB@RUNDIR@\fR.
+
+.TP
+\fB-f\fR, \fB--force\fR
+By default, when \fB-P\fR or \fB--pidfile\fR is specified and the
+specified pidfile already exists and is locked by a running process,
+\fB\*(PN\fR refuses to start. Specify \fB-f\fR or \fB--force\fR
+to cause it to instead overwrite the pidfile.
+
+When \fB-P\fR or \fB--pidfile\fR is not specified, this option has no
+effect.
+
+.TP
+\fB-D\fR, \fB--detach\fR
+Causes \fB\*(PN\fR to detach itself from the foreground session and
+run as a background process.
diff --git a/lib/dhcp-client.c b/lib/dhcp-client.c
index 7704c8a..5cbd297 100644
--- a/lib/dhcp-client.c
+++ b/lib/dhcp-client.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+/* Copyright (c) 2008, 2009 The Board of Trustees of The Leland Stanford
* Junior University
*
* We are making the OpenFlow specification and associated documentation
@@ -428,7 +428,7 @@ dhclient_configure_netdev(struct dhclient *cli)
}
if (!error && router.s_addr) {
- error = netdev_add_router(cli->netdev, router);
+ error = netdev_add_router(router);
if (error) {
VLOG_ERR("failed to add default route to "IP_FMT" on %s: %s",
IP_ARGS(&router), netdev_get_name(cli->netdev),
@@ -578,7 +578,7 @@ static bool
dhcp_receive(struct dhclient *cli, unsigned int msgs, struct dhcp_msg *msg)
{
while (do_receive_msg(cli, msg)) {
- if (msg->type < 0 || msg->type > 31 || !((1u << msg->type) & msgs)) {
+ if (msg->type > 31 || !((1u << msg->type) & msgs)) {
VLOG_DBG_RL(&rl, "received unexpected %s in %s state: %s",
dhcp_type_name(msg->type), state_name(cli->state),
dhcp_msg_to_string(msg, false, &cli->s));
@@ -851,11 +851,11 @@ state_transition(struct dhclient *cli, enum dhclient_state state)
cli->changed = true;
if (am_bound) {
assert(cli->binding != NULL);
- VLOG_WARN("%s: obtained address "IP_FMT", netmask "IP_FMT,
+ VLOG_INFO("%s: obtained address "IP_FMT", netmask "IP_FMT,
netdev_get_name(cli->netdev),
IP_ARGS(&cli->ipaddr), IP_ARGS(&cli->netmask));
if (cli->router) {
- VLOG_WARN("%s: obtained default gateway "IP_FMT,
+ VLOG_INFO("%s: obtained default gateway "IP_FMT,
netdev_get_name(cli->netdev), IP_ARGS(&cli->router));
}
} else {
@@ -863,7 +863,7 @@ state_transition(struct dhclient *cli, enum dhclient_state state)
free(cli->binding);
cli->binding = NULL;
- VLOG_WARN("%s: network address unbound",
+ VLOG_INFO("%s: network address unbound",
netdev_get_name(cli->netdev));
}
}
@@ -902,6 +902,10 @@ dhclient_msg_init(struct dhclient *cli, enum dhcp_msg_type type,
memcpy(msg->chaddr, netdev_get_etheraddr(cli->netdev), ETH_ADDR_LEN);
}
+/* If time goes backward this returns a large number, which makes it look like
+ * we've been in the current state a very long time. That's probably
+ * fine for that corner case--we'll just expire our lease, etc., and try to
+ * get a new one. */
static unsigned int
elapsed_in_this_state(const struct dhclient *cli)
{
@@ -963,7 +967,7 @@ do_receive_msg(struct dhclient *cli, struct dhcp_msg *msg)
VLOG_DBG_RL(&rl, "received %s",
dhcp_msg_to_string(msg, false, &cli->s));
} else {
- VLOG_WARN_RL(&rl, "received %s", dhcp_type_name(msg->type));
+ VLOG_INFO_RL(&rl, "received %s", dhcp_type_name(msg->type));
}
ofpbuf_uninit(&b);
return true;
@@ -1042,7 +1046,7 @@ do_send_msg(struct dhclient *cli, const struct dhcp_msg *msg)
if (VLOG_IS_DBG_ENABLED()) {
VLOG_DBG("sending %s", dhcp_msg_to_string(msg, false, &cli->s));
} else {
- VLOG_WARN("sending %s", dhcp_type_name(msg->type));
+ VLOG_INFO("sending %s", dhcp_type_name(msg->type));
}
error = netdev_send(cli->netdev, &b);
if (error) {
diff --git a/lib/dpif.c b/lib/dpif.c
index 6407490..3e4c3ce 100644
--- a/lib/dpif.c
+++ b/lib/dpif.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+/* Copyright (c) 2008, 2009 The Board of Trustees of The Leland Stanford
* Junior University
*
* We are making the OpenFlow specification and associated documentation
@@ -34,6 +34,7 @@
#include
#include "dpif.h"
+#include
#include
#include
#include
@@ -60,15 +61,15 @@ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 60);
static int openflow_family;
static int lookup_openflow_multicast_group(int dp_idx, int *multicast_group);
-static int send_mgmt_command(struct dpif *, int command,
+static int send_mgmt_command(struct dpif *, int dp_idx, int command,
const char *netdev);
-/* Opens the local datapath numbered 'dp_idx', initializing 'dp'. If
- * 'subscribe' is true, listens for asynchronous messages (packet-in, etc.)
- * from the datapath; otherwise, 'dp' will receive only replies to explicitly
- * initiated requests. */
+/* Opens a socket for a local datapath, initializing 'dp'. If
+ * 'subscribe_dp_idx' is nonnegative, listens for asynchronous messages
+ * (packet-in, etc.) from the datapath with that number; otherwise, 'dp' will
+ * receive only replies to explicitly initiated requests. */
int
-dpif_open(int dp_idx, bool subscribe, struct dpif *dp)
+dpif_open(int subscribe_dp_idx, struct dpif *dp)
{
struct nl_sock *sock;
int multicast_group = 0;
@@ -79,8 +80,9 @@ dpif_open(int dp_idx, bool subscribe, struct dpif *dp)
return retval;
}
- if (subscribe) {
- retval = lookup_openflow_multicast_group(dp_idx, &multicast_group);
+ if (subscribe_dp_idx >= 0) {
+ retval = lookup_openflow_multicast_group(subscribe_dp_idx,
+ &multicast_group);
if (retval) {
return retval;
}
@@ -94,7 +96,6 @@ dpif_open(int dp_idx, bool subscribe, struct dpif *dp)
return retval;
}
- dp->dp_idx = dp_idx;
dp->sock = sock;
return 0;
}
@@ -109,13 +110,15 @@ dpif_close(struct dpif *dp)
}
static const struct nl_policy openflow_policy[] = {
- [DP_GENL_A_DP_IDX] = { .type = NL_A_U32 },
+ [DP_GENL_A_DP_IDX] = { .type = NL_A_U32,
+ .optional = false },
[DP_GENL_A_OPENFLOW] = { .type = NL_A_UNSPEC,
- .min_len = sizeof(struct ofp_header),
- .max_len = 65535 },
+ .min_len = sizeof(struct ofp_header),
+ .max_len = 65535,
+ .optional = false },
};
-/* Tries to receive an openflow message from the kernel on 'sock'. If
+/* Tries to receive an openflow message from datapath 'dp_idx' on 'sock'. If
* successful, stores the received message into '*msgp' and returns 0. The
* caller is responsible for destroying the message with ofpbuf_delete(). On
* failure, returns a positive errno value and stores a null pointer into
@@ -127,50 +130,49 @@ static const struct nl_policy openflow_policy[] = {
* If 'wait' is true, dpif_recv_openflow waits for a message to be ready;
* otherwise, returns EAGAIN if the 'sock' receive buffer is empty. */
int
-dpif_recv_openflow(struct dpif *dp, struct ofpbuf **bufferp,
- bool wait)
+dpif_recv_openflow(struct dpif *dp, int dp_idx, struct ofpbuf **bufferp,
+ bool wait)
{
struct nlattr *attrs[ARRAY_SIZE(openflow_policy)];
struct ofpbuf *buffer;
struct ofp_header *oh;
uint16_t ofp_len;
- int retval;
buffer = *bufferp = NULL;
do {
- ofpbuf_delete(buffer);
- retval = nl_sock_recv(dp->sock, &buffer, wait);
- } while (retval == ENOBUFS
- || (!retval
- && (nl_msg_nlmsgerr(buffer, NULL)
- || nl_msg_nlmsghdr(buffer)->nlmsg_type == NLMSG_DONE)));
- if (retval) {
- if (retval != EAGAIN) {
- VLOG_WARN_RL(&rl, "dpif_recv_openflow: %s", strerror(retval));
+ int retval;
+
+ do {
+ ofpbuf_delete(buffer);
+ retval = nl_sock_recv(dp->sock, &buffer, wait);
+ } while (retval == ENOBUFS
+ || (!retval
+ && (nl_msg_nlmsghdr(buffer)->nlmsg_type == NLMSG_DONE
+ || nl_msg_nlmsgerr(buffer, NULL))));
+ if (retval) {
+ if (retval != EAGAIN) {
+ VLOG_WARN_RL(&rl, "dpif_recv_openflow: %s", strerror(retval));
+ }
+ return retval;
}
- return retval;
- }
- if (nl_msg_genlmsghdr(buffer) == NULL) {
- VLOG_DBG_RL(&rl, "received packet too short for Generic Netlink");
- goto error;
- }
- if (nl_msg_nlmsghdr(buffer)->nlmsg_type != openflow_family) {
- VLOG_DBG_RL(&rl, "received type (%"PRIu16") != openflow family (%d)",
- nl_msg_nlmsghdr(buffer)->nlmsg_type, openflow_family);
- goto error;
- }
+ if (nl_msg_genlmsghdr(buffer) == NULL) {
+ VLOG_DBG_RL(&rl, "received packet too short for Generic Netlink");
+ goto error;
+ }
+ if (nl_msg_nlmsghdr(buffer)->nlmsg_type != openflow_family) {
+ VLOG_DBG_RL(&rl,
+ "received type (%"PRIu16") != openflow family (%d)",
+ nl_msg_nlmsghdr(buffer)->nlmsg_type, openflow_family);
+ goto error;
+ }
- if (!nl_policy_parse(buffer, openflow_policy, attrs,
- ARRAY_SIZE(openflow_policy))) {
- goto error;
- }
- if (nl_attr_get_u32(attrs[DP_GENL_A_DP_IDX]) != dp->dp_idx) {
- VLOG_WARN_RL(&rl, "received dp_idx (%"PRIu32") differs from expected "
- "(%d)", nl_attr_get_u32(attrs[DP_GENL_A_DP_IDX]),
- dp->dp_idx);
- goto error;
- }
+ if (!nl_policy_parse(buffer, NLMSG_HDRLEN + GENL_HDRLEN,
+ openflow_policy, attrs,
+ ARRAY_SIZE(openflow_policy))) {
+ goto error;
+ }
+ } while (nl_attr_get_u32(attrs[DP_GENL_A_DP_IDX]) != dp_idx);
oh = buffer->data = (void *) nl_attr_get(attrs[DP_GENL_A_OPENFLOW]);
buffer->size = nl_attr_get_size(attrs[DP_GENL_A_OPENFLOW]);
@@ -190,18 +192,18 @@ error:
}
/* Encapsulates 'msg', which must contain an OpenFlow message, in a Netlink
- * message, and sends it to the OpenFlow kernel module via 'sock'.
+ * message, and sends it to the OpenFlow local datapath numbered 'dp_idx' via
+ * 'sock'.
*
- * Returns 0 if successful, otherwise a positive errno value. If
- * 'wait' is true, then the send will wait until buffer space is ready;
- * otherwise, returns EAGAIN if the 'sock' send buffer is full.
+ * Returns 0 if successful, otherwise a positive errno value. Returns EAGAIN
+ * if the 'sock' send buffer is full.
*
* If the send is successful, then the kernel module will receive it, but there
* is no guarantee that any reply will not be dropped (see nl_sock_transact()
* for details).
*/
int
-dpif_send_openflow(struct dpif *dp, struct ofpbuf *buffer, bool wait)
+dpif_send_openflow(struct dpif *dp, int dp_idx, struct ofpbuf *buffer)
{
struct ofp_header *oh;
unsigned int dump_flag;
@@ -221,7 +223,7 @@ dpif_send_openflow(struct dpif *dp, struct ofpbuf *buffer, bool wait)
ofpbuf_use(&hdr, fixed_buffer, sizeof fixed_buffer);
nl_msg_put_genlmsghdr(&hdr, dp->sock, 32, openflow_family,
NLM_F_REQUEST | dump_flag, DP_GENL_C_OPENFLOW, 1);
- nl_msg_put_u32(&hdr, DP_GENL_A_DP_IDX, dp->dp_idx);
+ nl_msg_put_u32(&hdr, DP_GENL_A_DP_IDX, dp_idx);
nla = ofpbuf_put_uninit(&hdr, sizeof *nla);
nla->nla_len = sizeof *nla + buffer->size;
nla->nla_type = DP_GENL_A_OPENFLOW;
@@ -245,48 +247,56 @@ dpif_send_openflow(struct dpif *dp, struct ofpbuf *buffer, bool wait)
return retval;
}
-/* Creates the datapath represented by 'dp'. Returns 0 if successful,
- * otherwise a positive errno value. */
+/* Creates local datapath numbered 'dp_idx' with the name 'dp_name'. A
+ * 'dp_idx' of -1 or null 'dp_name' will have the kernel module choose values.
+ * (At least one or the other must be provided, however, so that the caller can
+ * identify the datapath that was created.) Returns 0 if successful, otherwise
+ * a positive errno value. */
int
-dpif_add_dp(struct dpif *dp)
+dpif_add_dp(struct dpif *dp, int dp_idx, const char *dp_name)
{
- return send_mgmt_command(dp, DP_GENL_C_ADD_DP, NULL);
+ return send_mgmt_command(dp, dp_idx, DP_GENL_C_ADD_DP, dp_name);
}
-/* Destroys the datapath represented by 'dp'. Returns 0 if successful,
- * otherwise a positive errno value. */
-int
-dpif_del_dp(struct dpif *dp)
-{
- return send_mgmt_command(dp, DP_GENL_C_DEL_DP, NULL);
-}
-
-/* Adds the Ethernet device named 'netdev' to this datapath. Returns 0 if
+/* Destroys a local datapath. If 'dp_idx' is not -1, destroys the datapath
+ * with that number; if 'dp_name' is not NULL, destroys the datapath with that
+ * name. Exactly one of 'dp_idx' and 'dp_name' should be used. Returns 0 if
* successful, otherwise a positive errno value. */
int
-dpif_add_port(struct dpif *dp, const char *netdev)
+dpif_del_dp(struct dpif *dp, int dp_idx, const char *dp_name)
{
- return send_mgmt_command(dp, DP_GENL_C_ADD_PORT, netdev);
+ return send_mgmt_command(dp, dp_idx, DP_GENL_C_DEL_DP, dp_name);
}
-/* Removes the Ethernet device named 'netdev' from this datapath. Returns 0
- * if successful, otherwise a positive errno value. */
+/* Adds the Ethernet device named 'netdev' to the local datapath numbered
+ * 'dp_idx'. Returns 0 if successful, otherwise a positive errno value. */
int
-dpif_del_port(struct dpif *dp, const char *netdev)
+dpif_add_port(struct dpif *dp, int dp_idx, const char *netdev)
{
- return send_mgmt_command(dp, DP_GENL_C_DEL_PORT, netdev);
+ return send_mgmt_command(dp, dp_idx, DP_GENL_C_ADD_PORT, netdev);
+}
+
+/* Removes the Ethernet device named 'netdev' from the local datapath numbered
+ * 'dp_idx'. Returns 0 if successful, otherwise a positive errno value. */
+int
+dpif_del_port(struct dpif *dp, int dp_idx, const char *netdev)
+{
+ return send_mgmt_command(dp, dp_idx, DP_GENL_C_DEL_PORT, netdev);
}
static const struct nl_policy openflow_multicast_policy[] = {
[DP_GENL_A_DP_IDX] = { .type = NL_A_U32 },
+ [DP_GENL_A_DP_NAME] = { .type = NL_A_STRING },
[DP_GENL_A_MC_GROUP] = { .type = NL_A_U32 },
};
-/* Looks up the Netlink multicast group used by datapath 'dp_idx'. If
- * successful, stores the multicast group in '*multicast_group' and returns 0.
- * Otherwise, returns a positve errno value. */
+/* Looks up the Netlink multicast group and datapath index of a datapath
+ * by either the datapath index or name. If 'dp_idx' points to a value
+ * of '-1', then 'dp_name' is used to lookup the datapath. If successful,
+ * stores the multicast group in '*multicast_group' and the index in
+ * '*dp_idx' and returns 0. Otherwise, returns a positive errno value. */
static int
-lookup_openflow_multicast_group(int dp_idx, int *multicast_group)
+query_datapath(int *dp_idx, int *multicast_group, const char *dp_name)
{
struct nl_sock *sock;
struct ofpbuf request, *reply;
@@ -300,19 +310,26 @@ lookup_openflow_multicast_group(int dp_idx, int *multicast_group)
ofpbuf_init(&request, 0);
nl_msg_put_genlmsghdr(&request, sock, 0, openflow_family, NLM_F_REQUEST,
DP_GENL_C_QUERY_DP, 1);
- nl_msg_put_u32(&request, DP_GENL_A_DP_IDX, dp_idx);
+ if (*dp_idx != -1) {
+ nl_msg_put_u32(&request, DP_GENL_A_DP_IDX, *dp_idx);
+ }
+ if (dp_name) {
+ nl_msg_put_string(&request, DP_GENL_A_DP_NAME, dp_name);
+ }
retval = nl_sock_transact(sock, &request, &reply);
ofpbuf_uninit(&request);
if (retval) {
nl_sock_destroy(sock);
return retval;
}
- if (!nl_policy_parse(reply, openflow_multicast_policy, attrs,
+ if (!nl_policy_parse(reply, NLMSG_HDRLEN + GENL_HDRLEN,
+ openflow_multicast_policy, attrs,
ARRAY_SIZE(openflow_multicast_policy))) {
nl_sock_destroy(sock);
ofpbuf_delete(reply);
return EPROTO;
}
+ *dp_idx = nl_attr_get_u32(attrs[DP_GENL_A_DP_IDX]);
*multicast_group = nl_attr_get_u32(attrs[DP_GENL_A_MC_GROUP]);
nl_sock_destroy(sock);
ofpbuf_delete(reply);
@@ -320,11 +337,36 @@ lookup_openflow_multicast_group(int dp_idx, int *multicast_group)
return 0;
}
-/* Sends the given 'command' to datapath 'dp'. If 'netdev' is nonnull, adds it
- * to the command as the port name attribute. Returns 0 if successful,
- * otherwise a positive errno value. */
+/* Looks up the Netlink multicast group used by datapath 'dp_idx'. If
+ * successful, stores the multicast group in '*multicast_group' and returns 0.
+ * Otherwise, returns a positve errno value. */
static int
-send_mgmt_command(struct dpif *dp, int command, const char *netdev)
+lookup_openflow_multicast_group(int dp_idx, int *multicast_group)
+{
+ return query_datapath(&dp_idx, multicast_group, NULL);
+}
+
+/* Looks up the datatpath index based on the name. Returns the index, or
+ * -1 on error. */
+int
+dpif_get_idx(const char *name)
+{
+ int dp_idx = -1;
+ int mc_group = 0;
+
+ if (query_datapath(&dp_idx, &mc_group, name)) {
+ return -1;
+ }
+
+ return dp_idx;
+}
+
+/* Sends the given 'command' to datapath 'dp', related to the local datapath
+ * numbered 'dp_idx'. If 'arg' is nonnull, adds it to the command as the
+ * datapath or port name attribute depending on the requested operation.
+ * Returns 0 if successful, otherwise a positive errno value. */
+static int
+send_mgmt_command(struct dpif *dp, int dp_idx, int command, const char *arg)
{
struct ofpbuf request, *reply;
int retval;
@@ -332,9 +374,15 @@ send_mgmt_command(struct dpif *dp, int command, const char *netdev)
ofpbuf_init(&request, 0);
nl_msg_put_genlmsghdr(&request, dp->sock, 32, openflow_family,
NLM_F_REQUEST | NLM_F_ACK, command, 1);
- nl_msg_put_u32(&request, DP_GENL_A_DP_IDX, dp->dp_idx);
- if (netdev) {
- nl_msg_put_string(&request, DP_GENL_A_PORTNAME, netdev);
+ if (dp_idx != -1) {
+ nl_msg_put_u32(&request, DP_GENL_A_DP_IDX, dp_idx);
+ }
+ if (arg) {
+ if ((command == DP_GENL_C_ADD_DP) || (command == DP_GENL_C_DEL_DP)) {
+ nl_msg_put_string(&request, DP_GENL_A_DP_NAME, arg);
+ } else {
+ nl_msg_put_string(&request, DP_GENL_A_PORTNAME, arg);
+ }
}
retval = nl_sock_transact(dp->sock, &request, &reply);
ofpbuf_uninit(&request);
diff --git a/lib/dpif.h b/lib/dpif.h
index 0316e13..12b81b3 100644
--- a/lib/dpif.h
+++ b/lib/dpif.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+/* Copyright (c) 2008, 2009 The Board of Trustees of The Leland Stanford
* Junior University
*
* We are making the OpenFlow specification and associated documentation
@@ -48,17 +48,21 @@ struct ofp_match;
/* A datapath interface. Opaque. */
struct dpif
{
- int dp_idx;
struct nl_sock *sock;
};
-int dpif_open(int dp_idx, bool subscribe, struct dpif *);
+int dpif_open(int subscribe_dp_idx, struct dpif *);
void dpif_close(struct dpif *);
-int dpif_recv_openflow(struct dpif *, struct ofpbuf **, bool wait);
-int dpif_send_openflow(struct dpif *, struct ofpbuf *, bool wait);
-int dpif_add_dp(struct dpif *);
-int dpif_del_dp(struct dpif *);
-int dpif_add_port(struct dpif *, const char *netdev);
-int dpif_del_port(struct dpif *, const char *netdev);
+
+/* OpenFlow. */
+int dpif_recv_openflow(struct dpif *, int dp_idx, struct ofpbuf **, bool wait);
+int dpif_send_openflow(struct dpif *, int dp_idx, struct ofpbuf *);
+
+/* Management functions. */
+int dpif_add_dp(struct dpif *, int dp_idx, const char *dp_name);
+int dpif_del_dp(struct dpif *, int dp_idx, const char *dp_name);
+int dpif_add_port(struct dpif *, int dp_idx, const char *netdev);
+int dpif_del_port(struct dpif *, int dp_idx, const char *netdev);
+int dpif_get_idx(const char *dp_name);
#endif /* dpif.h */
diff --git a/lib/fatal-signal.c b/lib/fatal-signal.c
index 35a37bf..0bfa35b 100644
--- a/lib/fatal-signal.c
+++ b/lib/fatal-signal.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+/* Copyright (c) 2008, 2009 The Board of Trustees of The Leland Stanford
* Junior University
*
* We are making the OpenFlow specification and associated documentation
@@ -94,7 +94,7 @@ fatal_signal_add_hook(void (*func)(void *aux), void *aux, bool run_at_exit)
* fatal signal hook, so that the hook is not invoked while the data structure
* is in an inconsistent state. */
void
-fatal_signal_block()
+fatal_signal_block(void)
{
static bool inited = false;
if (!inited) {
@@ -128,7 +128,7 @@ fatal_signal_block()
* fatal_signal_unblock() must be called the same number of times to unblock
* signals. */
void
-fatal_signal_unblock()
+fatal_signal_unblock(void)
{
assert(block_level > 0);
if (--block_level == 0) {
@@ -201,8 +201,7 @@ fatal_signal_add_file_to_unlink(const char *file)
fatal_signal_block();
if (n_files >= max_files) {
- max_files = max_files * 2 + 1;
- files = xrealloc(files, sizeof *files * max_files);
+ files = x2nrealloc(files, &max_files, sizeof *files);
}
files[n_files++] = xstrdup(file);
fatal_signal_unblock();
diff --git a/lib/fault.c b/lib/fault.c
index 752d405..154978b 100644
--- a/lib/fault.c
+++ b/lib/fault.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+/* Copyright (c) 2008, 2009 The Board of Trustees of The Leland Stanford
* Junior University
*
* We are making the OpenFlow specification and associated documentation
@@ -44,7 +44,7 @@
#include "vlog.h"
#define THIS_MODULE VLM_fault
-void
+static void
fault_handler(int sig_nr)
{
VLOG_EMER("Caught signal %d.", sig_nr);
diff --git a/lib/flow.c b/lib/flow.c
index cb6b32c..489540a 100644
--- a/lib/flow.c
+++ b/lib/flow.c
@@ -118,17 +118,20 @@ flow_extract(struct ofpbuf *packet, uint16_t in_port, struct flow *flow)
flow->dl_type = eth->eth_type;
} else {
/* This is an 802.2 frame */
- struct llc_snap_header *h = ofpbuf_at(&b, 0, sizeof *h);
- if (h == NULL) {
+ struct llc_header *llc = ofpbuf_at(&b, 0, sizeof *llc);
+ struct snap_header *snap = ofpbuf_at(&b, sizeof *llc,
+ sizeof *snap);
+ if (llc == NULL) {
return 0;
}
- if (h->llc.llc_dsap == LLC_DSAP_SNAP
- && h->llc.llc_ssap == LLC_SSAP_SNAP
- && h->llc.llc_cntl == LLC_CNTL_SNAP
- && !memcmp(h->snap.snap_org, SNAP_ORG_ETHERNET,
- sizeof h->snap.snap_org)) {
- flow->dl_type = h->snap.snap_type;
- ofpbuf_pull(&b, sizeof *h);
+ if (snap
+ && llc->llc_dsap == LLC_DSAP_SNAP
+ && llc->llc_ssap == LLC_SSAP_SNAP
+ && llc->llc_cntl == LLC_CNTL_SNAP
+ && !memcmp(snap->snap_org, SNAP_ORG_ETHERNET,
+ sizeof snap->snap_org)) {
+ flow->dl_type = snap->snap_type;
+ ofpbuf_pull(&b, LLC_SNAP_HEADER_LEN);
} else {
flow->dl_type = htons(OFP_DL_TYPE_NOT_ETH_TYPE);
ofpbuf_pull(&b, sizeof(struct llc_header));
@@ -198,6 +201,24 @@ flow_extract(struct ofpbuf *packet, uint16_t in_port, struct flow *flow)
return retval;
}
+void
+flow_fill_match(struct ofp_match *to, const struct flow *from,
+ uint32_t wildcards)
+{
+ to->wildcards = htonl(wildcards);
+ to->in_port = from->in_port;
+ to->dl_vlan = from->dl_vlan;
+ memcpy(to->dl_src, from->dl_src, ETH_ADDR_LEN);
+ memcpy(to->dl_dst, from->dl_dst, ETH_ADDR_LEN);
+ to->dl_type = from->dl_type;
+ to->nw_src = from->nw_src;
+ to->nw_dst = from->nw_dst;
+ to->nw_proto = from->nw_proto;
+ to->tp_src = from->tp_src;
+ to->tp_dst = from->tp_dst;
+ to->pad = 0;
+}
+
void
flow_print(FILE *stream, const struct flow *flow)
{
@@ -210,15 +231,3 @@ flow_print(FILE *stream, const struct flow *flow)
IP_ARGS(&flow->nw_src), IP_ARGS(&flow->nw_dst),
ntohs(flow->tp_src), ntohs(flow->tp_dst));
}
-
-int
-flow_compare(const struct flow *a, const struct flow *b)
-{
- return memcmp(a, b, sizeof *a);
-}
-
-unsigned long int
-flow_hash(const struct flow *flow, uint32_t basis)
-{
- return hash_fnv(flow, sizeof *flow, basis);
-}
diff --git a/lib/flow.h b/lib/flow.h
index 0a952f9..5c6c71d 100644
--- a/lib/flow.h
+++ b/lib/flow.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+/* Copyright (c) 2008, 2009 The Board of Trustees of The Leland Stanford
* Junior University
*
* We are making the OpenFlow specification and associated documentation
@@ -35,8 +35,11 @@
#include
#include
+#include
+#include "hash.h"
#include "util.h"
+struct ofp_match;
struct ofpbuf;
/* Identification data for a flow.
@@ -56,11 +59,34 @@ struct flow {
uint8_t nw_proto; /* IP protocol. */
uint8_t reserved; /* Pad to 32-bit alignment. */
};
-BUILD_ASSERT_DECL(sizeof (struct flow) == 32);
+BUILD_ASSERT_DECL(sizeof(struct flow) == 32);
int flow_extract(struct ofpbuf *, uint16_t in_port, struct flow *);
+void flow_fill_match(struct ofp_match *, const struct flow *,
+ uint32_t wildcards);
void flow_print(FILE *, const struct flow *);
-int flow_compare(const struct flow *, const struct flow *);
-unsigned long int flow_hash(const struct flow *, uint32_t basis);
+static inline int flow_compare(const struct flow *, const struct flow *);
+static inline bool flow_equal(const struct flow *, const struct flow *);
+static inline size_t flow_hash(const struct flow *, uint32_t basis);
+
+static inline int
+flow_compare(const struct flow *a, const struct flow *b)
+{
+ return memcmp(a, b, sizeof *a);
+}
+
+static inline bool
+flow_equal(const struct flow *a, const struct flow *b)
+{
+ return !flow_compare(a, b);
+}
+
+static inline size_t
+flow_hash(const struct flow *flow, uint32_t basis)
+{
+ BUILD_ASSERT_DECL(!(sizeof *flow % sizeof(uint32_t)));
+ return hash_words((const uint32_t *) flow,
+ sizeof *flow / sizeof(uint32_t), basis);
+}
#endif /* flow.h */
diff --git a/lib/hash.c b/lib/hash.c
index 8e87216..104ce22 100644
--- a/lib/hash.c
+++ b/lib/hash.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+/* Copyright (c) 2008, 2009 The Board of Trustees of The Leland Stanford
* Junior University
*
* We are making the OpenFlow specification and associated documentation
@@ -32,15 +32,96 @@
*/
#include
#include "hash.h"
+#include
+/* This is the public domain lookup3 hash by Bob Jenkins from
+ * http://burtleburtle.net/bob/c/lookup3.c, modified for style. */
+#define rot(x, k) (((x) << (k)) | ((x) >> (32 - (k))))
+
+#define mix(a, b, c) \
+ do { \
+ a -= c; a ^= rot(c, 4); c += b; \
+ b -= a; b ^= rot(a, 6); a += c; \
+ c -= b; c ^= rot(b, 8); b += a; \
+ a -= c; a ^= rot(c, 16); c += b; \
+ b -= a; b ^= rot(a, 19); a += c; \
+ c -= b; c ^= rot(b, 4); b += a; \
+ } while (0)
+
+#define final(a, b, c) \
+ do { \
+ c ^= b; c -= rot(b, 14); \
+ a ^= c; a -= rot(c, 11); \
+ b ^= a; b -= rot(a, 25); \
+ c ^= b; c -= rot(b, 16); \
+ a ^= c; a -= rot(c, 4); \
+ b ^= a; b -= rot(a, 14); \
+ c ^= b; c -= rot(b, 24); \
+ } while (0)
+
+/* Returns the hash of the 'n' 32-bit words at 'p', starting from 'basis'.
+ * 'p' must be properly aligned. */
uint32_t
-hash_fnv(const void *p_, size_t n, uint32_t basis)
+hash_words(const uint32_t *p, size_t n, uint32_t basis)
+{
+ uint32_t a, b, c;
+
+ a = b = c = 0xdeadbeef + (((uint32_t) n) << 2) + basis;
+
+ while (n > 3) {
+ a += p[0];
+ b += p[1];
+ c += p[2];
+ mix(a, b, c);
+ n -= 3;
+ p += 3;
+ }
+
+ switch (n) {
+ case 3:
+ c += p[2];
+ /* fall through */
+ case 2:
+ b += p[1];
+ /* fall through */
+ case 1:
+ a += p[0];
+ final(a, b, c);
+ /* fall through */
+ case 0:
+ break;
+ }
+ return c;
+}
+
+/* Returns the hash of the 'n' bytes at 'p', starting from 'basis'. */
+uint32_t
+hash_bytes(const void *p_, size_t n, uint32_t basis)
{
const uint8_t *p = p_;
- uint32_t hash = basis;
- while (n--) {
- hash *= HASH_FNV_PRIME;
- hash ^= *p++;
+ uint32_t a, b, c;
+ uint32_t tmp[3];
+
+ a = b = c = 0xdeadbeef + n + basis;
+
+ while (n >= sizeof tmp) {
+ memcpy(tmp, p, sizeof tmp);
+ a += tmp[0];
+ b += tmp[1];
+ c += tmp[2];
+ mix(a, b, c);
+ n -= sizeof tmp;
+ p += sizeof tmp;
}
- return hash;
+
+ if (n) {
+ tmp[0] = tmp[1] = tmp[2] = 0;
+ memcpy(tmp, p, n);
+ a += tmp[0];
+ b += tmp[1];
+ c += tmp[2];
+ final(a, b, c);
+ }
+
+ return c;
}
diff --git a/lib/hash.h b/lib/hash.h
index 6fce347..a52d8ca 100644
--- a/lib/hash.h
+++ b/lib/hash.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+/* Copyright (c) 2008, 2009 The Board of Trustees of The Leland Stanford
* Junior University
*
* We are making the OpenFlow specification and associated documentation
@@ -36,9 +36,7 @@
#include
#include
-#define HASH_FNV_BASIS UINT32_C(2166136261)
-#define HASH_FNV_PRIME UINT32_C(16777619)
-
-uint32_t hash_fnv(const void *, size_t, uint32_t basis);
+uint32_t hash_words(const uint32_t *, size_t n_word, uint32_t basis);
+uint32_t hash_bytes(const void *, size_t n_bytes, uint32_t basis);
#endif /* hash.h */
diff --git a/lib/hmap.c b/lib/hmap.c
new file mode 100644
index 0000000..f05bdb7
--- /dev/null
+++ b/lib/hmap.c
@@ -0,0 +1,153 @@
+/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+ * Junior University
+ *
+ * We are making the OpenFlow specification and associated documentation
+ * (Software) available for public use and benefit with the expectation
+ * that others will use, modify and enhance the Software and contribute
+ * those enhancements back to the community. However, since we would
+ * like to make the Software available for broadest use, with as few
+ * restrictions as possible permission is hereby granted, free of
+ * charge, to any person obtaining a copy of this Software to deal in
+ * the Software under the copyrights without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * 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 AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * The name and trademarks of copyright holder(s) may NOT be used in
+ * advertising or publicity pertaining to the Software or any
+ * derivatives without specific, written prior permission.
+ */
+
+#include
+#include "hmap.h"
+#include
+#include
+#include "util.h"
+
+/* Initializes 'hmap' as an empty hash table. */
+void
+hmap_init(struct hmap *hmap)
+{
+ hmap->buckets = &hmap->one;
+ hmap->one = NULL;
+ hmap->mask = 0;
+ hmap->n = 0;
+}
+
+/* Frees memory reserved by 'hmap'. It is the client's responsibility to free
+ * the nodes themselves, if necessary. */
+void
+hmap_destroy(struct hmap *hmap)
+{
+ if (hmap && hmap->buckets != &hmap->one) {
+ free(hmap->buckets);
+ }
+}
+
+/* Exchanges hash maps 'a' and 'b'. */
+void
+hmap_swap(struct hmap *a, struct hmap *b)
+{
+ struct hmap tmp = *a;
+ *a = *b;
+ *b = tmp;
+ if (a->buckets == &b->one) {
+ a->buckets = &a->one;
+ }
+ if (b->buckets == &a->one) {
+ b->buckets = &b->one;
+ }
+}
+
+static void
+resize(struct hmap *hmap, size_t new_mask)
+{
+ struct hmap tmp;
+ size_t i;
+
+ assert(!(new_mask & (new_mask + 1)));
+ assert(new_mask != SIZE_MAX);
+
+ hmap_init(&tmp);
+ if (new_mask) {
+ tmp.buckets = xmalloc(sizeof *tmp.buckets * (new_mask + 1));
+ tmp.mask = new_mask;
+ for (i = 0; i <= tmp.mask; i++) {
+ tmp.buckets[i] = NULL;
+ }
+ }
+ for (i = 0; i <= hmap->mask; i++) {
+ struct hmap_node *node, *next;
+ for (node = hmap->buckets[i]; node; node = next) {
+ next = node->next;
+ hmap_insert_fast(&tmp, node, node->hash);
+ }
+ }
+ hmap_swap(hmap, &tmp);
+ hmap_destroy(&tmp);
+}
+
+static size_t
+calc_mask(size_t capacity)
+{
+ size_t mask = capacity / 2;
+ mask |= mask >> 1;
+ mask |= mask >> 2;
+ mask |= mask >> 4;
+ mask |= mask >> 8;
+ mask |= mask >> 16;
+#if SIZE_MAX > UINT32_MAX
+ mask |= mask >> 32;
+#endif
+
+ /* If we need to dynamically allocate buckets we might as well allocate at
+ * least 4 of them. */
+ mask |= (mask & 1) << 1;
+
+ return mask;
+}
+
+/* Expands 'hmap', if necessary, to optimize the performance of searches. */
+void
+hmap_expand(struct hmap *hmap)
+{
+ size_t new_mask = calc_mask(hmap->n);
+ if (new_mask > hmap->mask) {
+ resize(hmap, new_mask);
+ }
+}
+
+/* Shrinks 'hmap', if necessary, to optimize the performance of iteration. */
+void
+hmap_shrink(struct hmap *hmap)
+{
+ size_t new_mask = calc_mask(hmap->n);
+ if (new_mask < hmap->mask) {
+ resize(hmap, new_mask);
+ }
+}
+
+/* Expands 'hmap', if necessary, to optimize the performance of searches when
+ * it has up to 'n' elements. (But iteration will be slow in a hash map whose
+ * allocated capacity is much higher than its current number of nodes.) */
+void
+hmap_reserve(struct hmap *hmap, size_t n)
+{
+ size_t new_mask = calc_mask(n);
+ if (new_mask > hmap->mask) {
+ resize(hmap, new_mask);
+ }
+}
diff --git a/lib/hmap.h b/lib/hmap.h
new file mode 100644
index 0000000..5c6a54b
--- /dev/null
+++ b/lib/hmap.h
@@ -0,0 +1,235 @@
+/* Copyright (c) 2008, 2009 The Board of Trustees of The Leland Stanford
+ * Junior University
+ *
+ * We are making the OpenFlow specification and associated documentation
+ * (Software) available for public use and benefit with the expectation
+ * that others will use, modify and enhance the Software and contribute
+ * those enhancements back to the community. However, since we would
+ * like to make the Software available for broadest use, with as few
+ * restrictions as possible permission is hereby granted, free of
+ * charge, to any person obtaining a copy of this Software to deal in
+ * the Software under the copyrights without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * 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 AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * The name and trademarks of copyright holder(s) may NOT be used in
+ * advertising or publicity pertaining to the Software or any
+ * derivatives without specific, written prior permission.
+ */
+
+#ifndef HMAP_H
+#define HMAP_H 1
+
+#include
+#include
+#include "util.h"
+
+/* A hash map node, to be embedded inside the data structure being mapped. */
+struct hmap_node {
+ size_t hash; /* Hash value. */
+ struct hmap_node *next; /* Next in linked list. */
+};
+
+/* Returns the hash value embedded in 'node'. */
+static inline size_t hmap_node_hash(const struct hmap_node *node)
+{
+ return node->hash;
+}
+
+/* A hash map. */
+struct hmap {
+ struct hmap_node **buckets;
+ struct hmap_node *one;
+ size_t mask;
+ size_t n;
+};
+
+/* Initializer for an empty hash map. */
+#define HMAP_INITIALIZER(HMAP) { &(HMAP)->one, NULL, 0, 0 }
+
+/* Initialization. */
+void hmap_init(struct hmap *);
+void hmap_destroy(struct hmap *);
+void hmap_swap(struct hmap *a, struct hmap *b);
+static inline size_t hmap_count(const struct hmap *);
+static inline bool hmap_is_empty(const struct hmap *);
+
+/* Adjusting capacity. */
+void hmap_expand(struct hmap *);
+void hmap_shrink(struct hmap *);
+void hmap_reserve(struct hmap *, size_t capacity);
+
+/* Insertion and deletion. */
+static inline void hmap_insert_fast(struct hmap *,
+ struct hmap_node *, size_t hash);
+static inline void hmap_insert(struct hmap *, struct hmap_node *, size_t hash);
+static inline void hmap_remove(struct hmap *, struct hmap_node *);
+
+/* Search. */
+#define HMAP_FOR_EACH_WITH_HASH(NODE, STRUCT, MEMBER, HASH, HMAP) \
+ for ((NODE) = CONTAINER_OF(hmap_first_with_hash(HMAP, HASH), \
+ STRUCT, MEMBER); \
+ &(NODE)->MEMBER != NULL; \
+ (NODE) = CONTAINER_OF(hmap_next_with_hash(&(NODE)->MEMBER), \
+ STRUCT, MEMBER))
+
+static inline struct hmap_node *hmap_first_with_hash(const struct hmap *,
+ size_t hash);
+static inline struct hmap_node *hmap_next_with_hash(const struct hmap_node *);
+
+/* Iteration.
+ *
+ * The _SAFE version is needed when NODE may be freed. It is not needed when
+ * NODE may be removed from the hash map but its members remain accessible and
+ * intact. */
+#define HMAP_FOR_EACH(NODE, STRUCT, MEMBER, HMAP) \
+ for ((NODE) = CONTAINER_OF(hmap_first(HMAP), STRUCT, MEMBER); \
+ &(NODE)->MEMBER != NULL; \
+ (NODE) = CONTAINER_OF(hmap_next(HMAP, &(NODE)->MEMBER), \
+ STRUCT, MEMBER))
+
+#define HMAP_FOR_EACH_SAFE(NODE, NEXT, STRUCT, MEMBER, HMAP) \
+ for ((NODE) = CONTAINER_OF(hmap_first(HMAP), STRUCT, MEMBER); \
+ (&(NODE)->MEMBER != NULL \
+ ? (NEXT) = CONTAINER_OF(hmap_next(HMAP, &(NODE)->MEMBER), \
+ STRUCT, MEMBER), 1 \
+ : 0); \
+ (NODE) = (NEXT))
+
+static inline struct hmap_node *hmap_first(const struct hmap *);
+static inline struct hmap_node *hmap_next(const struct hmap *,
+ const struct hmap_node *);
+
+/* Returns the number of nodes currently in 'hmap'. */
+static inline size_t
+hmap_count(const struct hmap *hmap)
+{
+ return hmap->n;
+}
+
+/* Returns true if 'hmap' currently contains no nodes,
+ * false otherwise. */
+static inline bool
+hmap_is_empty(const struct hmap *hmap)
+{
+ return hmap->n == 0;
+}
+
+/* Inserts 'node', with the given 'hash', into 'hmap'. 'hmap' is never
+ * expanded automatically. */
+static inline void
+hmap_insert_fast(struct hmap *hmap, struct hmap_node *node, size_t hash)
+{
+ struct hmap_node **bucket = &hmap->buckets[hash & hmap->mask];
+ node->hash = hash;
+ node->next = *bucket;
+ *bucket = node;
+ hmap->n++;
+}
+
+/* Inserts 'node', with the given 'hash', into 'hmap', and expands 'hmap' if
+ * necessary to optimize search performance. */
+static inline void
+hmap_insert(struct hmap *hmap, struct hmap_node *node, size_t hash)
+{
+ hmap_insert_fast(hmap, node, hash);
+ if (hmap->n / 2 > hmap->mask) {
+ hmap_expand(hmap);
+ }
+}
+
+/* Removes 'node' from 'hmap'. Does not shrink the hash table; call
+ * hmap_shrink() directly if desired. */
+static inline void
+hmap_remove(struct hmap *hmap, struct hmap_node *node)
+{
+ struct hmap_node **bucket = &hmap->buckets[node->hash & hmap->mask];
+ while (*bucket != node) {
+ bucket = &(*bucket)->next;
+ }
+ *bucket = node->next;
+ hmap->n--;
+}
+
+static inline struct hmap_node *
+hmap_next_with_hash__(const struct hmap_node *node, size_t hash)
+{
+ while (node != NULL && node->hash != hash) {
+ node = node->next;
+ }
+ return (struct hmap_node *) node;
+}
+
+/* Returns the first node in 'hmap' with the given 'hash', or a null pointer if
+ * no nodes have that hash value. */
+static inline struct hmap_node *
+hmap_first_with_hash(const struct hmap *hmap, size_t hash)
+{
+ return hmap_next_with_hash__(hmap->buckets[hash & hmap->mask], hash);
+}
+
+/* Returns the next node in the same hash map as 'node' with the same hash
+ * value, or a null pointer if no more nodes have that hash value.
+ *
+ * If the hash map has been reallocated since 'node' was visited, some nodes
+ * may be skipped; if new nodes with the same hash value have been added, they
+ * will be skipped. (Removing 'node' from the hash map does not prevent
+ * calling this function, since node->next is preserved, although freeing
+ * 'node' of course does.) */
+static inline struct hmap_node *
+hmap_next_with_hash(const struct hmap_node *node)
+{
+ return hmap_next_with_hash__(node->next, node->hash);
+}
+
+static inline struct hmap_node *
+hmap_next__(const struct hmap *hmap, size_t start)
+{
+ size_t i;
+ for (i = start; i <= hmap->mask; i++) {
+ struct hmap_node *node = hmap->buckets[i];
+ if (node) {
+ return node;
+ }
+ }
+ return NULL;
+}
+
+/* Returns the first node in 'hmap', in arbitrary order, or a null pointer if
+ * 'hmap' is empty. */
+static inline struct hmap_node *
+hmap_first(const struct hmap *hmap)
+{
+ return hmap_next__(hmap, 0);
+}
+
+/* Returns the next node in 'hmap' following 'node', in arbitrary order, or a
+ * null pointer if 'node' is the last node in 'hmap'.
+ *
+ * If the hash map has been reallocated since 'node' was visited, some nodes
+ * may be skipped or visited twice. (Removing 'node' from the hash map does
+ * not prevent calling this function, since node->next is preserved, although
+ * freeing 'node' of course does.) */
+static inline struct hmap_node *
+hmap_next(const struct hmap *hmap, const struct hmap_node *node)
+{
+ return (node->next
+ ? node->next
+ : hmap_next__(hmap, (node->hash & hmap->mask) + 1));
+}
+
+#endif /* hmap.h */
diff --git a/lib/leak-checker.c b/lib/leak-checker.c
new file mode 100644
index 0000000..2a16211
--- /dev/null
+++ b/lib/leak-checker.c
@@ -0,0 +1,252 @@
+/* Copyright (c) 2008, 2009 The Board of Trustees of The Leland Stanford
+ * Junior University
+ *
+ * We are making the OpenFlow specification and associated documentation
+ * (Software) available for public use and benefit with the expectation
+ * that others will use, modify and enhance the Software and contribute
+ * those enhancements back to the community. However, since we would
+ * like to make the Software available for broadest use, with as few
+ * restrictions as possible permission is hereby granted, free of
+ * charge, to any person obtaining a copy of this Software to deal in
+ * the Software under the copyrights without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * 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 AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * The name and trademarks of copyright holder(s) may NOT be used in
+ * advertising or publicity pertaining to the Software or any
+ * derivatives without specific, written prior permission.
+ */
+
+#include
+#include "leak-checker.h"
+#include
+#include "backtrace.h"
+
+#define THIS_MODULE VLM_leak_checker
+#include "vlog.h"
+
+#ifndef HAVE_MALLOC_HOOKS
+void
+leak_checker_start(const char *file_name UNUSED)
+{
+ VLOG_WARN("not enabling leak checker because the libc in use does not "
+ "have the required hooks");
+}
+
+void
+leak_checker_set_limit(off_t max_size UNUSED)
+{
+}
+
+void
+leak_checker_claim(const void *p UNUSED)
+{
+}
+
+void
+leak_checker_usage(void)
+{
+ printf(" --check-leaks=FILE (accepted but ignored in this build)\n");
+}
+#else /* HAVE_MALLOC_HOOKS */
+#include
+#include
+#include
+#include
+
+typedef void *malloc_hook_type(size_t, const void *);
+typedef void *realloc_hook_type(void *, size_t, const void *);
+typedef void free_hook_type(void *, const void *);
+
+struct hooks {
+ malloc_hook_type *malloc_hook_func;
+ realloc_hook_type *realloc_hook_func;
+ free_hook_type *free_hook_func;
+};
+
+static malloc_hook_type hook_malloc;
+static realloc_hook_type hook_realloc;
+static free_hook_type hook_free;
+
+static struct hooks libc_hooks;
+static const struct hooks our_hooks = { hook_malloc, hook_realloc, hook_free };
+
+static FILE *file;
+static off_t limit = 10 * 1000 * 1000;
+
+static void
+get_hooks(struct hooks *hooks)
+{
+ hooks->malloc_hook_func = __malloc_hook;
+ hooks->realloc_hook_func = __realloc_hook;
+ hooks->free_hook_func = __free_hook;
+}
+
+static void
+set_hooks(const struct hooks *hooks)
+{
+ __malloc_hook = hooks->malloc_hook_func;
+ __realloc_hook = hooks->realloc_hook_func;
+ __free_hook = hooks->free_hook_func;
+}
+
+void
+leak_checker_start(const char *file_name)
+{
+ if (!file) {
+ file = fopen(file_name, "w");
+ if (!file) {
+ VLOG_WARN("failed to create \"%s\": %s",
+ file_name, strerror(errno));
+ return;
+ }
+ setvbuf(file, NULL, _IONBF, 0);
+ VLOG_WARN("enabled memory leak logging to \"%s\"", file_name);
+ get_hooks(&libc_hooks);
+ set_hooks(&our_hooks);
+ }
+}
+
+void
+leak_checker_stop(void)
+{
+ if (file) {
+ fclose(file);
+ file = NULL;
+ set_hooks(&libc_hooks);
+ VLOG_WARN("disabled memory leak logging");
+ }
+}
+
+void
+leak_checker_set_limit(off_t limit_)
+{
+ limit = limit_;
+}
+
+void
+leak_checker_usage(void)
+{
+ printf(" --check-leaks=FILE log malloc and free calls to FILE\n");
+}
+
+static void PRINTF_FORMAT(1, 2)
+log_callers(const char *format, ...)
+{
+ struct backtrace backtrace;
+ va_list args;
+ int i;
+
+ va_start(args, format);
+ vfprintf(file, format, args);
+ va_end(args);
+
+ putc(':', file);
+ backtrace_capture(&backtrace);
+ for (i = 0; i < backtrace.n_frames; i++) {
+ fprintf(file, " 0x%x", backtrace.frames[i]);
+ }
+ putc('\n', file);
+}
+
+static void
+reset_hooks(void)
+{
+ static int count;
+
+ if (count++ >= 100 && limit && file) {
+ struct stat s;
+ count = 0;
+ if (fstat(fileno(file), &s) < 0) {
+ VLOG_WARN("cannot fstat leak checker log file: %s",
+ strerror(errno));
+ return;
+ }
+ if (s.st_size > limit) {
+ VLOG_WARN("leak checker log file size exceeded limit");
+ leak_checker_stop();
+ return;
+ }
+ }
+ if (file) {
+ set_hooks(&our_hooks);
+ }
+}
+
+static void *
+hook_malloc(size_t size, const void *caller UNUSED)
+{
+ void *p;
+
+ set_hooks(&libc_hooks);
+ p = malloc(size);
+ get_hooks(&libc_hooks);
+
+ log_callers("malloc(%zu) -> %p", size, p);
+
+ reset_hooks();
+ return p;
+}
+
+void
+leak_checker_claim(const void *p)
+{
+ if (!file) {
+ return;
+ }
+
+ if (p) {
+ set_hooks(&libc_hooks);
+ log_callers("claim(%p)", p);
+ reset_hooks();
+ }
+}
+
+static void
+hook_free(void *p, const void *caller UNUSED)
+{
+ if (!p) {
+ return;
+ }
+
+ set_hooks(&libc_hooks);
+ free(p);
+ get_hooks(&libc_hooks);
+
+ log_callers("free(%p)", p);
+
+ reset_hooks();
+}
+
+static void *
+hook_realloc(void *p, size_t size, const void *caller UNUSED)
+{
+ void *q;
+
+ set_hooks(&libc_hooks);
+ q = realloc(p, size);
+ get_hooks(&libc_hooks);
+
+ if (p != q) {
+ log_callers("realloc(%p, %zu) -> %p", p, size, q);
+ }
+
+ reset_hooks();
+
+ return q;
+}
+#endif /* HAVE_MALLOC_HOOKS */
diff --git a/lib/leak-checker.h b/lib/leak-checker.h
new file mode 100644
index 0000000..c0317a7
--- /dev/null
+++ b/lib/leak-checker.h
@@ -0,0 +1,58 @@
+/* Copyright (c) 2008, 2009 The Board of Trustees of The Leland Stanford
+ * Junior University
+ *
+ * We are making the OpenFlow specification and associated documentation
+ * (Software) available for public use and benefit with the expectation
+ * that others will use, modify and enhance the Software and contribute
+ * those enhancements back to the community. However, since we would
+ * like to make the Software available for broadest use, with as few
+ * restrictions as possible permission is hereby granted, free of
+ * charge, to any person obtaining a copy of this Software to deal in
+ * the Software under the copyrights without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * 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 AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * The name and trademarks of copyright holder(s) may NOT be used in
+ * advertising or publicity pertaining to the Software or any
+ * derivatives without specific, written prior permission.
+ */
+
+#ifndef LEAK_CHECKER_H
+#define LEAK_CHECKER_H 1
+
+#include
+
+#define LEAK_CHECKER_OPTION_ENUMS \
+ OPT_CHECK_LEAKS, \
+ OPT_LEAK_LIMIT
+#define LEAK_CHECKER_LONG_OPTIONS \
+ {"check-leaks", required_argument, 0, OPT_CHECK_LEAKS}, \
+ {"leak-limit", required_argument, 0, OPT_LEAK_LIMIT}
+#define LEAK_CHECKER_OPTION_HANDLERS \
+ case OPT_CHECK_LEAKS: \
+ leak_checker_start(optarg); \
+ break; \
+ case OPT_LEAK_LIMIT: \
+ leak_checker_set_limit(atol(optarg)); \
+ break;
+void leak_checker_start(const char *file_name);
+void leak_checker_set_limit(off_t limit);
+void leak_checker_stop(void);
+void leak_checker_claim(const void *);
+void leak_checker_usage(void);
+
+#endif /* leak-checker.h */
diff --git a/lib/leak-checker.man b/lib/leak-checker.man
new file mode 100644
index 0000000..7b376e1
--- /dev/null
+++ b/lib/leak-checker.man
@@ -0,0 +1,7 @@
+.TP
+\fB--check-leaks=\fIfile\fR
+.
+Logs information about memory allocation and deallocation to
+\fIfile\fR, to allow for debugging memory leaks in \fB\*(PN\fR. This
+option slows down \fB\*(PN\fR considerably, so it should only be used
+when a memory leak is suspected.
diff --git a/lib/learning-switch.c b/lib/learning-switch.c
index e674881..3778823 100644
--- a/lib/learning-switch.c
+++ b/lib/learning-switch.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+/* Copyright (c) 2008, 2009 The Board of Trustees of The Leland Stanford
* Junior University
*
* We are making the OpenFlow specification and associated documentation
@@ -159,6 +159,10 @@ lswitch_run(struct lswitch *sw, struct rconn *rconn)
{
long long int now = time_msec();
+ if (sw->ml) {
+ mac_learning_run(sw->ml, NULL);
+ }
+
/* If we're waiting for more replies, keeping waiting for up to 10 s. */
if (sw->last_reply != LLONG_MIN) {
if (now - sw->last_reply > 10000) {
@@ -238,6 +242,10 @@ wait_timeout(long long int started)
void
lswitch_wait(struct lswitch *sw)
{
+ if (sw->ml) {
+ mac_learning_wait(sw->ml);
+ }
+
if (sw->last_reply != LLONG_MIN) {
wait_timeout(sw->last_reply);
} else if (sw->last_query != LLONG_MIN) {
@@ -353,7 +361,7 @@ queue_tx(struct lswitch *sw, struct rconn *rconn, struct ofpbuf *b)
int retval = rconn_send_with_limit(rconn, b, &sw->n_queued, 10);
if (retval && retval != ENOTCONN) {
if (retval == EAGAIN) {
- VLOG_WARN_RL(&rl, "%012llx: %s: tx queue overflow",
+ VLOG_INFO_RL(&rl, "%012llx: %s: tx queue overflow",
sw->datapath_id, rconn_get_name(rconn));
} else {
VLOG_WARN_RL(&rl, "%012llx: %s: send: %s",
@@ -410,7 +418,7 @@ process_packet_in(struct lswitch *sw, struct rconn *rconn, void *opi_)
flow_extract(&pkt, in_port, &flow);
if (may_learn(sw, in_port) && sw->ml) {
- if (mac_learning_learn(sw->ml, flow.dl_src, in_port)) {
+ if (mac_learning_learn(sw->ml, flow.dl_src, 0, in_port)) {
VLOG_DBG_RL(&rl, "%012llx: learned that "ETH_ADDR_FMT" is on "
"port %"PRIu16, sw->datapath_id,
ETH_ADDR_ARGS(flow.dl_src), in_port);
@@ -427,7 +435,7 @@ process_packet_in(struct lswitch *sw, struct rconn *rconn, void *opi_)
}
if (sw->ml) {
- uint16_t learned_port = mac_learning_lookup(sw->ml, flow.dl_dst);
+ uint16_t learned_port = mac_learning_lookup(sw->ml, flow.dl_dst, 0);
if (may_send(sw, learned_port)) {
out_port = learned_port;
}
@@ -489,7 +497,7 @@ process_port_status(struct lswitch *sw, struct rconn *rconn, void *ops_)
}
static void
-process_phy_port(struct lswitch *sw, struct rconn *rconn, void *opp_)
+process_phy_port(struct lswitch *sw, struct rconn *rconn UNUSED, void *opp_)
{
const struct ofp_phy_port *opp = opp_;
uint16_t port_no = ntohs(opp->port_no);
@@ -584,8 +592,6 @@ process_flow_stats(struct lswitch *sw, struct rconn *rconn,
size_t len;
for (a = ofs->actions; (char *) a < end; a += len / 8) {
- uint16_t type;
-
len = ntohs(a->len);
if (len > end - (char *) a) {
VLOG_DBG_RL(&rl, "%012llx: action exceeds available space "
@@ -598,7 +604,6 @@ process_flow_stats(struct lswitch *sw, struct rconn *rconn,
break;
}
- type = ntohs(a->type);
if (a->type == htons(OFPAT_OUTPUT)) {
struct ofp_action_output *oao = (struct ofp_action_output *) a;
if (!may_send(sw, ntohs(oao->port))) {
@@ -627,52 +632,18 @@ static void
process_stats_reply(struct lswitch *sw, struct rconn *rconn, void *osr_)
{
struct ofp_stats_reply *osr = osr_;
- const uint8_t *body = osr->body;
- const uint8_t *pos = body;
- size_t body_len;
+ struct flow_stats_iterator i;
+ const struct ofp_flow_stats *fs;
if (sw->last_query == LLONG_MIN
|| osr->type != htons(OFPST_FLOW)
|| osr->header.xid != sw->query_xid) {
return;
}
- body_len = (ntohs(osr->header.length)
- - offsetof(struct ofp_stats_reply, body));
- for (;;) {
- const struct ofp_flow_stats *fs;
- ptrdiff_t bytes_left = body + body_len - pos;
- size_t length;
-
- if (bytes_left < sizeof *fs) {
- if (bytes_left != 0) {
- VLOG_WARN_RL(&rl, "%012llx: %td leftover bytes in flow "
- "stats reply", sw->datapath_id, bytes_left);
- }
- break;
- }
-
- fs = (const void *) pos;
- length = ntohs(fs->length);
- if (length < sizeof *fs) {
- VLOG_WARN_RL(&rl, "%012llx: flow stats length %zu is shorter than "
- "min %zu", sw->datapath_id, length, sizeof *fs);
- break;
- } else if (length > bytes_left) {
- VLOG_WARN_RL(&rl, "%012llx: flow stats length %zu but only %td "
- "bytes left", sw->datapath_id, length, bytes_left);
- break;
- } else if ((length - sizeof *fs) % sizeof fs->actions[0]) {
- VLOG_WARN_RL(&rl, "%012llx: flow stats length %zu has %zu bytes "
- "left over in final action", sw->datapath_id, length,
- (length - sizeof *fs) % sizeof fs->actions[0]);
- break;
- }
-
+ for (fs = flow_stats_first(&i, osr); fs; fs = flow_stats_next(&i)) {
sw->n_flows++;
process_flow_stats(sw, rconn, fs);
-
- pos += length;
- }
+ }
if (!(osr->flags & htons(OFPSF_REPLY_MORE))) {
VLOG_DBG("%012llx: Deleted %d of %d received flows to "
"implement STP, %d because of no-recv, %d because of "
diff --git a/lib/mac-learning.c b/lib/mac-learning.c
index b924e49..aa1e3ad 100644
--- a/lib/mac-learning.c
+++ b/lib/mac-learning.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+/* Copyright (c) 2008, 2009 The Board of Trustees of The Leland Stanford
* Junior University
*
* We are making the OpenFlow specification and associated documentation
@@ -41,6 +41,8 @@
#include "hash.h"
#include "list.h"
#include "openflow/openflow.h"
+#include "poll-loop.h"
+#include "tag.h"
#include "timeval.h"
#include "util.h"
@@ -56,41 +58,96 @@
/* A MAC learning table entry. */
struct mac_entry {
struct list hash_node; /* Element in a mac_learning 'table' list. */
- struct list lru_node; /* Element in mac_learning 'lrus' list. */
- time_t used; /* Last used time. */
+ struct list lru_node; /* Element in 'lrus' or 'free' list. */
+ time_t expires; /* Expiration time. */
uint8_t mac[ETH_ADDR_LEN]; /* Known MAC address. */
+ uint16_t vlan; /* VLAN tag. */
int port; /* Port on which MAC was most recently seen. */
+ tag_type tag; /* Tag for this learning entry. */
};
/* MAC learning table. */
struct mac_learning {
- struct list lrus; /* All entries, least recently used at the
+ struct list free; /* Not-in-use entries. */
+ struct list lrus; /* In-use entries, least recently used at the
front, most recently used at the back. */
struct list table[MAC_HASH_SIZE]; /* Hash table. */
struct mac_entry entries[MAC_MAX]; /* All entries. */
+ uint32_t secret; /* Secret for */
};
+static uint32_t
+mac_table_hash(const uint8_t mac[ETH_ADDR_LEN], uint16_t vlan)
+{
+ return hash_bytes(mac, ETH_ADDR_LEN, vlan);
+}
+
+static struct mac_entry *
+mac_entry_from_lru_node(struct list *list)
+{
+ return CONTAINER_OF(list, struct mac_entry, lru_node);
+}
+
+/* Returns a tag that represents that 'mac' is on an unknown port in 'vlan'.
+ * (When we learn where 'mac' is in 'vlan', this allows flows that were
+ * flooded to be revalidated.) */
+static tag_type
+make_unknown_mac_tag(const struct mac_learning *ml,
+ const uint8_t mac[ETH_ADDR_LEN], uint16_t vlan)
+{
+ uint32_t h = hash_bytes(&ml->secret, sizeof ml->secret,
+ mac_table_hash(mac, vlan));
+ return tag_create_deterministic(h);
+}
+
static struct list *
mac_table_bucket(const struct mac_learning *ml,
- const uint8_t mac[ETH_ADDR_LEN])
+ const uint8_t mac[ETH_ADDR_LEN],
+ uint16_t vlan)
{
- uint32_t hash = hash_fnv(mac, ETH_ADDR_LEN, HASH_FNV_BASIS);
+ uint32_t hash = mac_table_hash(mac, vlan);
const struct list *list = &ml->table[hash & MAC_HASH_BITS];
return (struct list *) list;
}
static struct mac_entry *
-search_bucket(struct list *bucket, const uint8_t mac[ETH_ADDR_LEN])
+search_bucket(struct list *bucket, const uint8_t mac[ETH_ADDR_LEN],
+ uint16_t vlan)
{
struct mac_entry *e;
LIST_FOR_EACH (e, struct mac_entry, hash_node, bucket) {
- if (eth_addr_equals(e->mac, mac)) {
+ if (eth_addr_equals(e->mac, mac) && e->vlan == vlan) {
return e;
}
}
return NULL;
}
+/* If the LRU list is not empty, stores the least-recently-used entry in '*e'
+ * and returns true. Otherwise, if the LRU list is empty, stores NULL in '*e'
+ * and return false. */
+static bool
+get_lru(struct mac_learning *ml, struct mac_entry **e)
+{
+ if (!list_is_empty(&ml->lrus)) {
+ *e = mac_entry_from_lru_node(ml->lrus.next);
+ return true;
+ } else {
+ *e = NULL;
+ return false;
+ }
+}
+
+/* Removes 'e' from the 'ml' hash table. 'e' must not already be on the free
+ * list. */
+static void
+free_mac_entry(struct mac_learning *ml, struct mac_entry *e)
+{
+ list_remove(&e->hash_node);
+ list_remove(&e->lru_node);
+ list_push_front(&ml->free, &e->lru_node);
+}
+
/* Creates and returns a new MAC learning table. */
struct mac_learning *
mac_learning_create(void)
@@ -100,14 +157,15 @@ mac_learning_create(void)
ml = xmalloc(sizeof *ml);
list_init(&ml->lrus);
+ list_init(&ml->free);
for (i = 0; i < MAC_HASH_SIZE; i++) {
list_init(&ml->table[i]);
}
for (i = 0; i < MAC_MAX; i++) {
struct mac_entry *s = &ml->entries[i];
- list_push_front(&ml->lrus, &s->lru_node);
- s->hash_node.next = NULL;
+ list_push_front(&ml->free, &s->lru_node);
}
+ ml->secret = random_uint32();
return ml;
}
@@ -119,11 +177,18 @@ mac_learning_destroy(struct mac_learning *ml)
}
/* Attempts to make 'ml' learn from the fact that a frame from 'src_mac' was
- * just observed arriving on 'src_port'. Returns true if we actually learned
- * something from this, false if it just confirms what we already knew. */
-bool
+ * just observed arriving from 'src_port' on the given 'vlan'.
+ *
+ * Returns nonzero if we actually learned something from this, zero if it just
+ * confirms what we already knew. The nonzero return value is the tag of flows
+ * that now need revalidation.
+ *
+ * The 'vlan' parameter is used to maintain separate per-VLAN learning tables.
+ * Specify 0 if this behavior is undesirable. */
+tag_type
mac_learning_learn(struct mac_learning *ml,
- const uint8_t src_mac[ETH_ADDR_LEN], uint16_t src_port)
+ const uint8_t src_mac[ETH_ADDR_LEN], uint16_t vlan,
+ uint16_t src_port)
{
struct mac_entry *e;
struct list *bucket;
@@ -132,45 +197,105 @@ mac_learning_learn(struct mac_learning *ml,
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(30, 30);
VLOG_DBG_RL(&rl, "multicast packet source "ETH_ADDR_FMT,
ETH_ADDR_ARGS(src_mac));
- return false;
+ return 0;
}
- bucket = mac_table_bucket(ml, src_mac);
- e = search_bucket(bucket, src_mac);
+ bucket = mac_table_bucket(ml, src_mac, vlan);
+ e = search_bucket(bucket, src_mac, vlan);
if (!e) {
- e = CONTAINER_OF(ml->lrus.next, struct mac_entry, lru_node);
- memcpy(e->mac, src_mac, ETH_ADDR_LEN);
- if (e->hash_node.next) {
+ if (!list_is_empty(&ml->free)) {
+ e = mac_entry_from_lru_node(ml->free.next);
+ } else {
+ e = mac_entry_from_lru_node(ml->lrus.next);
list_remove(&e->hash_node);
}
+ memcpy(e->mac, src_mac, ETH_ADDR_LEN);
list_push_front(bucket, &e->hash_node);
e->port = -1;
+ e->vlan = vlan;
+ e->tag = make_unknown_mac_tag(ml, src_mac, vlan);
}
/* Make the entry most-recently-used. */
list_remove(&e->lru_node);
list_push_back(&ml->lrus, &e->lru_node);
- e->used = time_now();
+ e->expires = time_now() + 60;
/* Did we learn something? */
if (e->port != src_port) {
+ tag_type old_tag = e->tag;
e->port = src_port;
- return true;
+ e->tag = tag_create_random();
+ return old_tag;
}
- return false;
+ return 0;
}
-/* Looks up address 'dst' in 'ml'. Returns the port on which a frame destined
- * for 'dst' should be sent, OFPP_FLOOD if unknown. */
+/* Looks up MAC 'dst' for VLAN 'vlan' in 'ml'. Returns the port on which a
+ * frame destined for 'dst' should be sent, OFPP_FLOOD if unknown. */
uint16_t
mac_learning_lookup(const struct mac_learning *ml,
- const uint8_t dst[ETH_ADDR_LEN])
+ const uint8_t dst[ETH_ADDR_LEN], uint16_t vlan)
{
- if (!eth_addr_is_multicast(dst)) {
- struct mac_entry *e = search_bucket(mac_table_bucket(ml, dst), dst);
- if (e && time_now() - e->used < 60) {
+ tag_type tag = 0;
+ return mac_learning_lookup_tag(ml, dst, vlan, &tag);
+}
+
+/* Looks up MAC 'dst' for VLAN 'vlan' in 'ml'. Returns the port on which a
+ * frame destined for 'dst' should be sent, OFPP_FLOOD if unknown.
+ *
+ * Adds to '*tag' (which the caller must have initialized) the tag that should
+ * be attached to any flow created based on the return value, if any, to allow
+ * those flows to be revalidated when the MAC learning entry changes. */
+uint16_t
+mac_learning_lookup_tag(const struct mac_learning *ml,
+ const uint8_t dst[ETH_ADDR_LEN], uint16_t vlan,
+ tag_type *tag)
+{
+ if (eth_addr_is_multicast(dst)) {
+ return OFPP_FLOOD;
+ } else {
+ struct mac_entry *e = search_bucket(mac_table_bucket(ml, dst, vlan),
+ dst, vlan);
+ if (e) {
+ *tag |= e->tag;
return e->port;
+ } else {
+ *tag |= make_unknown_mac_tag(ml, dst, vlan);
+ return OFPP_FLOOD;
}
}
- return OFPP_FLOOD;
+}
+
+/* Expires all the mac-learning entries in 'ml'. The tags in 'ml' are
+ * discarded, so the client is responsible for revalidating any flows that
+ * depend on 'ml', if necessary. */
+void
+mac_learning_flush(struct mac_learning *ml)
+{
+ struct mac_entry *e;
+ while (get_lru(ml, &e)){
+ free_mac_entry(ml, e);
+ }
+}
+
+void
+mac_learning_run(struct mac_learning *ml, struct tag_set *set)
+{
+ struct mac_entry *e;
+ while (get_lru(ml, &e) && time_now() >= e->expires) {
+ if (set) {
+ tag_set_add(set, e->tag);
+ }
+ free_mac_entry(ml, e);
+ }
+}
+
+void
+mac_learning_wait(struct mac_learning *ml)
+{
+ if (!list_is_empty(&ml->lrus)) {
+ struct mac_entry *e = mac_entry_from_lru_node(ml->lrus.next);
+ poll_timer_wait((e->expires - time_now()) * 1000);
+ }
}
diff --git a/lib/mac-learning.h b/lib/mac-learning.h
index 3c1b3dc..8e3db91 100644
--- a/lib/mac-learning.h
+++ b/lib/mac-learning.h
@@ -35,12 +35,20 @@
#define MAC_LEARNING_H 1
#include "packets.h"
+#include "tag.h"
struct mac_learning *mac_learning_create(void);
void mac_learning_destroy(struct mac_learning *);
-bool mac_learning_learn(struct mac_learning *,
- const uint8_t src[ETH_ADDR_LEN], uint16_t src_port);
+tag_type mac_learning_learn(struct mac_learning *,
+ const uint8_t src[ETH_ADDR_LEN], uint16_t vlan,
+ uint16_t src_port);
uint16_t mac_learning_lookup(const struct mac_learning *,
- const uint8_t dst[ETH_ADDR_LEN]);
+ const uint8_t dst[ETH_ADDR_LEN], uint16_t vlan);
+uint16_t mac_learning_lookup_tag(const struct mac_learning *,
+ const uint8_t dst[ETH_ADDR_LEN],
+ uint16_t vlan, tag_type *tag);
+void mac_learning_flush(struct mac_learning *);
+void mac_learning_run(struct mac_learning *, struct tag_set *);
+void mac_learning_wait(struct mac_learning *);
#endif /* mac-learning.h */
diff --git a/lib/netdev.c b/lib/netdev.c
index b9c1bac..fc1975c 100644
--- a/lib/netdev.c
+++ b/lib/netdev.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+/* Copyright (c) 2008, 2009 The Board of Trustees of The Leland Stanford
* Junior University
*
* We are making the OpenFlow specification and associated documentation
@@ -56,8 +56,9 @@
#define s32 __s32
#define s64 __s64
#endif
-#include
+#include
+#include
#include
#include
#include
@@ -76,6 +77,7 @@
#include "fatal-signal.h"
#include "list.h"
+#include "netlink.h"
#include "ofpbuf.h"
#include "openflow/openflow.h"
#include "packets.h"
@@ -83,12 +85,13 @@
#include "socket-util.h"
#include "svec.h"
-/* This doesn't seem to be defined in the linux/ethtool.h for linux 2.4 */
-#ifndef SPEED_2500
-#define SPEED_2500 2500
+/* linux/if.h defines IFF_LOWER_UP, net/if.h doesn't.
+ * net/if.h defines if_nameindex(), linux/if.h doesn't.
+ * We can't include both headers, so define IFF_LOWER_UP ourselves. */
+#ifndef IFF_LOWER_UP
+#define IFF_LOWER_UP 0x10000
#endif
-
#define THIS_MODULE VLM_netdev
#include "vlog.h"
@@ -136,8 +139,8 @@ static void init_netdev(void);
static int do_open_netdev(const char *name, int ethertype, int tap_fd,
struct netdev **netdev_);
static int restore_flags(struct netdev *netdev);
-static int get_flags(const struct netdev *, int fd, int *flagsp);
-static int set_flags(struct netdev *, int fd, int flags);
+static int get_flags(const char *netdev_name, int *flagsp);
+static int set_flags(const char *netdev_name, int flags);
/* Obtains the IPv6 address for 'name' into 'in6'. */
static void
@@ -478,7 +481,7 @@ do_open_netdev(const char *name, int ethertype, int tap_fd,
do_ethtool(netdev);
/* Save flags to restore at close or exit. */
- error = get_flags(netdev, netdev_fd, &netdev->save_flags);
+ error = get_flags(netdev->name, &netdev->save_flags);
if (error) {
goto error_already_set;
}
@@ -702,31 +705,6 @@ netdev_get_mtu(const struct netdev *netdev)
return netdev->mtu;
}
-/* Checks the link status. Returns 1 or 0 to indicate the link is active
- * or not, respectively. Any other return value indicates an error. */
-int
-netdev_get_link_status(const struct netdev *netdev)
-{
- struct ifreq ifr;
- struct ethtool_value edata;
-
- memset(&ifr, 0, sizeof ifr);
- strncpy(ifr.ifr_name, netdev->name, sizeof ifr.ifr_name);
- ifr.ifr_data = (caddr_t) &edata;
-
- memset(&edata, 0, sizeof edata);
- edata.cmd = ETHTOOL_GLINK;
- if (ioctl(netdev->netdev_fd, SIOCETHTOOL, &ifr) == 0) {
- if (edata.data) {
- return 1;
- } else {
- return 0;
- }
- }
-
- return -1;
-}
-
/* Returns the features supported by 'netdev' of type 'type', as a bitmap
* of bits from enum ofp_phy_features, in host byte order. */
uint32_t
@@ -817,9 +795,9 @@ netdev_set_in4(struct netdev *netdev, struct in_addr addr, struct in_addr mask)
return error;
}
-/* Adds 'router' as a default gateway for 'netdev''s IP address. */
+/* Adds 'router' as a default IP gateway. */
int
-netdev_add_router(struct netdev *netdev, struct in_addr router)
+netdev_add_router(struct in_addr router)
{
struct in_addr any = { INADDR_ANY };
struct rtentry rt;
@@ -853,21 +831,7 @@ netdev_get_in6(const struct netdev *netdev, struct in6_addr *in6)
int
netdev_get_flags(const struct netdev *netdev, enum netdev_flags *flagsp)
{
- int error, flags;
-
- error = get_flags(netdev, netdev->netdev_fd, &flags);
- if (error) {
- return error;
- }
-
- *flagsp = 0;
- if (flags & IFF_UP) {
- *flagsp |= NETDEV_UP;
- }
- if (flags & IFF_PROMISC) {
- *flagsp |= NETDEV_PROMISC;
- }
- return 0;
+ return netdev_nodev_get_flags(netdev->name, flagsp);
}
static int
@@ -888,13 +852,13 @@ nd_to_iff_flags(enum netdev_flags nd)
* will be reverted when 'netdev' is closed or the program exits. Returns 0 if
* successful, otherwise a positive errno value. */
static int
-do_update_flags(struct netdev *netdev, int fd, enum netdev_flags off,
+do_update_flags(struct netdev *netdev, enum netdev_flags off,
enum netdev_flags on, bool permanent)
{
int old_flags, new_flags;
int error;
- error = get_flags(netdev, fd, &old_flags);
+ error = get_flags(netdev->name, &old_flags);
if (error) {
return error;
}
@@ -904,7 +868,7 @@ do_update_flags(struct netdev *netdev, int fd, enum netdev_flags off,
netdev->changed_flags |= new_flags ^ old_flags;
}
if (new_flags != old_flags) {
- error = set_flags(netdev, fd, new_flags);
+ error = set_flags(netdev->name, new_flags);
}
return error;
}
@@ -917,7 +881,7 @@ int
netdev_set_flags(struct netdev *netdev, enum netdev_flags flags,
bool permanent)
{
- return do_update_flags(netdev, netdev->netdev_fd, -1, flags, permanent);
+ return do_update_flags(netdev, -1, flags, permanent);
}
/* Turns on the specified 'flags' on 'netdev'.
@@ -928,7 +892,7 @@ int
netdev_turn_flags_on(struct netdev *netdev, enum netdev_flags flags,
bool permanent)
{
- return do_update_flags(netdev, netdev->netdev_fd, 0, flags, permanent);
+ return do_update_flags(netdev, 0, flags, permanent);
}
/* Turns off the specified 'flags' on 'netdev'.
@@ -939,7 +903,7 @@ int
netdev_turn_flags_off(struct netdev *netdev, enum netdev_flags flags,
bool permanent)
{
- return do_update_flags(netdev, netdev->netdev_fd, flags, 0, permanent);
+ return do_update_flags(netdev, flags, 0, permanent);
}
/* Looks up the ARP table entry for 'ip' on 'netdev'. If one exists and can be
@@ -992,6 +956,202 @@ netdev_enumerate(struct svec *svec)
strerror(errno));
}
}
+
+/* Obtains the current flags for the network device named 'netdev_name' and
+ * stores them into '*flagsp'. Returns 0 if successful, otherwise a positive
+ * errno value.
+ *
+ * If only device flags are needed, this is more efficient than calling
+ * netdev_open(), netdev_get_flags(), netdev_close(). */
+int
+netdev_nodev_get_flags(const char *netdev_name, enum netdev_flags *flagsp)
+{
+ int error, flags;
+
+ init_netdev();
+
+ error = get_flags(netdev_name, &flags);
+ if (error) {
+ return error;
+ }
+
+ *flagsp = 0;
+ if (flags & IFF_UP) {
+ *flagsp |= NETDEV_UP;
+ }
+ if (flags & IFF_PROMISC) {
+ *flagsp |= NETDEV_PROMISC;
+ }
+ if (flags & IFF_LOWER_UP) {
+ *flagsp |= NETDEV_CARRIER;
+ }
+ return 0;
+}
+
+struct netdev_monitor {
+ struct nl_sock *sock;
+ struct svec netdevs;
+ struct svec changed;
+};
+
+/* Policy for RTNLGRP_LINK messages.
+ *
+ * There are *many* more fields in these messages, but currently we only care
+ * about interface names. */
+static const struct nl_policy rtnlgrp_link_policy[] = {
+ [IFLA_IFNAME] = { .type = NL_A_STRING, .optional = false },
+};
+
+static const char *lookup_netdev(const struct netdev_monitor *, const char *);
+static const char *pop_changed(struct netdev_monitor *);
+static const char *all_netdevs_changed(struct netdev_monitor *);
+
+/* Creates a new network device monitor that initially monitors no
+ * devices. On success, sets '*monp' to the new network monitor and returns
+ * 0; on failure, sets '*monp' to a null pointer and returns a positive errno
+ * value. */
+int
+netdev_monitor_create(struct netdev_monitor **monp)
+{
+ struct netdev_monitor *mon;
+ struct nl_sock *sock;
+ int error;
+
+ *monp = NULL;
+ error = nl_sock_create(NETLINK_ROUTE, RTNLGRP_LINK, 0, 0, &sock);
+ if (error) {
+ /* XXX Fall back to polling? Non-root is not allowed to subscribe to
+ * multicast groups but can still poll network device state. */
+ VLOG_WARN("could not create rtnetlink socket: %s", strerror(error));
+ return error;
+ }
+
+ mon = *monp = xmalloc(sizeof *mon);
+ mon->sock = sock;
+ svec_init(&mon->netdevs);
+ svec_init(&mon->changed);
+ return 0;
+}
+
+void
+netdev_monitor_destroy(struct netdev_monitor *mon)
+{
+ if (mon) {
+ nl_sock_destroy(mon->sock);
+ svec_destroy(&mon->netdevs);
+ svec_destroy(&mon->changed);
+ free(mon);
+ }
+}
+
+/* Sets the set of network devices monitored by 'mon' to the 'n_netdevs'
+ * network devices named in 'netdevs'. The caller retains ownership of
+ * 'netdevs'. */
+void
+netdev_monitor_set_devices(struct netdev_monitor *mon,
+ char **netdevs, size_t n_netdevs)
+{
+ size_t i;
+
+ svec_clear(&mon->netdevs);
+ for (i = 0; i < n_netdevs; i++) {
+ svec_add(&mon->netdevs, netdevs[i]);
+ }
+ svec_sort(&mon->netdevs);
+}
+
+/* If the state of any network device has changed, returns its name. The
+ * caller must not modify or free the name.
+ *
+ * This function can return "false positives". The caller is responsible for
+ * verifying that the network device's state actually changed, if necessary.
+ *
+ * If no network device's state has changed, returns a null pointer. */
+const char *
+netdev_monitor_poll(struct netdev_monitor *mon)
+{
+ static struct vlog_rate_limit slow_rl = VLOG_RATE_LIMIT_INIT(1, 5);
+ const char *changed_name;
+
+ changed_name = pop_changed(mon);
+ if (changed_name) {
+ return changed_name;
+ }
+
+ for (;;) {
+ struct ofpbuf *buf;
+ int retval;
+
+ retval = nl_sock_recv(mon->sock, &buf, false);
+ if (retval == EAGAIN) {
+ return NULL;
+ } else if (retval == ENOBUFS) {
+ VLOG_WARN_RL(&slow_rl, "network monitor socket overflowed");
+ return all_netdevs_changed(mon);
+ } else if (retval) {
+ VLOG_WARN_RL(&slow_rl, "error on network monitor socket: %s",
+ strerror(retval));
+ return NULL;
+ } else {
+ struct nlattr *attrs[ARRAY_SIZE(rtnlgrp_link_policy)];
+ const char *name;
+
+ if (!nl_policy_parse(buf, NLMSG_HDRLEN + sizeof(struct ifinfomsg),
+ rtnlgrp_link_policy,
+ attrs, ARRAY_SIZE(rtnlgrp_link_policy))) {
+ VLOG_WARN_RL(&slow_rl, "received bad rtnl message");
+ return all_netdevs_changed(mon);
+ }
+ name = lookup_netdev(mon, nl_attr_get_string(attrs[IFLA_IFNAME]));
+ ofpbuf_delete(buf);
+ if (name) {
+ /* Return the looked-up string instead of the attribute string,
+ * because we freed the buffer that contains the attribute. */
+ return name;
+ }
+ }
+ }
+}
+
+void
+netdev_monitor_run(struct netdev_monitor *mon UNUSED)
+{
+ /* Nothing to do in this implementation. */
+}
+
+void
+netdev_monitor_wait(struct netdev_monitor *mon)
+{
+ nl_sock_wait(mon->sock, POLLIN);
+}
+
+static const char *
+lookup_netdev(const struct netdev_monitor *mon, const char *name)
+{
+ size_t idx = svec_find(&mon->netdevs, name);
+ return idx != SIZE_MAX ? mon->netdevs.names[idx] : NULL;
+}
+
+static const char *
+pop_changed(struct netdev_monitor *mon)
+{
+ while (mon->changed.n) {
+ const char *name = lookup_netdev(mon, svec_back(&mon->changed));
+ svec_pop_back(&mon->changed);
+ if (name) {
+ return name;
+ }
+ }
+ return NULL;
+}
+
+static const char *
+all_netdevs_changed(struct netdev_monitor *mon)
+{
+ svec_clear(&mon->changed);
+ svec_append(&mon->changed, &mon->netdevs);
+ return pop_changed(mon);
+}
static void restore_all_flags(void *aux);
@@ -1053,13 +1213,13 @@ restore_all_flags(void *aux UNUSED)
}
static int
-get_flags(const struct netdev *netdev, int fd, int *flags)
+get_flags(const char *netdev_name, int *flags)
{
struct ifreq ifr;
- strncpy(ifr.ifr_name, netdev->name, sizeof ifr.ifr_name);
- if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0) {
+ strncpy(ifr.ifr_name, netdev_name, sizeof ifr.ifr_name);
+ if (ioctl(af_inet_sock, SIOCGIFFLAGS, &ifr) < 0) {
VLOG_ERR("ioctl(SIOCGIFFLAGS) on %s device failed: %s",
- netdev->name, strerror(errno));
+ netdev_name, strerror(errno));
return errno;
}
*flags = ifr.ifr_flags;
@@ -1067,14 +1227,14 @@ get_flags(const struct netdev *netdev, int fd, int *flags)
}
static int
-set_flags(struct netdev *netdev, int fd, int flags)
+set_flags(const char *netdev_name, int flags)
{
struct ifreq ifr;
- strncpy(ifr.ifr_name, netdev->name, sizeof ifr.ifr_name);
+ strncpy(ifr.ifr_name, netdev_name, sizeof ifr.ifr_name);
ifr.ifr_flags = flags;
- if (ioctl(netdev->netdev_fd, SIOCSIFFLAGS, &ifr) < 0) {
+ if (ioctl(af_inet_sock, SIOCSIFFLAGS, &ifr) < 0) {
VLOG_ERR("ioctl(SIOCSIFFLAGS) on %s device failed: %s",
- netdev->name, strerror(errno));
+ netdev_name, strerror(errno));
return errno;
}
return 0;
diff --git a/lib/netdev.h b/lib/netdev.h
index 46f0cb0..ae77397 100644
--- a/lib/netdev.h
+++ b/lib/netdev.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+/* Copyright (c) 2008, 2009 The Board of Trustees of The Leland Stanford
* Junior University
*
* We are making the OpenFlow specification and associated documentation
@@ -31,18 +31,19 @@
* derivatives without specific, written prior permission.
*/
+#ifndef NETDEV_H
+#define NETDEV_H 1
+
+#include
+#include
+#include
+
/* Generic interface to network devices.
*
* Currently, there is a single implementation of this interface that supports
* Linux. The interface should be generic enough to be implementable on other
* operating systems as well. */
-#ifndef NETDEV_H
-#define NETDEV_H 1
-
-#include
-#include
-
struct ofpbuf;
struct in_addr;
struct in6_addr;
@@ -57,7 +58,8 @@ enum netdev_feature_type {
enum netdev_flags {
NETDEV_UP = 0x0001, /* Device enabled? */
- NETDEV_PROMISC = 0x0002 /* Promiscuous mode? */
+ NETDEV_PROMISC = 0x0002, /* Promiscuous mode? */
+ NETDEV_CARRIER = 0x0004 /* Carrier detected? */
};
enum netdev_pseudo_ethertype {
@@ -76,15 +78,15 @@ int netdev_recv(struct netdev *, struct ofpbuf *);
void netdev_recv_wait(struct netdev *);
int netdev_drain(struct netdev *);
int netdev_send(struct netdev *, const struct ofpbuf *);
+void netdev_send_wait(struct netdev *);
int netdev_set_etheraddr(struct netdev *, const uint8_t mac[6]);
const uint8_t *netdev_get_etheraddr(const struct netdev *);
const char *netdev_get_name(const struct netdev *);
int netdev_get_mtu(const struct netdev *);
-int netdev_get_link_status(const struct netdev *);
uint32_t netdev_get_features(struct netdev *, int);
bool netdev_get_in4(const struct netdev *, struct in_addr *);
int netdev_set_in4(struct netdev *, struct in_addr addr, struct in_addr mask);
-int netdev_add_router(struct netdev *, struct in_addr router);
+int netdev_add_router(struct in_addr router);
bool netdev_get_in6(const struct netdev *, struct in6_addr *);
int netdev_get_flags(const struct netdev *, enum netdev_flags *);
int netdev_set_flags(struct netdev *, enum netdev_flags, bool permanent);
@@ -93,5 +95,20 @@ int netdev_turn_flags_off(struct netdev *, enum netdev_flags, bool permanent);
int netdev_arp_lookup(const struct netdev *, uint32_t ip, uint8_t mac[6]);
void netdev_enumerate(struct svec *);
+int netdev_nodev_get_flags(const char *netdev_name, enum netdev_flags *);
+
+/* Generic interface for monitoring network devices.
+ *
+ * A network device monitor keeps track of the state of network devices and
+ * reports when that state changes. At a minimum, it must report when a
+ * device's link status changes.
+ */
+struct netdev_monitor;
+int netdev_monitor_create(struct netdev_monitor **);
+void netdev_monitor_destroy(struct netdev_monitor *);
+void netdev_monitor_set_devices(struct netdev_monitor *, char **, size_t);
+const char *netdev_monitor_poll(struct netdev_monitor *);
+void netdev_monitor_run(struct netdev_monitor *);
+void netdev_monitor_wait(struct netdev_monitor *);
#endif /* netdev.h */
diff --git a/lib/netlink.c b/lib/netlink.c
index cb4e6c1..e1b27a7 100644
--- a/lib/netlink.c
+++ b/lib/netlink.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+/* Copyright (c) 2008, 2009 The Board of Trustees of The Leland Stanford
* Junior University
*
* We are making the OpenFlow specification and associated documentation
@@ -44,6 +44,7 @@
#include "dynamic-string.h"
#include "netlink-protocol.h"
#include "ofpbuf.h"
+#include "poll-loop.h"
#include "timeval.h"
#include "util.h"
@@ -413,6 +414,7 @@ recv:
goto recv;
}
if (nl_msg_nlmsgerr(reply, &retval)) {
+ ofpbuf_delete(reply);
if (retval) {
VLOG_DBG_RL(&rl, "received NAK error=%d (%s)",
retval, strerror(retval));
@@ -424,11 +426,12 @@ recv:
return 0;
}
-/* Returns 'sock''s underlying file descriptor. */
-int
-nl_sock_fd(const struct nl_sock *sock)
+/* Causes poll_block() to wake up when any of the specified 'events' (which is
+ * a OR'd combination of POLLIN, POLLOUT, etc.) occur on 'sock'. */
+void
+nl_sock_wait(const struct nl_sock *sock, short int events)
{
- return sock->fd;
+ poll_fd_wait(sock->fd, events);
}
/* Netlink messages. */
@@ -758,12 +761,16 @@ static const size_t attr_len_range[][2] = {
[NL_A_NESTED] = { NLMSG_HDRLEN, SIZE_MAX },
};
-/* Parses the Generic Netlink payload of 'msg' as a sequence of Netlink
+/* Parses the 'msg' starting at the given 'nla_offset' as a sequence of Netlink
* attributes. 'policy[i]', for 0 <= i < n_attrs, specifies how the attribute
* with nla_type == i is parsed; a pointer to attribute i is stored in
- * attrs[i]. Returns true if successful, false on failure. */
+ * attrs[i]. Returns true if successful, false on failure.
+ *
+ * If the Netlink attributes in 'msg' follow a Netlink header and a Generic
+ * Netlink header, then 'nla_offset' should be NLMSG_HDRLEN + GENL_HDRLEN. */
bool
-nl_policy_parse(const struct ofpbuf *msg, const struct nl_policy policy[],
+nl_policy_parse(const struct ofpbuf *msg, size_t nla_offset,
+ const struct nl_policy policy[],
struct nlattr *attrs[], size_t n_attrs)
{
void *p, *tail;
@@ -782,7 +789,7 @@ nl_policy_parse(const struct ofpbuf *msg, const struct nl_policy policy[],
}
}
- p = ofpbuf_at(msg, NLMSG_HDRLEN + GENL_HDRLEN, 0);
+ p = ofpbuf_at(msg, nla_offset, 0);
if (p == NULL) {
VLOG_DBG_RL(&rl, "missing headers in nl_policy_parse");
return false;
@@ -883,8 +890,8 @@ static int do_lookup_genl_family(const char *name)
return -retval;
}
- if (!nl_policy_parse(reply, family_policy, attrs,
- ARRAY_SIZE(family_policy))) {
+ if (!nl_policy_parse(reply, NLMSG_HDRLEN + GENL_HDRLEN,
+ family_policy, attrs, ARRAY_SIZE(family_policy))) {
nl_sock_destroy(sock);
ofpbuf_delete(reply);
return -EPROTO;
diff --git a/lib/netlink.h b/lib/netlink.h
index 8163a31..d9269c7 100644
--- a/lib/netlink.h
+++ b/lib/netlink.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+/* Copyright (c) 2008, 2009 The Board of Trustees of The Leland Stanford
* Junior University
*
* We are making the OpenFlow specification and associated documentation
@@ -65,7 +65,7 @@ int nl_sock_recv(struct nl_sock *, struct ofpbuf **, bool wait);
int nl_sock_transact(struct nl_sock *, const struct ofpbuf *request,
struct ofpbuf **reply);
-int nl_sock_fd(const struct nl_sock *);
+void nl_sock_wait(const struct nl_sock *, short int events);
/* Netlink messages. */
@@ -112,9 +112,9 @@ enum nl_attr_type
};
/* Netlink attribute parsing. */
-const void* nl_attr_get(const struct nlattr *);
+const void *nl_attr_get(const struct nlattr *);
size_t nl_attr_get_size(const struct nlattr *);
-const void* nl_attr_get_unspec(const struct nlattr *, size_t size);
+const void *nl_attr_get_unspec(const struct nlattr *, size_t size);
bool nl_attr_get_flag(const struct nlattr *);
uint8_t nl_attr_get_u8(const struct nlattr *);
uint16_t nl_attr_get_u16(const struct nlattr *);
@@ -125,8 +125,6 @@ const char *nl_attr_get_string(const struct nlattr *);
/* Netlink attribute policy.
*
* Specifies how to parse a single attribute from a Netlink message payload.
- *
- * See Nl_policy for example.
*/
struct nl_policy
{
@@ -135,7 +133,8 @@ struct nl_policy
bool optional;
};
-bool nl_policy_parse(const struct ofpbuf *, const struct nl_policy[],
+bool nl_policy_parse(const struct ofpbuf *, size_t offset,
+ const struct nl_policy[],
struct nlattr *[], size_t n_attrs);
/* Miscellaneous. */
diff --git a/lib/ofp-print.c b/lib/ofp-print.c
index 2057473..1be73d0 100644
--- a/lib/ofp-print.c
+++ b/lib/ofp-print.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+/* Copyright (c) 2008, 2009 The Board of Trustees of The Leland Stanford
* Junior University
*
* We are making the OpenFlow specification and associated documentation
@@ -50,6 +50,7 @@
#include "openflow/openflow.h"
#include "openflow/nicira-ext.h"
#include "packets.h"
+#include "pcap.h"
#include "util.h"
static void ofp_print_port_name(struct ds *string, uint16_t port);
@@ -65,29 +66,10 @@ static void ofp_print_match(struct ds *, const struct ofp_match *,
*
* This starts and kills a tcpdump subprocess so it's quite expensive. */
char *
-ofp_packet_to_string(const void *data, size_t len, size_t total_len)
+ofp_packet_to_string(const void *data, size_t len, size_t total_len UNUSED)
{
- struct pcap_hdr {
- uint32_t magic_number; /* magic number */
- uint16_t version_major; /* major version number */
- uint16_t version_minor; /* minor version number */
- int32_t thiszone; /* GMT to local correction */
- uint32_t sigfigs; /* accuracy of timestamps */
- uint32_t snaplen; /* max length of captured packets */
- uint32_t network; /* data link type */
- } PACKED;
-
- struct pcaprec_hdr {
- uint32_t ts_sec; /* timestamp seconds */
- uint32_t ts_usec; /* timestamp microseconds */
- uint32_t incl_len; /* number of octets of packet saved in file */
- uint32_t orig_len; /* actual length of packet */
- } PACKED;
-
- struct pcap_hdr ph;
- struct pcaprec_hdr prh;
-
struct ds ds = DS_EMPTY_INITIALIZER;
+ struct ofpbuf buf;
char command[128];
FILE *pcap;
@@ -95,31 +77,16 @@ ofp_packet_to_string(const void *data, size_t len, size_t total_len)
int status;
int c;
+ buf.data = (void *) data;
+ buf.size = len;
+
pcap = tmpfile();
if (!pcap) {
ofp_error(errno, "tmpfile");
return xstrdup("");
}
-
- /* The pcap reader is responsible for figuring out endianness based on the
- * magic number, so the lack of htonX calls here is intentional. */
- ph.magic_number = 0xa1b2c3d4;
- ph.version_major = 2;
- ph.version_minor = 4;
- ph.thiszone = 0;
- ph.sigfigs = 0;
- ph.snaplen = 1518;
- ph.network = 1; /* Ethernet */
-
- prh.ts_sec = 0;
- prh.ts_usec = 0;
- prh.incl_len = len;
- prh.orig_len = total_len;
-
- fwrite(&ph, 1, sizeof ph, pcap);
- fwrite(&prh, 1, sizeof prh, pcap);
- fwrite(data, 1, len, pcap);
-
+ pcap_write_header(pcap);
+ pcap_write(pcap, &buf);
fflush(pcap);
if (ferror(pcap)) {
ofp_error(errno, "error writing temporary file");
@@ -597,7 +564,7 @@ ofp_print_phy_port(struct ds *string, const struct ofp_phy_port *port)
* 'string' at the given 'verbosity' level. */
static void
ofp_print_switch_features(struct ds *string, const void *oh, size_t len,
- int verbosity)
+ int verbosity UNUSED)
{
const struct ofp_switch_features *osf = oh;
struct ofp_phy_port *port_list;
@@ -627,8 +594,8 @@ ofp_print_switch_features(struct ds *string, const void *oh, size_t len,
/* Pretty-print the struct ofp_switch_config of 'len' bytes at 'oh' to 'string'
* at the given 'verbosity' level. */
static void
-ofp_print_switch_config(struct ds *string, const void *oh, size_t len,
- int verbosity)
+ofp_print_switch_config(struct ds *string, const void *oh, size_t len UNUSED,
+ int verbosity UNUSED)
{
const struct ofp_switch_config *osc = oh;
uint16_t flags;
@@ -687,10 +654,18 @@ print_ip_netmask(struct ds *string, const char *leader, uint32_t ip,
ds_put_char(string, ',');
}
-/* Pretty-print the ofp_match structure */
-static void ofp_print_match(struct ds *f, const struct ofp_match *om,
- int verbosity)
+static void
+ofp_print_match(struct ds *f, const struct ofp_match *om, int verbosity)
{
+ char *s = ofp_match_to_string(om, verbosity);
+ ds_put_cstr(f, s);
+ free(s);
+}
+
+char *
+ofp_match_to_string(const struct ofp_match *om, int verbosity)
+{
+ struct ds f = DS_EMPTY_INITIALIZER;
uint32_t w = ntohl(om->wildcards);
bool skip_type = false;
bool skip_proto = false;
@@ -701,55 +676,56 @@ static void ofp_print_match(struct ds *f, const struct ofp_match *om,
if (!(w & OFPFW_NW_PROTO)) {
skip_proto = true;
if (om->nw_proto == IP_TYPE_ICMP) {
- ds_put_cstr(f, "icmp,");
+ ds_put_cstr(&f, "icmp,");
} else if (om->nw_proto == IP_TYPE_TCP) {
- ds_put_cstr(f, "tcp,");
+ ds_put_cstr(&f, "tcp,");
} else if (om->nw_proto == IP_TYPE_UDP) {
- ds_put_cstr(f, "udp,");
+ ds_put_cstr(&f, "udp,");
} else {
- ds_put_cstr(f, "ip,");
+ ds_put_cstr(&f, "ip,");
skip_proto = false;
}
} else {
- ds_put_cstr(f, "ip,");
+ ds_put_cstr(&f, "ip,");
}
} else if (om->dl_type == htons(ETH_TYPE_ARP)) {
- ds_put_cstr(f, "arp,");
+ ds_put_cstr(&f, "arp,");
} else {
skip_type = false;
}
}
- print_wild(f, "in_port=", w & OFPFW_IN_PORT, verbosity,
+ print_wild(&f, "in_port=", w & OFPFW_IN_PORT, verbosity,
"%d", ntohs(om->in_port));
- print_wild(f, "dl_vlan=", w & OFPFW_DL_VLAN, verbosity,
+ print_wild(&f, "dl_vlan=", w & OFPFW_DL_VLAN, verbosity,
"0x%04x", ntohs(om->dl_vlan));
- print_wild(f, "dl_src=", w & OFPFW_DL_SRC, verbosity,
+ print_wild(&f, "dl_src=", w & OFPFW_DL_SRC, verbosity,
ETH_ADDR_FMT, ETH_ADDR_ARGS(om->dl_src));
- print_wild(f, "dl_dst=", w & OFPFW_DL_DST, verbosity,
+ print_wild(&f, "dl_dst=", w & OFPFW_DL_DST, verbosity,
ETH_ADDR_FMT, ETH_ADDR_ARGS(om->dl_dst));
if (!skip_type) {
- print_wild(f, "dl_type=", w & OFPFW_DL_TYPE, verbosity,
+ print_wild(&f, "dl_type=", w & OFPFW_DL_TYPE, verbosity,
"0x%04x", ntohs(om->dl_type));
}
- print_ip_netmask(f, "nw_src=", om->nw_src,
+ print_ip_netmask(&f, "nw_src=", om->nw_src,
(w & OFPFW_NW_SRC_MASK) >> OFPFW_NW_SRC_SHIFT, verbosity);
- print_ip_netmask(f, "nw_dst=", om->nw_dst,
+ print_ip_netmask(&f, "nw_dst=", om->nw_dst,
(w & OFPFW_NW_DST_MASK) >> OFPFW_NW_DST_SHIFT, verbosity);
if (!skip_proto) {
- print_wild(f, "nw_proto=", w & OFPFW_NW_PROTO, verbosity,
+ print_wild(&f, "nw_proto=", w & OFPFW_NW_PROTO, verbosity,
"%u", om->nw_proto);
}
if (om->nw_proto == IP_TYPE_ICMP) {
- print_wild(f, "icmp_type=", w & OFPFW_ICMP_TYPE, verbosity,
+ print_wild(&f, "icmp_type=", w & OFPFW_ICMP_TYPE, verbosity,
"%d", ntohs(om->icmp_type));
- print_wild(f, "icmp_code=", w & OFPFW_ICMP_CODE, verbosity,
+ print_wild(&f, "icmp_code=", w & OFPFW_ICMP_CODE, verbosity,
"%d", ntohs(om->icmp_code));
} else {
- print_wild(f, "tp_src=", w & OFPFW_TP_SRC, verbosity,
+ print_wild(&f, "tp_src=", w & OFPFW_TP_SRC, verbosity,
"%d", ntohs(om->tp_src));
- print_wild(f, "tp_dst=", w & OFPFW_TP_DST, verbosity,
+ print_wild(&f, "tp_dst=", w & OFPFW_TP_DST, verbosity,
"%d", ntohs(om->tp_dst));
}
+ return ds_cstr(&f);
}
/* Pretty-print the OFPT_FLOW_MOD packet of 'len' bytes at 'oh' to 'string'
@@ -792,7 +768,7 @@ ofp_print_flow_mod(struct ds *string, const void *oh, size_t len,
/* Pretty-print the OFPT_FLOW_EXPIRED packet of 'len' bytes at 'oh' to 'string'
* at the given 'verbosity' level. */
static void
-ofp_print_flow_expired(struct ds *string, const void *oh, size_t len,
+ofp_print_flow_expired(struct ds *string, const void *oh, size_t len UNUSED,
int verbosity)
{
const struct ofp_flow_expired *ofe = oh;
@@ -875,10 +851,9 @@ nx_print_msg(struct ds *string, const void *oh, size_t len, int verbosity)
}
}
-
static void
-ofp_print_port_mod(struct ds *string, const void *oh, size_t len,
- int verbosity)
+ofp_print_port_mod(struct ds *string, const void *oh, size_t len UNUSED,
+ int verbosity UNUSED)
{
const struct ofp_port_mod *opm = oh;
@@ -953,7 +928,7 @@ lookup_error_code(int type, int code)
* at the given 'verbosity' level. */
static void
ofp_print_error_msg(struct ds *string, const void *oh, size_t len,
- int verbosity)
+ int verbosity UNUSED)
{
const struct ofp_error_msg *oem = oh;
int type = ntohs(oem->type);
@@ -984,8 +959,8 @@ ofp_print_error_msg(struct ds *string, const void *oh, size_t len,
/* Pretty-print the OFPT_PORT_STATUS packet of 'len' bytes at 'oh' to 'string'
* at the given 'verbosity' level. */
static void
-ofp_print_port_status(struct ds *string, const void *oh, size_t len,
- int verbosity)
+ofp_print_port_status(struct ds *string, const void *oh, size_t len UNUSED,
+ int verbosity UNUSED)
{
const struct ofp_port_status *ops = oh;
@@ -1001,8 +976,8 @@ ofp_print_port_status(struct ds *string, const void *oh, size_t len,
}
static void
-ofp_desc_stats_reply(struct ds *string, const void *body, size_t len,
- int verbosity)
+ofp_desc_stats_reply(struct ds *string, const void *body, size_t len UNUSED,
+ int verbosity UNUSED)
{
const struct ofp_desc_stats *ods = body;
@@ -1013,7 +988,7 @@ ofp_desc_stats_reply(struct ds *string, const void *body, size_t len,
}
static void
-ofp_flow_stats_request(struct ds *string, const void *oh, size_t len,
+ofp_flow_stats_request(struct ds *string, const void *oh, size_t len UNUSED,
int verbosity)
{
const struct ofp_flow_stats_request *fsr = oh;
@@ -1086,8 +1061,8 @@ ofp_flow_stats_reply(struct ds *string, const void *body_, size_t len,
}
static void
-ofp_aggregate_stats_request(struct ds *string, const void *oh, size_t len,
- int verbosity)
+ofp_aggregate_stats_request(struct ds *string, const void *oh,
+ size_t len UNUSED, int verbosity)
{
const struct ofp_aggregate_stats_request *asr = oh;
@@ -1101,8 +1076,8 @@ ofp_aggregate_stats_request(struct ds *string, const void *oh, size_t len,
}
static void
-ofp_aggregate_stats_reply(struct ds *string, const void *body_, size_t len,
- int verbosity)
+ofp_aggregate_stats_reply(struct ds *string, const void *body_,
+ size_t len UNUSED, int verbosity UNUSED)
{
const struct ofp_aggregate_stats_reply *asr = body_;
@@ -1536,6 +1511,33 @@ ofp_to_string(const void *oh_, size_t len, int verbosity)
}
return ds_cstr(&string);
}
+
+/* Returns the name for the specified OpenFlow message type as a string,
+ * e.g. "OFPT_FEATURES_REPLY". If no name is known, the string returned is a
+ * hex number, e.g. "0x55".
+ *
+ * The caller must free the returned string when it is no longer needed. */
+char *
+ofp_message_type_to_string(uint8_t type)
+{
+ struct ds s = DS_EMPTY_INITIALIZER;
+ const struct openflow_packet *pkt;
+ for (pkt = packets; ; pkt++) {
+ if (pkt >= &packets[ARRAY_SIZE(packets)]) {
+ ds_put_format(&s, "0x%02"PRIx8, type);
+ break;
+ } else if (type == pkt->type) {
+ const char *p;
+
+ ds_put_cstr(&s, "OFPT_");
+ for (p = pkt->name; *p; p++) {
+ ds_put_char(&s, toupper((unsigned char) *p));
+ }
+ break;
+ }
+ }
+ return ds_cstr(&s);
+}
static void
print_and_free(FILE *stream, char *string)
diff --git a/lib/ofp-print.h b/lib/ofp-print.h
index b71212c..d559d9c 100644
--- a/lib/ofp-print.h
+++ b/lib/ofp-print.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+/* Copyright (c) 2008, 2009 The Board of Trustees of The Leland Stanford
* Junior University
*
* We are making the OpenFlow specification and associated documentation
@@ -36,9 +36,11 @@
#ifndef OFP_PRINT_H
#define OFP_PRINT_H 1
+#include
#include
struct ofp_flow_mod;
+struct ofp_match;
#ifdef __cplusplus
extern "C" {
@@ -48,7 +50,9 @@ void ofp_print(FILE *, const void *, size_t, int verbosity);
void ofp_print_packet(FILE *stream, const void *data, size_t len, size_t total_len);
char *ofp_to_string(const void *, size_t, int verbosity);
+char *ofp_match_to_string(const struct ofp_match *, int verbosity);
char *ofp_packet_to_string(const void *data, size_t len, size_t total_len);
+char *ofp_message_type_to_string(uint8_t type);
#ifdef __cplusplus
}
diff --git a/lib/ofpbuf.c b/lib/ofpbuf.c
index 77631e1..ab469db 100644
--- a/lib/ofpbuf.c
+++ b/lib/ofpbuf.c
@@ -92,11 +92,16 @@ ofpbuf_new(size_t size)
}
struct ofpbuf *
-ofpbuf_clone(const struct ofpbuf *buffer)
+ofpbuf_clone(const struct ofpbuf *buffer)
{
- /* FIXME: reference counting. */
- struct ofpbuf *b = ofpbuf_new(buffer->size);
- ofpbuf_put(b, buffer->data, buffer->size);
+ return ofpbuf_clone_data(buffer->data, buffer->size);
+}
+
+struct ofpbuf *
+ofpbuf_clone_data(const void *data, size_t size)
+{
+ struct ofpbuf *b = ofpbuf_new(size);
+ ofpbuf_put(b, data, size);
return b;
}
diff --git a/lib/ofpbuf.h b/lib/ofpbuf.h
index 0e94e35..f3a94c2 100644
--- a/lib/ofpbuf.h
+++ b/lib/ofpbuf.h
@@ -62,6 +62,7 @@ void ofpbuf_reinit(struct ofpbuf *, size_t);
struct ofpbuf *ofpbuf_new(size_t);
struct ofpbuf *ofpbuf_clone(const struct ofpbuf *);
+struct ofpbuf *ofpbuf_clone_data(const void *, size_t);
void ofpbuf_delete(struct ofpbuf *);
void *ofpbuf_at(const struct ofpbuf *, size_t offset, size_t size);
diff --git a/lib/pcap.c b/lib/pcap.c
new file mode 100644
index 0000000..d923f17
--- /dev/null
+++ b/lib/pcap.c
@@ -0,0 +1,180 @@
+/* Copyright (c) 2009 The Board of Trustees of The Leland Stanford
+ * Junior University
+ *
+ * We are making the OpenFlow specification and associated documentation
+ * (Software) available for public use and benefit with the expectation
+ * that others will use, modify and enhance the Software and contribute
+ * those enhancements back to the community. However, since we would
+ * like to make the Software available for broadest use, with as few
+ * restrictions as possible permission is hereby granted, free of
+ * charge, to any person obtaining a copy of this Software to deal in
+ * the Software under the copyrights without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * 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 AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * The name and trademarks of copyright holder(s) may NOT be used in
+ * advertising or publicity pertaining to the Software or any
+ * derivatives without specific, written prior permission.
+ */
+
+#include
+#include "pcap.h"
+#include
+#include
+#include
+#include
+#include "compiler.h"
+#include "ofpbuf.h"
+
+#define THIS_MODULE VLM_pcap
+#include "vlog.h"
+
+struct pcap_hdr {
+ uint32_t magic_number; /* magic number */
+ uint16_t version_major; /* major version number */
+ uint16_t version_minor; /* minor version number */
+ int32_t thiszone; /* GMT to local correction */
+ uint32_t sigfigs; /* accuracy of timestamps */
+ uint32_t snaplen; /* max length of captured packets */
+ uint32_t network; /* data link type */
+} PACKED;
+
+struct pcaprec_hdr {
+ uint32_t ts_sec; /* timestamp seconds */
+ uint32_t ts_usec; /* timestamp microseconds */
+ uint32_t incl_len; /* number of octets of packet saved in file */
+ uint32_t orig_len; /* actual length of packet */
+} PACKED;
+
+FILE *
+pcap_open(const char *file_name, const char *mode)
+{
+ FILE *file;
+
+ assert(!strcmp(mode, "rb") || !strcmp(mode, "wb"));
+
+ file = fopen(file_name, mode);
+ if (file == NULL) {
+ VLOG_WARN("%s: failed to open pcap file for %s",
+ file_name, mode[0] == 'r' ? "reading" : "writing");
+ return NULL;
+ }
+
+ if (mode[0] == 'r') {
+ if (!pcap_read_header(file)) {
+ fclose(file);
+ return NULL;
+ }
+ } else {
+ pcap_write_header(file);
+ }
+ return file;
+}
+
+int
+pcap_read_header(FILE *file)
+{
+ struct pcap_hdr ph;
+ if (fread(&ph, sizeof ph, 1, file) != 1) {
+ int error = ferror(file) ? errno : EOF;
+ VLOG_WARN("failed to read pcap header: %s",
+ error > 0 ? strerror(error) : "end of file");
+ return error;
+ }
+ if (ph.magic_number != 0xa1b2c3d4 && ph.magic_number != 0xd4c3b2a1) {
+ VLOG_WARN("bad magic 0x%08"PRIx32" reading pcap file "
+ "(expected 0xa1b2c3d4 or 0xd4c3b2a1)", ph.magic_number);
+ return EPROTO;
+ }
+ return 0;
+}
+
+void
+pcap_write_header(FILE *file)
+{
+ /* The pcap reader is responsible for figuring out endianness based on the
+ * magic number, so the lack of htonX calls here is intentional. */
+ struct pcap_hdr ph;
+ ph.magic_number = 0xa1b2c3d4;
+ ph.version_major = 2;
+ ph.version_minor = 4;
+ ph.thiszone = 0;
+ ph.sigfigs = 0;
+ ph.snaplen = 1518;
+ ph.network = 1; /* Ethernet */
+ fwrite(&ph, sizeof ph, 1, file);
+}
+
+int
+pcap_read(FILE *file, struct ofpbuf **bufp)
+{
+ struct pcaprec_hdr prh;
+ struct ofpbuf *buf;
+ void *data;
+ size_t len;
+
+ *bufp = NULL;
+
+ /* Read header. */
+ if (fread(&prh, sizeof prh, 1, file) != 1) {
+ int error = ferror(file) ? errno : EOF;
+ VLOG_WARN("failed to read pcap record header: %s",
+ error > 0 ? strerror(error) : "end of file");
+ return error;
+ }
+
+ /* Calculate length. */
+ len = prh.incl_len;
+ if (len > 0xffff) {
+ uint32_t swapped_len = (((len & 0xff000000) >> 24) |
+ ((len & 0x00ff0000) >> 8) |
+ ((len & 0x0000ff00) << 8) |
+ ((len & 0x000000ff) << 24));
+ if (swapped_len > 0xffff) {
+ VLOG_WARN("bad packet length %"PRIu32" or %"PRIu32" "
+ "reading pcap file",
+ len, swapped_len);
+ return EPROTO;
+ }
+ len = swapped_len;
+ }
+
+ /* Read packet. */
+ buf = ofpbuf_new(len);
+ data = ofpbuf_put_uninit(buf, len);
+ if (fread(data, len, 1, file) != 1) {
+ int error = ferror(file) ? errno : EOF;
+ VLOG_WARN("failed to read pcap packet: %s",
+ error > 0 ? strerror(error) : "end of file");
+ ofpbuf_delete(buf);
+ return error;
+ }
+ *bufp = buf;
+ return 0;
+}
+
+void
+pcap_write(FILE *file, struct ofpbuf *buf)
+{
+ struct pcaprec_hdr prh;
+ prh.ts_sec = 0;
+ prh.ts_usec = 0;
+ prh.incl_len = buf->size;
+ prh.orig_len = buf->size;
+ fwrite(&prh, sizeof prh, 1, file);
+ fwrite(buf->data, buf->size, 1, file);
+}
diff --git a/lib/pcap.h b/lib/pcap.h
new file mode 100644
index 0000000..f72708f
--- /dev/null
+++ b/lib/pcap.h
@@ -0,0 +1,47 @@
+/* Copyright (c) 2009 The Board of Trustees of The Leland Stanford
+ * Junior University
+ *
+ * We are making the OpenFlow specification and associated documentation
+ * (Software) available for public use and benefit with the expectation
+ * that others will use, modify and enhance the Software and contribute
+ * those enhancements back to the community. However, since we would
+ * like to make the Software available for broadest use, with as few
+ * restrictions as possible permission is hereby granted, free of
+ * charge, to any person obtaining a copy of this Software to deal in
+ * the Software under the copyrights without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * 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 AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * The name and trademarks of copyright holder(s) may NOT be used in
+ * advertising or publicity pertaining to the Software or any
+ * derivatives without specific, written prior permission.
+ */
+
+#ifndef PCAP_H
+#define PCAP_H 1
+
+#include
+
+struct ofpbuf;
+
+FILE *pcap_open(const char *file_name, const char *mode);
+int pcap_read_header(FILE *);
+void pcap_write_header(FILE *);
+int pcap_read(FILE *, struct ofpbuf **);
+void pcap_write(FILE *, struct ofpbuf *);
+
+#endif /* dhcp.h */
diff --git a/lib/poll-loop.c b/lib/poll-loop.c
index 1be0ea6..09df036 100644
--- a/lib/poll-loop.c
+++ b/lib/poll-loop.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+/* Copyright (c) 2008, 2009 The Board of Trustees of The Leland Stanford
* Junior University
*
* We are making the OpenFlow specification and associated documentation
@@ -38,6 +38,8 @@
#include
#include
#include
+#include "backtrace.h"
+#include "dynamic-string.h"
#include "list.h"
#include "timeval.h"
@@ -52,6 +54,7 @@ struct poll_waiter {
short int events; /* Events to wait for (POLLIN, POLLOUT). */
poll_fd_func *function; /* Callback function, if any, or null. */
void *aux; /* Argument to callback function. */
+ struct backtrace *backtrace; /* Optionally, event that created waiter. */
/* Set only when poll_block() is called. */
struct pollfd *pollfd; /* Pointer to element of the pollfds array
@@ -68,6 +71,9 @@ static size_t n_waiters;
* wait forever. */
static int timeout = -1;
+/* Backtrace of 'timeout''s registration, if debugging is enabled. */
+static struct backtrace timeout_backtrace;
+
/* Callback currently running, to allow verifying that poll_cancel() is not
* being called on a running callback. */
#ifndef NDEBUG
@@ -101,6 +107,9 @@ poll_timer_wait(int msec)
{
if (timeout < 0 || msec < timeout) {
timeout = MAX(0, msec);
+ if (VLOG_IS_DBG_ENABLED()) {
+ backtrace_capture(&timeout_backtrace);
+ }
}
}
@@ -109,7 +118,30 @@ poll_timer_wait(int msec)
void
poll_immediate_wake(void)
{
- timeout = 0;
+ poll_timer_wait(0);
+}
+
+static void PRINTF_FORMAT(2, 3)
+log_wakeup(const struct backtrace *backtrace, const char *format, ...)
+{
+ struct ds ds;
+ va_list args;
+
+ ds_init(&ds);
+ va_start(args, format);
+ ds_put_format_valist(&ds, format, args);
+ va_end(args);
+
+ if (backtrace) {
+ int i;
+
+ ds_put_char(&ds, ':');
+ for (i = 0; i < backtrace->n_frames; i++) {
+ ds_put_format(&ds, " 0x%x", backtrace->frames[i]);
+ }
+ }
+ VLOG_DBG("%s", ds_cstr(&ds));
+ ds_destroy(&ds);
}
/* Blocks until one or more of the events registered with poll_fd_wait()
@@ -148,6 +180,8 @@ poll_block(void)
if (retval < 0) {
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
VLOG_ERR_RL(&rl, "poll: %s", strerror(-retval));
+ } else if (!retval && VLOG_IS_DBG_ENABLED()) {
+ log_wakeup(&timeout_backtrace, "%d-ms timeout", timeout);
}
for (node = waiters.next; node != &waiters; ) {
@@ -157,20 +191,33 @@ poll_block(void)
node = node->next;
continue;
}
- } else if (pw->function) {
+ } else {
+ if (VLOG_IS_DBG_ENABLED()) {
+ log_wakeup(pw->backtrace, "%s%s%s%s%s on fd %d",
+ pw->pollfd->revents & POLLIN ? "[POLLIN]" : "",
+ pw->pollfd->revents & POLLOUT ? "[POLLOUT]" : "",
+ pw->pollfd->revents & POLLERR ? "[POLLERR]" : "",
+ pw->pollfd->revents & POLLHUP ? "[POLLHUP]" : "",
+ pw->pollfd->revents & POLLNVAL ? "[POLLNVAL]" : "",
+ pw->fd);
+ }
+
+ if (pw->function) {
#ifndef NDEBUG
- running_cb = pw;
+ running_cb = pw;
#endif
- pw->function(pw->fd, pw->pollfd->revents, pw->aux);
+ pw->function(pw->fd, pw->pollfd->revents, pw->aux);
#ifndef NDEBUG
- running_cb = NULL;
+ running_cb = NULL;
#endif
+ }
}
node = node->next;
poll_cancel(pw);
}
timeout = -1;
+ timeout_backtrace.n_frames = 0;
}
/* Registers 'function' to be called with argument 'aux' by poll_block() when
@@ -208,6 +255,7 @@ poll_cancel(struct poll_waiter *pw)
if (pw) {
assert(pw != running_cb);
list_remove(&pw->node);
+ free(pw->backtrace);
free(pw);
n_waiters--;
}
@@ -221,6 +269,10 @@ new_waiter(int fd, short int events)
assert(fd >= 0);
waiter->fd = fd;
waiter->events = events;
+ if (VLOG_IS_DBG_ENABLED()) {
+ waiter->backtrace = xmalloc(sizeof *waiter->backtrace);
+ backtrace_capture(waiter->backtrace);
+ }
list_push_back(&waiters, &waiter->node);
n_waiters++;
return waiter;
diff --git a/lib/port-array.c b/lib/port-array.c
index 31e9a64..5d3eb8c 100644
--- a/lib/port-array.c
+++ b/lib/port-array.c
@@ -33,6 +33,7 @@
#include
#include "port-array.h"
+#include
static struct port_array_l2 l2_sentinel;
static struct port_array_l3 l3_sentinel;
@@ -54,6 +55,38 @@ port_array_init(struct port_array *pa)
}
}
+/* Frees all the memory allocated for 'pa'. It is the client's responsibility
+ * to free memory that 'pa' elements point to. */
+void
+port_array_destroy(struct port_array *pa)
+{
+ unsigned int l1_idx;
+
+ for (l1_idx = 0; l1_idx < PORT_ARRAY_L1_SIZE; l1_idx++) {
+ struct port_array_l2 *l2 = pa->l1[l1_idx];
+
+ if (l2 != &l2_sentinel) {
+ unsigned int l2_idx;
+
+ for (l2_idx = 0; l2_idx < PORT_ARRAY_L2_SIZE; l2_idx++) {
+ struct port_array_l3 *l3 = l2->l2[l2_idx];
+ if (l3 != &l3_sentinel) {
+ free(l3);
+ }
+ }
+ free(l2);
+ }
+ }
+}
+
+/* Clears all elements of 'pa' to null pointers. */
+void
+port_array_clear(struct port_array *pa)
+{
+ port_array_destroy(pa);
+ port_array_init(pa);
+}
+
/* Sets 'pa' element numbered 'idx' to 'p'. */
void
port_array_set(struct port_array *pa, uint16_t idx, void *p)
diff --git a/lib/port-array.h b/lib/port-array.h
index 478a073..01b3c10 100644
--- a/lib/port-array.h
+++ b/lib/port-array.h
@@ -99,6 +99,7 @@ port_array_get(const struct port_array *pa, uint16_t idx)
void port_array_init(struct port_array *);
void port_array_destroy(struct port_array *);
+void port_array_clear(struct port_array *);
void port_array_set(struct port_array *, uint16_t idx, void *);
void *port_array_first(const struct port_array *, unsigned int *);
void *port_array_next(const struct port_array *, unsigned int *);
diff --git a/lib/queue.c b/lib/queue.c
index 04610cf..33e5006 100644
--- a/lib/queue.c
+++ b/lib/queue.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+/* Copyright (c) 2008, 2009 The Board of Trustees of The Leland Stanford
* Junior University
*
* We are making the OpenFlow specification and associated documentation
@@ -34,6 +34,8 @@
#include
#include "queue.h"
#include
+#include "compiler.h"
+#include "leak-checker.h"
#include "ofpbuf.h"
static void check_queue(struct ofp_queue *q);
@@ -89,6 +91,7 @@ void
queue_push_tail(struct ofp_queue *q, struct ofpbuf *b)
{
check_queue(q);
+ leak_checker_claim(b);
b->next = NULL;
if (q->n++) {
@@ -114,7 +117,7 @@ queue_pop_head(struct ofp_queue *q)
/* Checks the internal integrity of 'q'. For use in debugging. */
static void
-check_queue(struct ofp_queue *q)
+check_queue(struct ofp_queue *q UNUSED)
{
#if 0
struct ofpbuf *iter;
diff --git a/lib/random.c b/lib/random.c
index f4fab4c..14be87e 100644
--- a/lib/random.c
+++ b/lib/random.c
@@ -64,12 +64,40 @@ random_bytes(void *p_, size_t n)
}
}
+uint8_t
+random_uint8(void)
+{
+ random_init();
+ return rand();
+}
+
+uint16_t
+random_uint16(void)
+{
+ if (RAND_MAX >= UINT16_MAX) {
+ random_init();
+ return rand();
+ } else {
+ uint16_t x;
+ random_bytes(&x, sizeof x);
+ return x;
+ }
+}
+
uint32_t
random_uint32(void)
{
- uint32_t x;
- random_bytes(&x, sizeof x);
- return x;
+ if (RAND_MAX >= UINT32_MAX) {
+ random_init();
+ return rand();
+ } else if (RAND_MAX == INT32_MAX) {
+ random_init();
+ return rand() | ((rand() & 1u) << 31);
+ } else {
+ uint32_t x;
+ random_bytes(&x, sizeof x);
+ return x;
+ }
}
int
diff --git a/lib/random.h b/lib/random.h
index d9f5f9d..eb29a22 100644
--- a/lib/random.h
+++ b/lib/random.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+/* Copyright (c) 2008, 2009 The Board of Trustees of The Leland Stanford
* Junior University
*
* We are making the OpenFlow specification and associated documentation
@@ -37,7 +37,10 @@
#include
#include
+void random_init(void);
void random_bytes(void *, size_t);
+uint8_t random_uint8(void);
+uint16_t random_uint16(void);
uint32_t random_uint32(void);
int random_range(int max);
diff --git a/lib/rconn.c b/lib/rconn.c
index c8bddab..0ec8474 100644
--- a/lib/rconn.c
+++ b/lib/rconn.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+/* Copyright (c) 2008, 2009 The Board of Trustees of The Leland Stanford
* Junior University
*
* We are making the OpenFlow specification and associated documentation
@@ -91,6 +91,7 @@ struct rconn {
time_t last_received;
time_t last_connected;
unsigned int packets_sent;
+ unsigned int seqno;
/* In S_ACTIVE and S_IDLE, probably_admitted reports whether we believe
* that the peer has made a (positive) admission control decision on our
@@ -194,6 +195,7 @@ rconn_create(int probe_interval, int max_backoff)
rc->backoff_deadline = TIME_MIN;
rc->last_received = time_now();
rc->last_connected = time_now();
+ rc->seqno = 0;
rc->packets_sent = 0;
@@ -278,13 +280,13 @@ rconn_destroy(struct rconn *rc)
}
static unsigned int
-timeout_VOID(const struct rconn *rc)
+timeout_VOID(const struct rconn *rc UNUSED)
{
return UINT_MAX;
}
static void
-run_VOID(struct rconn *rc)
+run_VOID(struct rconn *rc UNUSED)
{
/* Nothing to do. */
}
@@ -294,10 +296,13 @@ reconnect(struct rconn *rc)
{
int retval;
- VLOG_WARN("%s: connecting...", rc->name);
+ VLOG_INFO("%s: connecting...", rc->name);
rc->n_attempted_connections++;
retval = vconn_open(rc->name, OFP_VERSION, &rc->vconn);
if (!retval) {
+ if (!vconn_is_reconnectable(rc->vconn)) {
+ rc->reliable = false;
+ }
rc->backoff_deadline = time_now() + rc->backoff;
state_transition(rc, S_CONNECTING);
} else {
@@ -333,15 +338,15 @@ run_CONNECTING(struct rconn *rc)
{
int retval = vconn_connect(rc->vconn);
if (!retval) {
- VLOG_WARN("%s: connected", rc->name);
+ VLOG_INFO("%s: connected", rc->name);
rc->n_successful_connections++;
state_transition(rc, S_ACTIVE);
rc->last_connected = rc->state_entered;
} else if (retval != EAGAIN) {
- VLOG_WARN("%s: connection failed (%s)", rc->name, strerror(retval));
+ VLOG_INFO("%s: connection failed (%s)", rc->name, strerror(retval));
disconnect(rc, retval);
} else if (timed_out(rc)) {
- VLOG_WARN("%s: connection timed out", rc->name);
+ VLOG_INFO("%s: connection timed out", rc->name);
rc->backoff_deadline = TIME_MAX; /* Prevent resetting backoff. */
disconnect(rc, 0);
}
@@ -512,6 +517,11 @@ rconn_send(struct rconn *rc, struct ofpbuf *b, int *n_queued)
++*n_queued;
}
queue_push_tail(&rc->txq, b);
+
+ /* If the queue was empty before we added 'b', try to send some
+ * packets. (But if the queue had packets in it, it's because the
+ * vconn is backlogged and there's no point in stuffing more into it
+ * now. We'll get back to that in rconn_run().) */
if (rc->txq.n == 1) {
try_send(rc);
}
@@ -560,7 +570,7 @@ void
rconn_add_monitor(struct rconn *rc, struct vconn *vconn)
{
if (rc->n_monitors < ARRAY_SIZE(rc->monitors)) {
- VLOG_WARN("new monitor connection from %s", vconn_get_name(vconn));
+ VLOG_INFO("new monitor connection from %s", vconn_get_name(vconn));
rc->monitors[rc->n_monitors++] = vconn;
} else {
VLOG_DBG("too many monitor connections, discarding %s",
@@ -694,6 +704,14 @@ rconn_get_state_elapsed(const struct rconn *rc)
{
return elapsed_in_this_state(rc);
}
+
+/* Returns 'rc''s current connection sequence number, a number that changes
+ * every time that 'rconn' connects or disconnects. */
+unsigned int
+rconn_get_connection_seqno(const struct rconn *rc)
+{
+ return rc->seqno;
+}
/* Tries to send a packet from 'rc''s send buffer. Returns 0 if successful,
* otherwise a positive errno value. */
@@ -733,10 +751,10 @@ disconnect(struct rconn *rc, int error)
rc->name, strerror(error));
} else if (error == EOF) {
if (rc->reliable) {
- VLOG_WARN("%s: connection closed by peer", rc->name);
+ VLOG_INFO("%s: connection closed by peer", rc->name);
}
} else {
- VLOG_WARN("%s: connection dropped", rc->name);
+ VLOG_INFO("%s: connection dropped", rc->name);
}
vconn_close(rc->vconn);
rc->vconn = NULL;
@@ -747,7 +765,7 @@ disconnect(struct rconn *rc, int error)
rc->backoff = 1;
} else {
rc->backoff = MIN(rc->max_backoff, MAX(1, 2 * rc->backoff));
- VLOG_WARN("%s: waiting %d seconds before reconnect\n",
+ VLOG_INFO("%s: waiting %d seconds before reconnect\n",
rc->name, rc->backoff);
}
rc->backoff_deadline = now + rc->backoff;
@@ -806,6 +824,7 @@ timed_out(const struct rconn *rc)
static void
state_transition(struct rconn *rc, enum state state)
{
+ rc->seqno += (rc->state == S_ACTIVE) != (state == S_ACTIVE);
if (is_connected_state(state) && !is_connected_state(rc->state)) {
rc->probably_admitted = false;
}
diff --git a/lib/rconn.h b/lib/rconn.h
index 1ce73e9..694665e 100644
--- a/lib/rconn.h
+++ b/lib/rconn.h
@@ -91,5 +91,6 @@ time_t rconn_get_creation_time(const struct rconn *);
unsigned long int rconn_get_total_time_connected(const struct rconn *);
int rconn_get_backoff(const struct rconn *);
unsigned int rconn_get_state_elapsed(const struct rconn *);
+unsigned int rconn_get_connection_seqno(const struct rconn *);
#endif /* rconn.h */
diff --git a/lib/shash.c b/lib/shash.c
new file mode 100644
index 0000000..a6a2d7b
--- /dev/null
+++ b/lib/shash.c
@@ -0,0 +1,111 @@
+/* Copyright (c) 2009 The Board of Trustees of The Leland Stanford Junior
+ * University
+ *
+ * We are making the OpenFlow specification and associated documentation
+ * (Software) available for public use and benefit with the expectation
+ * that others will use, modify and enhance the Software and contribute
+ * those enhancements back to the community. However, since we would
+ * like to make the Software available for broadest use, with as few
+ * restrictions as possible permission is hereby granted, free of
+ * charge, to any person obtaining a copy of this Software to deal in
+ * the Software under the copyrights without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * 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 AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * The name and trademarks of copyright holder(s) may NOT be used in
+ * advertising or publicity pertaining to the Software or any
+ * derivatives without specific, written prior permission.
+ */
+
+#include
+#include "shash.h"
+#include
+#include "hash.h"
+
+static size_t
+hash_name(const char *name)
+{
+ return hash_bytes(name, strlen(name), 0);
+}
+
+void
+shash_init(struct shash *sh)
+{
+ hmap_init(&sh->map);
+}
+
+void
+shash_destroy(struct shash *sh)
+{
+ if (sh) {
+ shash_clear(sh);
+ free(sh);
+ }
+}
+
+void
+shash_clear(struct shash *sh)
+{
+ struct shash_node *node, *next;
+
+ HMAP_FOR_EACH_SAFE (node, next, struct shash_node, node, &sh->map) {
+ hmap_remove(&sh->map, &node->node);
+ free(node->name);
+ free(node);
+ }
+}
+
+/* It is the caller's responsible to avoid duplicate names, if that is
+ * desirable. */
+void
+shash_add(struct shash *sh, const char *name, void *data)
+{
+ struct shash_node *node = xmalloc(sizeof *node);
+ node->name = xstrdup(name);
+ node->data = data;
+ hmap_insert(&sh->map, &node->node, hash_name(name));
+}
+
+void
+shash_delete(struct shash *sh, struct shash_node *node)
+{
+ hmap_remove(&sh->map, &node->node);
+ free(node->name);
+ free(node);
+}
+
+/* If there are duplicates, returns a random element. */
+struct shash_node *
+shash_find(const struct shash *sh, const char *name)
+{
+ struct shash_node *node;
+
+ HMAP_FOR_EACH_WITH_HASH (node, struct shash_node, node,
+ hash_name(name), &sh->map) {
+ if (!strcmp(node->name, name)) {
+ return node;
+ }
+ }
+ return NULL;
+}
+
+void *
+shash_find_data(const struct shash *sh, const char *name)
+{
+ struct shash_node *node = shash_find(sh, name);
+ return node ? node->data : NULL;
+}
diff --git a/lib/shash.h b/lib/shash.h
new file mode 100644
index 0000000..20681e1
--- /dev/null
+++ b/lib/shash.h
@@ -0,0 +1,59 @@
+/* Copyright (c) 2009 The Board of Trustees of The Leland Stanford Junior
+ * University
+ *
+ * We are making the OpenFlow specification and associated documentation
+ * (Software) available for public use and benefit with the expectation
+ * that others will use, modify and enhance the Software and contribute
+ * those enhancements back to the community. However, since we would
+ * like to make the Software available for broadest use, with as few
+ * restrictions as possible permission is hereby granted, free of
+ * charge, to any person obtaining a copy of this Software to deal in
+ * the Software under the copyrights without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * 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 AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * The name and trademarks of copyright holder(s) may NOT be used in
+ * advertising or publicity pertaining to the Software or any
+ * derivatives without specific, written prior permission.
+ */
+
+#ifndef SHASH_H
+#define SHASH_H 1
+
+#include "hmap.h"
+
+struct shash_node {
+ struct hmap_node node;
+ char *name;
+ void *data;
+};
+
+struct shash {
+ struct hmap map;
+};
+
+#define SHASH_INITIALIZER(SHASH) { HMAP_INITIALIZER(&(SHASH)->map) }
+
+void shash_init(struct shash *);
+void shash_destroy(struct shash *);
+void shash_clear(struct shash *);
+void shash_add(struct shash *, const char *, void *);
+void shash_delete(struct shash *, struct shash_node *);
+struct shash_node *shash_find(const struct shash *, const char *);
+void *shash_find_data(const struct shash *, const char *);
+
+#endif /* shash.h */
diff --git a/lib/signals.c b/lib/signals.c
new file mode 100644
index 0000000..93327b1
--- /dev/null
+++ b/lib/signals.c
@@ -0,0 +1,145 @@
+/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+ * Junior University
+ *
+ * We are making the OpenFlow specification and associated documentation
+ * (Software) available for public use and benefit with the expectation
+ * that others will use, modify and enhance the Software and contribute
+ * those enhancements back to the community. However, since we would
+ * like to make the Software available for broadest use, with as few
+ * restrictions as possible permission is hereby granted, free of
+ * charge, to any person obtaining a copy of this Software to deal in
+ * the Software under the copyrights without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * 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 AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * The name and trademarks of copyright holder(s) may NOT be used in
+ * advertising or publicity pertaining to the Software or any
+ * derivatives without specific, written prior permission.
+ */
+
+#include
+#include "signals.h"
+#include
+#include
+#include
+#include
+#include
+#include "poll-loop.h"
+#include "socket-util.h"
+#include "util.h"
+
+#if defined(_NSIG)
+#define N_SIGNALS _NSIG
+#elif defined(NSIG)
+#define N_SIGNALS NSIG
+#else
+/* We could try harder to get the maximum signal number, but in practice we
+ * only care about SIGHUP, which is normally signal 1 anyway. */
+#define N_SIGNALS 32
+#endif
+
+struct signal {
+ int signr;
+};
+
+static volatile sig_atomic_t signaled[N_SIGNALS];
+
+static int fds[2];
+
+static void signal_handler(int signr);
+
+/* Initializes the signals subsystem (if it is not already initialized). Calls
+ * exit() if initialization fails.
+ *
+ * Calling this function is optional; it will be called automatically by
+ * signal_start() if necessary. Calling it explicitly allows the client to
+ * prevent the process from exiting at an unexpected time. */
+void
+signal_init(void)
+{
+ static bool inited;
+ if (!inited) {
+ inited = true;
+ if (pipe(fds)) {
+ ofp_fatal(errno, "could not create pipe");
+ }
+ set_nonblocking(fds[0]);
+ set_nonblocking(fds[1]);
+ }
+}
+
+/* Sets up a handler for 'signr' and returns a structure that represents it.
+ *
+ * Only one handler for a given signal may be registered at a time. */
+struct signal *
+signal_register(int signr)
+{
+ struct sigaction sa;
+ struct signal *s;
+
+ signal_init();
+
+ /* Set up signal handler. */
+ assert(signr >= 1 && signr < N_SIGNALS);
+ memset(&sa, 0, sizeof sa);
+ sa.sa_handler = signal_handler;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = SA_RESTART;
+ if (sigaction(signr, &sa, NULL)) {
+ ofp_fatal(errno, "sigaction(%d) failed", signr);
+ }
+
+ /* Return structure. */
+ s = xmalloc(sizeof *s);
+ s->signr = signr;
+ return s;
+}
+
+/* Returns true if signal 's' has been received since the last call to this
+ * function with argument 's'. */
+bool
+signal_poll(struct signal *s)
+{
+ char buf[_POSIX_PIPE_BUF];
+ read(fds[0], buf, sizeof buf);
+ if (signaled[s->signr]) {
+ signaled[s->signr] = 0;
+ return true;
+ }
+ return false;
+}
+
+/* Causes the next call to poll_block() to wake up when signal_poll(s) would
+ * return true. */
+void
+signal_wait(struct signal *s)
+{
+ if (signaled[s->signr]) {
+ poll_immediate_wake();
+ } else {
+ poll_fd_wait(fds[0], POLLIN);
+ }
+}
+
+static void
+signal_handler(int signr)
+{
+ if (signr >= 1 && signr < N_SIGNALS) {
+ write(fds[1], "", 1);
+ signaled[signr] = true;
+ }
+}
diff --git a/lib/signals.h b/lib/signals.h
new file mode 100644
index 0000000..fd91bd7
--- /dev/null
+++ b/lib/signals.h
@@ -0,0 +1,44 @@
+/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+ * Junior University
+ *
+ * We are making the OpenFlow specification and associated documentation
+ * (Software) available for public use and benefit with the expectation
+ * that others will use, modify and enhance the Software and contribute
+ * those enhancements back to the community. However, since we would
+ * like to make the Software available for broadest use, with as few
+ * restrictions as possible permission is hereby granted, free of
+ * charge, to any person obtaining a copy of this Software to deal in
+ * the Software under the copyrights without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * 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 AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * The name and trademarks of copyright holder(s) may NOT be used in
+ * advertising or publicity pertaining to the Software or any
+ * derivatives without specific, written prior permission.
+ */
+
+#ifndef SIGNALS_H
+#define SIGNALS_H 1
+
+#include
+
+void signal_init(void);
+struct signal *signal_register(int signr);
+bool signal_poll(struct signal *);
+void signal_wait(struct signal *);
+
+#endif /* signals.h */
diff --git a/lib/stp.c b/lib/stp.c
index 0847f57..b638311 100644
--- a/lib/stp.c
+++ b/lib/stp.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+/* Copyright (c) 2008, 2009 The Board of Trustees of The Leland Stanford
* Junior University
*
* We are making the OpenFlow specification and associated documentation
@@ -39,6 +39,7 @@
#include
#include
#include
+#include "ofpbuf.h"
#include "packets.h"
#include "util.h"
#include "xtoxll.h"
@@ -121,6 +122,10 @@ struct stp {
int bridge_max_age; /* 8.5.3.8: max_age when we're root. */
int bridge_hello_time; /* 8.5.3.9: hello_time as root. */
int bridge_forward_delay; /* 8.5.3.10: forward_delay as root. */
+ int rq_max_age; /* User-requested max age, in ms. */
+ int rq_hello_time; /* User-requested hello time, in ms. */
+ int rq_forward_delay; /* User-requested forward delay, in ms. */
+ int elapsed_remainder; /* Left-over msecs from last stp_tick(). */
/* Dynamic bridge data. */
stp_identifier designated_root; /* 8.5.3.1: Bridge believed to be root. */
@@ -139,8 +144,7 @@ struct stp {
/* Interface to client. */
struct stp_port *first_changed_port;
- void (*send_bpdu)(const void *bpdu, size_t bpdu_size,
- int port_no, void *aux);
+ void (*send_bpdu)(struct ofpbuf *bpdu, int port_no, void *aux);
void *aux;
};
@@ -159,8 +163,6 @@ stp_next_enabled_port(const struct stp *stp, const struct stp_port *port)
return NULL;
}
-#define SECONDS_TO_TIMER(SECS) ((SECS) * 0x100)
-
#define MESSAGE_AGE_INCREMENT 1
static void stp_transmit_config(struct stp_port *);
@@ -188,8 +190,7 @@ static void stp_topology_change_acknowledged(struct stp *);
static void stp_acknowledge_topology_change(struct stp_port *);
static void stp_received_config_bpdu(struct stp *, struct stp_port *,
const struct stp_config_bpdu *);
-static void stp_received_tcn_bpdu(struct stp *, struct stp_port *,
- const struct stp_tcn_bpdu *);
+static void stp_received_tcn_bpdu(struct stp *, struct stp_port *);
static void stp_hello_timer_expiry(struct stp *);
static void stp_message_age_timer_expiry(struct stp_port *);
static bool stp_is_designated_for_some_port(const struct stp *);
@@ -198,27 +199,33 @@ static void stp_tcn_timer_expiry(struct stp *);
static void stp_topology_change_timer_expiry(struct stp *);
static void stp_hold_timer_expiry(struct stp_port *);
static void stp_initialize_port(struct stp_port *, enum stp_state);
-static void stp_become_root_bridge(struct stp *stp);
+static void stp_become_root_bridge(struct stp *);
+static void stp_update_bridge_timers(struct stp *);
+static int clamp(int x, int min, int max);
+static int ms_to_timer(int ms);
+static int ms_to_timer_remainder(int ms);
+static int timer_to_ms(int timer);
static void stp_start_timer(struct stp_timer *, int value);
static void stp_stop_timer(struct stp_timer *);
static bool stp_timer_expired(struct stp_timer *, int elapsed, int timeout);
-/* Creates and returns a new STP instance that initially has no port enabled.
+static void stp_send_bpdu(struct stp_port *, const void *, size_t);
+
+/* Creates and returns a new STP instance that initially has no ports enabled.
*
* 'bridge_id' should be a 48-bit MAC address as returned by
* eth_addr_to_uint64(). 'bridge_id' may also have a priority value in its top
- * 16 bits; if those bits are set to 0, the default bridge priority of 32768 is
- * used. (This priority may be changed with stp_set_bridge_priority().)
+ * 16 bits; if those bits are set to 0, STP_DEFAULT_BRIDGE_PRIORITY is used.
+ * (This priority may be changed with stp_set_bridge_priority().)
*
- * When the bridge needs to send out a BPDU, it calls 'send_bpdu', passing
- * 'aux' as auxiliary data. This callback may be called from stp_tick() or
- * stp_received_bpdu().
+ * When the bridge needs to send out a BPDU, it calls 'send_bpdu'. This
+ * callback may be called from stp_tick() or stp_received_bpdu(). The
+ * arguments to 'send_bpdu' are an STP BPDU encapsulated in
*/
struct stp *
stp_create(const char *name, stp_identifier bridge_id,
- void (*send_bpdu)(const void *bpdu, size_t bpdu_size,
- int port_no, void *aux),
+ void (*send_bpdu)(struct ofpbuf *bpdu, int port_no, void *aux),
void *aux)
{
struct stp *stp;
@@ -228,18 +235,16 @@ stp_create(const char *name, stp_identifier bridge_id,
stp->name = xstrdup(name);
stp->bridge_id = bridge_id;
if (!(stp->bridge_id >> 48)) {
- stp->bridge_id |= UINT64_C(32768) << 48;
+ stp->bridge_id |= (uint64_t) STP_DEFAULT_BRIDGE_PRIORITY << 48;
}
- stp->max_age = SECONDS_TO_TIMER(6);
- stp->hello_time = SECONDS_TO_TIMER(2);
- stp->forward_delay = SECONDS_TO_TIMER(4);
- stp->bridge_max_age = stp->max_age;
- stp->bridge_hello_time = stp->hello_time;
- stp->bridge_forward_delay = stp->forward_delay;
- /* Verify constraints stated by 802.1D. */
- assert(2 * (stp->forward_delay - SECONDS_TO_TIMER(1)) >= stp->max_age);
- assert(stp->max_age >= 2 * (stp->hello_time + SECONDS_TO_TIMER(1)));
+ stp->rq_max_age = 6000;
+ stp->rq_hello_time = 2000;
+ stp->rq_forward_delay = 4000;
+ stp_update_bridge_timers(stp);
+ stp->max_age = stp->bridge_max_age;
+ stp->hello_time = stp->bridge_hello_time;
+ stp->forward_delay = stp->bridge_forward_delay;
stp->designated_root = stp->bridge_id;
stp->root_path_cost = 0;
@@ -257,7 +262,7 @@ stp_create(const char *name, stp_identifier bridge_id,
stp->first_changed_port = &stp->ports[ARRAY_SIZE(stp->ports)];
for (p = stp->ports; p < &stp->ports[ARRAY_SIZE(stp->ports)]; p++) {
p->stp = stp;
- p->port_id = (stp_port_no(p) + 1) | (128 << 8);
+ p->port_id = (stp_port_no(p) + 1) | (STP_DEFAULT_PORT_PRIORITY << 8);
p->path_cost = 19; /* Recommended default for 100 Mb/s link. */
stp_initialize_port(p, STP_DISABLED);
}
@@ -271,10 +276,22 @@ stp_destroy(struct stp *stp)
free(stp);
}
+/* Runs 'stp' given that 'ms' milliseconds have passed. */
void
-stp_tick(struct stp *stp, int elapsed)
+stp_tick(struct stp *stp, int ms)
{
struct stp_port *p;
+ int elapsed;
+
+ /* Convert 'ms' to STP timer ticks. Preserve any leftover milliseconds
+ * from previous stp_tick() calls so that we don't lose STP ticks when we
+ * are called too frequently. */
+ ms = clamp(ms, 0, INT_MAX - 1000) + stp->elapsed_remainder;
+ elapsed = ms_to_timer(ms);
+ stp->elapsed_remainder = ms_to_timer_remainder(ms);
+ if (!elapsed) {
+ return;
+ }
if (stp_timer_expired(&stp->hello_timer, elapsed, stp->hello_time)) {
stp_hello_timer_expiry(stp);
@@ -296,7 +313,7 @@ stp_tick(struct stp *stp, int elapsed)
stp->forward_delay)) {
stp_forward_delay_timer_expiry(p);
}
- if (stp_timer_expired(&p->hold_timer, elapsed, SECONDS_TO_TIMER(1))) {
+ if (stp_timer_expired(&p->hold_timer, elapsed, ms_to_timer(1000))) {
stp_hold_timer_expiry(p);
}
}
@@ -340,6 +357,40 @@ stp_set_bridge_priority(struct stp *stp, uint16_t new_priority)
| ((uint64_t) new_priority << 48)));
}
+/* Sets the desired hello time for 'stp' to 'ms', in milliseconds. The actual
+ * hello time is clamped to the range of 1 to 10 seconds and subject to the
+ * relationship (bridge_max_age >= 2 * (bridge_hello_time + 1 s)). The bridge
+ * hello time is only used when 'stp' is the root bridge. */
+void
+stp_set_hello_time(struct stp *stp, int ms)
+{
+ stp->rq_hello_time = ms;
+ stp_update_bridge_timers(stp);
+}
+
+/* Sets the desired max age for 'stp' to 'ms', in milliseconds. The actual max
+ * age is clamped to the range of 6 to 40 seconds and subject to the
+ * relationships (2 * (bridge_forward_delay - 1 s) >= bridge_max_age) and
+ * (bridge_max_age >= 2 * (bridge_hello_time + 1 s)). The bridge max age is
+ * only used when 'stp' is the root bridge. */
+void
+stp_set_max_age(struct stp *stp, int ms)
+{
+ stp->rq_max_age = ms;
+ stp_update_bridge_timers(stp);
+}
+
+/* Sets the desired forward delay for 'stp' to 'ms', in milliseconds. The
+ * actual forward delay is clamped to the range of 4 to 30 seconds and subject
+ * to the relationship (2 * (bridge_forward_delay - 1 s) >= bridge_max_age).
+ * The bridge forward delay is only used when 'stp' is the root bridge. */
+void
+stp_set_forward_delay(struct stp *stp, int ms)
+{
+ stp->rq_forward_delay = ms;
+ stp_update_bridge_timers(stp);
+}
+
/* Returns the name given to 'stp' in the call to stp_create(). */
const char *
stp_get_name(const struct stp *stp)
@@ -376,6 +427,35 @@ stp_get_root_path_cost(const struct stp *stp)
return stp->root_path_cost;
}
+/* Returns the bridge hello time, in ms. The returned value is not necessarily
+ * the value passed to stp_set_hello_time(): it is clamped to the valid range
+ * and quantized to the STP timer resolution. */
+int
+stp_get_hello_time(const struct stp *stp)
+{
+ return timer_to_ms(stp->bridge_hello_time);
+}
+
+/* Returns the bridge max age, in ms. The returned value is not necessarily
+ * the value passed to stp_set_max_age(): it is clamped to the valid range,
+ * quantized to the STP timer resolution, and adjusted to match the constraints
+ * due to the hello time. */
+int
+stp_get_max_age(const struct stp *stp)
+{
+ return timer_to_ms(stp->bridge_max_age);
+}
+
+/* Returns the bridge forward delay, in ms. The returned value is not
+ * necessarily the value passed to stp_set_forward_delay(): it is clamped to
+ * the valid range, quantized to the STP timer resolution, and adjusted to
+ * match the constraints due to the forward delay. */
+int
+stp_get_forward_delay(const struct stp *stp)
+{
+ return timer_to_ms(stp->bridge_forward_delay);
+}
+
/* Returns the port in 'stp' with index 'port_no', which must be between 0 and
* STP_MAX_PORTS. */
struct stp_port *
@@ -437,19 +517,25 @@ stp_state_name(enum stp_state state)
}
/* Returns true if 'state' is one in which packets received on a port should
- * be forwarded, false otherwise. */
+ * be forwarded, false otherwise.
+ *
+ * Returns true if 'state' is STP_DISABLED, since presumably in that case the
+ * port should still work, just not have STP applied to it. */
bool
stp_forward_in_state(enum stp_state state)
{
- return state == STP_FORWARDING;
+ return (state & (STP_DISABLED | STP_FORWARDING)) != 0;
}
/* Returns true if 'state' is one in which MAC learning should be done on
- * packets received on a port, false otherwise. */
+ * packets received on a port, false otherwise.
+ *
+ * Returns true if 'state' is STP_DISABLED, since presumably in that case the
+ * port should still work, just not have STP applied to it. */
bool
stp_learn_in_state(enum stp_state state)
{
- return state & (STP_LEARNING | STP_FORWARDING);
+ return (state & (STP_DISABLED | STP_LEARNING | STP_FORWARDING)) != 0;
}
/* Notifies the STP entity that bridge protocol data unit 'bpdu', which is
@@ -498,7 +584,7 @@ stp_received_bpdu(struct stp_port *p, const void *bpdu, size_t bpdu_size)
stp->name, bpdu_size);
return;
}
- stp_received_tcn_bpdu(stp, p, bpdu);
+ stp_received_tcn_bpdu(stp, p);
break;
default:
@@ -586,7 +672,7 @@ stp_port_set_priority(struct stp_port *p, uint8_t new_priority)
* used to indicate faster links. Use stp_port_set_speed() to automatically
* generate a default path cost from a link speed. */
void
-stp_port_set_path_cost(struct stp_port *p, unsigned int path_cost)
+stp_port_set_path_cost(struct stp_port *p, uint16_t path_cost)
{
if (p->path_cost != path_cost) {
struct stp *stp = p->stp;
@@ -662,7 +748,7 @@ stp_transmit_config(struct stp_port *p)
if (ntohs(config.message_age) < stp->max_age) {
p->topology_change_ack = false;
p->config_pending = false;
- stp->send_bpdu(&config, sizeof config, stp_port_no(p), stp->aux);
+ stp_send_bpdu(p, &config, sizeof config);
stp_start_timer(&p->hold_timer, 0);
}
}
@@ -735,7 +821,7 @@ stp_transmit_tcn(struct stp *stp)
tcn_bpdu.header.protocol_id = htons(STP_PROTOCOL_ID);
tcn_bpdu.header.protocol_version = STP_PROTOCOL_VERSION;
tcn_bpdu.header.bpdu_type = STP_TYPE_TCN;
- stp->send_bpdu(&tcn_bpdu, sizeof tcn_bpdu, stp_port_no(p), stp->aux);
+ stp_send_bpdu(p, &tcn_bpdu, sizeof tcn_bpdu);
}
static void
@@ -941,8 +1027,7 @@ stp_received_config_bpdu(struct stp *stp, struct stp_port *p,
}
void
-stp_received_tcn_bpdu(struct stp *stp, struct stp_port *p,
- const struct stp_tcn_bpdu *tcn)
+stp_received_tcn_bpdu(struct stp *stp, struct stp_port *p)
{
if (p->state != STP_DISABLED) {
if (stp_is_designated_port(p)) {
@@ -1081,3 +1166,78 @@ stp_timer_expired(struct stp_timer *timer, int elapsed, int timeout)
return false;
}
+/* Returns the number of whole STP timer ticks in 'ms' milliseconds. There
+ * are 256 STP timer ticks per second. */
+static int
+ms_to_timer(int ms)
+{
+ return ms * 0x100 / 1000;
+}
+
+/* Returns the number of leftover milliseconds when 'ms' is converted to STP
+ * timer ticks. */
+static int
+ms_to_timer_remainder(int ms)
+{
+ return ms * 0x100 % 1000;
+}
+
+/* Returns the number of whole milliseconds in 'timer' STP timer ticks. There
+ * are 256 STP timer ticks per second. */
+static int
+timer_to_ms(int timer)
+{
+ return timer * 1000 / 0x100;
+}
+
+static int
+clamp(int x, int min, int max)
+{
+ return x < min ? min : x > max ? max : x;
+}
+
+static void
+stp_update_bridge_timers(struct stp *stp)
+{
+ int ht, ma, fd;
+
+ ht = clamp(stp->rq_hello_time, 1000, 10000);
+ ma = clamp(stp->rq_max_age, MAX(2 * (ht + 1000), 6000), 40000);
+ fd = clamp(stp->rq_forward_delay, ma / 2 + 1000, 30000);
+
+ stp->bridge_hello_time = ms_to_timer(ht);
+ stp->bridge_max_age = ms_to_timer(ma);
+ stp->bridge_forward_delay = ms_to_timer(fd);
+
+ if (stp_is_root_bridge(stp)) {
+ stp->max_age = stp->bridge_max_age;
+ stp->hello_time = stp->bridge_hello_time;
+ stp->forward_delay = stp->bridge_forward_delay;
+ }
+}
+
+static void
+stp_send_bpdu(struct stp_port *p, const void *bpdu, size_t bpdu_size)
+{
+ struct eth_header *eth;
+ struct llc_header *llc;
+ struct ofpbuf *pkt;
+
+ /* Skeleton. */
+ pkt = ofpbuf_new(ETH_HEADER_LEN + LLC_HEADER_LEN + bpdu_size);
+ pkt->l2 = eth = ofpbuf_put_zeros(pkt, sizeof *eth);
+ llc = ofpbuf_put_zeros(pkt, sizeof *llc);
+ pkt->l3 = ofpbuf_put(pkt, bpdu, bpdu_size);
+
+ /* 802.2 header. */
+ memcpy(eth->eth_dst, stp_eth_addr, ETH_ADDR_LEN);
+ /* p->stp->send_bpdu() must fill in source address. */
+ eth->eth_type = htons(pkt->size - ETH_HEADER_LEN);
+
+ /* LLC header. */
+ llc->llc_dsap = STP_LLC_DSAP;
+ llc->llc_ssap = STP_LLC_SSAP;
+ llc->llc_cntl = STP_LLC_CNTL;
+
+ p->stp->send_bpdu(pkt, stp_port_no(p), p->stp->aux);
+}
diff --git a/lib/stp.h b/lib/stp.h
index 5e09c2f..a51c92f 100644
--- a/lib/stp.h
+++ b/lib/stp.h
@@ -42,6 +42,8 @@
#include "compiler.h"
#include "util.h"
+struct ofpbuf;
+
/* Ethernet address used as the destination for STP frames. */
extern const uint8_t stp_eth_addr[6];
@@ -50,6 +52,10 @@ extern const uint8_t stp_eth_addr[6];
#define STP_LLC_DSAP 0x42
#define STP_LLC_CNTL 0x03
+/* Bridge and port priorities that should be used by default. */
+#define STP_DEFAULT_BRIDGE_PRIORITY 32768
+#define STP_DEFAULT_PORT_PRIORITY 128
+
/* Bridge identifier. Top 16 bits are a priority value (numerically lower
* values are higher priorities). Bottom 48 bits are MAC address of bridge. */
typedef uint64_t stp_identifier;
@@ -57,13 +63,16 @@ typedef uint64_t stp_identifier;
/* Basic STP functionality. */
#define STP_MAX_PORTS 255
struct stp *stp_create(const char *name, stp_identifier bridge_id,
- void (*send_bpdu)(const void *bpdu, size_t bpdu_size,
- int port_no, void *aux),
+ void (*send_bpdu)(struct ofpbuf *bpdu, int port_no,
+ void *aux),
void *aux);
void stp_destroy(struct stp *);
-void stp_tick(struct stp *, int elapsed);
+void stp_tick(struct stp *, int ms);
void stp_set_bridge_id(struct stp *, stp_identifier bridge_id);
void stp_set_bridge_priority(struct stp *, uint16_t new_priority);
+void stp_set_hello_time(struct stp *, int ms);
+void stp_set_max_age(struct stp *, int ms);
+void stp_set_forward_delay(struct stp *, int ms);
/* STP properties. */
const char *stp_get_name(const struct stp *);
@@ -71,6 +80,9 @@ stp_identifier stp_get_bridge_id(const struct stp *);
stp_identifier stp_get_designated_root(const struct stp *);
bool stp_is_root_bridge(const struct stp *);
int stp_get_root_path_cost(const struct stp *);
+int stp_get_hello_time(const struct stp *);
+int stp_get_max_age(const struct stp *);
+int stp_get_forward_delay(const struct stp *);
/* Obtaining STP ports. */
struct stp_port *stp_get_port(struct stp *, int port_no);
@@ -100,7 +112,7 @@ enum stp_state stp_port_get_state(const struct stp_port *);
void stp_port_enable(struct stp_port *);
void stp_port_disable(struct stp_port *);
void stp_port_set_priority(struct stp_port *, uint8_t new_priority);
-void stp_port_set_path_cost(struct stp_port *, unsigned int path_cost);
+void stp_port_set_path_cost(struct stp_port *, uint16_t path_cost);
void stp_port_set_speed(struct stp_port *, unsigned int speed);
void stp_port_enable_change_detection(struct stp_port *);
void stp_port_disable_change_detection(struct stp_port *);
diff --git a/lib/svec.c b/lib/svec.c
index b568f67..e2ac25d 100644
--- a/lib/svec.c
+++ b/lib/svec.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+/* Copyright (c) 2008, 2009 The Board of Trustees of The Leland Stanford
* Junior University
*
* We are making the OpenFlow specification and associated documentation
@@ -121,6 +121,13 @@ svec_sort(struct svec *svec)
qsort(svec->names, svec->n, sizeof *svec->names, compare_strings);
}
+void
+svec_sort_unique(struct svec *svec)
+{
+ svec_sort(svec);
+ svec_unique(svec);
+}
+
void
svec_unique(struct svec *svec)
{
@@ -195,9 +202,18 @@ svec_diff(const struct svec *a, const struct svec *b,
bool
svec_contains(const struct svec *svec, const char *name)
{
+ return svec_find(svec, name) != SIZE_MAX;
+}
+
+size_t
+svec_find(const struct svec *svec, const char *name)
+{
+ char **p;
+
assert(svec_is_sorted(svec));
- return bsearch(&name, svec->names, svec->n, sizeof *svec->names,
- compare_strings) != NULL;
+ p = bsearch(&name, svec->names, svec->n, sizeof *svec->names,
+ compare_strings);
+ return p ? p - svec->names : SIZE_MAX;
}
bool
@@ -213,6 +229,27 @@ svec_is_sorted(const struct svec *svec)
return true;
}
+bool
+svec_is_unique(const struct svec *svec)
+{
+ return svec_get_duplicate(svec) == NULL;
+}
+
+const char *
+svec_get_duplicate(const struct svec *svec)
+{
+ assert(svec_is_sorted(svec));
+ if (svec->n > 1) {
+ size_t i;
+ for (i = 1; i < svec->n; i++) {
+ if (!strcmp(svec->names[i - 1], svec->names[i])) {
+ return svec->names[i];
+ }
+ }
+ }
+ return NULL;
+}
+
void
svec_swap(struct svec *a, struct svec *b)
{
@@ -309,3 +346,17 @@ svec_join(const struct svec *svec, const char *delimiter)
}
return ds_cstr(&ds);
}
+
+const char *
+svec_back(const struct svec *svec)
+{
+ assert(svec->n);
+ return svec->names[svec->n - 1];
+}
+
+void
+svec_pop_back(struct svec *svec)
+{
+ assert(svec->n);
+ free(svec->names[--svec->n]);
+}
diff --git a/lib/svec.h b/lib/svec.h
index 3362c00..0cd2192 100644
--- a/lib/svec.h
+++ b/lib/svec.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+/* Copyright (c) 2008, 2009 The Board of Trustees of The Leland Stanford
* Junior University
*
* We are making the OpenFlow specification and associated documentation
@@ -53,15 +53,21 @@ void svec_add_nocopy(struct svec *, char *);
void svec_append(struct svec *, const struct svec *);
void svec_terminate(struct svec *);
void svec_sort(struct svec *);
+void svec_sort_unique(struct svec *);
void svec_unique(struct svec *);
void svec_diff(const struct svec *a, const struct svec *b,
struct svec *a_only, struct svec *both, struct svec *b_only);
bool svec_contains(const struct svec *, const char *);
+size_t svec_find(const struct svec *, const char *);
bool svec_is_sorted(const struct svec *);
+bool svec_is_unique(const struct svec *);
+const char *svec_get_duplicate(const struct svec *);
void svec_swap(struct svec *a, struct svec *b);
void svec_print(const struct svec *svec, const char *title);
void svec_parse_words(struct svec *svec, const char *words);
bool svec_equal(const struct svec *, const struct svec *);
char *svec_join(const struct svec *, const char *delimiter);
+const char *svec_back(const struct svec *);
+void svec_pop_back(struct svec *);
#endif /* svec.h */
diff --git a/lib/tag.c b/lib/tag.c
new file mode 100644
index 0000000..11d4dbe
--- /dev/null
+++ b/lib/tag.c
@@ -0,0 +1,99 @@
+/* Copyright (c) 2008, 2009 The Board of Trustees of The Leland Stanford
+ * Junior University
+ *
+ * We are making the OpenFlow specification and associated documentation
+ * (Software) available for public use and benefit with the expectation
+ * that others will use, modify and enhance the Software and contribute
+ * those enhancements back to the community. However, since we would
+ * like to make the Software available for broadest use, with as few
+ * restrictions as possible permission is hereby granted, free of
+ * charge, to any person obtaining a copy of this Software to deal in
+ * the Software under the copyrights without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * 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 AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * The name and trademarks of copyright holder(s) may NOT be used in
+ * advertising or publicity pertaining to the Software or any
+ * derivatives without specific, written prior permission.
+ */
+
+#include
+#include "tag.h"
+#include
+#include "random.h"
+#include "type-props.h"
+#include "util.h"
+
+#define N_TAG_BITS (CHAR_BIT * sizeof(tag_type))
+BUILD_ASSERT_DECL(IS_POW2(N_TAG_BITS));
+
+#define LOG2_N_TAG_BITS (N_TAG_BITS == 32 ? 5 : N_TAG_BITS == 64 ? 6 : 0)
+BUILD_ASSERT_DECL(LOG2_N_TAG_BITS > 0);
+
+/* Returns a randomly selected tag. */
+tag_type
+tag_create_random(void)
+{
+ int x, y;
+ do {
+ uint16_t r = random_uint16();
+ x = r & (N_TAG_BITS - 1);
+ y = r >> (16 - LOG2_N_TAG_BITS);
+ } while (x == y);
+ return (1u << x) | (1u << y);
+}
+
+/* Returns a tag deterministically generated from 'seed'.
+ *
+ * 'seed' should have data in all of its bits; if it has data only in its
+ * low-order bits then the resulting tags will be poorly distributed. Use a
+ * hash function such as hash_bytes() to generate 'seed' if necessary. */
+tag_type
+tag_create_deterministic(uint32_t seed)
+{
+ int x = seed & (N_TAG_BITS - 1);
+ int y = (seed >> LOG2_N_TAG_BITS) % 31;
+ y += y >= x;
+ return (1u << x) | (1u << y);
+}
+
+/* Initializes 'set' as an empty tag set. */
+void
+tag_set_init(struct tag_set *set)
+{
+ memset(set, 0, sizeof *set);
+}
+
+/* Adds 'tag' to 'set'. */
+void
+tag_set_add(struct tag_set *set, tag_type tag)
+{
+ if (tag && (!tag_is_valid(tag) || !tag_set_intersects(set, tag))) {
+ /* XXX We could do better by finding the set member to which we would
+ * add the fewest number of 1-bits. This would reduce the amount of
+ * ambiguity, since e.g. three 1-bits match 3 * 2 / 2 = 3 unique tags
+ * whereas four 1-bits match 4 * 3 / 2 = 6 unique tags. */
+ tag_type *t = &set->tags[set->n++ % TAG_SET_SIZE];
+ *t |= tag;
+ if (*t == TYPE_MAXIMUM(tag_type)) {
+ set->tags[0] = *t;
+ }
+
+ set->total |= tag;
+ }
+}
+
diff --git a/lib/tag.h b/lib/tag.h
new file mode 100644
index 0000000..964737b
--- /dev/null
+++ b/lib/tag.h
@@ -0,0 +1,151 @@
+/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+ * Junior University
+ *
+ * We are making the OpenFlow specification and associated documentation
+ * (Software) available for public use and benefit with the expectation
+ * that others will use, modify and enhance the Software and contribute
+ * those enhancements back to the community. However, since we would
+ * like to make the Software available for broadest use, with as few
+ * restrictions as possible permission is hereby granted, free of
+ * charge, to any person obtaining a copy of this Software to deal in
+ * the Software under the copyrights without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * 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 AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * The name and trademarks of copyright holder(s) may NOT be used in
+ * advertising or publicity pertaining to the Software or any
+ * derivatives without specific, written prior permission.
+ */
+
+#ifndef TAG_H
+#define TAG_H 1
+
+#include
+#include
+#include
+#include "util.h"
+
+/*
+ * Tagging support.
+ *
+ * A 'tag' represents an arbitrary category. Currently, tags are used to
+ * represent categories of flows and in particular the dependencies for a flow
+ * switching decision. For example, if a flow's output port is based on
+ * knowledge that source MAC 00:02:e3:0f:80:a4 is on eth0, then a tag that
+ * represents that dependency is attached to that flow in the flowtracking hash
+ * table.
+ *
+ * As this example shows, the universe of possible categories is very large,
+ * and even the number of categories that are in use at a given time can be
+ * very large. This means that keeping track of category membership via
+ * conventional means (lists, bitmaps, etc.) is likely to be expensive.
+ *
+ * Tags are actually implemented via a "superimposed coding", as discussed in
+ * Knuth TAOCP v.3 section 6.5 "Retrieval on Secondary Keys". A tag is an
+ * unsigned integer in which exactly 2 bits are set to 1 and the rest set to 0.
+ * For 32-bit integers (as currently used) there are 32 * 31 / 2 = 496 unique
+ * tags; for 64-bit integers there are 64 * 63 / 2 = 2,016.
+ *
+ * Because there is a small finite number of unique tags, tags must collide
+ * after some number of them have been created. In practice we generally
+ * create tags by choosing bits randomly.
+ *
+ * The key property of tags is that we can combine them without increasing the
+ * amount of data required using bitwise-OR, since the result has the 1-bits
+ * from both tags set. The necessary tradeoff is that the result is even more
+ * ambiguous: if combining two tags yields a value with 4 bits set to 1, then
+ * the result value will test as having 4 * 3 / 2 = 6 unique tags, not just the
+ * two tags that we combined.
+ *
+ * The upshot is this: a value that is the bitwise-OR combination of a number
+ * of tags will always include the tags that were combined, but it may contain
+ * any number of additional tags as well. This is acceptable for flowtracking,
+ * since we want to be sure that we catch every flow that needs to be
+ * revalidated, but it is OK if we revalidate a few extra flows as well.
+ *
+ * If we combine too many tags, then the result will have every bit set, so
+ * that it will test as including every tag. Fortunately, this is not a big
+ * problem for us: although there are many flows overall, each individual flow
+ * belongs only to a small number of categories.
+ */
+
+/* Represents a tag, or the combination of 0 or more tags. */
+typedef uint32_t tag_type;
+
+tag_type tag_create_random(void);
+tag_type tag_create_deterministic(uint32_t seed);
+static inline bool tag_intersects(tag_type, tag_type);
+static inline bool tag_is_valid(tag_type);
+
+/* Returns true if 'a' and 'b' have at least one tag in common,
+ * false if their set of tags is disjoint. . */
+static inline bool
+tag_intersects(tag_type a, tag_type b)
+{
+ tag_type x = a & b;
+ return (x & (x - 1)) != 0;
+}
+
+/* Returns true if 'tag' is a valid tag, that is, if exactly two bits are set
+ * to 1 and the rest to 0. Otherwise, returns false. */
+static inline bool
+tag_is_valid(tag_type tag)
+{
+ tag_type x = tag & (tag - 1);
+ tag_type y = x & (x - 1);
+ return x && !y;
+}
+
+/*
+ * A tag set accumulates tags with reduced ambiguity compared to a single tag.
+ * The flow tracking uses tag sets to keep track of tags that need to
+ * revalidated after a number of packets have been processed.
+ */
+#define TAG_SET_SIZE 4
+struct tag_set {
+ tag_type total;
+ tag_type tags[TAG_SET_SIZE];
+ unsigned int n;
+};
+
+void tag_set_init(struct tag_set *);
+void tag_set_add(struct tag_set *, tag_type);
+static inline bool tag_set_is_empty(const struct tag_set *);
+static inline bool tag_set_intersects(const struct tag_set *, tag_type);
+
+/* Returns true if 'set' will match no tags at all,
+ * false if it will match at least one tag. */
+static inline bool
+tag_set_is_empty(const struct tag_set *set)
+{
+ return !set->n;
+}
+
+/* Returns true if any of the tags in 'tags' are also in 'set',
+ * false if the intersection is empty. */
+static inline bool
+tag_set_intersects(const struct tag_set *set, tag_type tags)
+{
+ BUILD_ASSERT_DECL(TAG_SET_SIZE == 4);
+ return (tag_intersects(set->total, tags)
+ && (tag_intersects(set->tags[0], tags)
+ || tag_intersects(set->tags[1], tags)
+ || tag_intersects(set->tags[2], tags)
+ || tag_intersects(set->tags[3], tags)));
+}
+
+#endif /* tag.h */
diff --git a/lib/util.c b/lib/util.c
index abf005d..21cc28d 100644
--- a/lib/util.c
+++ b/lib/util.c
@@ -242,3 +242,56 @@ ofp_hex_dump(FILE *stream, const void *buf_, size_t size,
size -= n;
}
}
+
+bool
+str_to_int(const char *s, int base, int *i)
+{
+ long long ll;
+ bool ok = str_to_llong(s, base, &ll);
+ *i = ll;
+ return ok;
+}
+
+bool
+str_to_long(const char *s, int base, long *li)
+{
+ long long ll;
+ bool ok = str_to_llong(s, base, &ll);
+ *li = ll;
+ return ok;
+}
+
+bool
+str_to_llong(const char *s, int base, long long *x)
+{
+ int save_errno = errno;
+ char *tail;
+ errno = 0;
+ *x = strtoll(s, &tail, base);
+ if (errno == EINVAL || errno == ERANGE || tail == s || *tail != '\0') {
+ errno = save_errno;
+ *x = 0;
+ return false;
+ } else {
+ errno = save_errno;
+ return true;
+ }
+}
+
+bool
+str_to_uint(const char *s, int base, unsigned int *u)
+{
+ return str_to_int(s, base, (int *) u);
+}
+
+bool
+str_to_ulong(const char *s, int base, unsigned long *ul)
+{
+ return str_to_long(s, base, (long *) ul);
+}
+
+bool
+str_to_ullong(const char *s, int base, unsigned long long *ull)
+{
+ return str_to_llong(s, base, (long long *) ull);
+}
diff --git a/lib/util.h b/lib/util.h
index 8172b69..fde681f 100644
--- a/lib/util.h
+++ b/lib/util.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+/* Copyright (c) 2008, 2009 The Board of Trustees of The Leland Stanford
* Junior University
*
* We are making the OpenFlow specification and associated documentation
@@ -69,6 +69,7 @@ extern const char *program_name;
#define ARRAY_SIZE(ARRAY) (sizeof ARRAY / sizeof *ARRAY)
#define ROUND_UP(X, Y) (((X) + ((Y) - 1)) / (Y) * (Y))
#define ROUND_DOWN(X, Y) ((X) / (Y) * (Y))
+#define IS_POW2(X) ((X) && !((X) & ((X) - 1)))
#ifndef MIN
#define MIN(X, Y) ((X) < (Y) ? (X) : (Y))
@@ -93,15 +94,15 @@ extern "C" {
void set_program_name(const char *);
-void out_of_memory(void);
-void *xmalloc(size_t);
-void *xcalloc(size_t, size_t);
+void out_of_memory(void) NO_RETURN;
+void *xmalloc(size_t) MALLOC_LIKE;
+void *xcalloc(size_t, size_t) MALLOC_LIKE;
void *xrealloc(void *, size_t);
-void *xmemdup(const void *, size_t);
-char *xmemdup0(const char *, size_t);
-char *xstrdup(const char *);
-char *xasprintf(const char *format, ...) PRINTF_FORMAT(1, 2);
-char *xvasprintf(const char *format, va_list) PRINTF_FORMAT(1, 0);
+void *xmemdup(const void *, size_t) MALLOC_LIKE;
+char *xmemdup0(const char *, size_t) MALLOC_LIKE;
+char *xstrdup(const char *) MALLOC_LIKE;
+char *xasprintf(const char *format, ...) PRINTF_FORMAT(1, 2) MALLOC_LIKE;
+char *xvasprintf(const char *format, va_list) PRINTF_FORMAT(1, 0) MALLOC_LIKE;
void *x2nrealloc(void *p, size_t *n, size_t s);
#ifndef HAVE_STRLCPY
@@ -113,6 +114,13 @@ void ofp_fatal(int err_no, const char *format, ...)
void ofp_error(int err_no, const char *format, ...) PRINTF_FORMAT(2, 3);
void ofp_hex_dump(FILE *, const void *, size_t, uintptr_t offset, bool ascii);
+bool str_to_int(const char *, int base, int *);
+bool str_to_long(const char *, int base, long *);
+bool str_to_llong(const char *, int base, long long *);
+bool str_to_uint(const char *, int base, unsigned int *);
+bool str_to_ulong(const char *, int base, unsigned long *);
+bool str_to_ullong(const char *, int base, unsigned long long *);
+
#ifdef __cplusplus
}
#endif
diff --git a/lib/vconn-fd.c b/lib/vconn-fd.c
new file mode 100644
index 0000000..ccc6768
--- /dev/null
+++ b/lib/vconn-fd.c
@@ -0,0 +1,77 @@
+/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+ * Junior University
+ *
+ * We are making the OpenFlow specification and associated documentation
+ * (Software) available for public use and benefit with the expectation
+ * that others will use, modify and enhance the Software and contribute
+ * those enhancements back to the community. However, since we would
+ * like to make the Software available for broadest use, with as few
+ * restrictions as possible permission is hereby granted, free of
+ * charge, to any person obtaining a copy of this Software to deal in
+ * the Software under the copyrights without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * 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 AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * The name and trademarks of copyright holder(s) may NOT be used in
+ * advertising or publicity pertaining to the Software or any
+ * derivatives without specific, written prior permission.
+ */
+
+#include
+#include "vconn.h"
+#include
+#include
+#include
+#include
+#include "util.h"
+#include "vconn-provider.h"
+#include "vconn-stream.h"
+
+#include "vlog.h"
+#define THIS_MODULE VLM_vconn_fd
+
+/* File descriptor. */
+
+static int
+fd_open(const char *name, char *suffix, struct vconn **vconnp)
+{
+ int fd = atoi(suffix);
+ struct stat s;
+
+ /* Check that 'fd' is really open and is really the right type of fd. */
+ if (fstat(fd, &s) < 0) {
+ VLOG_ERR("%s: failed to stat file descriptor %d: %s",
+ name, fd, strerror(errno));
+ return errno;
+ }
+ if (!S_ISSOCK(s.st_mode)) {
+ VLOG_ERR("%s: file descriptor %d is not a socket", name, fd);
+ return errno;
+ }
+
+ return new_stream_vconn(name, fd, 0, 0, false, vconnp);
+}
+
+struct vconn_class fd_vconn_class = {
+ "fd", /* name */
+ fd_open, /* open */
+ NULL, /* close */
+ NULL, /* connect */
+ NULL, /* recv */
+ NULL, /* send */
+ NULL, /* wait */
+};
diff --git a/lib/vconn-netlink.c b/lib/vconn-netlink.c
index a0fcc8d..ef0fac3 100644
--- a/lib/vconn-netlink.c
+++ b/lib/vconn-netlink.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+/* Copyright (c) 2008, 2009 The Board of Trustees of The Leland Stanford
* Junior University
*
* We are making the OpenFlow specification and associated documentation
@@ -60,6 +60,7 @@ struct netlink_vconn
{
struct vconn vconn;
struct dpif dp;
+ int dp_idx;
};
static struct netlink_vconn *
@@ -73,8 +74,8 @@ static int
netlink_open(const char *name, char *suffix, struct vconn **vconnp)
{
struct netlink_vconn *netlink;
- int dp_idx;
int subscribe;
+ int dp_idx;
int retval;
subscribe = 1;
@@ -84,8 +85,9 @@ netlink_open(const char *name, char *suffix, struct vconn **vconnp)
}
netlink = xmalloc(sizeof *netlink);
- vconn_init(&netlink->vconn, &netlink_vconn_class, 0, 0, name);
- retval = dpif_open(dp_idx, subscribe, &netlink->dp);
+ vconn_init(&netlink->vconn, &netlink_vconn_class, 0, 0, name, true);
+ retval = dpif_open(subscribe ? dp_idx : -1, &netlink->dp);
+ netlink->dp_idx = dp_idx;
if (retval) {
free(netlink);
*vconnp = NULL;
@@ -107,14 +109,14 @@ static int
netlink_recv(struct vconn *vconn, struct ofpbuf **bufferp)
{
struct netlink_vconn *netlink = netlink_vconn_cast(vconn);
- return dpif_recv_openflow(&netlink->dp, bufferp, false);
+ return dpif_recv_openflow(&netlink->dp, netlink->dp_idx, bufferp, false);
}
static int
netlink_send(struct vconn *vconn, struct ofpbuf *buffer)
{
struct netlink_vconn *netlink = netlink_vconn_cast(vconn);
- int retval = dpif_send_openflow(&netlink->dp, buffer, false);
+ int retval = dpif_send_openflow(&netlink->dp, netlink->dp_idx, buffer);
if (!retval) {
ofpbuf_delete(buffer);
}
@@ -127,6 +129,9 @@ netlink_wait(struct vconn *vconn, enum vconn_wait_type wait)
struct netlink_vconn *netlink = netlink_vconn_cast(vconn);
short int events = 0;
switch (wait) {
+ case WAIT_CONNECT:
+ NOT_REACHED();
+
case WAIT_RECV:
events = POLLIN;
break;
@@ -138,7 +143,7 @@ netlink_wait(struct vconn *vconn, enum vconn_wait_type wait)
default:
NOT_REACHED();
}
- poll_fd_wait(nl_sock_fd(netlink->dp.sock), events);
+ nl_sock_wait(netlink->dp.sock, events);
}
struct vconn_class netlink_vconn_class = {
diff --git a/lib/vconn-provider.h b/lib/vconn-provider.h
index cf033af..caf739b 100644
--- a/lib/vconn-provider.h
+++ b/lib/vconn-provider.h
@@ -53,10 +53,11 @@ struct vconn {
int version;
uint32_t ip;
char *name;
+ bool reconnectable;
};
void vconn_init(struct vconn *, struct vconn_class *, int connect_status,
- uint32_t ip, const char *name);
+ uint32_t ip, const char *name, bool reconnectable);
static inline void vconn_assert_class(const struct vconn *vconn,
const struct vconn_class *class)
{
@@ -178,6 +179,7 @@ extern struct vconn_class tcp_vconn_class;
extern struct pvconn_class ptcp_pvconn_class;
extern struct vconn_class unix_vconn_class;
extern struct pvconn_class punix_pvconn_class;
+extern struct vconn_class fd_vconn_class;
#ifdef HAVE_OPENSSL
extern struct vconn_class ssl_vconn_class;
extern struct pvconn_class pssl_pvconn_class;
diff --git a/lib/vconn-ssl.c b/lib/vconn-ssl.c
index 383714a..7f415a0 100644
--- a/lib/vconn-ssl.c
+++ b/lib/vconn-ssl.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+/* Copyright (c) 2008, 2009 The Board of Trustees of The Leland Stanford
* Junior University
*
* We are making the OpenFlow specification and associated documentation
@@ -48,6 +48,7 @@
#include
#include
#include "dynamic-string.h"
+#include "leak-checker.h"
#include "ofpbuf.h"
#include "openflow/openflow.h"
#include "packets.h"
@@ -177,7 +178,7 @@ static void ssl_tx_poll_callback(int fd, short int revents, void *vconn_);
static DH *tmp_dh_callback(SSL *ssl, int is_export UNUSED, int keylength);
static void log_ca_cert(const char *file_name, X509 *cert);
-short int
+static short int
want_to_poll_events(int want)
{
switch (want) {
@@ -250,7 +251,7 @@ new_ssl_vconn(const char *name, int fd, enum session_type type,
/* Create and return the ssl_vconn. */
sslv = xmalloc(sizeof *sslv);
vconn_init(&sslv->vconn, &ssl_vconn_class, EAGAIN, sin->sin_addr.s_addr,
- name);
+ name, true);
sslv->state = state;
sslv->type = type;
sslv->fd = fd;
@@ -403,7 +404,7 @@ do_ca_cert_bootstrap(struct vconn *vconn)
return error;
}
- VLOG_WARN("successfully bootstrapped CA cert to %s", ca_cert_file);
+ VLOG_INFO("successfully bootstrapped CA cert to %s", ca_cert_file);
log_ca_cert(ca_cert_file, ca_cert);
bootstrap_ca_cert = false;
has_ca_cert = true;
@@ -422,7 +423,7 @@ do_ca_cert_bootstrap(struct vconn *vconn)
ERR_error_string(ERR_get_error(), NULL));
return EPROTO;
}
- VLOG_WARN("killing successful connection to retry using CA cert");
+ VLOG_INFO("killing successful connection to retry using CA cert");
return EPROTO;
}
@@ -718,6 +719,7 @@ ssl_send(struct vconn *vconn, struct ofpbuf *buffer)
ssl_clear_txbuf(sslv);
return 0;
case EAGAIN:
+ leak_checker_claim(buffer);
ssl_register_tx_waiter(vconn);
return 0;
default:
@@ -974,7 +976,7 @@ do_ssl_init(void)
}
static DH *
-tmp_dh_callback(SSL *ssl, int is_export UNUSED, int keylength)
+tmp_dh_callback(SSL *ssl UNUSED, int is_export UNUSED, int keylength)
{
struct dh {
int keylength;
@@ -1049,7 +1051,7 @@ vconn_ssl_set_certificate_file(const char *file_name)
* in '*n_certs', and returns a positive errno value.
*
* The caller is responsible for freeing '*certs'. */
-int
+static int
read_cert_file(const char *file_name, X509 ***certs, size_t *n_certs)
{
FILE *file;
@@ -1087,8 +1089,7 @@ read_cert_file(const char *file_name, X509 ***certs, size_t *n_certs)
/* Add certificate to array. */
if (*n_certs >= allocated_certs) {
- allocated_certs = 1 + 2 * allocated_certs;
- *certs = xrealloc(*certs, sizeof *certs * allocated_certs);
+ *certs = x2nrealloc(*certs, &allocated_certs, sizeof **certs);
}
(*certs)[(*n_certs)++] = certificate;
@@ -1154,7 +1155,7 @@ log_ca_cert(const char *file_name, X509 *cert)
}
}
subject = X509_NAME_oneline(X509_get_subject_name(cert), NULL, 0);
- VLOG_WARN("Trusting CA cert from %s (%s) (fingerprint %s)", file_name,
+ VLOG_INFO("Trusting CA cert from %s (%s) (fingerprint %s)", file_name,
subject ? subject : "", ds_cstr(&fp));
free(subject);
ds_destroy(&fp);
diff --git a/lib/vconn-stream.c b/lib/vconn-stream.c
index 0b85261..038976e 100644
--- a/lib/vconn-stream.c
+++ b/lib/vconn-stream.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+/* Copyright (c) 2008, 2009 The Board of Trustees of The Leland Stanford
* Junior University
*
* We are making the OpenFlow specification and associated documentation
@@ -40,6 +40,7 @@
#include
#include
#include
+#include "leak-checker.h"
#include "ofpbuf.h"
#include "openflow/openflow.h"
#include "poll-loop.h"
@@ -70,12 +71,13 @@ static void stream_clear_txbuf(struct stream_vconn *);
int
new_stream_vconn(const char *name, int fd, int connect_status,
- uint32_t ip, struct vconn **vconnp)
+ uint32_t ip, bool reconnectable, struct vconn **vconnp)
{
struct stream_vconn *s;
s = xmalloc(sizeof *s);
- vconn_init(&s->vconn, &stream_vconn_class, connect_status, ip, name);
+ vconn_init(&s->vconn, &stream_vconn_class, connect_status, ip, name,
+ reconnectable);
s->fd = fd;
s->txbuf = NULL;
s->tx_waiter = NULL;
@@ -212,6 +214,7 @@ stream_send(struct vconn *vconn, struct ofpbuf *buffer)
ofpbuf_delete(buffer);
return 0;
} else if (retval >= 0 || errno == EAGAIN) {
+ leak_checker_claim(buffer);
s->txbuf = buffer;
if (retval > 0) {
ofpbuf_pull(buffer, retval);
diff --git a/lib/vconn-stream.h b/lib/vconn-stream.h
index a9b1e7b..7238a1f 100644
--- a/lib/vconn-stream.h
+++ b/lib/vconn-stream.h
@@ -34,6 +34,7 @@
#ifndef VCONN_STREAM_H
#define VCONN_STREAM_H 1
+#include
#include
#include
@@ -42,7 +43,7 @@ struct pvconn;
struct sockaddr;
int new_stream_vconn(const char *name, int fd, int connect_status,
- uint32_t ip, struct vconn **vconnp);
+ uint32_t ip, bool reconnectable, struct vconn **vconnp);
int new_pstream_pvconn(const char *name, int fd,
int (*accept_cb)(int fd, const struct sockaddr *,
size_t sa_len, struct vconn **),
diff --git a/lib/vconn-tcp.c b/lib/vconn-tcp.c
index 79d8332..ed6072a 100644
--- a/lib/vconn-tcp.c
+++ b/lib/vconn-tcp.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+/* Copyright (c) 2008, 2009 The Board of Trustees of The Leland Stanford
* Junior University
*
* We are making the OpenFlow specification and associated documentation
@@ -68,7 +68,7 @@ new_tcp_vconn(const char *name, int fd, int connect_status,
}
return new_stream_vconn(name, fd, connect_status, sin->sin_addr.s_addr,
- vconnp);
+ true, vconnp);
}
static int
@@ -196,5 +196,8 @@ ptcp_accept(int fd, const struct sockaddr *sa, size_t sa_len,
struct pvconn_class ptcp_pvconn_class = {
"ptcp",
ptcp_open,
+ NULL,
+ NULL,
+ NULL
};
diff --git a/lib/vconn-unix.c b/lib/vconn-unix.c
index b1a7d8a..4fdb6ca 100644
--- a/lib/vconn-unix.c
+++ b/lib/vconn-unix.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+/* Copyright (c) 2008, 2009 The Board of Trustees of The Leland Stanford
* Junior University
*
* We are making the OpenFlow specification and associated documentation
@@ -77,7 +77,7 @@ unix_open(const char *name, char *suffix, struct vconn **vconnp)
}
return new_stream_vconn(name, fd, check_connection_completion(fd),
- 0, vconnp);
+ 0, true, vconnp);
}
struct vconn_class unix_vconn_class = {
@@ -96,7 +96,7 @@ static int punix_accept(int fd, const struct sockaddr *sa, size_t sa_len,
struct vconn **vconnp);
static int
-punix_open(const char *name, char *suffix, struct pvconn **pvconnp)
+punix_open(const char *name UNUSED, char *suffix, struct pvconn **pvconnp)
{
int fd;
@@ -122,11 +122,14 @@ punix_accept(int fd, const struct sockaddr *sa, size_t sa_len,
} else {
strcpy(name, "unix");
}
- return new_stream_vconn(name, fd, 0, 0, vconnp);
+ return new_stream_vconn(name, fd, 0, 0, true, vconnp);
}
struct pvconn_class punix_pvconn_class = {
"punix",
punix_open,
+ NULL,
+ NULL,
+ NULL
};
diff --git a/lib/vconn.c b/lib/vconn.c
index e630258..c9269b5 100644
--- a/lib/vconn.c
+++ b/lib/vconn.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+/* Copyright (c) 2008, 2009 The Board of Trustees of The Leland Stanford
* Junior University
*
* We are making the OpenFlow specification and associated documentation
@@ -74,6 +74,7 @@ static struct vconn_class *vconn_classes[] = {
#ifdef HAVE_OPENSSL
&ssl_vconn_class,
#endif
+ &fd_vconn_class,
};
static struct pvconn_class *pvconn_classes[] = {
@@ -152,6 +153,7 @@ vconn_usage(bool active, bool passive, bool bootstrap UNUSED)
"SSL PORT (default: %d) on remote HOST\n", OFP_SSL_PORT);
#endif
printf(" unix:FILE Unix domain socket named FILE\n");
+ printf(" fd:N File descriptor N\n");
}
if (passive) {
@@ -272,6 +274,19 @@ vconn_get_ip(const struct vconn *vconn)
return vconn->ip;
}
+/* Returns true if, when 'vconn' is closed, it is possible to try to reconnect
+ * to it using the name that was originally used. This is ordinarily the case.
+ *
+ * Returns false if reconnecting under the same name will never work in the way
+ * that you would expect. This is the case if 'vconn' represents a "fd:N" type
+ * vconn; one can never connect to such a vconn more than once, because closing
+ * it closes the file descriptor. */
+bool
+vconn_is_reconnectable(const struct vconn *vconn)
+{
+ return vconn->reconnectable;
+}
+
static void
vcs_connecting(struct vconn *vconn)
{
@@ -449,6 +464,7 @@ do_recv(struct vconn *vconn, struct ofpbuf **msgp)
{
int retval;
+again:
retval = (vconn->class->recv)(vconn, msgp);
if (!retval) {
struct ofp_header *oh;
@@ -468,6 +484,20 @@ do_recv(struct vconn *vconn, struct ofpbuf **msgp)
&& oh->type != OFPT_VENDOR)
{
if (vconn->version < 0) {
+ if (oh->type == OFPT_PACKET_IN
+ || oh->type == OFPT_FLOW_EXPIRED
+ || oh->type == OFPT_PORT_STATUS) {
+ /* The kernel datapath is stateless and doesn't really
+ * support version negotiation, so it can end up sending
+ * these asynchronous message before version negotiation
+ * is complete. Just ignore them.
+ *
+ * (After we move OFPT_PORT_STATUS messages from the kernel
+ * into secchan, we won't get those here, since secchan
+ * does proper version negotiation.) */
+ ofpbuf_delete(*msgp);
+ goto again;
+ }
VLOG_ERR_RL(&rl, "%s: received OpenFlow message type %"PRIu8" "
"before version negotiation complete",
vconn->name, oh->type);
@@ -728,35 +758,94 @@ pvconn_wait(struct pvconn *pvconn)
(pvconn->class->wait)(pvconn);
}
-/* Allocates and returns the first byte of a buffer 'openflow_len' bytes long,
- * containing an OpenFlow header with the given 'type' and a random transaction
- * id. Stores the new buffer in '*bufferp'. The caller must free the buffer
- * when it is no longer needed. */
-void *
-make_openflow(size_t openflow_len, uint8_t type, struct ofpbuf **bufferp)
+/* XXX we should really use consecutive xids to avoid probabilistic
+ * failures. */
+static inline uint32_t
+alloc_xid(void)
{
- return make_openflow_xid(openflow_len, type, random_uint32(), bufferp);
+ return random_uint32();
}
-/* Allocates and returns the first byte of a buffer 'openflow_len' bytes long,
- * containing an OpenFlow header with the given 'type' and transaction id
- * 'xid'. Stores the new buffer in '*bufferp'. The caller must free the
- * buffer when it is no longer needed. */
+/* Allocates and stores in '*bufferp' a new ofpbuf with a size of
+ * 'openflow_len', starting with an OpenFlow header with the given 'type' and
+ * an arbitrary transaction id. Allocated bytes beyond the header, if any, are
+ * zeroed.
+ *
+ * The caller is responsible for freeing '*bufferp' when it is no longer
+ * needed.
+ *
+ * The OpenFlow header length is initially set to 'openflow_len'; if the
+ * message is later extended, the length should be updated with
+ * update_openflow_length() before sending.
+ *
+ * Returns the header. */
+void *
+make_openflow(size_t openflow_len, uint8_t type, struct ofpbuf **bufferp)
+{
+ *bufferp = ofpbuf_new(openflow_len);
+ return put_openflow_xid(openflow_len, type, alloc_xid(), *bufferp);
+}
+
+/* Allocates and stores in '*bufferp' a new ofpbuf with a size of
+ * 'openflow_len', starting with an OpenFlow header with the given 'type' and
+ * transaction id 'xid'. Allocated bytes beyond the header, if any, are
+ * zeroed.
+ *
+ * The caller is responsible for freeing '*bufferp' when it is no longer
+ * needed.
+ *
+ * The OpenFlow header length is initially set to 'openflow_len'; if the
+ * message is later extended, the length should be updated with
+ * update_openflow_length() before sending.
+ *
+ * Returns the header. */
void *
make_openflow_xid(size_t openflow_len, uint8_t type, uint32_t xid,
struct ofpbuf **bufferp)
{
- struct ofpbuf *buffer;
+ *bufferp = ofpbuf_new(openflow_len);
+ return put_openflow_xid(openflow_len, type, xid, *bufferp);
+}
+
+/* Appends 'openflow_len' bytes to 'buffer', starting with an OpenFlow header
+ * with the given 'type' and an arbitrary transaction id. Allocated bytes
+ * beyond the header, if any, are zeroed.
+ *
+ * The OpenFlow header length is initially set to 'openflow_len'; if the
+ * message is later extended, the length should be updated with
+ * update_openflow_length() before sending.
+ *
+ * Returns the header. */
+void *
+put_openflow(size_t openflow_len, uint8_t type, struct ofpbuf *buffer)
+{
+ return put_openflow_xid(openflow_len, type, alloc_xid(), buffer);
+}
+
+/* Appends 'openflow_len' bytes to 'buffer', starting with an OpenFlow header
+ * with the given 'type' and an transaction id 'xid'. Allocated bytes beyond
+ * the header, if any, are zeroed.
+ *
+ * The OpenFlow header length is initially set to 'openflow_len'; if the
+ * message is later extended, the length should be updated with
+ * update_openflow_length() before sending.
+ *
+ * Returns the header. */
+void *
+put_openflow_xid(size_t openflow_len, uint8_t type, uint32_t xid,
+ struct ofpbuf *buffer)
+{
struct ofp_header *oh;
assert(openflow_len >= sizeof *oh);
assert(openflow_len <= UINT16_MAX);
- buffer = *bufferp = ofpbuf_new(openflow_len);
- oh = ofpbuf_put_zeros(buffer, openflow_len);
+
+ oh = ofpbuf_put_uninit(buffer, openflow_len);
oh->version = OFP_VERSION;
oh->type = type;
oh->length = htons(openflow_len);
oh->xid = xid;
+ memset(oh + 1, 0, openflow_len - sizeof *oh);
return oh;
}
@@ -770,13 +859,12 @@ update_openflow_length(struct ofpbuf *buffer)
}
struct ofpbuf *
-make_add_flow(const struct flow *flow, uint32_t buffer_id,
- uint16_t idle_timeout, size_t actions_len)
+make_flow_mod(uint16_t command, const struct flow *flow, size_t actions_len)
{
struct ofp_flow_mod *ofm;
size_t size = sizeof *ofm + actions_len;
struct ofpbuf *out = ofpbuf_new(size);
- ofm = ofpbuf_put_zeros(out, size);
+ ofm = ofpbuf_put_zeros(out, sizeof *ofm);
ofm->header.version = OFP_VERSION;
ofm->header.type = OFPT_FLOW_MOD;
ofm->header.length = htons(size);
@@ -791,23 +879,40 @@ make_add_flow(const struct flow *flow, uint32_t buffer_id,
ofm->match.nw_proto = flow->nw_proto;
ofm->match.tp_src = flow->tp_src;
ofm->match.tp_dst = flow->tp_dst;
- ofm->command = htons(OFPFC_ADD);
+ ofm->command = htons(command);
+ return out;
+}
+
+struct ofpbuf *
+make_add_flow(const struct flow *flow, uint32_t buffer_id,
+ uint16_t idle_timeout, size_t actions_len)
+{
+ struct ofpbuf *out = make_flow_mod(OFPFC_ADD, flow, actions_len);
+ struct ofp_flow_mod *ofm = out->data;
ofm->idle_timeout = htons(idle_timeout);
ofm->hard_timeout = htons(OFP_FLOW_PERMANENT);
ofm->buffer_id = htonl(buffer_id);
return out;
}
+struct ofpbuf *
+make_del_flow(const struct flow *flow)
+{
+ struct ofpbuf *out = make_flow_mod(OFPFC_DELETE_STRICT, flow, 0);
+ struct ofp_flow_mod *ofm = out->data;
+ ofm->out_port = htons(OFPP_NONE);
+ return out;
+}
+
struct ofpbuf *
make_add_simple_flow(const struct flow *flow,
uint32_t buffer_id, uint16_t out_port,
uint16_t idle_timeout)
{
struct ofp_action_output *oao;
- struct ofpbuf *buffer = make_add_flow(flow, buffer_id, idle_timeout,
- sizeof *oao);
- struct ofp_flow_mod *ofm = buffer->data;
- oao = (struct ofp_action_output *)&ofm->actions[0];
+ struct ofpbuf *buffer = make_add_flow(flow, buffer_id, idle_timeout,
+ sizeof *oao);
+ oao = ofpbuf_put_zeros(buffer, sizeof *oao);
oao->type = htons(OFPAT_OUTPUT);
oao->len = htons(sizeof *oao);
oao->port = htons(out_port);
@@ -891,9 +996,138 @@ make_echo_reply(const struct ofp_header *rq)
return out;
}
+static int
+check_message_type(uint8_t got_type, uint8_t want_type)
+{
+ if (got_type != want_type) {
+ char *want_type_name = ofp_message_type_to_string(want_type);
+ char *got_type_name = ofp_message_type_to_string(got_type);
+ VLOG_WARN("received bad message type %s (expected %s)",
+ got_type_name, want_type_name);
+ free(want_type_name);
+ free(got_type_name);
+ return false;
+ }
+ return true;
+}
+
+/* Checks that 'msg' has type 'type' and that it is exactly 'size' bytes long.
+ * Returns 0 if the checks pass, otherwise EINVAL. */
+int
+check_ofp_message(const struct ofp_header *msg, uint8_t type, size_t size)
+{
+ size_t got_size;
+
+ if (!check_message_type(msg->type, type)) {
+ return EINVAL;
+ }
+
+ got_size = ntohs(msg->length);
+ if (got_size != size) {
+ char *type_name = ofp_message_type_to_string(type);
+ VLOG_WARN("received %s message of length %"PRIu16" (expected %zu)",
+ type_name, got_size, size);
+ free(type_name);
+ return EINVAL;
+ }
+
+ return 0;
+}
+
+/* Checks that 'msg' has type 'type' and that 'msg' is 'size' plus a
+ * nonnegative integer multiple of 'array_elt_size' bytes long. Returns 0 if
+ * the checks pass, otherwise EINVAL.
+ *
+ * If 'n_array_elts' is nonnull, then '*n_array_elts' is set to the number of
+ * 'array_elt_size' blocks in 'msg' past the first 'min_size' bytes, when
+ * successful. */
+int
+check_ofp_message_array(const struct ofp_header *msg, uint8_t type,
+ size_t min_size, size_t array_elt_size,
+ size_t *n_array_elts)
+{
+ size_t got_size;
+
+ assert(array_elt_size);
+
+ if (!check_message_type(msg->type, type)) {
+ return EINVAL;
+ }
+
+ got_size = ntohs(msg->length);
+ if (got_size < min_size) {
+ char *type_name = ofp_message_type_to_string(type);
+ VLOG_WARN("received %s message of length %"PRIu16" "
+ "(expected at least %zu)",
+ type_name, got_size, min_size);
+ free(type_name);
+ return EINVAL;
+ }
+ if ((got_size - min_size) % array_elt_size) {
+ char *type_name = ofp_message_type_to_string(type);
+ VLOG_WARN("received %s message of bad length %"PRIu16": the "
+ "excess over %zu (%zu) is not evenly divisible by %zu "
+ "(remainder is %zu)",
+ type_name, got_size, min_size, got_size - min_size,
+ array_elt_size, (got_size - min_size) % array_elt_size);
+ free(type_name);
+ return EINVAL;
+ }
+ if (n_array_elts) {
+ *n_array_elts = (got_size - min_size) / array_elt_size;
+ }
+ return 0;
+}
+
+const struct ofp_flow_stats *
+flow_stats_first(struct flow_stats_iterator *iter,
+ const struct ofp_stats_reply *osr)
+{
+ iter->pos = osr->body;
+ iter->end = osr->body + (ntohs(osr->header.length)
+ - offsetof(struct ofp_stats_reply, body));
+ return flow_stats_next(iter);
+}
+
+const struct ofp_flow_stats *
+flow_stats_next(struct flow_stats_iterator *iter)
+{
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+ ptrdiff_t bytes_left = iter->end - iter->pos;
+ const struct ofp_flow_stats *fs;
+ size_t length;
+
+ if (bytes_left < sizeof *fs) {
+ if (bytes_left != 0) {
+ VLOG_WARN_RL(&rl, "%td leftover bytes in flow stats reply",
+ bytes_left);
+ }
+ return NULL;
+ }
+
+ fs = (const void *) iter->pos;
+ length = ntohs(fs->length);
+ if (length < sizeof *fs) {
+ VLOG_WARN_RL(&rl, "flow stats length %zu is shorter than min %zu",
+ length, sizeof *fs);
+ return NULL;
+ } else if (length > bytes_left) {
+ VLOG_WARN_RL(&rl, "flow stats length %zu but only %td bytes left",
+ length, bytes_left);
+ return NULL;
+ } else if ((length - sizeof *fs) % sizeof fs->actions[0]) {
+ VLOG_WARN_RL(&rl, "flow stats length %zu has %zu bytes "
+ "left over in final action", length,
+ (length - sizeof *fs) % sizeof fs->actions[0]);
+ return NULL;
+ }
+ iter->pos += length;
+ return fs;
+}
+
void
vconn_init(struct vconn *vconn, struct vconn_class *class, int connect_status,
- uint32_t ip, const char *name)
+ uint32_t ip, const char *name, bool reconnectable)
{
vconn->class = class;
vconn->state = (connect_status == EAGAIN ? VCS_CONNECTING
@@ -904,6 +1138,7 @@ vconn_init(struct vconn *vconn, struct vconn_class *class, int connect_status,
vconn->min_version = -1;
vconn->ip = ip;
vconn->name = xstrdup(name);
+ vconn->reconnectable = reconnectable;
}
void
diff --git a/lib/vconn.h b/lib/vconn.h
index 6057c8a..fd38475 100644
--- a/lib/vconn.h
+++ b/lib/vconn.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+/* Copyright (c) 2008, 2009 The Board of Trustees of The Leland Stanford
* Junior University
*
* We are making the OpenFlow specification and associated documentation
@@ -41,6 +41,7 @@
struct ofpbuf;
struct flow;
struct ofp_header;
+struct ofp_stats_reply;
struct pvconn;
struct vconn;
@@ -51,6 +52,7 @@ int vconn_open(const char *name, int min_version, struct vconn **);
void vconn_close(struct vconn *);
const char *vconn_get_name(const struct vconn *);
uint32_t vconn_get_ip(const struct vconn *);
+bool vconn_is_reconnectable(const struct vconn *);
int vconn_connect(struct vconn *);
int vconn_recv(struct vconn *, struct ofpbuf **);
int vconn_send(struct vconn *, struct ofpbuf *);
@@ -81,9 +83,15 @@ void pvconn_wait(struct pvconn *);
void *make_openflow(size_t openflow_len, uint8_t type, struct ofpbuf **);
void *make_openflow_xid(size_t openflow_len, uint8_t type,
uint32_t xid, struct ofpbuf **);
+void *put_openflow(size_t openflow_len, uint8_t type, struct ofpbuf *);
+void *put_openflow_xid(size_t openflow_len, uint8_t type, uint32_t xid,
+ struct ofpbuf *);
void update_openflow_length(struct ofpbuf *);
+struct ofpbuf *make_flow_mod(uint16_t command, const struct flow *,
+ size_t actions_len);
struct ofpbuf *make_add_flow(const struct flow *, uint32_t buffer_id,
uint16_t max_idle, size_t actions_len);
+struct ofpbuf *make_del_flow(const struct flow *);
struct ofpbuf *make_add_simple_flow(const struct flow *,
uint32_t buffer_id, uint16_t out_port,
uint16_t max_idle);
@@ -93,5 +101,16 @@ struct ofpbuf *make_unbuffered_packet_out(const struct ofpbuf *packet,
uint16_t in_port, uint16_t out_port);
struct ofpbuf *make_echo_request(void);
struct ofpbuf *make_echo_reply(const struct ofp_header *rq);
+int check_ofp_message(const struct ofp_header *, uint8_t type, size_t size);
+int check_ofp_message_array(const struct ofp_header *, uint8_t type,
+ size_t size, size_t array_elt_size,
+ size_t *n_array_elts);
+
+struct flow_stats_iterator {
+ const uint8_t *pos, *end;
+};
+const struct ofp_flow_stats *flow_stats_first(struct flow_stats_iterator *,
+ const struct ofp_stats_reply *);
+const struct ofp_flow_stats *flow_stats_next(struct flow_stats_iterator *);
#endif /* vconn.h */
diff --git a/lib/vlog-modules.def b/lib/vlog-modules.def
index 472a405..7ff4004 100644
--- a/lib/vlog-modules.def
+++ b/lib/vlog-modules.def
@@ -1,5 +1,9 @@
/* Modules that can emit log messages. */
+VLOG_MODULE(backtrace)
+VLOG_MODULE(brcompat)
+VLOG_MODULE(bridge)
VLOG_MODULE(chain)
+VLOG_MODULE(cfg)
VLOG_MODULE(controller)
VLOG_MODULE(ctlpath)
VLOG_MODULE(daemon)
@@ -15,11 +19,13 @@ VLOG_MODULE(fault)
VLOG_MODULE(flow)
VLOG_MODULE(flow_end)
VLOG_MODULE(in_band)
+VLOG_MODULE(leak_checker)
VLOG_MODULE(learning_switch)
VLOG_MODULE(mac_learning)
VLOG_MODULE(netdev)
VLOG_MODULE(netlink)
VLOG_MODULE(ofp_discover)
+VLOG_MODULE(pcap)
VLOG_MODULE(poll_loop)
VLOG_MODULE(port_watcher)
VLOG_MODULE(process)
@@ -28,11 +34,13 @@ VLOG_MODULE(rconn)
VLOG_MODULE(snat)
VLOG_MODULE(stp)
VLOG_MODULE(stp_secchan)
+VLOG_MODULE(stats)
VLOG_MODULE(status)
VLOG_MODULE(svec)
VLOG_MODULE(switch)
VLOG_MODULE(terminal)
VLOG_MODULE(socket_util)
+VLOG_MODULE(vconn_fd)
VLOG_MODULE(vconn_netlink)
VLOG_MODULE(vconn_tcp)
VLOG_MODULE(vconn_ssl)
@@ -41,6 +49,7 @@ VLOG_MODULE(vconn_unix)
VLOG_MODULE(vconn)
VLOG_MODULE(vlog)
VLOG_MODULE(vlog_socket)
+VLOG_MODULE(vswitchd)
#ifdef HAVE_EXT
#include "ext/vlogext-modules.def"
diff --git a/lib/vlog-socket.c b/lib/vlog-socket.c
index 0dc836f..11bf225 100644
--- a/lib/vlog-socket.c
+++ b/lib/vlog-socket.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+/* Copyright (c) 2008, 2009 The Board of Trustees of The Leland Stanford
* Junior University
*
* We are making the OpenFlow specification and associated documentation
@@ -238,7 +238,7 @@ recv_with_creds(const struct vlog_server *server,
/* Processes incoming requests for 'server'. */
static void
-poll_server(int fd UNUSED, short int events, void *server_)
+poll_server(int fd UNUSED, short int events UNUSED, void *server_)
{
struct vlog_server *server = server_;
for (;;) {
diff --git a/lib/vlog.c b/lib/vlog.c
index ddfab76..e23d017 100644
--- a/lib/vlog.c
+++ b/lib/vlog.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+/* Copyright (c) 2008, 2009 The Board of Trustees of The Leland Stanford
* Junior University
*
* We are making the OpenFlow specification and associated documentation
@@ -98,6 +98,11 @@ static long long int boot_time;
static char *log_file_name;
static FILE *log_file;
+static void format_log_message(enum vlog_module, enum vlog_level,
+ enum vlog_facility, unsigned int msg_num,
+ const char *message, va_list, struct ds *)
+ PRINTF_FORMAT(5, 0);
+
/* Searches the 'n_names' in 'names'. Returns the index of a match for
* 'target', or 'n_names' if no name matches. */
static size_t
@@ -272,7 +277,7 @@ vlog_set_log_file(const char *file_name)
/* Close old log file. */
if (log_file) {
- VLOG_WARN("closing log file");
+ VLOG_INFO("closing log file");
fclose(log_file);
log_file = NULL;
}
@@ -299,7 +304,7 @@ vlog_set_log_file(const char *file_name)
log_file_name, strerror(errno));
error = errno;
} else {
- VLOG_WARN("opened log file %s", log_file_name);
+ VLOG_INFO("opened log file %s", log_file_name);
error = 0;
}
@@ -402,7 +407,7 @@ vlog_init(void)
time_t now;
openlog(program_name, LOG_NDELAY, LOG_DAEMON);
- vlog_set_levels(VLM_ANY_MODULE, VLF_ANY_FACILITY, VLL_WARN);
+ vlog_set_levels(VLM_ANY_MODULE, VLF_ANY_FACILITY, VLL_INFO);
boot_time = time_msec();
now = time_now();
diff --git a/lib/vlog.h b/lib/vlog.h
index a01309b..8785851 100644
--- a/lib/vlog.h
+++ b/lib/vlog.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+/* Copyright (c) 2008, 2009 The Board of Trustees of The Leland Stanford
* Junior University
*
* We are making the OpenFlow specification and associated documentation
@@ -45,6 +45,7 @@
VLOG_LEVEL(EMER, LOG_ALERT) \
VLOG_LEVEL(ERR, LOG_ERR) \
VLOG_LEVEL(WARN, LOG_WARNING) \
+ VLOG_LEVEL(INFO, LOG_NOTICE) \
VLOG_LEVEL(DBG, LOG_DEBUG)
enum vlog_level {
#define VLOG_LEVEL(NAME, SYSLOG_LEVEL) VLL_##NAME,
@@ -110,6 +111,7 @@ struct vlog_rate_limit {
* VLOG_MSG_TOKENS), /* burst */ \
0, /* tokens */ \
0, /* last_fill */ \
+ 0, /* first_dropped */ \
0, /* n_dropped */ \
}
@@ -147,6 +149,7 @@ void vlog_rate_limit(enum vlog_module, enum vlog_level,
#define VLOG_EMER(...) VLOG(VLL_EMER, __VA_ARGS__)
#define VLOG_ERR(...) VLOG(VLL_ERR, __VA_ARGS__)
#define VLOG_WARN(...) VLOG(VLL_WARN, __VA_ARGS__)
+#define VLOG_INFO(...) VLOG(VLL_INFO, __VA_ARGS__)
#define VLOG_DBG(...) VLOG(VLL_DBG, __VA_ARGS__)
/* More convenience macros, for testing whether a given level is enabled in
@@ -155,6 +158,7 @@ void vlog_rate_limit(enum vlog_module, enum vlog_level,
#define VLOG_IS_EMER_ENABLED() true
#define VLOG_IS_ERR_ENABLED() vlog_is_enabled(THIS_MODULE, VLL_EMER)
#define VLOG_IS_WARN_ENABLED() vlog_is_enabled(THIS_MODULE, VLL_WARN)
+#define VLOG_IS_INFO_ENABLED() vlog_is_enabled(THIS_MODULE, VLL_INFO)
#define VLOG_IS_DBG_ENABLED() vlog_is_enabled(THIS_MODULE, VLL_DBG)
/* Convenience macros. To use these, define THIS_MODULE as a macro that
@@ -165,6 +169,7 @@ void vlog_rate_limit(enum vlog_module, enum vlog_level,
*/
#define VLOG_ERR_RL(RL, ...) VLOG_RL(RL, VLL_ERR, __VA_ARGS__)
#define VLOG_WARN_RL(RL, ...) VLOG_RL(RL, VLL_WARN, __VA_ARGS__)
+#define VLOG_INFO_RL(RL, ...) VLOG_RL(RL, VLL_INFO, __VA_ARGS__)
#define VLOG_DBG_RL(RL, ...) VLOG_RL(RL, VLL_DBG, __VA_ARGS__)
/* Command line processing. */
diff --git a/lib/vlog.man b/lib/vlog.man
index 6bc7d29..0e439f8 100644
--- a/lib/vlog.man
+++ b/lib/vlog.man
@@ -21,7 +21,8 @@ will not take place unless \fB--log-file\fR is also specified (see
below).
.IP \(bu
-\fIlevel\fR must be one of \fBemer\fR, \fBerr\fR, \fBwarn\fR, or
+\fIlevel\fR must be one of \fBemer\fR, \fBerr\fR, \fBwarn\fR,
+\fBinfo\fR, or
\fBdbg\fR, designating the minimum severity of a message for it to be
logged. If it is omitted, \fIlevel\fR defaults to \fBdbg\fR.
.RE
@@ -40,6 +41,4 @@ Sets the log pattern for \fIfacility\fR to \fIpattern\fR. Refer to
\fB--log-file\fR[\fB=\fIfile\fR]
Enables logging to a file. If \fIfile\fR is specified, then it is
used as the exact name for the log file. The default log file name
-used if \fIfile\fR is omitted is @LOGDIR@/\fIprogram\fR.log, where
-\fIprogram\fR is the name of the program as invoked
-(e.g. \fBsecchan\fR).
+used if \fIfile\fR is omitted is \fB@LOGDIR@/\*(PN.log\fR.
diff --git a/m4/libopenflow.m4 b/m4/libopenflow.m4
index c1787f4..294932b 100644
--- a/m4/libopenflow.m4
+++ b/m4/libopenflow.m4
@@ -150,6 +150,26 @@ AC_DEFUN([OFP_CHECK_LOGDIR],
[LOGDIR='${localstatedir}/log/${PACKAGE}'])
AC_SUBST([LOGDIR])])
+dnl Checks for __malloc_hook, etc., supported by glibc.
+AC_DEFUN([OFP_CHECK_MALLOC_HOOKS],
+ [AC_CACHE_CHECK(
+ [whether libc supports hooks for malloc and related functions],
+ [ofp_cv_malloc_hooks],
+ [AC_COMPILE_IFELSE(
+ [AC_LANG_PROGRAM(
+ [#include
+ ],
+ [(void) __malloc_hook;
+ (void) __realloc_hook;
+ (void) __free_hook;])],
+ [ofp_cv_malloc_hooks=yes],
+ [ofp_cv_malloc_hooks=no])])
+ if test $ofp_cv_malloc_hooks = yes; then
+ AC_DEFINE([HAVE_MALLOC_HOOKS], [1],
+ [Define to 1 if you have __malloc_hook, __realloc_hook, and
+ __free_hook in .])
+ fi])
+
dnl Runs the checks required to include the headers in include/ and
dnl link against lib/libopenflow.a.
AC_DEFUN([OFP_CHECK_LIBOPENFLOW],
@@ -163,5 +183,6 @@ AC_DEFUN([OFP_CHECK_LIBOPENFLOW],
AC_REQUIRE([OFP_CHECK_PKIDIR])
AC_REQUIRE([OFP_CHECK_RUNDIR])
AC_REQUIRE([OFP_CHECK_LOGDIR])
+ AC_REQUIRE([OFP_CHECK_MALLOC_HOOKS])
AC_CHECK_FUNCS([strlcpy])])
diff --git a/regress/lib/Perl5/OF/OFUtil.pm b/regress/lib/Perl5/OF/OFUtil.pm
index 2fb10e5..ee1c754 100644
--- a/regress/lib/Perl5/OF/OFUtil.pm
+++ b/regress/lib/Perl5/OF/OFUtil.pm
@@ -71,7 +71,7 @@ use Time::HiRes qw(sleep gettimeofday tv_interval usleep);
);
my $nf2_kernel_module_path = 'datapath/linux-2.6';
-my $nf2_kernel_module_name_no_ext = 'hwtable_nf2_mod';
+my $nf2_kernel_module_name_no_ext = 'ofdatapath_netfpga';
my $nf2_kernel_module_name = $nf2_kernel_module_name_no_ext . '.ko';
my $openflow_dir = $ENV{OF_ROOT};
@@ -165,7 +165,7 @@ sub setup_kmod {
setup_pcap_interfaces();
# verify kernel module not loaded
- my $of_kmod_loaded = `lsmod | grep openflow`;
+ my $of_kmod_loaded = `lsmod | grep ofdatapath`;
if ( $of_kmod_loaded ne "" ) {
print "openflow kernel module already loaded... please fix!\n";
exit 1;
@@ -181,7 +181,7 @@ sub setup_kmod {
my $openflow_dir = $ENV{'OF_ROOT'};
# create openflow switch on four ports
- `insmod ${openflow_dir}/datapath/linux-2.6/openflow_mod.ko`;
+ `insmod ${openflow_dir}/datapath/linux-2.6/ofdatapath.ko`;
`${openflow_dir}/utilities/dpctl adddp nl:0`;
@@ -190,7 +190,7 @@ sub setup_kmod {
`${openflow_dir}/utilities/dpctl addif nl:0 $iface`;
}
- system("${openflow_dir}/secchan/secchan --inactivity-probe=999999 nl:0 tcp:127.0.0.1 &");
+ system("${openflow_dir}/secchan/ofprotocol --inactivity-probe=999999 nl:0 tcp:127.0.0.1 &");
}
sub setup_NF2 {
@@ -198,7 +198,7 @@ sub setup_NF2 {
setup_pcap_interfaces();
# verify kernel module not loaded
- my $of_kmod_loaded = `lsmod | grep openflow`;
+ my $of_kmod_loaded = `lsmod | grep ofdatapath`;
if ( $of_kmod_loaded ne "" ) {
print "$of_kmod_loaded\n";
print "openflow kernel module already loaded... please fix!\n";
@@ -224,7 +224,7 @@ sub setup_NF2 {
`regwrite 0x04c01ec 0xffff`;
# create openflow switch on four ports
- `insmod ${openflow_dir}/datapath/linux-2.6/openflow_mod.ko`;
+ `insmod ${openflow_dir}/datapath/linux-2.6/ofdatapath.ko`;
# add the hardware kernel module
`insmod ${openflow_dir}/${nf2_kernel_module_path}/${nf2_kernel_module_name}`;
@@ -239,7 +239,7 @@ sub setup_NF2 {
print "added interface\n";
}
- system("${openflow_dir}/secchan/secchan --inactivity-probe=999999 nl:0 tcp:127.0.0.1 &");
+ system("${openflow_dir}/secchan/ofprotocol --inactivity-probe=999999 nl:0 tcp:127.0.0.1 &");
}
sub setup_user {
@@ -252,11 +252,11 @@ sub setup_user {
$if_string .= nftest_get_iface("eth$i") . ',';
}
$if_string .= nftest_get_iface("eth8");
- print "about to create udatapath punix:/var/run/test -i $if_string \& \
+ print "about to create ofdatapath punix:/var/run/test -i $if_string \& \
n";
- system("${openflow_dir}/udatapath/udatapath punix:/var/run/test -i $if_string \&");
- print "about to create secchan \& \n";
- system("${openflow_dir}/secchan/secchan --inactivity-probe=999999 unix:/var/run/test tcp:127.0.0.1 &");
+ system("${openflow_dir}/udatapath/ofdatapath punix:/var/run/test -i $if_string \&");
+ print "about to create ofprotocol \& \n";
+ system("${openflow_dir}/secchan/ofprotocol --inactivity-probe=999999 unix:/var/run/test tcp:127.0.0.1 &");
}
sub teardown_kmod {
@@ -265,10 +265,10 @@ sub teardown_kmod {
my $who = `whoami`;
if ( trim($who) ne 'root' ) { die "must be root\n"; }
- `killall secchan`;
+ `killall ofprotocol`;
# check if openflow kernel module loaded
- my $of_kmod_loaded = `lsmod | grep openflow_mod`;
+ my $of_kmod_loaded = `lsmod | grep ofdatapath`;
if ( $of_kmod_loaded eq "" ) { exit 0; }
print "tearing down interfaces and datapaths\n";
@@ -281,12 +281,12 @@ sub teardown_kmod {
`${openflow_dir}/utilities/dpctl deldp nl:0`;
- my $of_kmod_removed = `rmmod openflow_mod`;
+ my $of_kmod_removed = `rmmod ofdatapath`;
if ( $of_kmod_removed ne "" ) {
die "failed to remove kernel module... please fix!\n";
}
- $of_kmod_loaded = `lsmod | grep openflow_mod`;
+ $of_kmod_loaded = `lsmod | grep ofdatapath`;
if ( $of_kmod_loaded ne "" ) {
die "failed to remove kernel module... please fix!\n";
}
@@ -300,10 +300,10 @@ sub teardown_NF2 {
my $who = `whoami`;
if ( trim($who) ne 'root' ) { die "must be root\n"; }
- `killall secchan`;
+ `killall ofprotocol`;
# check if openflow kernel module loaded
- my $of_kmod_loaded = `lsmod | grep openflow_mod`;
+ my $of_kmod_loaded = `lsmod | grep ofdatapath`;
if ( $of_kmod_loaded eq "" ) { exit 0; }
print "tearing down interfaces and datapaths\n";
@@ -322,12 +322,12 @@ sub teardown_NF2 {
die "failed to remove hardware kernel module... please fix!\n";
}
- my $of_kmod_removed = `rmmod openflow_mod`;
+ my $of_kmod_removed = `rmmod ofdatapath`;
if ( $of_kmod_removed ne "" ) {
die "failed to remove kernel module... please fix!\n";
}
- $of_kmod_loaded = `lsmod | grep openflow_mod`;
+ $of_kmod_loaded = `lsmod | grep ofdatapath`;
if ( $of_kmod_loaded ne "" ) {
die "failed to remove kernel module... please fix!\n";
}
@@ -341,8 +341,8 @@ sub teardown_user {
my $who = `whoami`;
if ( trim($who) ne 'root' ) { die "must be root\n"; }
- `killall udatapath`;
- `killall secchan`;
+ `killall ofdatapath`;
+ `killall ofprotocol`;
exit 0;
}
@@ -638,8 +638,8 @@ sub run_black_box_test {
my $total_errors = 0;
try {
- # Wait for secchan to connect
- print "waiting for secchan to connect\n";
+ # Wait for ofprotocol to connect
+ print "waiting for ofprotocol to connect\n";
my $new_sock = $sock->accept();
do_hello_sequence( $ofp, $new_sock );
@@ -1258,7 +1258,7 @@ sub forward_simple {
#print Dumper($msg);
# Verify fields
- print "Verifying secchan message for packet sent in to eth" . ( $in_port + 1 ) . "\n";
+ print "Verifying ofprotocol message for packet sent in to eth" . ( $in_port + 1 ) . "\n";
verify_header( $msg, 'OFPT_PACKET_IN', $msg_size );
@@ -1506,7 +1506,7 @@ sub forward_simple_icmp {
#print Dumper($msg);
# Verify fields
- print "Verifying secchan message for packet sent in to eth" . ( $in_port + 1 ) . "\n";
+ print "Verifying ofprotocol message for packet sent in to eth" . ( $in_port + 1 ) . "\n";
verify_header( $msg, 'OFPT_PACKET_IN', $msg_size );
diff --git a/secchan/.gitignore b/secchan/.gitignore
index 930a188..d6d189d 100644
--- a/secchan/.gitignore
+++ b/secchan/.gitignore
@@ -3,5 +3,5 @@
/controller-lite
/ctlpath-lite
/dpctl-lite
-/secchan
-/secchan.8
+/ofprotocol
+/ofprotocol.8
diff --git a/secchan/automake.mk b/secchan/automake.mk
index f1f832c..f7ae451 100644
--- a/secchan/automake.mk
+++ b/secchan/automake.mk
@@ -1,7 +1,7 @@
-bin_PROGRAMS += secchan/secchan
-man_MANS += secchan/secchan.8
+bin_PROGRAMS += secchan/ofprotocol
+man_MANS += secchan/ofprotocol.8
-secchan_secchan_SOURCES = \
+secchan_ofprotocol_SOURCES = \
secchan/discovery.c \
secchan/discovery.h \
secchan/executer.c \
@@ -24,13 +24,13 @@ secchan_secchan_SOURCES = \
secchan/stp-secchan.c \
secchan/stp-secchan.h
if SUPPORT_SNAT
-secchan_secchan_SOURCES += \
+secchan_ofprotocol_SOURCES += \
secchan/snat.c \
secchan/snat.h
endif
-secchan_secchan_LDADD = lib/libopenflow.a $(FAULT_LIBS) $(SSL_LIBS)
+secchan_ofprotocol_LDADD = lib/libopenflow.a $(FAULT_LIBS) $(SSL_LIBS)
-EXTRA_DIST += secchan/secchan.8.in
-DISTCLEANFILES += secchan/secchan.8
+EXTRA_DIST += secchan/ofprotocol.8.in
+DISTCLEANFILES += secchan/ofprotocol.8
include secchan/commands/automake.mk
diff --git a/secchan/discovery.c b/secchan/discovery.c
index 9ed5102..feb9c33 100644
--- a/secchan/discovery.c
+++ b/secchan/discovery.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+/* Copyright (c) 2008, 2009 The Board of Trustees of The Leland Stanford
* Junior University
*
* We are making the OpenFlow specification and associated documentation
@@ -203,12 +203,12 @@ discovery_run(struct discovery *d, char **controller_name)
if (dhclient_is_bound(d->dhcp)) {
*controller_name = dhcp_msg_get_string(dhclient_get_config(d->dhcp),
DHCP_CODE_OFP_CONTROLLER_VCONN);
- VLOG_WARN("%s: discovered controller", *controller_name);
+ VLOG_INFO("%s: discovered controller", *controller_name);
d->n_changes++;
} else {
*controller_name = NULL;
if (d->n_changes) {
- VLOG_WARN("discovered controller no longer available");
+ VLOG_INFO("discovered controller no longer available");
d->n_changes++;
}
}
@@ -224,7 +224,7 @@ discovery_wait(struct discovery *d)
}
static void
-modify_dhcp_request(struct dhcp_msg *msg, void *aux)
+modify_dhcp_request(struct dhcp_msg *msg, void *aux UNUSED)
{
dhcp_msg_put_string(msg, DHCP_CODE_VENDOR_CLASS, "OpenFlow");
}
diff --git a/secchan/executer.c b/secchan/executer.c
index 008d2cc..964a8b9 100644
--- a/secchan/executer.c
+++ b/secchan/executer.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+/* Copyright (c) 2008, 2009 The Board of Trustees of The Leland Stanford
* Junior University
*
* We are making the OpenFlow specification and associated documentation
@@ -126,7 +126,7 @@ executer_is_permitted(const char *acl_, const char *cmd)
/* Check the command white/blacklisted state. */
if (allowed && !denied) {
- VLOG_WARN("permitting command execution: \"%s\" is whitelisted", cmd);
+ VLOG_INFO("permitting command execution: \"%s\" is whitelisted", cmd);
} else if (allowed && denied) {
VLOG_WARN("denying command execution: \"%s\" is both blacklisted "
"and whitelisted", cmd);
@@ -252,7 +252,7 @@ executer_remote_packet_cb(struct relay *r, void *e_)
/* Running in parent. */
struct child *child;
- VLOG_WARN("started \"%s\" subprocess", argv[0]);
+ VLOG_INFO("started \"%s\" subprocess", argv[0]);
send_child_status(r, request->header.xid, NXT_STATUS_STARTED, NULL, 0);
child = &e->children[e->n_children++];
child->name = xstrdup(argv[0]);
@@ -330,7 +330,7 @@ child_terminated(struct child *child, int status)
if (WCOREDUMP(status)) {
ds_put_cstr(&ds, " (core dumped)");
}
- VLOG_WARN("child process \"%s\" with pid %ld terminated %s",
+ VLOG_INFO("child process \"%s\" with pid %ld terminated %s",
child->name, (long int) child->pid, ds_cstr(&ds));
ds_destroy(&ds);
diff --git a/secchan/fail-open.c b/secchan/fail-open.c
index 06e1c31..eb28bc3 100644
--- a/secchan/fail-open.c
+++ b/secchan/fail-open.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+/* Copyright (c) 2008, 2009 The Board of Trustees of The Leland Stanford
* Junior University
*
* We are making the OpenFlow specification and associated documentation
@@ -84,7 +84,7 @@ fail_open_periodic_cb(void *fail_open_)
fail_open->last_disconn_secs = disconn_secs;
}
} else if (open && disconn_secs > fail_open->last_disconn_secs + 60) {
- VLOG_WARN("Still in fail-open mode after %d seconds disconnected "
+ VLOG_INFO("Still in fail-open mode after %d seconds disconnected "
"from controller", disconn_secs);
fail_open->last_disconn_secs = disconn_secs;
}
diff --git a/secchan/in-band.c b/secchan/in-band.c
index 8556a6e..46109da 100644
--- a/secchan/in-band.c
+++ b/secchan/in-band.c
@@ -130,7 +130,7 @@ static void
in_band_learn_mac(struct in_band_data *in_band,
uint16_t in_port, const uint8_t src_mac[ETH_ADDR_LEN])
{
- if (mac_learning_learn(in_band->ml, src_mac, in_port)) {
+ if (mac_learning_learn(in_band->ml, src_mac, 0, in_port)) {
VLOG_DBG_RL(&rl, "learned that "ETH_ADDR_FMT" is on port %"PRIu16,
ETH_ADDR_ARGS(src_mac), in_port);
}
@@ -158,7 +158,7 @@ in_band_local_packet_cb(struct relay *r, void *in_band_)
/* Deal with local stuff. */
if (in_port == OFPP_LOCAL) {
/* Sent by secure channel. */
- out_port = mac_learning_lookup(in_band->ml, eth->eth_dst);
+ out_port = mac_learning_lookup(in_band->ml, eth->eth_dst, 0);
} else if (eth_addr_equals(eth->eth_dst,
netdev_get_etheraddr(in_band->of_device))) {
/* Sent to secure channel. */
@@ -179,7 +179,7 @@ in_band_local_packet_cb(struct relay *r, void *in_band_)
|| flow.tp_dst == htons(OFP_SSL_PORT))) {
/* Traffic to or from controller. Switch it by hand. */
in_band_learn_mac(in_band, in_port, eth->eth_src);
- out_port = mac_learning_lookup(in_band->ml, eth->eth_dst);
+ out_port = mac_learning_lookup(in_band->ml, eth->eth_dst, 0);
} else {
const uint8_t *controller_mac;
controller_mac = get_controller_mac(in_band);
@@ -190,7 +190,7 @@ in_band_local_packet_cb(struct relay *r, void *in_band_)
out_port = OFPP_FLOOD;
} else if (is_controller_mac(eth->eth_dst, in_band)
&& in_port == mac_learning_lookup(in_band->ml,
- controller_mac)) {
+ controller_mac, 0)) {
/* Drop controller traffic that arrives on the controller port. */
out_port = -1;
} else {
@@ -290,11 +290,25 @@ in_band_local_port_cb(const struct ofp_phy_port *port, void *in_band_)
}
}
+static void
+in_band_periodic_cb(void *in_band_)
+{
+ struct in_band_data *in_band = in_band_;
+ mac_learning_run(in_band->ml, NULL);
+}
+
+static void
+in_band_wait_cb(void *in_band_)
+{
+ struct in_band_data *in_band = in_band_;
+ mac_learning_wait(in_band->ml);
+}
+
static struct hook_class in_band_hook_class = {
in_band_local_packet_cb, /* local_packet_cb */
NULL, /* remote_packet_cb */
- NULL, /* periodic_cb */
- NULL, /* wait_cb */
+ in_band_periodic_cb, /* periodic_cb */
+ in_band_wait_cb, /* wait_cb */
NULL, /* closing_cb */
};
diff --git a/secchan/secchan.8.in b/secchan/ofprotocol.8.in
similarity index 77%
rename from secchan/secchan.8.in
rename to secchan/ofprotocol.8.in
index 66bed8f..68771f9 100644
--- a/secchan/secchan.8.in
+++ b/secchan/ofprotocol.8.in
@@ -1,15 +1,16 @@
-.TH secchan 8 "October 2008" "OpenFlow" "OpenFlow Manual"
+.ds PN ofprotocol
+.TH ofprotocol 8 "October 2008" "OpenFlow" "OpenFlow Manual"
.SH NAME
-secchan \- secure channel connecting an OpenFlow datapath to a controller
+ofprotocol \- secure channel connecting an OpenFlow datapath to a controller
.SH SYNOPSIS
-.B secchan
+.B ofprotocol
[\fIoptions\fR] \fIdatapath\fR [\fIcontroller\fR]
.SH DESCRIPTION
-The \fBsecchan\fR program sets up a secure channel between a local
-OpenFlow datapath and a remote controller. \fBsecchan\fR connects to
+The \fBofprotocol\fR program sets up a secure channel between a local
+OpenFlow datapath and a remote controller. \fBofprotocol\fR connects to
the local datapath over Netlink and to the controller over TCP or SSL,
and then forwards OpenFlow messages from one endpoint to the other.
@@ -24,9 +25,9 @@ The \fIdp_idx\fR argument is the number of a datapath created with
.TP
\fBunix:\fIfile\fR
-Attach to the userspace datapath implemented by \fBudatapath\fR(8).
+Attach to the userspace datapath implemented by \fBofdatapath\fR(8).
The \fIfile\fR argument must the same one specified on the
-\fBudatapath\fR command line.
+\fBofdatapath\fR command line.
.PP
The optional \fIcontroller\fR argument specifies how to connect to
@@ -48,7 +49,7 @@ The specified TCP \fIport\fR (default: 6633) on the given remote
The Unix domain server socket named \fIfile\fR.
.PP
-If \fIcontroller\fR is omitted, \fBsecchan\fR attempts to discover the
+If \fIcontroller\fR is omitted, \fBofprotocol\fR attempts to discover the
location of the controller automatically (see below).
.SH "CONTACTING THE CONTROLLER"
@@ -61,9 +62,9 @@ the data traffic that it controls, that is, the switch does not use
any of the network devices added to the datapath with \fBdpctl
addif\fR in its communication with the controller.
-To use \fBsecchan\fR in a network with out-of-band control, specify
-\fB--out-of-band\fR on the \fBsecchan\fR command line. The control
-network must be configured separately, before or after \fBsecchan\fR
+To use \fBofprotocol\fR in a network with out-of-band control, specify
+\fB--out-of-band\fR on the \fBofprotocol\fR command line. The control
+network must be configured separately, before or after \fBofprotocol\fR
is started.
.IP in-band
@@ -74,7 +75,7 @@ addif\fR. This configuration is often more convenient than
out-of-band control, because it is not necessary to maintain two
independent networks.
-In-band control is the default for \fBsecchan\fR, so no special
+In-band control is the default for \fBofprotocol\fR, so no special
command-line option is required.
With in-band control, the location of the controller can be configured
@@ -82,23 +83,23 @@ manually or discovered automatically:
.RS
.IP "controller discovery"
-To make \fBsecchan\fR discover the location of the controller
+To make \fBofprotocol\fR discover the location of the controller
automatically, do not specify the location of the controller on the
-\fBsecchan\fR command line.
+\fBofprotocol\fR command line.
-In this mode, \fBsecchan\fR will broadcast a DHCP request with vendor
+In this mode, \fBofprotocol\fR will broadcast a DHCP request with vendor
class identifier \fBOpenFlow\fR across the network devices added to
the datapath with \fBdpctl addif\fR. It will accept any valid DHCP
reply that has the same vendor class identifier and includes a
vendor-specific option with code 1 whose contents are a string
specifying the location of the controller in the same format used on
-the \fBsecchan\fR command line (e.g. \fBssl:192.168.0.1\fR).
+the \fBofprotocol\fR command line (e.g. \fBssl:192.168.0.1\fR).
The DHCP reply may also, optionally, include a vendor-specific option
with code 2 whose contents are a string specifying the URI to the base
of the OpenFlow PKI (e.g. \fBhttp://192.168.0.1/openflow/pki\fR).
This URI is used only for bootstrapping the OpenFlow PKI at initial
-switch setup; \fBsecchan\fR does not use it at all.
+switch setup; \fBofprotocol\fR does not use it at all.
The following ISC DHCP server configuration file assigns the IP
address range 192.168.0.20 through 192.168.0.30 to OpenFlow switches
@@ -152,12 +153,12 @@ subnet 192.168.0.0 netmask 255.255.255.0 {
.IP "manual configuration"
To configure in-band control manually, specify the location of the
-controller on the \fBsecchan\fR command line as the \fIcontroller\fR
+controller on the \fBofprotocol\fR command line as the \fIcontroller\fR
argument. You must also configure the network device for the OpenFlow
-``local port'' to allow \fBsecchan\fR to connect to that controller.
-The OpenFlow local port is a virtual network port that \fBsecchan\fR
+``local port'' to allow \fBofprotocol\fR to connect to that controller.
+The OpenFlow local port is a virtual network port that \fBofprotocol\fR
bridges to the physical switch ports. Its network device name depends
-on the \fIdatapath\fR specified on the \fBsecchan\fR command line:
+on the \fIdatapath\fR specified on the \fBofprotocol\fR command line:
.RS
.TP
@@ -168,12 +169,12 @@ The local port network device for \fBnl:\fIdp_idx\fR is always named
.TP
\fBunix:\fIfile\fR
The local port network device name may be specified on the
-\fBudatapath\fR command line, using the \fB--local-port\fR option. It
+\fBofdatapath\fR command line, using the \fB--local-port\fR option. It
is often \fBtap0\fR.
.RE
.IP
-Before \fBsecchan\fR starts, the local port network device is not
+Before \fBofprotocol\fR starts, the local port network device is not
bridged to any physical network, so the next step depends on whether
connectivity is required to configure the device's IP address. If the
switch has a static IP address, you may configure its IP address now
@@ -183,19 +184,20 @@ with a command such as:
ifconfig of0 192.168.1.1
.RE
.IP
-and then invoke \fBsecchan\fR.
+and then invoke \fBofprotocol\fR.
On the other hand, if the switch does not have a static IP address,
e.g. it obtains its IP address dynamically via DHCP, the DHCP client
will not be able to contact the DHCP server until the secure channel
-has started up. Thus, start \fBsecchan\fR without configuring
+has started up. Thus, start \fBofprotocol\fR without configuring
the local port network device, and start the DHCP client afterward.
.RE
.SH OPTIONS
+.SS "Controller Discovery Options"
.TP
\fB--accept-vconn=\fIregex\fR
-When \fBsecchan\fR performs controller discovery (see \fBCONTACTING
+When \fBofprotocol\fR performs controller discovery (see \fBCONTACTING
THE CONTROLLER\fR, above, for more information about controller
discovery), it validates the controller location obtained via DHCP
with a POSIX extended regular expression. Only controllers whose
@@ -214,7 +216,7 @@ When controller discovery is not performed, this option has no effect.
.TP
\fB--no-resolv-conf\fR
-When \fBsecchan\fR performs controller discovery (see \fBCONTACTING
+When \fBofprotocol\fR performs controller discovery (see \fBCONTACTING
THE CONTROLLER\fR, above, for more information about controller
discovery), by default it overwrites the system's
\fB/etc/resolv.conf\fR with domain information and DNS servers
@@ -222,14 +224,15 @@ obtained via DHCP. If the location of the controller is specified
using a hostname, rather than an IP address, and the network's DNS
servers ever change, this behavior is essential. But because it also
interferes with any administrator or process that manages
-\fB/etc/resolv.conf\fR, when this option is specified, \fBsecchan\fR
+\fB/etc/resolv.conf\fR, when this option is specified, \fBofprotocol\fR
will not modify \fB/etc/resolv.conf\fR.
-\fBsecchan\fR will only modify \fBresolv.conf\fR if the DHCP response
+\fBofprotocol\fR will only modify \fBresolv.conf\fR if the DHCP response
that it receives specifies one or more DNS servers.
When controller discovery is not performed, this option has no effect.
+.SS "Networking Options"
.TP
\fB-F\fR, \fB--fail=\fR[\fBopen\fR|\fBclosed\fR]
The controller is, ordinarily, responsible for setting up all flows on
@@ -238,17 +241,17 @@ no new network connections can be set up. If the connection to the
controller stays down long enough, no packets can pass through the
switch at all.
-If this option is set to \fBopen\fR (the default), \fBsecchan\fR will
+If this option is set to \fBopen\fR (the default), \fBofprotocol\fR will
take over responsibility for setting up flows in the local datapath
when no message has been received from the controller for three times
the inactivity probe interval (see below), or 45 seconds by default.
-In this ``fail open'' mode, \fBsecchan\fR causes the datapath to act
-like an ordinary MAC-learning switch. \fBsecchan\fR will continue to
+In this ``fail open'' mode, \fBofprotocol\fR causes the datapath to act
+like an ordinary MAC-learning switch. \fBofprotocol\fR will continue to
retry connection to the controller in the background and, when the
connection succeeds, it discontinues its fail-open behavior. The
secure channel enters the fail-open mode when
-If this option is set to \fBclosed\fR, then \fBsecchan\fR will not
+If this option is set to \fBclosed\fR, then \fBofprotocol\fR will not
set up flows on its own when the controller connection fails.
.TP
@@ -333,15 +336,15 @@ command. The \fImethod\fR must be given as one of the passive
OpenFlow connection methods listed above as acceptable for
\fB--listen\fR.
-When \fBdpctl monitor\fR makes a monitoring connection, \fBsecchan\fR
+When \fBdpctl monitor\fR makes a monitoring connection, \fBofprotocol\fR
sends it a copy of every OpenFlow message sent to or received from the
kernel in the normal course of its operations. It does not send a
copy of any messages sent to or from the OpenFlow connection to the
controller. Most of these messages will be seen anyhow, however,
-because \fBsecchan\fR mainly acts as a relay between the controller
-and the kernel. \fBsecchan\fR also does not send a copy of any
+because \fBofprotocol\fR mainly acts as a relay between the controller
+and the kernel. \fBofprotocol\fR also does not send a copy of any
messages sent to or from the OpenFlow connection to the controller.
-Such messages will typically \fBnot\fR be seen, because \fBsecchan\fR
+Such messages will typically \fBnot\fR be seen, because \fBofprotocol\fR
maintains a separate connection to the kernel for each management
connection.
@@ -351,7 +354,7 @@ connection fills up, some messages will be lost.
.TP
\fB--in-band\fR, \fB--out-of-band\fR
-Configures \fBsecchan\fR to operate in in-band or out-of-band control
+Configures \fBofprotocol\fR to operate in in-band or out-of-band control
mode (see \fBCONTACTING THE CONTROLLER\fR above). When neither option
is given, the default is in-band control.
@@ -362,6 +365,41 @@ at the switch. The default is \fB--no-stp\fR in this distribution,
because bugs in the STP implementation are still being worked out.
The default will change to \fB--stp\fR at some point in the future.
+.TP
+\fB--netflow=\fIhost\fB:\fIport\fR
+When flows end on the switch, send NetFlow v5 messages to
+\fIhost\fR on UDP \fIport\fR.
+
+.SS "Rate-Limiting Options"
+
+These options configure how the switch applies a ``token bucket'' to
+limit the rate at which packets in unknown flows are forwarded to an
+OpenFlow controller for flow-setup processing. This feature prevents
+a single OpenFlow switch from overwhelming a controller.
+
+.TP
+\fB--rate-limit\fR[\fB=\fIrate\fR]
+.
+Limits the maximum rate at which packets will be forwarded to the
+OpenFlow controller to \fIrate\fR packets per second. If \fIrate\fR
+is not specified then the default of 1,000 packets per second is used.
+
+If \fB--rate-limit\fR is not used, then the switch does not limit the
+rate at which packets are forwarded to the controller.
+
+.TP
+\fB--burst-limit=\fIburst\fR
+.
+Sets the maximum number of unused packet credits that the switch will
+allow to accumulate during time in which no packets are being
+forwarded to the OpenFlow controller to \fIburst\fR (measured in
+packets). The default \fIburst\fR is one-quarter of the \fIrate\fR
+specified on \fB--rate-limit\fR.
+
+This option takes effect only when \fB--rate-limit\fR is also specified.
+
+.SS "Remote Command Execution Options"
+
.TP
\fB--command-acl=\fR[\fB!\fR]\fIglob\fR[\fB,\fR[\fB!\fR]\fIglob\fR...]
Configures the commands that remote OpenFlow connections are allowed
@@ -378,7 +416,7 @@ Command names that include characters other than upper- and lower-case
English letters, digits, and the underscore and hyphen characters are
unconditionally disallowed.
-When the whitelist and blacklist permit a command name, \fBsecchan\fR
+When the whitelist and blacklist permit a command name, \fBofprotocol\fR
looks for a program with the same name as the command in the commands
directory (see below). Other directories are not searched.
@@ -388,6 +426,11 @@ Sets the directory searched for remote command execution to
\fBdirectory\fR. The default directory is
\fB@pkgdatadir@/commands\fR.
+.SS "Daemon Options"
+.so lib/daemon.man
+
+.SS "Public Key Infrastructure Options"
+
.TP
\fB--netflow=\fIhost\fB:\fIport\fR
When flows end on the switch, send NetFlow v5 messages to
@@ -412,7 +455,7 @@ the switch is connected to a trustworthy controller.
.TP
\fB--bootstrap-ca-cert=\fIcacert.pem\fR
When \fIcacert.pem\fR exists, this option has the same effect as
-\fB-C\fR or \fB--ca-cert\fR. If it does not exist, then \fBsecchan\fR
+\fB-C\fR or \fB--ca-cert\fR. If it does not exist, then \fBofprotocol\fR
will attempt to obtain the CA certificate from the controller on its
first SSL connection and save it to the named PEM file. If it is
successful, it will immediately drop the connection and reconnect, and
@@ -429,37 +472,11 @@ require the controller to send the CA certificate, but
\fBcontroller\fR(8) can be configured to do so with the
\fB--peer-ca-cert\fR option.
-.TP
-\fB-P\fR[\fIpidfile\fR], \fB--pidfile\fR[\fB=\fIpidfile\fR]
-Causes a file (by default, \fBsecchan.pid\fR) to be created indicating
-the PID of the running process. If \fIpidfile\fR is not specified, or
-if it does not begin with \fB/\fR, then it is created in
-\fB@RUNDIR@\fR.
-
-.TP
-\fB-f\fR, \fB--force\fR
-By default, when \fB-P\fR or \fB--pidfile\fR is specified and the
-specified pidfile already exists and is locked by a running process,
-\fBsecchan\fR refuses to start. Specify \fB-f\fR or \fB--force\fR
-to cause it to instead overwrite the pidfile.
-
-When \fB-P\fR or \fB--pidfile\fR is not specified, this option has no
-effect.
-
-.TP
-\fB-D\fR, \fB--detach\fR
-Causes \fBsecchan\fR to detach itself from the foreground session and
-run as a background process.
-
-.TP
-.BR \-h ", " \-\^\-help
-Prints a brief help message to the console.
-
-@VLOG_OPTIONS@
-
-.TP
-.BR \-V ", " \-\^\-version
-Prints version information to the console.
+.SS "Logging Options"
+.so lib/vlog.man
+.SS "Other Options"
+.so lib/common.man
+.so lib/leak-checker.man
.SH "SEE ALSO"
@@ -467,5 +484,5 @@ Prints version information to the console.
.BR ofp-discover (8),
.BR controller (8),
.BR ofp-pki (8),
-.BR udatapath (8),
+.BR ofdatapath (8),
.BR vlogconf (8)
diff --git a/secchan/port-watcher.c b/secchan/port-watcher.c
index 7daa370..1d8d69d 100644
--- a/secchan/port-watcher.c
+++ b/secchan/port-watcher.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+/* Copyright (c) 2008, 2009 The Board of Trustees of The Leland Stanford
* Junior University
*
* We are making the OpenFlow specification and associated documentation
@@ -38,11 +38,14 @@
#include
#include
#include "dynamic-string.h"
+#include "netdev.h"
#include "ofpbuf.h"
#include "openflow/openflow.h"
#include "poll-loop.h"
#include "port-array.h"
#include "rconn.h"
+#include "shash.h"
+#include "svec.h"
#include "timeval.h"
#include "vconn.h"
#include "xtoxll.h"
@@ -73,6 +76,8 @@ struct port_watcher {
struct port_watcher_local_cb local_cbs[4];
int n_local_cbs;
char local_port_name[OFP_MAX_PORT_NAME_LEN + 1];
+ struct netdev_monitor *mon;
+ struct shash port_by_name;
};
/* Returns the number of fields that differ from 'a' to 'b'. */
@@ -156,7 +161,7 @@ call_local_port_changed_callbacks(struct port_watcher *pw)
}
if (strcmp(pw->local_port_name, name)) {
if (name[0]) {
- VLOG_WARN("Identified data path local port as \"%s\".", name);
+ VLOG_INFO("Identified data path local port as \"%s\".", name);
} else {
VLOG_WARN("Data path has no local port.");
}
@@ -202,6 +207,25 @@ update_phy_port(struct port_watcher *pw, struct ofp_phy_port *opp,
}
}
+static void
+update_netdev_monitor_devices(struct port_watcher *pw)
+{
+ struct ofp_phy_port *p;
+ struct svec netdevs;
+ unsigned int port_no;
+
+ svec_init(&netdevs);
+ shash_clear(&pw->port_by_name);
+ for (p = port_array_first(&pw->ports, &port_no); p;
+ p = port_array_next(&pw->ports, &port_no)) {
+ const char *name = (const char *) p->name;
+ svec_add(&netdevs, name);
+ shash_add(&pw->port_by_name, name, p);
+ }
+ netdev_monitor_set_devices(pw->mon, netdevs.names, netdevs.n);
+ svec_destroy(&netdevs);
+}
+
static bool
port_watcher_local_packet_cb(struct relay *r, void *pw_)
{
@@ -221,7 +245,7 @@ port_watcher_local_packet_cb(struct relay *r, void *pw_)
pw->got_feature_reply = true;
if (pw->datapath_id != osf->datapath_id) {
pw->datapath_id = osf->datapath_id;
- VLOG_WARN("Datapath id is %012"PRIx64, ntohll(pw->datapath_id));
+ VLOG_INFO("Datapath id is %012"PRIx64, ntohll(pw->datapath_id));
}
/* Update each port included in the message. */
@@ -242,6 +266,8 @@ port_watcher_local_packet_cb(struct relay *r, void *pw_)
}
}
+ update_netdev_monitor_devices(pw);
+
call_local_port_changed_callbacks(pw);
} else if (oh->type == OFPT_PORT_STATUS
&& msg->size >= sizeof(struct ofp_port_status)) {
@@ -250,10 +276,38 @@ port_watcher_local_packet_cb(struct relay *r, void *pw_)
if (ops->desc.port_no == htons(OFPP_LOCAL)) {
call_local_port_changed_callbacks(pw);
}
+ if (ops->reason == OFPPR_ADD || OFPPR_DELETE) {
+ update_netdev_monitor_devices(pw);
+ }
}
return false;
}
+static void
+bring_netdev_up_or_down(const char *name, bool down)
+{
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+ struct netdev *netdev;
+ int retval;
+
+ retval = netdev_open(name, NETDEV_ETH_TYPE_NONE, &netdev);
+ if (!retval) {
+ if (down) {
+ retval = netdev_turn_flags_off(netdev, NETDEV_UP, true);
+ } else {
+ retval = netdev_turn_flags_on(netdev, NETDEV_UP, true);
+ }
+ if (retval) {
+ VLOG_WARN_RL(&rl, "failed to bring network device %s %s: %s",
+ name, down ? "down" : "up", strerror(retval));
+ }
+ netdev_close(netdev);
+ } else {
+ VLOG_WARN_RL(&rl, "failed to open network device %s: %s",
+ name, strerror(retval));
+ }
+}
+
static bool
port_watcher_remote_packet_cb(struct relay *r, void *pw_)
{
@@ -274,15 +328,32 @@ port_watcher_remote_packet_cb(struct relay *r, void *pw_)
if (pw_opp->port_no == htons(OFPP_LOCAL)) {
call_local_port_changed_callbacks(pw);
}
+
+ if (opm->mask & htonl(OFPPC_PORT_DOWN)) {
+ bring_netdev_up_or_down((const char *) pw_opp->name,
+ opm->config & htonl(OFPPC_PORT_DOWN));
+ }
}
}
return false;
}
+/* Sets 'bit' in '*word' to 0 or 1 according to 'value'. */
+static void
+set_bit(uint32_t bit, bool value, uint32_t *word)
+{
+ if (value) {
+ *word |= bit;
+ } else {
+ *word &= ~bit;
+ }
+}
+
static void
port_watcher_periodic_cb(void *pw_)
{
struct port_watcher *pw = pw_;
+ const char *name;
if (!pw->got_feature_reply
&& time_now() >= pw->last_feature_request + 5
@@ -292,6 +363,47 @@ port_watcher_periodic_cb(void *pw_)
rconn_send_with_limit(pw->local_rconn, b, &pw->n_txq, 1);
pw->last_feature_request = time_now();
}
+
+ netdev_monitor_run(pw->mon);
+ while ((name = netdev_monitor_poll(pw->mon)) != NULL) {
+ struct ofp_phy_port *opp;
+ struct ofp_phy_port new_opp;
+ enum netdev_flags flags;
+ int retval;
+
+ opp = shash_find_data(&pw->port_by_name, name);
+ if (!opp) {
+ continue;
+ }
+
+ retval = netdev_nodev_get_flags(name, &flags);
+ if (retval) {
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+ VLOG_WARN_RL(&rl, "could not get flags for %s", name);
+ continue;
+ }
+
+ new_opp = *opp;
+ set_bit(htonl(OFPPC_PORT_DOWN), flags & NETDEV_UP, &new_opp.config);
+ set_bit(htonl(OFPPS_LINK_DOWN), flags & NETDEV_CARRIER,
+ &new_opp.state);
+ if (opp->config != new_opp.config || opp->state != new_opp.state) {
+ struct ofp_port_status *ops;
+ struct ofpbuf *b;
+
+ /* Notify other secchan modules. */
+ update_phy_port(pw, &new_opp, OFPPR_MODIFY);
+ if (new_opp.port_no == htons(OFPP_LOCAL)) {
+ call_local_port_changed_callbacks(pw);
+ }
+
+ /* Notify the controller that the flags changed. */
+ ops = make_openflow(sizeof *ops, OFPT_PORT_STATUS, &b);
+ ops->reason = OFPPR_MODIFY;
+ ops->desc = new_opp;
+ rconn_send(pw->remote_rconn, b, NULL);
+ }
+ }
}
static void
@@ -305,6 +417,7 @@ port_watcher_wait_cb(void *pw_)
poll_immediate_wake();
}
}
+ netdev_monitor_wait(pw->mon);
}
static void
@@ -352,7 +465,7 @@ static void
log_port_status(uint16_t port_no,
const struct ofp_phy_port *old,
const struct ofp_phy_port *new,
- void *aux)
+ void *aux UNUSED)
{
if (VLOG_IS_DBG_ENABLED()) {
if (old && new && (opp_differs(old, new)
@@ -489,6 +602,7 @@ port_watcher_start(struct secchan *secchan,
struct port_watcher **pwp)
{
struct port_watcher *pw;
+ int retval;
pw = *pwp = xcalloc(1, sizeof *pw);
pw->local_rconn = local_rconn;
@@ -496,6 +610,11 @@ port_watcher_start(struct secchan *secchan,
pw->last_feature_request = TIME_MIN;
port_array_init(&pw->ports);
pw->local_port_name[0] = '\0';
+ retval = netdev_monitor_create(&pw->mon);
+ if (retval) {
+ ofp_fatal(retval, "failed to start network device monitoring");
+ }
+ shash_init(&pw->port_by_name);
port_watcher_register_callback(pw, log_port_status, NULL);
add_hook(secchan, &port_watcher_hook_class, pw);
}
diff --git a/secchan/secchan.c b/secchan/secchan.c
index bf1aaca..c17d02c 100644
--- a/secchan/secchan.c
+++ b/secchan/secchan.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+/* Copyright (c) 2008, 2009 The Board of Trustees of The Leland Stanford
* Junior University
*
* We are making the OpenFlow specification and associated documentation
@@ -50,6 +50,7 @@
#include "fail-open.h"
#include "fault.h"
#include "in-band.h"
+#include "leak-checker.h"
#include "list.h"
#include "ofpbuf.h"
#include "openflow/openflow.h"
@@ -153,8 +154,8 @@ main(int argc, char *argv[])
ofp_fatal(retval, "Could not listen for vlog connections");
}
- VLOG_WARN("OpenFlow reference implementation version %s", VERSION BUILDNR);
- VLOG_WARN("OpenFlow protocol version 0x%02x", OFP_VERSION);
+ VLOG_INFO("OpenFlow reference implementation version %s", VERSION BUILDNR);
+ VLOG_INFO("OpenFlow protocol version 0x%02x", OFP_VERSION);
/* Check datapath name, to try to catch command-line invocation errors. */
if (strncmp(s.dp_name, "nl:", 3) && strncmp(s.dp_name, "unix:", 5)
@@ -215,7 +216,7 @@ main(int argc, char *argv[])
#endif
flow_end_start(&secchan, s.netflow_dst, local_rconn, remote_rconn);
if (s.enable_stp) {
- stp_start(&secchan, &s, pw, local_rconn, remote_rconn);
+ stp_start(&secchan, pw, local_rconn, remote_rconn);
}
if (s.in_band) {
in_band_start(&secchan, &s, switch_status, pw, remote_rconn);
@@ -231,7 +232,7 @@ main(int argc, char *argv[])
executer_start(&secchan, &s);
}
- for (;;) {
+ while (s.discovery || rconn_is_alive(remote_rconn)) {
struct relay *r, *n;
size_t i;
@@ -331,10 +332,8 @@ add_hook(struct secchan *secchan, const struct hook_class *class, void *aux)
struct hook *hook;
if (secchan->n_hooks >= secchan->allocated_hooks) {
- secchan->allocated_hooks = secchan->allocated_hooks * 2 + 1;
- secchan->hooks = xrealloc(secchan->hooks,
- (sizeof *secchan->hooks
- * secchan->allocated_hooks));
+ secchan->hooks = x2nrealloc(secchan->hooks, &secchan->allocated_hooks,
+ sizeof *secchan->hooks);
}
hook = &secchan->hooks[secchan->n_hooks++];
hook->class = class;
@@ -592,7 +591,8 @@ parse_options(int argc, char *argv[], struct settings *s)
OPT_COMMAND_ACL,
OPT_COMMAND_DIR,
OPT_NETFLOW,
- VLOG_OPTION_ENUMS
+ VLOG_OPTION_ENUMS,
+ LEAK_CHECKER_OPTION_ENUMS
};
static struct option long_options[] = {
{"accept-vconn", required_argument, 0, OPT_ACCEPT_VCONN},
@@ -617,6 +617,7 @@ parse_options(int argc, char *argv[], struct settings *s)
{"version", no_argument, 0, 'V'},
DAEMON_LONG_OPTIONS,
VLOG_LONG_OPTIONS,
+ LEAK_CHECKER_LONG_OPTIONS,
#ifdef HAVE_OPENSSL
VCONN_SSL_LONG_OPTIONS
{"bootstrap-ca-cert", required_argument, 0, OPT_BOOTSTRAP_CA_CERT},
@@ -777,6 +778,8 @@ parse_options(int argc, char *argv[], struct settings *s)
VLOG_OPTION_HANDLERS
+ LEAK_CHECKER_OPTION_HANDLERS
+
#ifdef HAVE_OPENSSL
VCONN_SSL_OPTION_HANDLERS
@@ -880,5 +883,6 @@ usage(void)
printf("\nOther options:\n"
" -h, --help display this help message\n"
" -V, --version display version information\n");
+ leak_checker_usage();
exit(EXIT_SUCCESS);
}
diff --git a/secchan/snat.c b/secchan/snat.c
index 17aa243..b27372c 100644
--- a/secchan/snat.c
+++ b/secchan/snat.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+/* Copyright (c) 2008, 2009 The Board of Trustees of The Leland Stanford
* Junior University
*
* We are making the OpenFlow specification and associated documentation
@@ -243,7 +243,7 @@ snat_remote_packet_cb(struct relay *r, void *snat_)
}
static void
-snat_port_changed_cb(uint16_t port_no,
+snat_port_changed_cb(uint16_t port_no UNUSED,
const struct ofp_phy_port *old,
const struct ofp_phy_port *new,
void *snat_)
diff --git a/secchan/status.c b/secchan/status.c
index 8b6224c..cf22812 100644
--- a/secchan/status.c
+++ b/secchan/status.c
@@ -58,7 +58,7 @@ struct switch_status {
const struct settings *s;
time_t booted;
struct switch_status_category *categories;
- int n_categories, allocated_categories;
+ size_t n_categories, allocated_categories;
};
struct status_reply {
@@ -195,10 +195,8 @@ switch_status_register_category(struct switch_status *ss,
{
struct switch_status_category *c;
if (ss->n_categories >= ss->allocated_categories) {
- ss->allocated_categories = 1 + ss->allocated_categories * 2;
- ss->categories = xrealloc(ss->categories,
- (sizeof *ss->categories
- * ss->allocated_categories));
+ ss->categories = x2nrealloc(ss->categories, &ss->allocated_categories,
+ sizeof *ss->categories);
}
c = &ss->categories[ss->n_categories++];
c->cb = cb;
diff --git a/secchan/stp-secchan.c b/secchan/stp-secchan.c
index 97ad02b..152595e 100644
--- a/secchan/stp-secchan.c
+++ b/secchan/stp-secchan.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+/* Copyright (c) 2008, 2009 The Board of Trustees of The Leland Stanford
* Junior University
*
* We are making the OpenFlow specification and associated documentation
@@ -54,7 +54,7 @@ struct stp_data {
struct port_watcher *pw;
struct rconn *local_rconn;
struct rconn *remote_rconn;
- long long int last_tick_256ths;
+ long long int last_tick;
int n_txq;
};
@@ -126,18 +126,12 @@ stp_local_packet_cb(struct relay *r, void *stp_)
return true;
}
-static long long int
-time_256ths(void)
-{
- return time_msec() * 256 / 1000;
-}
-
static void
stp_periodic_cb(void *stp_)
{
struct stp_data *stp = stp_;
- long long int now_256ths = time_256ths();
- long long int elapsed_256ths = now_256ths - stp->last_tick_256ths;
+ long long int now = time_msec();
+ long long int elapsed = now - stp->last_tick;
struct stp_port *p;
if (!port_watcher_is_ready(stp->pw)) {
@@ -145,19 +139,19 @@ stp_periodic_cb(void *stp_)
* disable STP. */
return;
}
- if (elapsed_256ths <= 0) {
+ if (elapsed <= 0) {
return;
}
- stp_tick(stp->stp, MIN(INT_MAX, elapsed_256ths));
- stp->last_tick_256ths = now_256ths;
+ stp_tick(stp->stp, MIN(INT_MAX, elapsed));
+ stp->last_tick = now;
while (stp_get_changed_port(stp->stp, &p)) {
int port_no = stp_port_no(p);
enum stp_state s_state = stp_port_get_state(p);
if (s_state != STP_DISABLED) {
- VLOG_WARN("STP: Port %d entered %s state",
+ VLOG_INFO("STP: Port %d entered %s state",
port_no, stp_state_name(s_state));
}
if (!(port_watcher_get_config(stp->pw, port_no) & OFPPC_NO_STP)) {
@@ -202,39 +196,22 @@ stp_wait_cb(void *stp_ UNUSED)
}
static void
-send_bpdu(const void *bpdu, size_t bpdu_size, int port_no, void *stp_)
+send_bpdu(struct ofpbuf *pkt, int port_no, void *stp_)
{
struct stp_data *stp = stp_;
- const uint8_t *port_mac;
- struct eth_header *eth;
- struct llc_header *llc;
- struct ofpbuf pkt, *opo;
+ const uint8_t *port_mac = port_watcher_get_hwaddr(stp->pw, port_no);
+ if (port_mac) {
+ struct eth_header *eth = pkt->l2;
+ struct ofpbuf *opo;
- port_mac = port_watcher_get_hwaddr(stp->pw, port_no);
- if (!port_mac) {
+ memcpy(eth->eth_src, port_mac, ETH_ADDR_LEN);
+ opo = make_unbuffered_packet_out(pkt, OFPP_NONE, port_no);
+
+ rconn_send_with_limit(stp->local_rconn, opo, &stp->n_txq, OFPP_MAX);
+ } else {
VLOG_WARN_RL(&rl, "cannot send BPDU on missing port %d", port_no);
- return;
}
-
- /* Packet skeleton. */
- ofpbuf_init(&pkt, ETH_HEADER_LEN + LLC_HEADER_LEN + bpdu_size);
- eth = ofpbuf_put_uninit(&pkt, sizeof *eth);
- llc = ofpbuf_put_uninit(&pkt, sizeof *llc);
- ofpbuf_put(&pkt, bpdu, bpdu_size);
-
- /* 802.2 header. */
- memcpy(eth->eth_dst, stp_eth_addr, ETH_ADDR_LEN);
- memcpy(eth->eth_src, port_mac, ETH_ADDR_LEN);
- eth->eth_type = htons(pkt.size - ETH_HEADER_LEN);
-
- /* LLC header. */
- llc->llc_dsap = STP_LLC_DSAP;
- llc->llc_ssap = STP_LLC_SSAP;
- llc->llc_cntl = STP_LLC_CNTL;
-
- opo = make_unbuffered_packet_out(&pkt, OFPP_NONE, port_no);
- ofpbuf_uninit(&pkt);
- rconn_send_with_limit(stp->local_rconn, opo, &stp->n_txq, OFPP_MAX);
+ ofpbuf_delete(pkt);
}
static bool
@@ -245,7 +222,7 @@ stp_is_port_supported(uint16_t port_no)
static void
stp_port_changed_cb(uint16_t port_no,
- const struct ofp_phy_port *old,
+ const struct ofp_phy_port *old UNUSED,
const struct ofp_phy_port *new,
void *stp_)
{
@@ -295,8 +272,7 @@ static struct hook_class stp_hook_class = {
};
void
-stp_start(struct secchan *secchan, const struct settings *s,
- struct port_watcher *pw,
+stp_start(struct secchan *secchan, struct port_watcher *pw,
struct rconn *local, struct rconn *remote)
{
uint8_t dpid[ETH_ADDR_LEN];
@@ -308,7 +284,7 @@ stp_start(struct secchan *secchan, const struct settings *s,
stp->pw = pw;
stp->local_rconn = local;
stp->remote_rconn = remote;
- stp->last_tick_256ths = time_256ths();
+ stp->last_tick = time_msec();
port_watcher_register_callback(pw, stp_port_changed_cb, stp);
port_watcher_register_local_port_callback(pw, stp_local_port_changed_cb,
diff --git a/secchan/stp-secchan.h b/secchan/stp-secchan.h
index ac4076f..2d1105f 100644
--- a/secchan/stp-secchan.h
+++ b/secchan/stp-secchan.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+/* Copyright (c) 2008, 2009 The Board of Trustees of The Leland Stanford
* Junior University
*
* We are making the OpenFlow specification and associated documentation
@@ -41,10 +41,8 @@
struct port_watcher;
struct rconn;
struct secchan;
-struct settings;
-void stp_start(struct secchan *, const struct settings *,
- struct port_watcher *,
+void stp_start(struct secchan *, struct port_watcher *,
struct rconn *local, struct rconn *remote);
#endif /* stp-secchan.h */
diff --git a/soexpand.pl b/soexpand.pl
new file mode 100755
index 0000000..4e13005
--- /dev/null
+++ b/soexpand.pl
@@ -0,0 +1,26 @@
+use strict;
+use warnings;
+use Getopt::Long;
+
+my ($exit_code) = 0;
+my (@include_dirs);
+Getopt::Long::Configure ("bundling");
+GetOptions("I|include=s" => \@include_dirs) or exit(1);
+@include_dirs = ('.') if !@include_dirs;
+OUTER: while () {
+ if (my ($name) = /^\.so (\S+)$/) {
+ foreach my $dir (@include_dirs, '.') {
+ if (open(INNER, "$dir/$name")) {
+ while () {
+ print $_;
+ }
+ close(INNER);
+ next OUTER;
+ }
+ }
+ print STDERR "$name not found in: ", join(' ', @include_dirs), "\n";
+ $exit_code = 1;
+ }
+ print $_;
+}
+exit $exit_code;
diff --git a/subst b/subst
deleted file mode 100755
index c2e71df..0000000
--- a/subst
+++ /dev/null
@@ -1,5 +0,0 @@
-#! /bin/sh
-exec sed -e "/^@$1@\$/{
-r $2
-d
-}"
diff --git a/tests/automake.mk b/tests/automake.mk
index 0c7edd1..a4e945a 100644
--- a/tests/automake.mk
+++ b/tests/automake.mk
@@ -1,3 +1,14 @@
+TESTS += tests/test-flows.sh
+noinst_PROGRAMS += tests/test-flows
+tests_test_flows_SOURCES = tests/test-flows.c
+tests_test_flows_LDADD = lib/libopenflow.a
+dist_check_SCRIPTS = tests/test-flows.sh tests/flowgen.pl
+
+TESTS += tests/test-hmap
+noinst_PROGRAMS += tests/test-hmap
+tests_test_hmap_SOURCES = tests/test-hmap.c
+tests_test_hmap_LDADD = lib/libopenflow.a
+
TESTS += tests/test-list
noinst_PROGRAMS += tests/test-list
tests_test_list_SOURCES = tests/test-list.c
diff --git a/tests/flowgen.pl b/tests/flowgen.pl
new file mode 100755
index 0000000..eb17b2a
--- /dev/null
+++ b/tests/flowgen.pl
@@ -0,0 +1,224 @@
+#! /usr/bin/perl
+
+use strict;
+use warnings;
+
+open(FLOWS, ">&=3");# or die "failed to open fd 3 for writing: $!\n";
+open(PACKETS, ">&=4");# or die "failed to open fd 4 for writing: $!\n";
+
+# Print pcap file header.
+print PACKETS pack('NnnNNNN',
+ 0xa1b2c3d4, # magic number
+ 2, # major version
+ 4, # minor version
+ 0, # time zone offset
+ 0, # time stamp accuracy
+ 1518, # snaplen
+ 1); # Ethernet
+
+output(DL_HEADER => '802.2');
+
+for my $dl_header qw(802.2+SNAP Ethernet) {
+ my %a = (DL_HEADER => $dl_header);
+ for my $dl_vlan qw(none zero nonzero) {
+ my %b = (%a, DL_VLAN => $dl_vlan);
+
+ # Non-IP case.
+ output(%b, DL_TYPE => 'non-ip');
+
+ for my $ip_options qw(no yes) {
+ my %c = (%b, DL_TYPE => 'ip', IP_OPTIONS => $ip_options);
+ for my $ip_fragment qw(no first middle last) {
+ my %d = (%c, IP_FRAGMENT => $ip_fragment);
+ for my $tp_proto qw(TCP TCP+options UDP ICMP other) {
+ output(%d, TP_PROTO => $tp_proto);
+ }
+ }
+ }
+ }
+}
+
+sub output {
+ my (%attrs) = @_;
+
+ # Compose flow.
+ my (%flow);
+ $flow{DL_SRC} = "00:02:e3:0f:80:a4";
+ $flow{DL_DST} = "00:1a:92:40:ac:05";
+ $flow{NW_PROTO} = 0;
+ $flow{NW_SRC} = '0.0.0.0';
+ $flow{NW_DST} = '0.0.0.0';
+ $flow{TP_SRC} = 0;
+ $flow{TP_DST} = 0;
+ if (defined($attrs{DL_VLAN})) {
+ my (%vlan_map) = ('none' => 0xffff,
+ 'zero' => 0,
+ 'nonzero' => 0x0123);
+ $flow{DL_VLAN} = $vlan_map{$attrs{DL_VLAN}};
+ } else {
+ $flow{DL_VLAN} = 0xffff; # OFP_VLAN_NONE
+ }
+ if ($attrs{DL_HEADER} eq '802.2') {
+ $flow{DL_TYPE} = 0x5ff; # OFP_DL_TYPE_NOT_ETH_TYPE
+ } elsif ($attrs{DL_TYPE} eq 'ip') {
+ $flow{DL_TYPE} = 0x0800; # ETH_TYPE_IP
+ $flow{NW_SRC} = '10.0.2.15';
+ $flow{NW_DST} = '192.168.1.20';
+ if ($attrs{TP_PROTO} eq 'other') {
+ $flow{NW_PROTO} = 42;
+ } elsif ($attrs{TP_PROTO} eq 'TCP' ||
+ $attrs{TP_PROTO} eq 'TCP+options') {
+ $flow{NW_PROTO} = 6; # IP_TYPE_TCP
+ $flow{TP_SRC} = 6667;
+ $flow{TP_DST} = 9998;
+ } elsif ($attrs{TP_PROTO} eq 'UDP') {
+ $flow{NW_PROTO} = 17; # IP_TYPE_UDP
+ $flow{TP_SRC} = 1112;
+ $flow{TP_DST} = 2223;
+ } elsif ($attrs{TP_PROTO} eq 'ICMP') {
+ $flow{NW_PROTO} = 1; # IP_TYPE_ICMP
+ $flow{TP_SRC} = 8; # echo request
+ $flow{TP_DST} = 0; # code
+ } else {
+ die;
+ }
+ if ($attrs{IP_FRAGMENT} ne 'no') {
+ $flow{TP_SRC} = $flow{TP_DST} = 0;
+ }
+ } elsif ($attrs{DL_TYPE} eq 'non-ip') {
+ $flow{DL_TYPE} = 0x5678;
+ } else {
+ die;
+ }
+
+ # Compose packet.
+ my $packet = '';
+ $packet .= pack_ethaddr($flow{DL_DST});
+ $packet .= pack_ethaddr($flow{DL_SRC});
+ $packet .= pack('n', 0) if $attrs{DL_HEADER} =~ /^802.2/;
+ if ($attrs{DL_HEADER} eq '802.2') {
+ $packet .= pack('CCC', 0x42, 0x42, 0x03); # LLC for 802.1D STP.
+ } else {
+ if ($attrs{DL_HEADER} eq '802.2+SNAP') {
+ $packet .= pack('CCC', 0xaa, 0xaa, 0x03); # LLC for SNAP.
+ $packet .= pack('CCC', 0, 0, 0); # SNAP OUI.
+ }
+ if ($attrs{DL_VLAN} ne 'none') {
+ $packet .= pack('nn', 0x8100, $flow{DL_VLAN});
+ }
+ $packet .= pack('n', $flow{DL_TYPE});
+ if ($attrs{DL_TYPE} eq 'ip') {
+ my $ip = pack('CCnnnCCnNN',
+ (4 << 4) | 5, # version, hdrlen
+ 0, # type of service
+ 0, # total length (filled in later)
+ 65432, # id
+ 0, # frag offset
+ 64, # ttl
+ $flow{NW_PROTO}, # protocol
+ 0, # checksum
+ 0x0a00020f, # source
+ 0xc0a80114); # dest
+ if ($attrs{IP_OPTIONS} eq 'yes') {
+ substr($ip, 0, 1) = pack('C', (4 << 4) | 8);
+ $ip .= pack('CCnnnCCCx',
+ 130, # type
+ 11, # length
+ 0x6bc5, # top secret
+ 0xabcd,
+ 0x1234,
+ 1,
+ 2,
+ 3);
+ }
+ if ($attrs{IP_FRAGMENT} ne 'no') {
+ my (%frag_map) = ('first' => 0x2000, # more frags, ofs 0
+ 'middle' => 0x2111, # more frags, ofs 0x888
+ 'last' => 0x0222); # last frag, ofs 0x1110
+ substr($ip, 6, 2)
+ = pack('n', $frag_map{$attrs{IP_FRAGMENT}});
+ }
+
+ if ($attrs{TP_PROTO} =~ '^TCP') {
+ my $tcp = pack('nnNNnnnn',
+ $flow{TP_SRC}, # source port
+ $flow{TP_DST}, # dest port
+ 87123455, # seqno
+ 712378912, # ackno
+ (5 << 12) | 0x02 | 0x10, # hdrlen, SYN, ACK
+ 5823, # window size
+ 18923, # checksum
+ 12893); # urgent pointer
+ if ($attrs{TP_PROTO} eq 'TCP+options') {
+ substr($tcp, 12, 2) = pack('n', (6 << 12) | 0x02 | 0x10);
+ $tcp .= pack('CCn', 2, 4, 1975); # MSS option
+ }
+ $tcp .= 'payload';
+ $ip .= $tcp;
+ } elsif ($attrs{TP_PROTO} eq 'UDP') {
+ my $len = 15;
+ my $udp = pack('nnnn', $flow{TP_SRC}, $flow{TP_DST}, $len, 0);
+ $udp .= chr($len) while length($udp) < $len;
+ $ip .= $udp;
+ } elsif ($attrs{TP_PROTO} eq 'ICMP') {
+ $ip .= pack('CCnnn',
+ 8, # echo request
+ 0, # code
+ 0, # checksum
+ 736, # identifier
+ 931); # sequence number
+ } elsif ($attrs{TP_PROTO} eq 'other') {
+ $ip .= 'other header';
+ } else {
+ die;
+ }
+
+ substr($ip, 2, 2) = pack('n', length($ip));
+ $packet .= $ip;
+ }
+ }
+ substr($packet, 12, 2) = pack('n', length($packet))
+ if $attrs{DL_HEADER} =~ /^802.2/;
+
+ print join(' ', map("$_=$attrs{$_}", keys(%attrs))), "\n";
+ print join(' ', map("$_=$flow{$_}", keys(%flow))), "\n";
+ print "\n";
+
+ print FLOWS pack('Nn',
+ 0, # wildcards
+ 0); # in_port
+ print FLOWS pack_ethaddr($flow{DL_SRC});
+ print FLOWS pack_ethaddr($flow{DL_DST});
+ print FLOWS pack('nnCxNNnn',
+ $flow{DL_VLAN},
+ $flow{DL_TYPE},
+ $flow{NW_PROTO},
+ inet_aton($flow{NW_SRC}),
+ inet_aton($flow{NW_DST}),
+ $flow{TP_SRC},
+ $flow{TP_DST});
+
+ print PACKETS pack('NNNN',
+ 0, # timestamp seconds
+ 0, # timestamp microseconds
+ length($packet), # bytes saved
+ length($packet)), # total length
+ $packet;
+}
+
+sub pack_ethaddr {
+ local ($_) = @_;
+ my $xx = '([0-9a-fA-F][0-9a-fA-F])';
+ my (@octets) = /$xx:$xx:$xx:$xx:$xx:$xx/;
+ @octets == 6 or die $_;
+ my ($out) = '';
+ $out .= pack('C', hex($_)) foreach @octets;
+ return $out;
+}
+
+sub inet_aton {
+ local ($_) = @_;
+ my ($a, $b, $c, $d) = /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/;
+ defined $d or die $_;
+ return ($a << 24) | ($b << 16) | ($c << 8) | $d;
+}
diff --git a/tests/test-flows.c b/tests/test-flows.c
new file mode 100644
index 0000000..fb4026f
--- /dev/null
+++ b/tests/test-flows.c
@@ -0,0 +1,76 @@
+#include
+#include "flow.h"
+#include
+#include
+#include
+#include "openflow/openflow.h"
+#include "timeval.h"
+#include "ofpbuf.h"
+#include "ofp-print.h"
+#include "pcap.h"
+#include "util.h"
+#include "vlog.h"
+
+#undef NDEBUG
+#include
+
+int
+main(int argc UNUSED, char *argv[])
+{
+ struct ofp_match expected_match;
+ FILE *flows, *pcap;
+ int retval;
+ int n = 0, errors = 0;
+
+ set_program_name(argv[0]);
+ time_init();
+ vlog_init();
+
+ flows = stdin;
+ pcap = fdopen(3, "rb");
+ if (!pcap) {
+ ofp_fatal(errno, "failed to open fd 3 for reading");
+ }
+
+ retval = pcap_read_header(pcap);
+ if (retval) {
+ ofp_fatal(retval > 0 ? retval : 0, "reading pcap header failed");
+ }
+
+ while (fread(&expected_match, sizeof expected_match, 1, flows)) {
+ struct ofpbuf *packet;
+ struct ofp_match extracted_match;
+ struct flow flow;
+
+ n++;
+
+ retval = pcap_read(pcap, &packet);
+ if (retval == EOF) {
+ ofp_fatal(0, "unexpected end of file reading pcap file");
+ } else if (retval) {
+ ofp_fatal(retval, "error reading pcap file");
+ }
+
+ flow_extract(packet, 0, &flow);
+ flow_fill_match(&extracted_match, &flow, 0);
+
+ if (memcmp(&expected_match, &extracted_match, sizeof expected_match)) {
+ char *exp_s = ofp_match_to_string(&expected_match, 2);
+ char *got_s = ofp_match_to_string(&extracted_match, 2);
+ errors++;
+ printf("mismatch on packet #%d (1-based).\n", n);
+ printf("Packet:\n");
+ ofp_print_packet(stdout, packet->data, packet->size, packet->size);
+ printf("Expected flow:\n%s\n", exp_s);
+ printf("Actually extracted flow:\n%s\n", got_s);
+ printf("\n");
+ free(exp_s);
+ free(got_s);
+ }
+
+ ofpbuf_delete(packet);
+ }
+ printf("checked %d packets, %d errors\n", n, errors);
+ return errors != 0;
+}
+
diff --git a/tests/test-flows.sh b/tests/test-flows.sh
new file mode 100755
index 0000000..0d38ad7
--- /dev/null
+++ b/tests/test-flows.sh
@@ -0,0 +1,9 @@
+#! /bin/sh -e
+srcdir=`cd $srcdir && pwd`
+trap 'rm -f flows$$ pcap$$ out$$' 0 1 2 13 15
+cd tests
+"$srcdir"/tests/flowgen.pl >/dev/null 3>flows$$ 4>pcap$$
+./test-flows out$$ || true
+diff -u - out$$ <
+#include "hmap.h"
+#include
+#include "hash.h"
+#include "util.h"
+
+#undef NDEBUG
+#include
+
+/* Sample hmap element. */
+struct element {
+ int value;
+ struct hmap_node node;
+};
+
+typedef size_t hash_func(int value);
+
+static int
+compare_ints(const void *a_, const void *b_)
+{
+ const int *a = a_;
+ const int *b = b_;
+ return *a < *b ? -1 : *a > *b;
+}
+
+/* Verifies that 'hmap' contains exactly the 'n' values in 'values'. */
+static void
+check_hmap(struct hmap *hmap, const int values[], size_t n,
+ hash_func *hash)
+{
+ int *sort_values, *hmap_values;
+ struct element *e;
+ size_t i;
+
+ /* Check that all the values are there in iteration. */
+ sort_values = xmalloc(sizeof *sort_values * n);
+ hmap_values = xmalloc(sizeof *sort_values * n);
+
+ i = 0;
+ HMAP_FOR_EACH (e, struct element, node, hmap) {
+ assert(i < n);
+ hmap_values[i++] = e->value;
+ }
+ assert(i == n);
+
+ memcpy(sort_values, values, sizeof *sort_values * n);
+ qsort(sort_values, n, sizeof *sort_values, compare_ints);
+ qsort(hmap_values, n, sizeof *hmap_values, compare_ints);
+
+ for (i = 0; i < n; i++) {
+ assert(sort_values[i] == hmap_values[i]);
+ }
+
+ free(hmap_values);
+ free(sort_values);
+
+ /* Check that all the values are there in lookup. */
+ for (i = 0; i < n; i++) {
+ size_t count = 0;
+
+ HMAP_FOR_EACH_WITH_HASH (e, struct element, node,
+ hash(values[i]), hmap) {
+ count += e->value == values[i];
+ }
+ assert(count == 1);
+ }
+
+ /* Check counters. */
+ assert(hmap_is_empty(hmap) == !n);
+ assert(hmap_count(hmap) == n);
+}
+
+/* Puts the 'n' values in 'values' into 'elements', and then puts those
+ * elements into 'hmap'. */
+static void
+make_hmap(struct hmap *hmap, struct element elements[],
+ int values[], size_t n, hash_func *hash)
+{
+ size_t i;
+
+ hmap_init(hmap);
+ for (i = 0; i < n; i++) {
+ elements[i].value = i;
+ hmap_insert(hmap, &elements[i].node, hash(elements[i].value));
+ values[i] = i;
+ }
+}
+
+static void
+shuffle(int *p, size_t n)
+{
+ for (; n > 1; n--, p++) {
+ int *q = &p[rand() % n];
+ int tmp = *p;
+ *p = *q;
+ *q = tmp;
+ }
+}
+
+#if 0
+/* Prints the values in 'hmap', plus 'name' as a title. */
+static void
+print_hmap(const char *name, struct hmap *hmap)
+{
+ struct element *e;
+
+ printf("%s:", name);
+ HMAP_FOR_EACH (e, struct element, node, hmap) {
+ printf(" %d(%zu)", e->value, e->node.hash & hmap->mask);
+ }
+ printf("\n");
+}
+
+/* Prints the 'n' values in 'values', plus 'name' as a title. */
+static void
+print_ints(const char *name, const int *values, size_t n)
+{
+ size_t i;
+
+ printf("%s:", name);
+ for (i = 0; i < n; i++) {
+ printf(" %d", values[i]);
+ }
+ printf("\n");
+}
+#endif
+
+static size_t
+identity_hash(int value)
+{
+ return value;
+}
+
+static size_t
+good_hash(int value)
+{
+ const uint32_t x = value;
+ return hash_words(&x, 1, 0x1234abcd);
+}
+
+static size_t
+constant_hash(int value UNUSED)
+{
+ return 123;
+}
+
+/* Tests basic hmap insertion and deletion. */
+static void
+test_hmap_insert_delete(hash_func *hash)
+{
+ enum { N_ELEMS = 100 };
+
+ struct element elements[N_ELEMS];
+ int values[N_ELEMS];
+ struct hmap hmap;
+ size_t i;
+
+ hmap_init(&hmap);
+ for (i = 0; i < N_ELEMS; i++) {
+ elements[i].value = i;
+ hmap_insert(&hmap, &elements[i].node, hash(i));
+ values[i] = i;
+ check_hmap(&hmap, values, i + 1, hash);
+ }
+ shuffle(values, N_ELEMS);
+ for (i = 0; i < N_ELEMS; i++) {
+ hmap_remove(&hmap, &elements[values[i]].node);
+ check_hmap(&hmap, values + (i + 1), N_ELEMS - (i + 1), hash);
+ }
+ hmap_destroy(&hmap);
+}
+
+/* Tests basic hmap_reserve() and hmap_shrink(). */
+static void
+test_hmap_reserve_shrink(hash_func *hash)
+{
+ enum { N_ELEMS = 32 };
+
+ size_t i;
+
+ for (i = 0; i < N_ELEMS; i++) {
+ struct element elements[N_ELEMS];
+ int values[N_ELEMS];
+ struct hmap hmap;
+ size_t j;
+
+ hmap_init(&hmap);
+ hmap_reserve(&hmap, i);
+ for (j = 0; j < N_ELEMS; j++) {
+ elements[j].value = j;
+ hmap_insert(&hmap, &elements[j].node, hash(j));
+ values[j] = j;
+ check_hmap(&hmap, values, j + 1, hash);
+ }
+ shuffle(values, N_ELEMS);
+ for (j = 0; j < N_ELEMS; j++) {
+ hmap_remove(&hmap, &elements[values[j]].node);
+ hmap_shrink(&hmap);
+ check_hmap(&hmap, values + (j + 1), N_ELEMS - (j + 1), hash);
+ }
+ hmap_destroy(&hmap);
+ }
+}
+
+/* Tests that HMAP_FOR_EACH_SAFE properly allows for deletion of the current
+ * element of a hmap. */
+static void
+test_hmap_for_each_safe(hash_func *hash)
+{
+ enum { MAX_ELEMS = 10 };
+ size_t n;
+ unsigned long int pattern;
+
+ for (n = 0; n <= MAX_ELEMS; n++) {
+ for (pattern = 0; pattern < 1ul << n; pattern++) {
+ struct element elements[MAX_ELEMS];
+ int values[MAX_ELEMS];
+ struct hmap hmap;
+ struct element *e, *next;
+ size_t n_remaining;
+ int i;
+
+ make_hmap(&hmap, elements, values, n, hash);
+
+ i = 0;
+ n_remaining = n;
+ HMAP_FOR_EACH_SAFE (e, next, struct element, node, &hmap) {
+ assert(i < n);
+ if (pattern & (1ul << e->value)) {
+ size_t j;
+ hmap_remove(&hmap, &e->node);
+ for (j = 0; ; j++) {
+ assert(j < n_remaining);
+ if (values[j] == e->value) {
+ values[j] = values[--n_remaining];
+ break;
+ }
+ }
+ }
+ check_hmap(&hmap, values, n_remaining, hash);
+ i++;
+ }
+ assert(i == n);
+
+ for (i = 0; i < n; i++) {
+ if (pattern & (1ul << i)) {
+ n_remaining++;
+ }
+ }
+ assert(n == n_remaining);
+
+ hmap_destroy(&hmap);
+ }
+ }
+}
+
+static void
+run_test(void (*function)(hash_func *))
+{
+ hash_func *hash_funcs[] = { identity_hash, good_hash, constant_hash };
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(hash_funcs); i++) {
+ function(hash_funcs[i]);
+ printf(".");
+ fflush(stdout);
+ }
+}
+
+int
+main(void)
+{
+ run_test(test_hmap_insert_delete);
+ run_test(test_hmap_for_each_safe);
+ run_test(test_hmap_reserve_shrink);
+ printf("\n");
+ return 0;
+}
+
diff --git a/tests/test-stp-ieee802.1d-1998 b/tests/test-stp-ieee802.1d-1998
index 88aedb4..f1982a0 100644
--- a/tests/test-stp-ieee802.1d-1998
+++ b/tests/test-stp-ieee802.1d-1998
@@ -4,7 +4,7 @@ bridge 1 0x97 = c:5 a d:5
bridge 2 0x45 = b e
bridge 3 0x57 = b:5 e:5
bridge 4 0x83 = a:5 e:5
-run 256
+run 1000
check 0 = root
check 1 = F F:10 F
check 2 = F:10 B
diff --git a/tests/test-stp-ieee802.1d-2004-fig17.4 b/tests/test-stp-ieee802.1d-2004-fig17.4
index 29b13ff..1f70863 100644
--- a/tests/test-stp-ieee802.1d-2004-fig17.4
+++ b/tests/test-stp-ieee802.1d-2004-fig17.4
@@ -7,7 +7,7 @@ bridge 4 0x555 = g i 0 0
bridge 5 0x666 = h k 0 0
bridge 6 0x777 = j m 0 0
bridge 7 0x888 = l n 0 0
-run 256
+run 1000
check 0 = root
check 1 = F:10 B F F
check 2 = F:10 B F F F F
@@ -20,7 +20,7 @@ check 7 = F:20 B F F
# Now connect two ports of bridge 7 to the same LAN.
bridge 7 = l n o o
# Same results except for bridge 7:
-run 256
+run 1000
check 0 = root
check 1 = F:10 B F F
check 2 = F:10 B F F F F
diff --git a/tests/test-stp-ieee802.1d-2004-fig17.6 b/tests/test-stp-ieee802.1d-2004-fig17.6
index 694d238..6ed5917 100644
--- a/tests/test-stp-ieee802.1d-2004-fig17.6
+++ b/tests/test-stp-ieee802.1d-2004-fig17.6
@@ -5,7 +5,7 @@ bridge 2 0x333 = d e f
bridge 3 0x444 = f g h
bridge 4 0x555 = j h i
bridge 5 0x666 = l j k
-run 256
+run 1000
check 0 = root
check 1 = F:10 F F
check 2 = F:20 F F
diff --git a/tests/test-stp-ieee802.1d-2004-fig17.7 b/tests/test-stp-ieee802.1d-2004-fig17.7
index 278655e..daa0cdf 100644
--- a/tests/test-stp-ieee802.1d-2004-fig17.7
+++ b/tests/test-stp-ieee802.1d-2004-fig17.7
@@ -2,7 +2,7 @@
bridge 0 0xaa = b
bridge 1 0x111 = a b d f h g e c
bridge 2 0x222 = g h j l n m k i
-run 256
+run 1000
check 0 = root
check 1 = F F:10 F F F F F F
check 2 = B F:20 F F F F F F
@@ -11,7 +11,7 @@ check 2 = B F:20 F F F F F F
# but I don't understand what port priority change would cause
# that change.
bridge 2 = g X j l n m k i
-run 256
+run 1000
check 0 = root
check 1 = F F:10 F F F F F F
check 2 = F:20 D F F F F F F
diff --git a/tests/test-stp-iol-io-1.1 b/tests/test-stp-iol-io-1.1
index 1b53fed..186d6c4 100644
--- a/tests/test-stp-iol-io-1.1
+++ b/tests/test-stp-iol-io-1.1
@@ -4,22 +4,22 @@
# STP.io.1.1: Link Failure
bridge 0 0x111 = a b c
bridge 1 0x222 = a b c
-run 256
+run 1000
check 0 = root
check 1 = F:10 B B
bridge 1 = 0 _ _
-run 256
+run 1000
check 0 = root
check 1 = F F:10 B
bridge 1 = X _ _
-run 256
+run 1000
check 0 = root
check 1 = D F:10 B
bridge 1 = _ 0 _
-run 256
+run 1000
check 0 = root
check 1 = D F F:10
bridge 1 = _ X _
-run 256
+run 1000
check 0 = root
check 1 = D D F:10
diff --git a/tests/test-stp-iol-io-1.2 b/tests/test-stp-iol-io-1.2
index a2ed48c..285bbd8 100644
--- a/tests/test-stp-iol-io-1.2
+++ b/tests/test-stp-iol-io-1.2
@@ -4,11 +4,11 @@
# STP.io.1.2: Repeated Network
bridge 0 0x111 = a a
bridge 1 0x222 = a a
-run 256
+run 1000
check 0 = rootid:0x111 F B
check 1 = rootid:0x111 F:10 B
bridge 1 = a^0x90 _
-run 256
+run 1000
check 0 = rootid:0x111 F B
check 1 = rootid:0x111 B F:10
diff --git a/tests/test-stp-iol-io-1.4 b/tests/test-stp-iol-io-1.4
index f8b0b72..0065aaf 100644
--- a/tests/test-stp-iol-io-1.4
+++ b/tests/test-stp-iol-io-1.4
@@ -6,7 +6,7 @@ bridge 0 0x111 = a b c
bridge 1 0x222 = b d e
bridge 2 0x333 = a d f
bridge 3 0x444 = c e f
-run 256
+run 1000
check 0 = root
check 1 = F:10 F F
check 2 = F:10 B F
diff --git a/tests/test-stp-iol-io-1.5 b/tests/test-stp-iol-io-1.5
index c5f08b8..285d29d 100644
--- a/tests/test-stp-iol-io-1.5
+++ b/tests/test-stp-iol-io-1.5
@@ -6,25 +6,25 @@ bridge 0 0x111 = a b d c
bridge 1 0x222 = a b f e
bridge 2 0x333 = c d g h
bridge 3 0x444 = e f g h
-run 256
+run 1000
check 0 = root
check 1 = F:10 B F F
check 2 = B F:10 F F
check 3 = B F:20 B B
bridge 1^0x7000
-run 256
+run 1000
check 0 = F:10 B F F
check 1 = root
check 2 = B F:20 B B
check 3 = B F:10 F F
bridge 2^0x6000
-run 256
+run 1000
check 0 = F F B F:10
check 1 = F:20 B B B
check 2 = root
check 3 = F F F:10 B
bridge 3^0x5000
-run 256
+run 1000
check 0 = B B B F:20
check 1 = F F B F:10
check 2 = F F F:10 B
@@ -33,7 +33,7 @@ bridge 0^0x4000
bridge 1^0x4001
bridge 2^0x4002
bridge 3^0x4003
-run 256
+run 1000
check 0 = root
check 1 = F:10 B F F
check 2 = B F:10 F F
diff --git a/tests/test-stp-iol-op-1.4 b/tests/test-stp-iol-op-1.4
index 18c09b8..6a12116 100644
--- a/tests/test-stp-iol-op-1.4
+++ b/tests/test-stp-iol-op-1.4
@@ -4,5 +4,5 @@
# Test STP.op.1.4 All Ports Initialized to Designated Ports
bridge 0 0x123 = a b c d e f
check 0 = Li Li Li Li Li Li
-run 256
+run 1000
check 0 = F F F F F F
diff --git a/tests/test-stp-iol-op-3.1 b/tests/test-stp-iol-op-3.1
index 2e0a2f2..3e1099c 100644
--- a/tests/test-stp-iol-op-3.1
+++ b/tests/test-stp-iol-op-3.1
@@ -6,6 +6,6 @@ bridge 0 0x111 = a
bridge 1 0x222 = a
check 0 = rootid:0x111 Li
check 1 = rootid:0x222 Li
-run 256
+run 1000
check 0 = rootid:0x111 root
check 1 = rootid:0x111 F:10
diff --git a/tests/test-stp-iol-op-3.3 b/tests/test-stp-iol-op-3.3
index 093c64e..2bcd45e 100644
--- a/tests/test-stp-iol-op-3.3
+++ b/tests/test-stp-iol-op-3.3
@@ -5,7 +5,7 @@
bridge 0 0x333^0x6000 = a
bridge 1 0x222^0x7000 = b
bridge 2 0x111 = a b
-run 256
+run 1000
check 0 = rootid:0x333^0x6000 root
check 1 = rootid:0x333^0x6000 F:20
check 2 = rootid:0x333^0x6000 F:10 F
diff --git a/tests/test-stp-iol-op-3.4 b/tests/test-stp-iol-op-3.4
index 093c64e..2bcd45e 100644
--- a/tests/test-stp-iol-op-3.4
+++ b/tests/test-stp-iol-op-3.4
@@ -5,7 +5,7 @@
bridge 0 0x333^0x6000 = a
bridge 1 0x222^0x7000 = b
bridge 2 0x111 = a b
-run 256
+run 1000
check 0 = rootid:0x333^0x6000 root
check 1 = rootid:0x333^0x6000 F:20
check 2 = rootid:0x333^0x6000 F:10 F
diff --git a/tests/test-stp.c b/tests/test-stp.c
index 9b87c0e..ddb7db7 100644
--- a/tests/test-stp.c
+++ b/tests/test-stp.c
@@ -38,6 +38,7 @@
#include
#include
#include
+#include "ofpbuf.h"
#include "packets.h"
struct bpdu {
@@ -97,28 +98,31 @@ new_test_case(void)
}
static void
-send_bpdu(const void *data, size_t size, int port_no, void *b_)
+send_bpdu(struct ofpbuf *pkt, int port_no, void *b_)
{
struct bridge *b = b_;
struct lan *lan;
- int i;
assert(port_no < b->n_ports);
lan = b->ports[port_no];
- if (!lan) {
- return;
- }
- for (i = 0; i < lan->n_conns; i++) {
- struct lan_conn *conn = &lan->conns[i];
- if (conn->bridge != b || conn->port_no != port_no) {
- struct bridge *dst = conn->bridge;
- struct bpdu *bpdu = &dst->rxq[dst->rxq_head++ % RXQ_SIZE];
- assert(dst->rxq_head - dst->rxq_tail <= RXQ_SIZE);
- bpdu->data = xmemdup(data, size);
- bpdu->size = size;
- bpdu->port_no = conn->port_no;
+ if (lan) {
+ const void *data = pkt->l3;
+ size_t size = (char *) ofpbuf_tail(pkt) - (char *) data;
+ int i;
+
+ for (i = 0; i < lan->n_conns; i++) {
+ struct lan_conn *conn = &lan->conns[i];
+ if (conn->bridge != b || conn->port_no != port_no) {
+ struct bridge *dst = conn->bridge;
+ struct bpdu *bpdu = &dst->rxq[dst->rxq_head++ % RXQ_SIZE];
+ assert(dst->rxq_head - dst->rxq_tail <= RXQ_SIZE);
+ bpdu->data = xmemdup(data, size);
+ bpdu->size = size;
+ bpdu->port_no = conn->port_no;
+ }
}
}
+ ofpbuf_delete(pkt);
}
static struct bridge *
@@ -299,7 +303,7 @@ simulate(struct test_case *tc, int granularity)
{
int time;
- for (time = 0; time < 256 * 180; time += granularity) {
+ for (time = 0; time < 1000 * 180; time += granularity) {
int round_trips;
int i;
diff --git a/udatapath/.gitignore b/udatapath/.gitignore
index 5ce5066..e4272b0 100644
--- a/udatapath/.gitignore
+++ b/udatapath/.gitignore
@@ -1,4 +1,4 @@
/Makefile
/Makefile.in
-/udatapath
-/udatapath.8
+/ofdatapath
+/ofdatapath.8
diff --git a/udatapath/automake.mk b/udatapath/automake.mk
index 207fa5f..a96b1a9 100644
--- a/udatapath/automake.mk
+++ b/udatapath/automake.mk
@@ -1,7 +1,7 @@
-bin_PROGRAMS += udatapath/udatapath
-man_MANS += udatapath/udatapath.8
+bin_PROGRAMS += udatapath/ofdatapath
+man_MANS += udatapath/ofdatapath.8
-udatapath_udatapath_SOURCES = \
+udatapath_ofdatapath_SOURCES = \
udatapath/chain.c \
udatapath/chain.h \
udatapath/crc32.c \
@@ -21,7 +21,7 @@ udatapath_udatapath_SOURCES = \
udatapath/table-hash.c \
udatapath/table-linear.c
-udatapath_udatapath_LDADD = lib/libopenflow.a $(SSL_LIBS) $(FAULT_LIBS)
+udatapath_ofdatapath_LDADD = lib/libopenflow.a $(SSL_LIBS) $(FAULT_LIBS)
-EXTRA_DIST += udatapath/udatapath.8.in
-DISTCLEANFILES += udatapath/udatapath.8
+EXTRA_DIST += udatapath/ofdatapath.8.in
+DISTCLEANFILES += udatapath/ofdatapath.8
diff --git a/udatapath/datapath.c b/udatapath/datapath.c
index 5c3f137..6324b19 100644
--- a/udatapath/datapath.c
+++ b/udatapath/datapath.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+/* Copyright (c) 2008, 2009 The Board of Trustees of The Leland Stanford
* Junior University
*
* We are making the OpenFlow specification and associated documentation
@@ -113,9 +113,7 @@ static void remote_wait(struct remote *);
static void remote_destroy(struct remote *);
static void update_port_flags(struct datapath *, const struct ofp_port_mod *);
-static int update_port_status(struct sw_port *p);
static void send_port_status(struct sw_port *p, uint8_t status);
-static void del_switch_port(struct sw_port *p);
/* Buffers are identified by a 31-bit opaque ID. We divide the ID
* into a buffer number (low bits) and a cookie (high bits). The buffer number
@@ -298,12 +296,6 @@ dp_run(struct datapath *dp)
struct list deleted = LIST_INITIALIZER(&deleted);
struct sw_flow *f, *n;
- LIST_FOR_EACH (p, struct sw_port, node, &dp->port_list) {
- if (update_port_status(p)) {
- send_port_status(p, OFPPR_MODIFY);
- }
- }
-
chain_timeout(dp->chain, &deleted);
LIST_FOR_EACH_SAFE (f, n, struct sw_flow, node, &deleted) {
dp_send_flow_end(dp, f, f->reason);
@@ -487,32 +479,6 @@ dp_wait(struct datapath *dp)
}
}
-/* Delete 'p' from switch. */
-static void
-del_switch_port(struct sw_port *p)
-{
- send_port_status(p, OFPPR_DELETE);
- netdev_close(p->netdev);
- p->netdev = NULL;
- list_remove(&p->node);
-}
-
-void
-dp_destroy(struct datapath *dp)
-{
- struct sw_port *p, *n;
-
- if (!dp) {
- return;
- }
-
- LIST_FOR_EACH_SAFE (p, n, struct sw_port, node, &dp->port_list) {
- del_switch_port(p);
- }
- chain_destroy(dp->chain);
- free(dp);
-}
-
/* Send packets out all the ports except the originating one. If the
* "flood" argument is set, don't send out ports with flooding disabled.
*/
@@ -544,7 +510,7 @@ output_all(struct datapath *dp, struct ofpbuf *buffer, int in_port, int flood)
return 0;
}
-void
+static void
output_packet(struct datapath *dp, struct ofpbuf *buffer, uint16_t out_port)
{
struct sw_port *p = lookup_port(dp, out_port);
@@ -688,8 +654,8 @@ dp_output_control(struct datapath *dp, struct ofpbuf *buffer, int in_port,
send_openflow_buffer(dp, buffer, NULL);
}
-static void fill_port_desc(struct datapath *dp, struct sw_port *p,
- struct ofp_phy_port *desc)
+static void
+fill_port_desc(struct sw_port *p, struct ofp_phy_port *desc)
{
desc->port_no = htons(p->port_no);
strncpy((char *) desc->name, netdev_get_name(p->netdev),
@@ -723,7 +689,7 @@ dp_send_features_reply(struct datapath *dp, const struct sender *sender)
LIST_FOR_EACH (p, struct sw_port, node, &dp->port_list) {
struct ofp_phy_port *opp = ofpbuf_put_uninit(buffer, sizeof *opp);
memset(opp, 0, sizeof *opp);
- fill_port_desc(dp, p, opp);
+ fill_port_desc(p, opp);
}
send_openflow_buffer(dp, buffer, sender);
}
@@ -745,56 +711,6 @@ update_port_flags(struct datapath *dp, const struct ofp_port_mod *opm)
p->config &= ~config_mask;
p->config |= ntohl(opm->config) & config_mask;
}
-
- if (opm->mask & htonl(OFPPC_PORT_DOWN)) {
- if ((opm->config & htonl(OFPPC_PORT_DOWN))
- && (p->config & OFPPC_PORT_DOWN) == 0) {
- p->config |= OFPPC_PORT_DOWN;
- netdev_turn_flags_off(p->netdev, NETDEV_UP, true);
- } else if ((opm->config & htonl(OFPPC_PORT_DOWN)) == 0
- && (p->config & OFPPC_PORT_DOWN)) {
- p->config &= ~OFPPC_PORT_DOWN;
- netdev_turn_flags_on(p->netdev, NETDEV_UP, true);
- }
- }
-}
-
-/* Update the port status field of the bridge port. A non-zero return
- * value indicates some field has changed.
- *
- * NB: Callers of this function may hold the RCU read lock, so any
- * additional checks must not sleep.
- */
-static int
-update_port_status(struct sw_port *p)
-{
- int retval;
- enum netdev_flags flags;
- uint32_t orig_config = p->config;
- uint32_t orig_state = p->state;
-
- if (netdev_get_flags(p->netdev, &flags) < 0) {
- VLOG_WARN_RL(&rl, "could not get netdev flags for %s",
- netdev_get_name(p->netdev));
- return 0;
- } else {
- if (flags & NETDEV_UP) {
- p->config &= ~OFPPC_PORT_DOWN;
- } else {
- p->config |= OFPPC_PORT_DOWN;
- }
- }
-
- /* Not all cards support this getting link status, so don't warn on
- * error. */
- retval = netdev_get_link_status(p->netdev);
- if (retval == 1) {
- p->state &= ~OFPPS_LINK_DOWN;
- } else if (retval == 0) {
- p->state |= OFPPS_LINK_DOWN;
- }
-
- return ((orig_config != p->config) || (orig_state != p->state));
}
static void
@@ -805,7 +721,7 @@ send_port_status(struct sw_port *p, uint8_t status)
ops = make_openflow_xid(sizeof *ops, OFPT_PORT_STATUS, 0, &buffer);
ops->reason = status;
memset(ops->pad, 0, sizeof ops->pad);
- fill_port_desc(p->dp, p, &ops->desc);
+ fill_port_desc(p, &ops->desc);
send_openflow_buffer(p->dp, buffer, NULL);
}
@@ -828,7 +744,7 @@ dp_send_flow_end(struct datapath *dp, struct sw_flow *flow,
nfe->header.vendor = htonl(NX_VENDOR_ID);
nfe->header.subtype = htonl(NXT_FLOW_END);
- flow_fill_match(&nfe->match, &flow->key);
+ flow_fill_match(&nfe->match, &flow->key.flow, flow->key.wildcards);
nfe->priority = htons(flow->priority);
nfe->reason = reason;
@@ -943,7 +859,7 @@ void fwd_port_input(struct datapath *dp, struct ofpbuf *buffer,
static int
recv_features_request(struct datapath *dp, const struct sender *sender,
- const void *msg)
+ const void *msg UNUSED)
{
dp_send_features_reply(dp, sender);
return 0;
@@ -951,7 +867,7 @@ recv_features_request(struct datapath *dp, const struct sender *sender,
static int
recv_get_config_request(struct datapath *dp, const struct sender *sender,
- const void *msg)
+ const void *msg UNUSED)
{
struct ofpbuf *buffer;
struct ofp_switch_config *osc;
@@ -1179,8 +1095,9 @@ recv_flow(struct datapath *dp, const struct sender *sender,
}
}
-static int desc_stats_dump(struct datapath *dp, void *state,
- struct ofpbuf *buffer)
+static int
+desc_stats_dump(struct datapath *dp UNUSED, void *state UNUSED,
+ struct ofpbuf *buffer)
{
struct ofp_desc_stats *ods = ofpbuf_put_uninit(buffer, sizeof *ods);
@@ -1203,8 +1120,8 @@ struct flow_stats_state {
#define MAX_FLOW_STATS_BYTES 4096
-static int flow_stats_init(struct datapath *dp, const void *body, int body_len,
- void **state)
+static int
+flow_stats_init(const void *body, int body_len UNUSED, void **state)
{
const struct ofp_flow_stats_request *fsr = body;
struct flow_stats_state *s = xmalloc(sizeof *s);
@@ -1255,9 +1172,8 @@ struct aggregate_stats_state {
struct ofp_aggregate_stats_request rq;
};
-static int aggregate_stats_init(struct datapath *dp,
- const void *body, int body_len,
- void **state)
+static int
+aggregate_stats_init(const void *body, int body_len UNUSED, void **state)
{
const struct ofp_aggregate_stats_request *rq = body;
struct aggregate_stats_state *s = xmalloc(sizeof *s);
@@ -1317,8 +1233,9 @@ static void aggregate_stats_done(void *state)
free(state);
}
-static int table_stats_dump(struct datapath *dp, void *state,
- struct ofpbuf *buffer)
+static int
+table_stats_dump(struct datapath *dp, void *state UNUSED,
+ struct ofpbuf *buffer)
{
int i;
for (i = 0; i < dp->chain->n_tables; i++) {
@@ -1341,8 +1258,8 @@ struct port_stats_state {
int port;
};
-static int port_stats_init(struct datapath *dp, const void *body, int body_len,
- void **state)
+static int
+port_stats_init(const void *body UNUSED, int body_len UNUSED, void **state)
{
struct port_stats_state *s = xmalloc(sizeof *s);
s->port = 0;
@@ -1404,13 +1321,12 @@ struct stats_type {
* struct ofp_stats_request. */
size_t min_body, max_body;
- /* Prepares to dump some kind of statistics on 'dp'. 'body' and
+ /* Prepares to dump some kind of datapath statistics. 'body' and
* 'body_len' are the 'body' member of the struct ofp_stats_request.
* Returns zero if successful, otherwise a negative error code.
* May initialize '*state' to state information. May be null if no
* initialization is required.*/
- int (*init)(struct datapath *dp, const void *body, int body_len,
- void **state);
+ int (*init)(const void *body, int body_len, void **state);
/* Appends statistics for 'dp' to 'buffer', which initially contains a
* struct ofp_stats_reply. On success, it should return 1 if it should be
@@ -1523,7 +1439,7 @@ stats_done(void *cb_)
}
static int
-recv_stats_request(struct datapath *dp, const struct sender *sender,
+recv_stats_request(struct datapath *dp UNUSED, const struct sender *sender,
const void *oh)
{
const struct ofp_stats_request *rq = oh;
@@ -1560,7 +1476,7 @@ recv_stats_request(struct datapath *dp, const struct sender *sender,
}
if (cb->s->init) {
- err = cb->s->init(dp, rq->body, body_len, &cb->state);
+ err = cb->s->init(rq->body, body_len, &cb->state);
if (err) {
VLOG_WARN_RL(&rl,
"failed initialization of stats request type %d: %s",
diff --git a/udatapath/dp_act.c b/udatapath/dp_act.c
index 2205c08..b4d1751 100644
--- a/udatapath/dp_act.c
+++ b/udatapath/dp_act.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+/* Copyright (c) 2008, 2009 The Board of Trustees of The Leland Stanford
* Junior University
*
* We are making the OpenFlow specification and associated documentation
@@ -42,7 +42,7 @@
static uint16_t
-validate_output(struct datapath *dp, const struct sw_flow_key *key,
+validate_output(struct datapath *dp UNUSED, const struct sw_flow_key *key,
const struct ofp_action_header *ah)
{
struct ofp_action_output *oa = (struct ofp_action_output *)ah;
@@ -50,10 +50,11 @@ validate_output(struct datapath *dp, const struct sw_flow_key *key,
/* To prevent loops, make sure there's no action to send to the
* OFP_TABLE virtual port.
*/
- if (oa->port == htons(OFPP_NONE)
- || (!(key->wildcards & OFPFW_IN_PORT) && oa->port == key->flow.in_port))
+ if (oa->port == htons(OFPP_NONE) ||
+ (!(key->wildcards & OFPFW_IN_PORT)
+ && oa->port == key->flow.in_port)) {
return OFPBAC_BAD_OUT_PORT;
-
+ }
return ACT_VALIDATION_OK;
}
@@ -143,14 +144,14 @@ set_vlan_pcp(struct ofpbuf *buffer, struct sw_flow_key *key,
static void
strip_vlan(struct ofpbuf *buffer, struct sw_flow_key *key,
- const struct ofp_action_header *ah)
+ const struct ofp_action_header *ah UNUSED)
{
vlan_pull_tag(buffer);
key->flow.dl_vlan = htons(OFP_VLAN_NONE);
}
static void
-set_dl_addr(struct ofpbuf *buffer, struct sw_flow_key *key,
+set_dl_addr(struct ofpbuf *buffer, struct sw_flow_key *key UNUSED,
const struct ofp_action_header *ah)
{
struct ofp_action_dl_addr *da = (struct ofp_action_dl_addr *)ah;
@@ -301,7 +302,7 @@ static uint16_t
validate_ofpat(struct datapath *dp, const struct sw_flow_key *key,
const struct ofp_action_header *ah, uint16_t type, uint16_t len)
{
- int ret = ACT_VALIDATION_OK;
+ uint16_t ret = ACT_VALIDATION_OK;
const struct openflow_action *act = &of_actions[type];
if ((len < act->min_size) || (len > act->max_size)) {
diff --git a/udatapath/nx_act.c b/udatapath/nx_act.c
index e2a6d4f..34ee2a7 100644
--- a/udatapath/nx_act.c
+++ b/udatapath/nx_act.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+/* Copyright (c) 2008, 2009 The Board of Trustees of The Leland Stanford
* Junior University
*
* We are making the OpenFlow specification and associated documentation
@@ -36,17 +36,19 @@
#include "nx_act.h"
uint16_t
-nx_validate_act(struct datapath *dp, const struct sw_flow_key *key,
- const struct ofp_action_vendor_header *avh, uint16_t len)
+nx_validate_act(struct datapath *dp UNUSED,
+ const struct sw_flow_key *key UNUSED,
+ const struct ofp_action_vendor_header *avh UNUSED,
+ uint16_t len UNUSED)
{
/* Nothing to validate yet */
return OFPBAC_BAD_VENDOR_TYPE;
}
void
-nx_execute_act(struct ofpbuf *buffer, const struct sw_flow_key *key,
- const struct ofp_action_vendor_header *avh)
+nx_execute_act(struct ofpbuf *buffer UNUSED,
+ const struct sw_flow_key *key UNUSED,
+ const struct ofp_action_vendor_header *avh UNUSED)
{
/* Nothing to execute yet */
}
-
diff --git a/udatapath/udatapath.8.in b/udatapath/ofdatapath.8.in
similarity index 54%
rename from udatapath/udatapath.8.in
rename to udatapath/ofdatapath.8.in
index f2414e5..898dc45 100644
--- a/udatapath/udatapath.8.in
+++ b/udatapath/ofdatapath.8.in
@@ -1,27 +1,29 @@
-.TH udatapath 8 "May 2008" "OpenFlow" "OpenFlow Manual"
+.ds PN ofdatapath
+
+.TH ofdatapath 8 "May 2008" "OpenFlow" "OpenFlow Manual"
.SH NAME
-udatapath \- userspace implementation of datapath for OpenFlow switch
+ofdatapath \- userspace implementation of datapath for OpenFlow switch
.SH SYNOPSIS
-.B udatapath
+.B ofdatapath
[\fIoptions\fR]
\fB-i\fR \fInetdev\fR[\fB,\fInetdev\fR].\|.\|.
\fImethod\fR [\fImethod\fR].\|.\|.
.SH DESCRIPTION
-The \fBudatapath\fR is a userspace implementation of an OpenFlow
+The \fBofdatapath\fR is a userspace implementation of an OpenFlow
datapath. It monitors one or more network device interfaces,
forwarding packets between them according to the entries in the flow
-table that it maintains. When it is used with \fBsecchan\fR(8), to
+table that it maintains. When it is used with \fBofprotocol\fR(8), to
connect the datapath to an OpenFlow controller, the combination is an
OpenFlow switch.
-For access to network devices, the udatapath program must normally run as
+For access to network devices, the ofdatapath program must normally run as
root.
-The mandatory \fImethod\fR argument specifies how \fBsecchan\fR(8)
-communicates with \fBudatapath\fR, as a passive OpenFlow connection
+The mandatory \fImethod\fR argument specifies how \fBofprotocol\fR(8)
+communicates with \fBofdatapath\fR, as a passive OpenFlow connection
method. Ordinarily \fImethod\fR takes the following form:
.TP
@@ -31,7 +33,7 @@ Listens for connections on the Unix domain server socket named
.PP
The following connection methods are also supported, but their use
-would be unusual because \fBudatapath\fR and \fBsecchan\fR should run
+would be unusual because \fBofdatapath\fR and \fBofprotocol\fR should run
on the same machine:
.TP
@@ -57,7 +59,7 @@ network devices.
.TP
\fB-L\fR, \fB--local-port=\fInetdev\fR
Specifies the network device to use as the userspace datapath's
-``local port,'' which is a network device that \fBsecchan\fR(8)
+``local port,'' which is a network device that \fBofprotocol\fR(8)
bridges to the physical switch ports for use in in-band control. When
this option is not specified, the default is \fBtap:\fR, which causes
a new TAP virtual network device to be allocated with a default name
@@ -65,10 +67,10 @@ assigned by the kernel. To do the same, but assign a specific name
\fBname\fR to the TAP network device, specify the option as
\fB--local-port=tap:\fIname\fR.
-Either way, the existence of TAP devices created by \fBudatapath\fR is
-temporary: they are destroyed when \fBudatapath\fR exits. If this is
+Either way, the existence of TAP devices created by \fBofdatapath\fR is
+temporary: they are destroyed when \fBofdatapath\fR exits. If this is
undesirable, you may use \fBtunctl\fR(8) to create a persistent TAP
-network device and then pass it to \fBudatapath\fR, like so:
+network device and then pass it to \fBofdatapath\fR, like so:
.RS
.IP 1.
@@ -76,13 +78,13 @@ Create a persistent TAP network device: \fBtunctl -t mytap\fR. (The
\fBtunctl\fR(8) utility is part of User Mode Linux. It is not
included with the OpenFlow reference implementation.)
.IP 2.
-Invoke \fBudatapath\fR(8) using \fBmytap\fR, e.g. \fBudatapath
+Invoke \fBofdatapath\fR(8) using \fBmytap\fR, e.g. \fBofdatapath
--local-port=mytap\fR .\|.\|. (Note the lack of \fBtap:\fR prefix on
the \fB--local-port\fR argument.)
.IP 3.
-Invoke \fBsecchan\fR(8), etc., and use the switch as desired.
+Invoke \fBofprotocol\fR(8), etc., and use the switch as desired.
.IP 4.
-When \fBsecchan\fR and \fBudatapath\fR have terminated and the TAP
+When \fBofprotocol\fR and \fBofdatapath\fR have terminated and the TAP
network device is no longer needed, you may destroy it with: \fBtunctl
-d mytap\fR
.RE
@@ -100,12 +102,12 @@ is used, the switch will not support in-band control.
\fB-d\fR, \fB--datapath-id=\fIdpid\fR
Specifies the OpenFlow datapath ID (a 48-bit number that uniquely
identifies a controller) as \fIdpid\fR, which consists of exactly 12
-hex digits. Without this option, \fBudatapath\fR picks an ID randomly.
+hex digits. Without this option, \fBofdatapath\fR picks an ID randomly.
.TP
\fB-p\fR, \fB--private-key=\fIprivkey.pem\fR
Specifies a PEM file containing the private key used as the datapath's
-identity for SSL connections to \fBsecchan\fR(8).
+identity for SSL connections to \fBofprotocol\fR(8).
.TP
\fB-c\fR, \fB--certificate=\fIcert.pem\fR
@@ -118,54 +120,9 @@ private key to identify a trustworthy datapath.
Specifies a PEM file containing the CA certificate used to verify that
the datapath is connected to a trustworthy secure channel.
-.TP
-\fB-P\fR[\fIpidfile\fR], \fB--pidfile\fR[\fB=\fIpidfile\fR]
-Causes a file (by default, \fBudatapath.pid\fR) to be created indicating
-the PID of the running process. If \fIpidfile\fR is not specified, or
-if it does not begin with \fB/\fR, then it is created in
-\fB@RUNDIR@\fR.
-
-.TP
-\fB-f\fR, \fB--force\fR
-By default, when \fB-P\fR or \fB--pidfile\fR is specified and the
-specified pidfile already exists and is locked by a running process,
-\fBudatapath\fR refuses to start. Specify \fB-f\fR or \fB--force\fR
-to cause it to instead overwrite the pidfile.
-
-When \fB-P\fR or \fB--pidfile\fR is not specified, this option has no
-effect.
-
-.TP
-\fB-D\fR, \fB--detach\fR
-Causes \fBudatapath\fR to detach itself from the foreground session and
-run as a background process.
-
-.TP
-.BR \-h ", " \-\^\-help
-Prints a brief help message to the console.
-
-.TP
-\fB-v\fImodule\fR[\fB:\fIfacility\fR[\fB:\fIlevel\fR]], \fB--verbose=\fImodule\fR[\fB:\fIfacility\fR[\fB:\fIlevel\fR]]
-Sets the logging level for \fImodule\fR in \fIfacility\fR to
-\fIlevel\fR. The \fImodule\fR may be any valid module name (as
-displayed by the \fB--list\fR action on \fBvlogconf\fR(8)), or the
-special name \fBANY\fR to set the logging levels for all modules. The
-\fIfacility\fR may be \fBsyslog\fR or \fBconsole\fR to set the levels
-for logging to the system log or to the console, respectively, or
-\fBANY\fR to set the logging levels for both facilities. If it is
-omitted, \fIfacility\fR defaults to \fBANY\fR. The \fIlevel\fR must
-be one of \fBemer\fR, \fBerr\fR, \fBwarn\fR, or \fBdbg\fR, designating
-the minimum severity of a message for it to be logged. If it is
-omitted, \fIlevel\fR defaults to \fBdbg\fR.
-
-.TP
-\fB-v\fR, \fB--verbose\fR
-Sets the maximum logging verbosity level, equivalent to
-\fB--verbose=ANY:ANY:dbg\fR.
-
-.TP
-.BR \-V ", " \-\^\-version
-Prints version information to the console.
+.so lib/daemon.man
+.so lib/vlog.man
+.so lib/common.man
.SH BUGS
The userspace datapath's performance lags significantly behind that of
@@ -177,7 +134,7 @@ by the Linux kernel AF_PACKET implementation.
.SH "SEE ALSO"
-.BR secchan (8),
+.BR ofprotocol (8),
.BR dpctl (8),
.BR controller (8),
.BR vlogconf (8).
diff --git a/udatapath/switch-flow.c b/udatapath/switch-flow.c
index 5eca39e..6bc2d3c 100644
--- a/udatapath/switch-flow.c
+++ b/udatapath/switch-flow.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+/* Copyright (c) 2008, 2009 The Board of Trustees of The Leland Stanford
* Junior University
*
* We are making the OpenFlow specification and associated documentation
@@ -152,23 +152,6 @@ flow_extract_match(struct sw_flow_key* to, const struct ofp_match* from)
to->nw_dst_mask = make_nw_mask(to->wildcards >> OFPFW_NW_DST_SHIFT);
}
-void
-flow_fill_match(struct ofp_match* to, const struct sw_flow_key* from)
-{
- to->wildcards = htonl(from->wildcards);
- to->in_port = from->flow.in_port;
- to->dl_vlan = from->flow.dl_vlan;
- memcpy(to->dl_src, from->flow.dl_src, ETH_ADDR_LEN);
- memcpy(to->dl_dst, from->flow.dl_dst, ETH_ADDR_LEN);
- to->dl_type = from->flow.dl_type;
- to->nw_src = from->flow.nw_src;
- to->nw_dst = from->flow.nw_dst;
- to->nw_proto = from->flow.nw_proto;
- to->tp_src = from->flow.tp_src;
- to->tp_dst = from->flow.tp_dst;
- to->pad = 0;
-}
-
/* Allocates and returns a new flow with room for 'actions_len' actions.
* Returns the new flow or a null pointer on failure. */
struct sw_flow *
diff --git a/udatapath/switch-flow.h b/udatapath/switch-flow.h
index 78fc114..5c5ab7b 100644
--- a/udatapath/switch-flow.h
+++ b/udatapath/switch-flow.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+/* Copyright (c) 2008, 2009 The Board of Trustees of The Leland Stanford
* Junior University
*
* We are making the OpenFlow specification and associated documentation
@@ -89,7 +89,6 @@ void flow_deferred_free_acts(struct sw_flow_actions *);
void flow_replace_acts(struct sw_flow *, const struct ofp_action_header *,
size_t);
void flow_extract_match(struct sw_flow_key* to, const struct ofp_match* from);
-void flow_fill_match(struct ofp_match* to, const struct sw_flow_key* from);
void print_flow(const struct sw_flow_key *);
bool flow_timeout(struct sw_flow *flow);
diff --git a/udatapath/table-hash.c b/udatapath/table-hash.c
index fd1ec8a..f713df5 100644
--- a/udatapath/table-hash.c
+++ b/udatapath/table-hash.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+/* Copyright (c) 2008, 2009 The Board of Trustees of The Leland Stanford
* Junior University
*
* We are making the OpenFlow specification and associated documentation
@@ -132,13 +132,13 @@ do_delete(struct sw_flow **bucket)
*bucket = NULL;
}
-/* Returns number of deleted flows. We can igonre the priority
+/* Returns number of deleted flows. We ignore the priority
* argument, since all exact-match entries are the same (highest)
* priority. */
static int table_hash_delete(struct datapath *dp, struct sw_table *swt,
const struct sw_flow_key *key,
uint16_t out_port,
- uint16_t priority, int strict)
+ uint16_t priority UNUSED, int strict)
{
struct sw_table_hash *th = (struct sw_table_hash *) swt;
unsigned int count = 0;
diff --git a/udatapath/udatapath.c b/udatapath/udatapath.c
index 46932da..314fc30 100644
--- a/udatapath/udatapath.c
+++ b/udatapath/udatapath.c
@@ -60,8 +60,8 @@
/* Strings to describe the manufacturer, hardware, and software. This data
* is queriable through the switch description stats message. */
-char mfr_desc[DESC_STR_LEN] = "Nicira Networks";
-char hw_desc[DESC_STR_LEN] = "Reference User-Space Switch";
+char mfr_desc[DESC_STR_LEN] = "Stanford University (developed by Nicira Networks, Inc.)";
+char hw_desc[DESC_STR_LEN] = "Reference Userspace Data plane Process";
char sw_desc[DESC_STR_LEN] = VERSION BUILDNR;
char serial_num[SERIAL_NUM_LEN] = "None";
diff --git a/utilities/.gitignore b/utilities/.gitignore
index 0be1ed0..c93169b 100644
--- a/utilities/.gitignore
+++ b/utilities/.gitignore
@@ -1,6 +1,7 @@
/Makefile
/Makefile.in
/dpctl
+/dpctl.8
/ofp-discover
/ofp-discover.8
/ofp-kill
@@ -9,3 +10,4 @@
/ofp-pki-cgi
/ofp-pki.8
/vlogconf
+/vlogconf.8
diff --git a/utilities/automake.mk b/utilities/automake.mk
index f9ce28d..d6f79a8 100644
--- a/utilities/automake.mk
+++ b/utilities/automake.mk
@@ -4,12 +4,13 @@ bin_PROGRAMS += \
utilities/ofp-discover \
utilities/ofp-kill
bin_SCRIPTS += utilities/ofp-pki
-noinst_SCRIPTS += utilities/ofp-pki-cgi
+noinst_SCRIPTS += utilities/ofp-pki-cgi utilities/ofp-parse-leaks
EXTRA_DIST += \
utilities/dpctl.8.in \
utilities/ofp-discover.8.in \
utilities/ofp-kill.8.in \
+ utilities/ofp-parse-leaks.in \
utilities/ofp-pki-cgi.in \
utilities/ofp-pki.8.in \
utilities/ofp-pki.in \
@@ -18,6 +19,7 @@ DISTCLEANFILES += \
utilities/dpctl.8 \
utilities/ofp-discover.8 \
utilities/ofp-kill.8 \
+ utilities/ofp-parse-leaks \
utilities/ofp-pki \
utilities/ofp-pki.8 \
utilities/ofp-pki-cgi \
diff --git a/utilities/dpctl.8.in b/utilities/dpctl.8.in
index 6f86124..e7370ab 100644
--- a/utilities/dpctl.8.in
+++ b/utilities/dpctl.8.in
@@ -1,3 +1,5 @@
+.ds PN dpctl
+
.TH dpctl 8 "May 2008" "OpenFlow" "OpenFlow Manual"
.SH NAME
@@ -83,6 +85,10 @@ traffic and the network device appears silent to the rest of the system.
Removes each \fInetdev\fR from the list of network devices datapath
\fIdp_idx\fR monitors.
+.TP
+\fBget-idx \fIof_dev\fR
+Prints the datapath index for OpenFlow device \fIof_dev\fR.
+
.PP
The following commands can be apply to OpenFlow switches regardless of
the connection method.
@@ -100,10 +106,10 @@ pairs whose key names begin with \fIkey\fR are printed. If \fIkey\fR is
omitted, all key-value pairs are printed.
(In the OpenFlow reference implementation, the \fBstatus\fR command is
-implemented in \fBsecchan\fR(8), not in the kernel module, so the
+implemented in \fBofprotocol\fR(8), not in the kernel module, so the
\fBnl:\fIdp_idx\fR connection method should not be used with this
command. Instead, specify \fB-l\fR or \fB--listen\fR on the
-\fBsecchan\fR command line and tell \fBdpctl\fR to use the connection
+\fBofprotocol\fR command line and tell \fBdpctl\fR to use the connection
method specified there.)
.TP
@@ -189,13 +195,13 @@ syntax of \fIflows\fR.
\fBmonitor \fIswitch\fR
Connects to \fIswitch\fR and prints to the console all OpenFlow
messages received. Usually, \fIswitch\fR should specify a connection
-named on \fBsecchan\fR(8)'s \fB-m\fR or \fB--monitor\fR command line
+named on \fBofprotocol\fR(8)'s \fB-m\fR or \fB--monitor\fR command line
option, in which the messages printed will be all those sent or
-received by \fBsecchan\fR to or from the kernel datapath module. A
+received by \fBofprotocol\fR to or from the kernel datapath module. A
\fIswitch\fR of the form \fBnl:\fIdp_idx\fR will print all
asynchronously generated OpenFlow messages (such as packet-in
messages), but it will not print any messages sent to the kernel by
-\fBsecchan\fR and other processes, nor will it print replies sent by
+\fBofprotocol\fR and other processes, nor will it print replies sent by
the kernel in response to those messages.
.TP
@@ -357,7 +363,7 @@ omitted, then the entire packet is sent.
.IP \fBlocal\fR
Outputs the packet on the ``local port,'' which corresponds to the
\fBof\fIn\fR network device (see \fBCONTACTING THE CONTROLLER\fR in
-\fBsecchan\fR(8) for information on the \fBof\fIn\fR network device).
+\fBofprotocol\fR(8) for information on the \fBof\fIn\fR network device).
.IP \fBmod_vlan_vid\fR:\fIvlan_vid\fR
Modifies the VLAN id on a packet. The VLAN tag is added or modified
@@ -371,6 +377,12 @@ as necessary to match the value specified. Valid values are between 0
(lowest) and 7 (highest). If the VLAN tag is added, a vid of zero is used
(see the \fBmod_vlan_vid\fR action to set this).
+.IP \fBmod_dl_dst\fR:\fIdst_mac\fR
+Modifies the destination mac address on a packet, e.g., actions=mod_dl_dst:12:34:56:78:9a:bc
+
+.IP \fBmod_dl_src\fR:\fIsrc_mac\fR
+Modifies the source mac address on a packet, e.g., actions=mod_dl_src:12:34:56:78:9a:bc
+
.IP \fBstrip_vlan\fR
Strips the VLAN tag from a packet if it is present.
.RE
@@ -450,15 +462,8 @@ private key to identify a trustworthy controller.
Specifies a PEM file containing the CA certificate used to verify that
a switch is trustworthy.
-.TP
-.BR \-h ", " \-\^\-help
-Prints a brief help message to the console.
-
-@VLOG_OPTIONS@
-
-.TP
-.BR \-V ", " \-\^\-version
-Prints version information to the console.
+.so lib/vlog.man
+.so lib/common.man
.SH EXAMPLES
@@ -504,7 +509,7 @@ Delete the datapath:
.fi
.SH "SEE ALSO"
-.BR secchan (8),
+.BR ofprotocol (8),
.BR controller (8),
-.BR udatapath (8),
+.BR ofdatapath (8),
.BR vlogconf (8)
diff --git a/utilities/dpctl.c b/utilities/dpctl.c
index d7f9f51..8e48ab0 100644
--- a/utilities/dpctl.c
+++ b/utilities/dpctl.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+/* Copyright (c) 2008, 2009 The Board of Trustees of The Leland Stanford
* Junior University
*
* We are making the OpenFlow specification and associated documentation
@@ -123,6 +123,12 @@ int main(int argc, char *argv[])
p->name, p->max_args);
else {
p->handler(&s, argc, argv);
+ if (ferror(stdout)) {
+ ofp_fatal(0, "write to stdout failed");
+ }
+ if (ferror(stderr)) {
+ ofp_fatal(0, "write to stderr failed");
+ }
exit(0);
}
}
@@ -211,6 +217,7 @@ usage(void)
" deldp nl:DP_ID delete local datapath DP_ID\n"
" addif nl:DP_ID IFACE... add each IFACE as a port on DP_ID\n"
" delif nl:DP_ID IFACE... delete each IFACE from DP_ID\n"
+ " get-idx OF_DEV get datapath index for OF_DEV\n"
#endif
"\nFor local datapaths and remote switches:\n"
" show SWITCH show basic information\n"
@@ -288,68 +295,90 @@ static int if_up(const char *netdev_name)
return retval;
}
-static void open_nl_vconn(const char *name, bool subscribe, struct dpif *dpif)
+static void
+do_get_idx(const struct settings *s UNUSED, int argc UNUSED, char *argv[])
+{
+ int dp_idx;
+
+ struct dpif dpif;
+ run(dpif_open(-1, &dpif), "opening management socket");
+ dp_idx = dpif_get_idx(argv[1]);
+ if (dp_idx == -1) {
+ dpif_close(&dpif);
+ ofp_fatal(0, "unknown OpenFlow device: %s", argv[1]);
+ }
+ printf("%d\n", dp_idx);
+ dpif_close(&dpif);
+}
+
+static int
+get_dp_idx(const char *name)
{
if (strncmp(name, "nl:", 3)
|| strlen(name) < 4
|| name[strspn(name + 3, "0123456789") + 3]) {
ofp_fatal(0, "%s: argument is not of the form \"nl:DP_ID\"", name);
}
- run(dpif_open(atoi(name + 3), subscribe, dpif), "opening datapath");
+ return atoi(name + 3);
}
-static void do_add_dp(const struct settings *s, int argc UNUSED, char *argv[])
+static void
+do_add_dp(const struct settings *s UNUSED, int argc UNUSED, char *argv[])
{
- struct dpif dp;
- open_nl_vconn(argv[1], false, &dp);
- run(dpif_add_dp(&dp), "add_dp");
- dpif_close(&dp);
+ struct dpif dpif;
+ run(dpif_open(-1, &dpif), "opening management socket");
+ run(dpif_add_dp(&dpif, get_dp_idx(argv[1]), NULL), "add_dp");
+ dpif_close(&dpif);
}
-static void do_del_dp(const struct settings *s, int argc UNUSED, char *argv[])
+static void
+do_del_dp(const struct settings *s UNUSED, int argc UNUSED, char *argv[])
{
- struct dpif dp;
- open_nl_vconn(argv[1], false, &dp);
- run(dpif_del_dp(&dp), "del_dp");
- dpif_close(&dp);
+ struct dpif dpif;
+ run(dpif_open(-1, &dpif), "opening management socket");
+ run(dpif_del_dp(&dpif, get_dp_idx(argv[1]), NULL), "del_dp");
+ dpif_close(&dpif);
}
static void add_del_ports(int argc UNUSED, char *argv[],
- int (*function)(struct dpif *, const char *netdev),
+ int (*function)(struct dpif *, int dp_idx,
+ const char *netdev),
const char *operation, const char *preposition)
{
- struct dpif dp;
bool failure = false;
+ struct dpif dpif;
+ int dp_idx;
int i;
- open_nl_vconn(argv[1], false, &dp);
+ run(dpif_open(-1, &dpif), "opening management socket");
+ dp_idx = get_dp_idx(argv[1]);
for (i = 2; i < argc; i++) {
- int retval = function(&dp, argv[i]);
+ int retval = function(&dpif, dp_idx, argv[i]);
if (retval) {
ofp_error(retval, "failed to %s %s %s %s",
operation, argv[i], preposition, argv[1]);
failure = true;
}
}
- dpif_close(&dp);
+ dpif_close(&dpif);
if (failure) {
exit(EXIT_FAILURE);
}
}
-static int ifup_and_add_port(struct dpif *dpif, const char *netdev)
+static int ifup_and_add_port(struct dpif *dpif, int dp_idx, const char *netdev)
{
int retval = if_up(netdev);
- return retval ? retval : dpif_add_port(dpif, netdev);
+ return retval ? retval : dpif_add_port(dpif, dp_idx, netdev);
}
-static void do_add_port(const struct settings *s, int argc UNUSED,
+static void do_add_port(const struct settings *s UNUSED, int argc UNUSED,
char *argv[])
{
add_del_ports(argc, argv, ifup_and_add_port, "add", "to");
}
-static void do_del_port(const struct settings *s, int argc UNUSED,
+static void do_del_port(const struct settings *s UNUSED, int argc UNUSED,
char *argv[])
{
add_del_ports(argc, argv, dpif_del_port, "remove", "from");
@@ -444,14 +473,14 @@ dump_trivial_stats_transaction(const char *vconn_name, uint8_t stats_type)
}
static void
-do_show(const struct settings *s, int argc UNUSED, char *argv[])
+do_show(const struct settings *s UNUSED, int argc UNUSED, char *argv[])
{
dump_trivial_transaction(argv[1], OFPT_FEATURES_REQUEST);
dump_trivial_transaction(argv[1], OFPT_GET_CONFIG_REQUEST);
}
static void
-do_status(const struct settings *s, int argc, char *argv[])
+do_status(const struct settings *s UNUSED, int argc, char *argv[])
{
struct nicira_header *request, *reply;
struct vconn *vconn;
@@ -482,20 +511,20 @@ do_status(const struct settings *s, int argc, char *argv[])
}
static void
-do_dump_desc(const struct settings *s, int argc, char *argv[])
+do_dump_desc(const struct settings *s UNUSED, int argc UNUSED, char *argv[])
{
dump_trivial_stats_transaction(argv[1], OFPST_DESC);
}
static void
-do_dump_tables(const struct settings *s, int argc, char *argv[])
+do_dump_tables(const struct settings *s UNUSED, int argc UNUSED, char *argv[])
{
dump_trivial_stats_transaction(argv[1], OFPST_TABLE);
}
static uint32_t
-str_to_int(const char *str)
+str_to_u32(const char *str)
{
char *tail;
uint32_t value;
@@ -572,124 +601,99 @@ str_to_ip(const char *str_, uint32_t *ip)
return n_wild;
}
+static void *
+put_action(struct ofpbuf *b, size_t size, uint16_t type)
+{
+ struct ofp_action_header *ah = ofpbuf_put_zeros(b, size);
+ ah->type = htons(type);
+ ah->len = htons(size);
+ return ah;
+}
+
+static struct ofp_action_output *
+put_output_action(struct ofpbuf *b, uint16_t port)
+{
+ struct ofp_action_output *oao = put_action(b, sizeof *oao, OFPAT_OUTPUT);
+ oao->port = htons(port);
+ return oao;
+}
+
static void
-str_to_action(char *str, struct ofp_action_header *actions,
- size_t *actions_len)
+str_to_action(char *str, struct ofpbuf *b)
{
- size_t len = *actions_len;
char *act, *arg;
char *saveptr = NULL;
- uint8_t *p = (uint8_t *)actions;
-
- memset(actions, 0, len);
- for (act = strtok_r(str, ", \t\r\n", &saveptr);
- (len >= sizeof(struct ofp_action_header)) && act;
+
+ for (act = strtok_r(str, ", \t\r\n", &saveptr); act;
act = strtok_r(NULL, ", \t\r\n", &saveptr))
{
- uint16_t port;
- struct ofp_action_header *ah = (struct ofp_action_header *)p;
- int act_len = sizeof *ah;
- port = OFPP_MAX;
-
/* Arguments are separated by colons */
arg = strchr(act, ':');
if (arg) {
*arg = '\0';
arg++;
- }
+ }
if (!strcasecmp(act, "mod_vlan_vid")) {
- struct ofp_action_vlan_vid *va = (struct ofp_action_vlan_vid *)ah;
-
- if (len < sizeof *va) {
- ofp_fatal(0, "Insufficient room for vlan vid action\n");
- }
-
- act_len = sizeof *va;
- va->type = htons(OFPAT_SET_VLAN_VID);
- va->vlan_vid = htons(str_to_int(arg));
+ struct ofp_action_vlan_vid *va;
+ va = put_action(b, sizeof *va, OFPAT_SET_VLAN_VID);
+ va->vlan_vid = htons(str_to_u32(arg));
} else if (!strcasecmp(act, "mod_vlan_pcp")) {
- struct ofp_action_vlan_pcp *va = (struct ofp_action_vlan_pcp *)ah;
-
- if (len < sizeof *va) {
- ofp_fatal(0, "Insufficient room for vlan pcp action\n");
- }
-
- act_len = sizeof *va;
- va->type = htons(OFPAT_SET_VLAN_PCP);
- va->vlan_pcp = str_to_int(arg);
+ struct ofp_action_vlan_pcp *va;
+ va = put_action(b, sizeof *va, OFPAT_SET_VLAN_PCP);
+ va->vlan_pcp = str_to_u32(arg);
+ } else if (!strcasecmp(act, "mod_dst_mac")) {
+ struct ofp_action_dl_addr *va;
+ va = put_action(b, sizeof *va, OFPAT_SET_DL_DST);
+ str_to_mac(arg, va->dl_addr);
+ } else if (!strcasecmp(act, "mod_src_mac")) {
+ struct ofp_action_dl_addr *va;
+ va = put_action(b, sizeof *va, OFPAT_SET_DL_SRC);
+ str_to_mac(arg, va->dl_addr);
} else if (!strcasecmp(act, "strip_vlan")) {
+ struct ofp_action_header *ah;
+ ah = put_action(b, sizeof *ah, OFPAT_STRIP_VLAN);
ah->type = htons(OFPAT_STRIP_VLAN);
} else if (!strcasecmp(act, "output")) {
- port = str_to_int(arg);
+ put_output_action(b, str_to_u32(arg));
#ifdef SUPPORT_SNAT
} else if (!strcasecmp(act, "nat")) {
- struct nx_action_snat *sa = (struct nx_action_snat *)ah;
+ struct nx_action_snat *sa;
- if (len < sizeof *sa) {
- ofp_fatal(0, "Insufficient room for SNAT action\n");
- }
-
- if (str_to_int(arg) > OFPP_MAX) {
+ if (str_to_u32(arg) > OFPP_MAX) {
ofp_fatal(0, "Invalid nat port: %s\n", arg);
}
- act_len = sizeof *sa;
- sa->type = htons(OFPAT_VENDOR);
+ sa = put_action(b, sizeof *sa, OFPAT_VENDOR);
sa->vendor = htonl(NX_VENDOR_ID);
sa->subtype = htons(NXAST_SNAT);
- sa->port = htons(str_to_int(arg));
+ sa->port = htons(str_to_u32(arg));
#endif
} else if (!strcasecmp(act, "TABLE")) {
- port = OFPP_TABLE;
+ put_output_action(b, OFPP_TABLE);
} else if (!strcasecmp(act, "NORMAL")) {
- port = OFPP_NORMAL;
+ put_output_action(b, OFPP_NORMAL);
} else if (!strcasecmp(act, "FLOOD")) {
- port = OFPP_FLOOD;
+ put_output_action(b, OFPP_FLOOD);
} else if (!strcasecmp(act, "ALL")) {
- port = OFPP_ALL;
+ put_output_action(b, OFPP_ALL);
} else if (!strcasecmp(act, "CONTROLLER")) {
- struct ofp_action_output *ca = (struct ofp_action_output *)ah;
-
- if (act_len < sizeof *ca) {
- ofp_fatal(0, "Insufficient room for controller action\n");
- }
-
- act_len = sizeof *ca;
- ca->type = htons(OFPAT_OUTPUT);
- ca->port = htons(OFPP_CONTROLLER);
+ struct ofp_action_output *oao;
+ oao = put_output_action(b, OFPP_CONTROLLER);
/* Unless a numeric argument is specified, we send the whole
* packet to the controller. */
if (arg && (strspn(act, "0123456789") == strlen(act))) {
- ca->max_len= htons(str_to_int(arg));
+ oao->max_len = htons(str_to_u32(arg));
}
} else if (!strcasecmp(act, "LOCAL")) {
- port = OFPP_LOCAL;
+ put_output_action(b, OFPP_LOCAL);
} else if (strspn(act, "0123456789") == strlen(act)) {
- port = str_to_int(act);
+ put_output_action(b, str_to_u32(act));
} else {
ofp_fatal(0, "Unknown action: %s", act);
}
-
- if (port != OFPP_MAX) {
- struct ofp_action_output *oa = (struct ofp_action_output *)p;
-
- if (act_len < sizeof *oa) {
- ofp_fatal(0, "Insufficient room for output action\n");
- }
-
- act_len = sizeof *oa;
- oa->type = htons(OFPAT_OUTPUT);
- oa->port = htons(port);
- }
-
- ah->len = htons(act_len);
- p += act_len;
- len -= act_len;
}
-
- *actions_len -= len;
}
struct protocol {
@@ -702,8 +706,8 @@ static bool
parse_protocol(const char *name, const struct protocol **p_out)
{
static const struct protocol protocols[] = {
- { "ip", ETH_TYPE_IP },
- { "arp", ETH_TYPE_ARP },
+ { "ip", ETH_TYPE_IP, 0 },
+ { "arp", ETH_TYPE_ARP, 0 },
{ "icmp", ETH_TYPE_IP, IP_TYPE_ICMP },
{ "tcp", ETH_TYPE_IP, IP_TYPE_TCP },
{ "udp", ETH_TYPE_IP, IP_TYPE_UDP },
@@ -732,20 +736,20 @@ parse_field(const char *name, const struct field **f_out)
{
#define F_OFS(MEMBER) offsetof(struct ofp_match, MEMBER)
static const struct field fields[] = {
- { "in_port", OFPFW_IN_PORT, F_U16, F_OFS(in_port) },
- { "dl_vlan", OFPFW_DL_VLAN, F_U16, F_OFS(dl_vlan) },
- { "dl_src", OFPFW_DL_SRC, F_MAC, F_OFS(dl_src) },
- { "dl_dst", OFPFW_DL_DST, F_MAC, F_OFS(dl_dst) },
- { "dl_type", OFPFW_DL_TYPE, F_U16, F_OFS(dl_type) },
+ { "in_port", OFPFW_IN_PORT, F_U16, F_OFS(in_port), 0 },
+ { "dl_vlan", OFPFW_DL_VLAN, F_U16, F_OFS(dl_vlan), 0 },
+ { "dl_src", OFPFW_DL_SRC, F_MAC, F_OFS(dl_src), 0 },
+ { "dl_dst", OFPFW_DL_DST, F_MAC, F_OFS(dl_dst), 0 },
+ { "dl_type", OFPFW_DL_TYPE, F_U16, F_OFS(dl_type), 0 },
{ "nw_src", OFPFW_NW_SRC_MASK, F_IP,
F_OFS(nw_src), OFPFW_NW_SRC_SHIFT },
{ "nw_dst", OFPFW_NW_DST_MASK, F_IP,
F_OFS(nw_dst), OFPFW_NW_DST_SHIFT },
- { "nw_proto", OFPFW_NW_PROTO, F_U8, F_OFS(nw_proto) },
- { "tp_src", OFPFW_TP_SRC, F_U16, F_OFS(tp_src) },
- { "tp_dst", OFPFW_TP_DST, F_U16, F_OFS(tp_dst) },
- { "icmp_type", OFPFW_ICMP_TYPE, F_U16, F_OFS(icmp_type) },
- { "icmp_code", OFPFW_ICMP_CODE, F_U16, F_OFS(icmp_code) }
+ { "nw_proto", OFPFW_NW_PROTO, F_U8, F_OFS(nw_proto), 0 },
+ { "tp_src", OFPFW_TP_SRC, F_U16, F_OFS(tp_src), 0 },
+ { "tp_dst", OFPFW_TP_DST, F_U16, F_OFS(tp_dst), 0 },
+ { "icmp_type", OFPFW_ICMP_TYPE, F_U16, F_OFS(icmp_type), 0 },
+ { "icmp_code", OFPFW_ICMP_CODE, F_U16, F_OFS(icmp_code), 0 }
};
const struct field *f;
@@ -760,12 +764,11 @@ parse_field(const char *name, const struct field **f_out)
}
static void
-str_to_flow(char *string, struct ofp_match *match,
- struct ofp_action_header *actions, size_t *actions_len,
+str_to_flow(char *string, struct ofp_match *match, struct ofpbuf *actions,
uint8_t *table_idx, uint16_t *out_port, uint16_t *priority,
uint16_t *idle_timeout, uint16_t *hard_timeout)
{
-
+ char *save_ptr = NULL;
char *name;
uint32_t wildcards;
@@ -798,12 +801,12 @@ str_to_flow(char *string, struct ofp_match *match,
act_str++;
- str_to_action(act_str, actions, actions_len);
+ str_to_action(act_str, actions);
}
memset(match, 0, sizeof *match);
wildcards = OFPFW_ALL;
- for (name = strtok(string, "=, \t\r\n"); name;
- name = strtok(NULL, "=, \t\r\n")) {
+ for (name = strtok_r(string, "=, \t\r\n", &save_ptr); name;
+ name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) {
const struct protocol *p;
if (parse_protocol(name, &p)) {
@@ -817,7 +820,7 @@ str_to_flow(char *string, struct ofp_match *match,
const struct field *f;
char *value;
- value = strtok(NULL, ", \t\r\n");
+ value = strtok_r(NULL, ", \t\r\n", &save_ptr);
if (!value) {
ofp_fatal(0, "field %s missing value", name);
}
@@ -839,9 +842,9 @@ str_to_flow(char *string, struct ofp_match *match,
} else {
wildcards &= ~f->wildcard;
if (f->type == F_U8) {
- *(uint8_t *) data = str_to_int(value);
+ *(uint8_t *) data = str_to_u32(value);
} else if (f->type == F_U16) {
- *(uint16_t *) data = htons(str_to_int(value));
+ *(uint16_t *) data = htons(str_to_u32(value));
} else if (f->type == F_MAC) {
str_to_mac(value, data);
} else if (f->type == F_IP) {
@@ -858,14 +861,15 @@ str_to_flow(char *string, struct ofp_match *match,
match->wildcards = htonl(wildcards);
}
-static void do_dump_flows(const struct settings *s, int argc, char *argv[])
+static void
+do_dump_flows(const struct settings *s UNUSED, int argc, char *argv[])
{
struct ofp_flow_stats_request *req;
uint16_t out_port;
struct ofpbuf *request;
req = alloc_stats_request(sizeof *req, OFPST_FLOW, &request);
- str_to_flow(argc > 2 ? argv[2] : "", &req->match, NULL, 0,
+ str_to_flow(argc > 2 ? argv[2] : "", &req->match, NULL,
&req->table_id, &out_port, NULL, NULL, NULL);
memset(&req->pad, 0, sizeof req->pad);
req->out_port = htons(out_port);
@@ -873,15 +877,15 @@ static void do_dump_flows(const struct settings *s, int argc, char *argv[])
dump_stats_transaction(argv[1], request);
}
-static void do_dump_aggregate(const struct settings *s, int argc,
- char *argv[])
+static void
+do_dump_aggregate(const struct settings *s UNUSED, int argc, char *argv[])
{
struct ofp_aggregate_stats_request *req;
struct ofpbuf *request;
uint16_t out_port;
req = alloc_stats_request(sizeof *req, OFPST_AGGREGATE, &request);
- str_to_flow(argc > 2 ? argv[2] : "", &req->match, NULL, 0,
+ str_to_flow(argc > 2 ? argv[2] : "", &req->match, NULL,
&req->table_id, &out_port, NULL, NULL, NULL);
memset(&req->pad, 0, sizeof req->pad);
req->out_port = htons(out_port);
@@ -890,7 +894,8 @@ static void do_dump_aggregate(const struct settings *s, int argc,
}
#ifdef SUPPORT_SNAT
-static void do_add_snat(const struct settings *s, int argc, char *argv[])
+static void
+do_add_snat(const struct settings *s UNUSED, int argc UNUSED, char *argv[])
{
struct vconn *vconn;
struct ofpbuf *buffer;
@@ -906,7 +911,7 @@ static void do_add_snat(const struct settings *s, int argc, char *argv[])
nac->type = htons(NXAST_SNAT);
nac->snat[0].command = NXSC_ADD;
- nac->snat[0].port = htons(str_to_int(argv[2]));
+ nac->snat[0].port = htons(str_to_u32(argv[2]));
nac->snat[0].mac_timeout = htons(0);
str_to_ip(argv[3], &nac->snat[0].ip_addr_start);
str_to_ip(argv[3], &nac->snat[0].ip_addr_end);
@@ -916,7 +921,8 @@ static void do_add_snat(const struct settings *s, int argc, char *argv[])
vconn_close(vconn);
}
-static void do_del_snat(const struct settings *s, int argc, char *argv[])
+static void
+do_del_snat(const struct settings *s UNUSED, int argc UNUSED, char *argv[])
{
struct vconn *vconn;
struct ofpbuf *buffer;
@@ -932,7 +938,7 @@ static void do_del_snat(const struct settings *s, int argc, char *argv[])
nac->type = htons(NXAST_SNAT);
nac->snat[0].command = NXSC_DELETE;
- nac->snat[0].port = htons(str_to_int(argv[2]));
+ nac->snat[0].port = htons(str_to_u32(argv[2]));
nac->snat[0].mac_timeout = htons(0);
open_vconn(argv[1], &vconn);
@@ -941,20 +947,22 @@ static void do_del_snat(const struct settings *s, int argc, char *argv[])
}
#endif /* SUPPORT_SNAT */
-static void do_add_flow(const struct settings *s, int argc, char *argv[])
+static void
+do_add_flow(const struct settings *s UNUSED, int argc UNUSED, char *argv[])
{
struct vconn *vconn;
struct ofpbuf *buffer;
struct ofp_flow_mod *ofm;
uint16_t priority, idle_timeout, hard_timeout;
- size_t size;
- size_t actions_len = MAX_ACT_LEN;
+ struct ofp_match match;
- /* Parse and send. */
- size = sizeof *ofm + actions_len;
- ofm = make_openflow(size, OFPT_FLOW_MOD, &buffer);
- str_to_flow(argv[2], &ofm->match, &ofm->actions[0], &actions_len,
+ /* Parse and send. str_to_flow() will expand and reallocate the data in
+ * 'buffer', so we can't keep pointers to across the str_to_flow() call. */
+ make_openflow(sizeof *ofm, OFPT_FLOW_MOD, &buffer);
+ str_to_flow(argv[2], &match, buffer,
NULL, NULL, &priority, &idle_timeout, &hard_timeout);
+ ofm = buffer->data;
+ ofm->match = match;
ofm->command = htons(OFPFC_ADD);
ofm->idle_timeout = htons(idle_timeout);
ofm->hard_timeout = htons(hard_timeout);
@@ -962,15 +970,13 @@ static void do_add_flow(const struct settings *s, int argc, char *argv[])
ofm->priority = htons(priority);
ofm->reserved = htonl(0);
- /* xxx Should we use the ofpbuf library? */
- buffer->size -= MAX_ACT_LEN - actions_len;
-
open_vconn(argv[1], &vconn);
send_openflow_buffer(vconn, buffer);
vconn_close(vconn);
}
-static void do_add_flows(const struct settings *s, int argc, char *argv[])
+static void
+do_add_flows(const struct settings *s UNUSED, int argc UNUSED, char *argv[])
{
struct vconn *vconn;
FILE *file;
@@ -986,8 +992,7 @@ static void do_add_flows(const struct settings *s, int argc, char *argv[])
struct ofpbuf *buffer;
struct ofp_flow_mod *ofm;
uint16_t priority, idle_timeout, hard_timeout;
- size_t size;
- size_t actions_len = MAX_ACT_LEN;
+ struct ofp_match match;
char *comment;
@@ -1002,11 +1007,14 @@ static void do_add_flows(const struct settings *s, int argc, char *argv[])
continue;
}
- /* Parse and send. */
- size = sizeof *ofm + actions_len;
- ofm = make_openflow(size, OFPT_FLOW_MOD, &buffer);
- str_to_flow(line, &ofm->match, &ofm->actions[0], &actions_len,
+ /* Parse and send. str_to_flow() will expand and reallocate the data
+ * in 'buffer', so we can't keep pointers to across the str_to_flow()
+ * call. */
+ ofm = make_openflow(sizeof *ofm, OFPT_FLOW_MOD, &buffer);
+ str_to_flow(line, &match, buffer,
NULL, NULL, &priority, &idle_timeout, &hard_timeout);
+ ofm = buffer->data;
+ ofm->match = match;
ofm->command = htons(OFPFC_ADD);
ofm->idle_timeout = htons(idle_timeout);
ofm->hard_timeout = htons(hard_timeout);
@@ -1014,28 +1022,23 @@ static void do_add_flows(const struct settings *s, int argc, char *argv[])
ofm->priority = htons(priority);
ofm->reserved = htonl(0);
- /* xxx Should we use the ofpbuf library? */
- buffer->size -= MAX_ACT_LEN - actions_len;
-
send_openflow_buffer(vconn, buffer);
}
vconn_close(vconn);
fclose(file);
}
-static void do_mod_flows(const struct settings *s, int argc, char *argv[])
+static void
+do_mod_flows(const struct settings *s, int argc UNUSED, char *argv[])
{
uint16_t priority, idle_timeout, hard_timeout;
struct vconn *vconn;
struct ofpbuf *buffer;
struct ofp_flow_mod *ofm;
- size_t size;
- size_t actions_len = MAX_ACT_LEN;
/* Parse and send. */
- size = sizeof *ofm + actions_len;
- ofm = make_openflow(size, OFPT_FLOW_MOD, &buffer);
- str_to_flow(argv[2], &ofm->match, &ofm->actions[0], &actions_len,
+ ofm = make_openflow(sizeof *ofm, OFPT_FLOW_MOD, &buffer);
+ str_to_flow(argv[2], &ofm->match, buffer,
NULL, NULL, &priority, &idle_timeout, &hard_timeout);
if (s->strict) {
ofm->command = htons(OFPFC_MODIFY_STRICT);
@@ -1048,9 +1051,6 @@ static void do_mod_flows(const struct settings *s, int argc, char *argv[])
ofm->priority = htons(priority);
ofm->reserved = htonl(0);
- /* xxx Should we use the buffer library? */
- buffer->size -= MAX_ACT_LEN - actions_len;
-
open_vconn(argv[1], &vconn);
send_openflow_buffer(vconn, buffer);
vconn_close(vconn);
@@ -1063,12 +1063,10 @@ static void do_del_flows(const struct settings *s, int argc, char *argv[])
uint16_t out_port;
struct ofpbuf *buffer;
struct ofp_flow_mod *ofm;
- size_t size;
/* Parse and send. */
- size = sizeof *ofm;
- ofm = make_openflow(size, OFPT_FLOW_MOD, &buffer);
- str_to_flow(argc > 2 ? argv[2] : "", &ofm->match, NULL, 0, NULL,
+ ofm = make_openflow(sizeof *ofm, OFPT_FLOW_MOD, &buffer);
+ str_to_flow(argc > 2 ? argv[2] : "", &ofm->match, NULL, NULL,
&out_port, &priority, NULL, NULL);
if (s->strict) {
ofm->command = htons(OFPFC_DELETE_STRICT);
@@ -1089,7 +1087,7 @@ static void do_del_flows(const struct settings *s, int argc, char *argv[])
static void
-do_monitor(const struct settings *s, int argc UNUSED, char *argv[])
+do_monitor(const struct settings *s UNUSED, int argc UNUSED, char *argv[])
{
struct vconn *vconn;
const char *name;
@@ -1112,13 +1110,13 @@ do_monitor(const struct settings *s, int argc UNUSED, char *argv[])
}
static void
-do_dump_ports(const struct settings *s, int argc, char *argv[])
+do_dump_ports(const struct settings *s UNUSED, int argc UNUSED, char *argv[])
{
dump_trivial_stats_transaction(argv[1], OFPST_PORT);
}
static void
-do_probe(const struct settings *s, int argc, char *argv[])
+do_probe(const struct settings *s UNUSED, int argc UNUSED, char *argv[])
{
struct ofpbuf *request;
struct vconn *vconn;
@@ -1135,7 +1133,7 @@ do_probe(const struct settings *s, int argc, char *argv[])
}
static void
-do_mod_port(const struct settings *s, int argc, char *argv[])
+do_mod_port(const struct settings *s UNUSED, int argc UNUSED, char *argv[])
{
struct ofpbuf *request, *reply;
struct ofp_switch_features *osf;
@@ -1215,7 +1213,7 @@ do_mod_port(const struct settings *s, int argc, char *argv[])
}
static void
-do_ping(const struct settings *s, int argc, char *argv[])
+do_ping(const struct settings *s UNUSED, int argc, char *argv[])
{
size_t max_payload = 65535 - sizeof(struct ofp_header);
unsigned int payload;
@@ -1262,7 +1260,7 @@ do_ping(const struct settings *s, int argc, char *argv[])
}
static void
-do_benchmark(const struct settings *s, int argc, char *argv[])
+do_benchmark(const struct settings *s UNUSED, int argc UNUSED, char *argv[])
{
size_t max_payload = 65535 - sizeof(struct ofp_header);
struct timeval start, end;
@@ -1305,7 +1303,7 @@ do_benchmark(const struct settings *s, int argc, char *argv[])
}
static void
-do_execute(const struct settings *s, int argc, char *argv[])
+do_execute(const struct settings *s UNUSED, int argc, char *argv[])
{
struct vconn *vconn;
struct ofpbuf *request;
@@ -1369,8 +1367,8 @@ do_execute(const struct settings *s, int argc, char *argv[])
}
}
-static void do_help(const struct settings *s, int argc UNUSED,
- char *argv[] UNUSED)
+static void
+do_help(const struct settings *s UNUSED, int argc UNUSED, char *argv[] UNUSED)
{
usage();
}
@@ -1381,6 +1379,7 @@ static struct command all_commands[] = {
{ "deldp", 1, 1, do_del_dp },
{ "addif", 2, INT_MAX, do_add_port },
{ "delif", 2, INT_MAX, do_del_port },
+ { "get-idx", 1, 1, do_get_idx },
#endif
{ "show", 1, 1, do_show },
diff --git a/utilities/ofp-discover.8.in b/utilities/ofp-discover.8.in
index 304c3c7..cf5ac54 100644
--- a/utilities/ofp-discover.8.in
+++ b/utilities/ofp-discover.8.in
@@ -1,3 +1,5 @@
+.ds PN ofp\-discover
+
.TH ofp\-discover 8 "May 2008" "OpenFlow" "OpenFlow Manual"
.SH NAME
@@ -16,7 +18,7 @@ receives an acceptable DHCP response. It will accept any valid DHCP
reply that has the same vendor class identifier and includes a
vendor-specific option with code 1 whose contents are a string
specifying the location of the controller in the same format used on
-the \fBsecchan\fR command line (e.g. \fBssl:192.168.0.1\fR).
+the \fBofprotocol\fR command line (e.g. \fBssl:192.168.0.1\fR).
When \fBofp\-discover\fR receives an acceptable response, it prints
the details of the response on \fBstdout\fR. Then, by default, it
@@ -93,15 +95,8 @@ to cause it to instead overwrite the pidfile.
When \fB-P\fR or \fB--pidfile\fR is not specified, this option has no
effect.
-.TP
-.BR \-h ", " \-\^\-help
-Prints a brief help message to the console.
-
-@VLOG_OPTIONS@
-
-.TP
-.BR \-V ", " \-\^\-version
-Prints version information to the console.
+.so lib/vlog.man
+.so lib/common.man
.SH BUGS
@@ -120,5 +115,5 @@ arriving IP packets, will not.
.SH "SEE ALSO"
-.BR secchan (8),
+.BR ofprotocol (8),
.BR ofp-pki (8)
diff --git a/utilities/ofp-discover.c b/utilities/ofp-discover.c
index 461aa7c..55f4042 100644
--- a/utilities/ofp-discover.c
+++ b/utilities/ofp-discover.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+/* Copyright (c) 2008, 2009 The Board of Trustees of The Leland Stanford
* Junior University
*
* We are making the OpenFlow specification and associated documentation
@@ -267,13 +267,13 @@ release_ifaces(void *aux UNUSED)
}
static void
-modify_dhcp_request(struct dhcp_msg *msg, void *aux)
+modify_dhcp_request(struct dhcp_msg *msg, void *aux UNUSED)
{
dhcp_msg_put_string(msg, DHCP_CODE_VENDOR_CLASS, "OpenFlow");
}
static bool
-validate_dhcp_offer(const struct dhcp_msg *msg, void *aux)
+validate_dhcp_offer(const struct dhcp_msg *msg, void *aux UNUSED)
{
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(60, 60);
char *vconn_name;
diff --git a/utilities/ofp-kill.8.in b/utilities/ofp-kill.8.in
index 7b45bf0..6c1298c 100644
--- a/utilities/ofp-kill.8.in
+++ b/utilities/ofp-kill.8.in
@@ -1,3 +1,5 @@
+.ds PN ofp\-kill
+
.TH ofp\-kill 8 "May 2008" "OpenFlow" "OpenFlow Manual"
.SH NAME
@@ -34,13 +36,7 @@ By default, \fBSIGTERM\fR is sent.
Causes \fBofp\-kill\fR to ignore all errors without printing a message
to \fBstderr\fR, and to exit with return code 0.
-.TP
-.BR \-h ", " \-\^\-help
-Prints a brief help message to the console.
-
-.TP
-.BR \-V ", " \-\^\-version
-Prints version information to the console.
+.so lib/common.man
.SH "EXIT CODE"
diff --git a/utilities/ofp-parse-leaks.in b/utilities/ofp-parse-leaks.in
new file mode 100755
index 0000000..059c850
--- /dev/null
+++ b/utilities/ofp-parse-leaks.in
@@ -0,0 +1,285 @@
+#! @PERL@
+
+use strict;
+use warnings;
+
+if (grep($_ eq '--help', @ARGV)) {
+ print < 1;
+die "$0: $ARGV[0] does not exist" if @ARGV > 0 && ! -e $ARGV[0];
+
+our ($binary);
+our ($a2l) = search_path("addr2line");
+my ($no_syms) = "symbols will not be translated";
+if (!@ARGV) {
+ print "no binary specified; $no_syms\n";
+} elsif (! -e $ARGV[0]) {
+ print "$ARGV[0] does not exist; $no_syms";
+} elsif (!defined($a2l)) {
+ print "addr2line not found in PATH; $no_syms";
+} else {
+ $binary = $ARGV[0];
+}
+
+our ($objdump) = search_path("objdump");
+print "objdump not found; dynamic library symbols will not be translated\n"
+ if !defined($objdump);
+
+our %blocks;
+our @segments;
+while () {
+ my $ptr = "((?:0x)?[0-9a-fA-F]+|\\(nil\\))";
+ my $callers = ":((?: $ptr)+)";
+ if (/^malloc\((\d+)\) -> $ptr$callers$/) {
+ allocated($., $2, $1, $3);
+ } elsif (/^claim\($ptr\)$callers$/) {
+ claimed($., $1, $2);
+ } elsif (/realloc\($ptr, (\d+)\) -> $ptr$callers$/) {
+ my ($callers) = $4;
+ freed($., $1, $callers);
+ allocated($., $3, $2, $callers);
+ } elsif (/^free\($ptr\)$callers$/) {
+ freed($., $1, $2);
+ } elsif (/^segment: $ptr-$ptr $ptr [-r][-w][-x][sp] (.*)/) {
+ add_segment(hex($1), hex($2), hex($3), $4);
+ } else {
+ print "stdin:$.: syntax error\n";
+ }
+}
+if (%blocks) {
+ my $n_blocks = scalar(keys(%blocks));
+ my $n_bytes = 0;
+ $n_bytes += $_->{SIZE} foreach values(%blocks);
+ print "$n_bytes bytes in $n_blocks blocks not freed at end of run\n";
+ my %blocks_by_callers;
+ foreach my $block (values(%blocks)) {
+ my ($trimmed_callers) = trim_callers($block->{CALLERS});
+ push (@{$blocks_by_callers{$trimmed_callers}}, $block);
+ }
+ foreach my $callers (sort {@{$b} <=> @{$a}} (values(%blocks_by_callers))) {
+ $n_blocks = scalar(@{$callers});
+ $n_bytes = 0;
+ $n_bytes += $_->{SIZE} foreach @{$callers};
+ print "$n_bytes bytes in these $n_blocks blocks were not freed:\n";
+ my $i = 0;
+ my $max = 5;
+ foreach my $block (sort {$a->{LINE} <=> $b->{LINE}} (@{$callers})) {
+ printf "\t%d-byte block at 0x%08x allocated on stdin:%d\n",
+ $block->{SIZE}, $block->{BASE}, $block->{LINE};
+ last if $i++ > $max;
+ }
+ print "\t...and ", $n_blocks - $max, " others...\n"
+ if $n_blocks > $max;
+ print "The blocks listed above were allocated by:\n";
+ print_callers("\t", ${$callers}[0]->{CALLERS});
+ }
+}
+sub interp_pointer {
+ my ($s_ptr) = @_;
+ return $s_ptr eq '(nil)' ? 0 : hex($s_ptr);
+}
+
+sub allocated {
+ my ($line, $s_base, $size, $callers) = @_;
+ my ($base) = interp_pointer($s_base);
+ return if !$base;
+ my ($info) = {LINE => $line,
+ BASE => $base,
+ SIZE => $size,
+ CALLERS => $callers};
+ if (exists($blocks{$base})) {
+ print "In-use address returned by allocator:\n";
+ print "\tInitial allocation:\n";
+ print_block("\t\t", $blocks{$base});
+ print "\tNew allocation:\n";
+ print_block("\t\t", $info);
+ }
+ $blocks{$base} = $info;
+}
+
+sub claimed {
+ my ($line, $s_base, $callers) = @_;
+ my ($base) = interp_pointer($s_base);
+ return if !$base;
+ if (exists($blocks{$base})) {
+ $blocks{$base}{LINE} = $line;
+ $blocks{$base}{CALLERS} = $callers;
+ } else {
+ printf "Claim asserted on not-in-use block 0x%08x by:\n", $base;
+ print_callers('', $callers);
+ }
+}
+
+sub freed {
+ my ($line, $s_base, $callers) = @_;
+ my ($base) = interp_pointer($s_base);
+ return if !$base;
+
+ if (!delete($blocks{$base})) {
+ printf "Bad free of not-allocated address 0x%08x on stdin:%d by:\n", $base, $line;
+ print_callers('', $callers);
+ }
+}
+
+sub print_block {
+ my ($prefix, $info) = @_;
+ printf '%s%d-byte block at 0x%08x allocated on stdin:%d by:' . "\n",
+ $prefix, $info->{SIZE}, $info->{BASE}, $info->{LINE};
+ print_callers($prefix, $info->{CALLERS});
+}
+
+sub print_callers {
+ my ($prefix, $callers) = @_;
+ foreach my $pc (split(' ', $callers)) {
+ print "$prefix\t", lookup_pc($pc), "\n";
+ }
+}
+
+our (%cache);
+sub lookup_pc {
+ my ($s_pc) = @_;
+ if (defined($binary)) {
+ my ($pc) = hex($s_pc);
+ my ($output) = "$s_pc: ";
+ if (!exists($cache{$pc})) {
+ open(A2L, "$a2l -fe $binary --demangle $s_pc|");
+ chomp(my $function = );
+ chomp(my $line = );
+ close(A2L);
+ if ($function eq '??') {
+ ($function, $line) = lookup_pc_by_segment($pc);
+ }
+ $line =~ s/^(\.\.\/)*//;
+ $line = "..." . substr($line, -25) if length($line) > 28;
+ $cache{$pc} = "$s_pc: $function ($line)";
+ }
+ return $cache{$pc};
+ } else {
+ return "$s_pc";
+ }
+}
+
+sub trim_callers {
+ my ($in) = @_;
+ my (@out);
+ foreach my $pc (split(' ', $in)) {
+ my $xlated = lookup_pc($pc);
+ if ($xlated =~ /\?\?/) {
+ push(@out, "...") if !@out || $out[$#out] ne '...';
+ } else {
+ push(@out, $pc);
+ }
+ }
+ return join(' ', @out);
+}
+
+sub search_path {
+ my ($target) = @_;
+ for my $dir (split (':', $ENV{PATH})) {
+ my ($file) = "$dir/$target";
+ return $file if -e $file;
+ }
+ return undef;
+}
+
+sub add_segment {
+ my ($vm_start, $vm_end, $vm_pgoff, $file) = @_;
+ for (my $i = 0; $i <= $#segments; $i++) {
+ my ($s) = $segments[$i];
+ next if $vm_end <= $s->{START} || $vm_start >= $s->{END};
+ if ($vm_start <= $s->{START} && $vm_end >= $s->{END}) {
+ splice(@segments, $i, 1);
+ --$i;
+ } else {
+ $s->{START} = $vm_end if $vm_end > $s->{START};
+ $s->{END} = $vm_start if $vm_start <= $s->{END};
+ }
+ }
+ push(@segments, {START => $vm_start,
+ END => $vm_end,
+ PGOFF => $vm_pgoff,
+ FILE => $file});
+ @segments = sort { $a->{START} <=> $b->{START} } @segments;
+}
+
+sub binary_search {
+ my ($array, $value) = @_;
+ my $l = 0;
+ my $r = $#{$array};
+ while ($l <= $r) {
+ my $m = int(($l + $r) / 2);
+ my $e = $array->[$m];
+ if ($value < $e->{START}) {
+ $r = $m - 1;
+ } elsif ($value >= $e->{END}) {
+ $l = $m + 1;
+ } else {
+ return $e;
+ }
+ }
+ return undef;
+}
+
+sub read_sections {
+ my ($file) = @_;
+ my (@sections);
+ open(OBJDUMP, "$objdump -h $file|");
+ while () {
+ my $ptr = "([0-9a-fA-F]+)";
+ my ($name, $size, $vma, $lma, $file_off)
+ = /^\s*\d+\s+(\S+)\s+$ptr\s+$ptr\s+$ptr\s+$ptr/
+ or next;
+ push(@sections, {START => hex($file_off),
+ END => hex($file_off) + hex($size),
+ NAME => $name});
+ }
+ close(OBJDUMP);
+ return [sort { $a->{START} <=> $b->{START} } @sections ];
+}
+
+our %file_to_sections;
+sub segment_to_section {
+ my ($file, $file_offset) = @_;
+ if (!defined($file_to_sections{$file})) {
+ $file_to_sections{$file} = read_sections($file);
+ }
+ return binary_search($file_to_sections{$file}, $file_offset);
+}
+
+sub address_to_segment {
+ my ($pc) = @_;
+ return binary_search(\@segments, $pc);
+}
+
+sub lookup_pc_by_segment {
+ return ('??', 0) if !defined($objdump);
+
+ my ($pc) = @_;
+ my ($segment) = address_to_segment($pc);
+ return ('??', 0) if !defined($segment) || $segment->{FILE} eq '';
+
+ my ($file_offset) = $pc - $segment->{START} + $segment->{PGOFF};
+ my ($section) = segment_to_section($segment->{FILE}, $file_offset);
+ return ('??', 0) if !defined($section);
+
+ my ($section_offset) = $file_offset - $section->{START};
+ open(A2L, sprintf("%s -fe %s --demangle --section=$section->{NAME} 0x%x|",
+ $a2l, $segment->{FILE}, $section_offset));
+ chomp(my $function = );
+ chomp(my $line = );
+ close(A2L);
+
+ return ($function, $line);
+}
+
+# Local Variables:
+# mode: perl
+# End:
diff --git a/utilities/ofp-pki.8.in b/utilities/ofp-pki.8.in
index 99572da..8255895 100644
--- a/utilities/ofp-pki.8.in
+++ b/utilities/ofp-pki.8.in
@@ -321,5 +321,5 @@ Prints a help usage message and exits.
.BR controller (8),
.BR dpctl (8),
.BR ofp\-pki\-cgi (8),
-.BR secchan (8),
-.BR udatapath (8)
+.BR ofprotocol (8),
+.BR ofdatapath (8)
diff --git a/utilities/vlogconf.8.in b/utilities/vlogconf.8.in
index f1acf62..eb9c192 100644
--- a/utilities/vlogconf.8.in
+++ b/utilities/vlogconf.8.in
@@ -1,3 +1,5 @@
+.ds PN vlogconf
+
.TH vlogconf 8 "June 2008" "OpenFlow" "OpenFlow Manual"
.SH NAME
@@ -51,8 +53,8 @@ None of the above, in which case \fItarget\fR prefixed by
\fB@RUNDIR@/\fR must match one of the cases for absolute paths listed
above. (The default name for a program's pidfile is
\fB@RUNDIR@/\fIprogram\fB.pid\fR, so this means that, say,
-\fBsecchan\fR's default pidfile may be referred to simply as
-\fBsecchan.pid\fR.)
+\fBofprotocol\fR's default pidfile may be referred to simply as
+\fBofprotocol.pid\fR.)
.RE
.PP
@@ -74,7 +76,7 @@ set the logging levels for all modules. The \fIfacility\fR may be
system log or to the console, respectively, or \fBANY\fR to set the
logging levels for both facilities. If it is omitted,
\fIfacility\fR defaults to \fBANY\fR. The \fIlevel\fR must be one of
-\fBemer\fR, \fBerr\fR, \fBwarn\fR, or \fBdbg\fR, designating the
+\fBemer\fR, \fBerr\fR, \fBwarn\fR, \fBinfo\fR, or \fBdbg\fR, designating the
minimum severity of a message for it to be logged. If it is omitted,
\fIlevel\fR defaults to \fBdbg\fR.
@@ -90,7 +92,7 @@ expanded as follows:
.RS
.TP
\fB%A\fR
-The name of the application logging the message, e.g. \fBsecchan\fR.
+The name of the application logging the message, e.g. \fBofprotocol\fR.
.TP
\fB%c\fR
@@ -171,13 +173,11 @@ is useful after rotating log files, to cause a new log file to be
used.)
.SH OPTIONS
-.TP
-\fB\-h\fR, \fB\-\^\-help\fR
-Prints a brief help message to the console and exits.
+.so lib/common.man
.SH "SEE ALSO"
.BR dpctl (8),
-.BR secchan (8),
+.BR ofprotocol (8),
.BR controller (8)
diff --git a/utilities/vlogconf.c b/utilities/vlogconf.c
index 6da0c17..5c48f72 100644
--- a/utilities/vlogconf.c
+++ b/utilities/vlogconf.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+/* Copyright (c) 2008, 2009 The Board of Trustees of The Leland Stanford
* Junior University
*
* We are making the OpenFlow specification and associated documentation
@@ -47,7 +47,7 @@
#include "util.h"
#include "vlog-socket.h"
-void
+static void
usage(char *prog_name, int exit_code)
{
printf("Usage: %s [TARGET] [ACTION...]\n"
@@ -62,7 +62,7 @@ usage(char *prog_name, int exit_code)
" Set MODULE and FACILITY log level to LEVEL\n"
" MODULE may be any valid module name or 'ANY'\n"
" FACILITY may be 'syslog', 'console', 'file', or 'ANY' (default)\n"
- " LEVEL may be 'emer', 'err', 'warn', or 'dbg' (default)\n"
+ " LEVEL may be 'emer', 'err', 'warn', 'info', or 'dbg' (default)\n"
" -r, --reopen Make the program reopen its log file\n"
" -h, --help Print this helpful information\n",
prog_name);
@@ -210,7 +210,7 @@ int main(int argc, char *argv[])
case 'r':
for (i = 0; i < n_clients; i++) {
struct vlog_client *client = clients[i];
- char *request = xasprintf("reopen");
+ char *request = xstrdup("reopen");
transact_ack(client, request, &ok);
free(request);
}