Change code base to Nicira's master branch

This commit is contained in:
Mikio Hara committed 2009-06-29 05:03:42 -07:00
1 parent 695db2964e
commit 5ff6ec248d
199 files changed
+10143 -1808

No files matched your search

+682 -2
View File
@@ -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. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
+7
View File
@@ -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
+39 -39
View File
@@ -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_<table>_mod.ko for each <table>
- datapath/linux-2.6/ofdatapath_<table>.ko for each <table>
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_<table>_mod.o for each <table>
- datapath/linux-2.6/ofdatapath_<table>.o for each <table>
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
-------------
+7 -9
View File
@@ -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
+4 -4
View File
@@ -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.
+111 -10
View File
@@ -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
@@ -143,3 +212,35 @@ AC_DEFUN([OFP_CHECK_DPKG_BUILDPACKAGE],
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 ----------------------------------------------------------------------
+20 -5
View File
@@ -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])
+10 -36
View File
@@ -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)
+14 -1
View File
@@ -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
+16 -8
View File
@@ -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)))
+23
View File
@@ -0,0 +1,23 @@
#ifndef BRC_SYSFS_H
#define BRC_SYSFS_H 1
struct datapath;
#include <linux/version.h>
#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 */
+519
View File
@@ -0,0 +1,519 @@
#include <linux/version.h>
#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 <linux/capability.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/netdevice.h>
#include <linux/if_bridge.h>
#include <linux/rtnetlink.h>
#include <linux/spinlock.h>
#include <linux/times.h>
#include <linux/version.h>
#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 */
+335
View File
@@ -0,0 +1,335 @@
#include <linux/version.h>
#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 <linux/capability.h>
#include <linux/kernel.h>
#include <linux/netdevice.h>
#include <linux/if_bridge.h>
#include <linux/rtnetlink.h>
#include <linux/spinlock.h>
#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 */
+504
View File
@@ -0,0 +1,504 @@
#include <linux/kernel.h>
#include <asm/uaccess.h>
#include <linux/completion.h>
#include <linux/etherdevice.h>
#include <linux/if_bridge.h>
#include <linux/netdevice.h>
#include <net/genetlink.h>
#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; i<ETH_ALEN; i++)
id |= (uint64_t)dev->dev_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<DP_MAX; i++) {
if (dp_get_by_idx(i)) {
rcu_read_unlock();
printk(KERN_EMERG "brcompat: no datapaths may exist!\n");
return -EEXIST;
}
}
rcu_read_unlock();
/* Set the bridge ioctl handler */
brioctl_set(brc_ioctl_deviceless_stub);
/* Set the OpenFlow device ioctl handler */
dp_ioctl_hook = brc_dev_ioctl;
/* Register hooks for datapath adds and deletes */
dp_add_dp_hook = brc_add_dp;
dp_del_dp_hook = brc_del_dp;
/* Register hooks for interface adds and deletes */
#if CONFIG_SYSFS
dp_add_if_hook = brc_sysfs_add_if;
dp_del_if_hook = brc_sysfs_del_if;
#endif
/* Register generic netlink family to communicate changes to
* userspace. */
err = genl_register_family(&brc_genl_family);
if (err)
goto error;
err = genl_register_ops(&brc_genl_family, &brc_genl_ops_query_dp);
if (err != 0)
goto err_unregister;
err = genl_register_ops(&brc_genl_family, &brc_genl_ops_dp_result);
if (err != 0)
goto err_unregister;
strcpy(brc_mc_group.name, "brcompat");
err = genl_register_mc_group(&brc_genl_family, &brc_mc_group);
if (err < 0)
goto err_unregister;
return 0;
err_unregister:
genl_unregister_family(&brc_genl_family);
error:
printk(KERN_EMERG "brcompat: failed to install!");
return err;
}
static void
brc_cleanup(void)
{
/* Unregister hooks for datapath adds and deletes */
dp_add_dp_hook = NULL;
dp_del_dp_hook = NULL;
/* Unregister hooks for interface adds and deletes */
dp_add_if_hook = NULL;
dp_del_if_hook = NULL;
/* Unregister ioctl hooks */
dp_ioctl_hook = NULL;
brioctl_set(NULL);
genl_unregister_family(&brc_genl_family);
}
module_init(brc_init);
module_exit(brc_cleanup);
MODULE_DESCRIPTION("OpenFlow bridge compatibility");
MODULE_AUTHOR("Copyright (c) 2009 The Board of Trustees of The Leland Stanford Junior University");
MODULE_LICENSE("GPL");
+4 -2
View File
@@ -1,10 +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
*/
#include "chain.h"
#include "datapath.h"
#include "flow.h"
#include "table.h"
#include <linux/module.h>
@@ -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;
}
+334 -159
View File
@@ -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; i++) {
if (!dps[i])
return i;
}
return -1;
}
/* Creates a new datapath numbered 'dp_idx'. If 'dp_idx' is -1, it
* allocates the lowest numbered index available. If 'dp_name' is not
* null, it is used as the device name instead of the default one.
* Returns 0 for success or a negative error code. */
static int new_dp(int dp_idx, const char *dp_name)
{
struct datapath *dp;
int err;
if (dp_idx < 0 || dp_idx >= 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; i<DP_MAX; i++) {
struct datapath *dp = rcu_dereference(dps[i]);
if (dp && !strcmp(dp->netdev->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();
+11 -10
View File
@@ -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 */
+11 -17
View File
@@ -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 <linux/skbuff.h>
#include <linux/in.h>
#include <linux/ip.h>
@@ -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;
}
}
+40 -57
View File
@@ -1,27 +1,27 @@
#include <linux/kernel.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/ethtool.h>
#include <linux/rcupdate.h>
#include <linux/skbuff.h>
#include <linux/workqueue.h>
#include <linux/dmi.h>
#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; i<ETH_ALEN; i++) {
unsigned char d[3];
d[0] = *uptr++;
d[1] = *uptr++;
d[2] = '\0';
mac[i] = simple_strtoul(d, NULL, 16);
}
/* If this is a Nicira one, then use it. */
if (mac[0] != 0x00 || mac[1] != 0x23 || mac[2] != 0x20)
return;
memcpy(netdev->dev_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<dp_idx>'.
*
* 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)
+9 -1
View File
@@ -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 */
+3 -23
View File
@@ -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;
}
+12 -9
View File
@@ -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);
+3 -3
View File
@@ -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
+6 -3
View File
@@ -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_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
+2 -2
View File
@@ -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 <openflow>/datapath/linux-2.6/hwtable_nf2_mod.ko
% insmod <openflow>/datapath/linux-2.6/openflow_netfpga2_mod.ko
Create an OpenFlow datapath:
+3
View File
@@ -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();
}
+1
View File
@@ -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"
+206
View File
@@ -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;
}
+52
View File
@@ -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
+209
View File
@@ -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 <linux/module.h>
#include <linux/rcupdate.h>
#include <linux/rculist.h>
#include <linux/slab.h>
#include <linux/list.h>
#include <linux/delay.h>
#include <linux/if_arp.h>
#include <linux/proc_fs.h>
#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);
}
+39
View File
@@ -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
+3 -3
View File
@@ -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
+1
View File
@@ -3,6 +3,7 @@
/Makefile
/Makefile.main
/attr.c
/brcompat.c
/chain.c
/datapath.c
/compat24.c
+4 -4
View File
@@ -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 <bh10@stanford.edu>
#
+3 -3
View File
@@ -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))
+5 -1
View File
@@ -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 \
@@ -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)
{
@@ -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
+5 -1
View File
@@ -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
+4 -4
View File
@@ -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))))
+7 -6
View File
@@ -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),)
+26 -5
View File
@@ -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 =
+3 -6
View File
@@ -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 */
@@ -0,0 +1,20 @@
#include "net/genetlink.h"
#include <linux/version.h>
#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 */
@@ -0,0 +1,22 @@
#include "net/genetlink.h"
#include <linux/version.h>
#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 */
-15
View File
@@ -1,15 +0,0 @@
#include "net/genetlink.h"
#include <linux/version.h>
#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 */
@@ -3,12 +3,7 @@
#include_next <asm-generic/bug.h>
#include <linux/version.h>
#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
@@ -3,17 +3,11 @@
#include_next <linux/icmp.h>
#include <linux/version.h>
#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
@@ -4,19 +4,13 @@
#include_next <linux/if_arp.h>
#include <linux/version.h>
#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 <linux/skbuff.h>
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
@@ -3,13 +3,7 @@
#include_next <linux/ip.h>
#include <linux/version.h>
#if ((LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22)) && \
(RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(5,2)))
#ifdef __KERNEL__
#include <linux/skbuff.h>
#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
@@ -3,19 +3,11 @@
#include_next <linux/ipv6.h>
#include <linux/version.h>
#if ((LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22)) && \
(RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(5,2)))
#ifdef __KERNEL__
#include <linux/skbuff.h>
#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
@@ -0,0 +1,26 @@
#ifndef __LINUX_JIFFIES_WRAPPER_H
#define __LINUX_JIFFIES_WRAPPER_H 1
#include_next <linux/jiffies.h>
#include <linux/version.h>
#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
@@ -0,0 +1,10 @@
#ifndef __LINUX_NETDEVICE_WRAPPER_H
#define __LINUX_NETDEVICE_WRAPPER_H 1
#include_next <linux/netdevice.h>
#ifndef to_net_dev
#define to_net_dev(class) container_of(class, struct net_device, class_dev)
#endif
#endif
@@ -0,0 +1,24 @@
#ifndef __LINUX_NETFILTER_BRIDGE_WRAPPER_H
#define __LINUX_NETFILTER_BRIDGE_WRAPPER_H
#include_next <linux/netfilter_bridge.h>
#include <linux/version.h>
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22)
#include <linux/if_vlan.h>
#include <linux/if_pppox.h>
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
@@ -1,21 +1,52 @@
#ifndef __LINUX_SKBUFF_WRAPPER_H
#define __LINUX_SKBUFF_WRAPPER_H 1
#include <linux/version.h>
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22)
#undef kmem_cache_create
#endif /* linux kernel <= 2.6.22 */
#include_next <linux/skbuff.h>
#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 <linux/version.h>
#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
@@ -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
@@ -1,27 +1,9 @@
#ifndef __LINUX_TCP_WRAPPER_H
#define __LINUX_TCP_WRAPPER_H 1
#include <linux/version.h>
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,23)
#undef dev_get_by_name
#endif
#include_next <linux/tcp.h>
#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
@@ -3,18 +3,11 @@
#include_next <linux/udp.h>
#include <linux/version.h>
#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
@@ -0,0 +1,22 @@
#ifndef __NET_NETLINK_WRAPPER_H
#define __NET_NETLINK_WRAPPER_H 1
#include_next <net/netlink.h>
#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 */
+537
View File
@@ -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 <xemul@openvz.org>
* Ethtool interface from: Eric W. Biederman <ebiederm@xmission.com>
*
*/
#include <linux/list.h>
#include <linux/netdevice.h>
#include <linux/ethtool.h>
#include <linux/etherdevice.h>
#include <net/dst.h>
#include <net/xfrm.h>
#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, &ethtool_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");
+19 -18
View File
@@ -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 <linux/etherdevice.h>
@@ -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;
}
+1
View File
@@ -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 *);
+7 -5
View File
@@ -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;
}
+3 -2
View File
@@ -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) {
+1
View File
@@ -5,6 +5,7 @@
/control
/corekeeper
/files
/nicira-switch
/openflow
/openflow-common
/openflow-common.copyright
+1 -1
View File
@@ -2,7 +2,7 @@ Source: openflow
Section: net
Priority: extra
Maintainer: OpenFlow Team <openflow-dev@lists.stanford.edu>
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
+1
View File
@@ -1,2 +1,3 @@
_debian/utilities/ofp-parse-leaks usr/bin
_debian/utilities/ofp-pki usr/sbin
_debian/utilities/vlogconf usr/sbin
+16
View File
@@ -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
+4 -3
View File
@@ -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).
+640
View File
@@ -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}
+1
View File
@@ -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
+64
View File
@@ -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 */
+5 -1
View File
@@ -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 */
+17
View File
@@ -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 \
+123
View File
@@ -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 <config.h>
#include "backtrace.h"
#include <errno.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stdio.h>
#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;
}
+48
View File
@@ -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 <stdint.h>
#define BACKTRACE_MAX_FRAMES 31
struct backtrace {
int n_frames;
uintptr_t frames[BACKTRACE_MAX_FRAMES];
};
void backtrace_capture(struct backtrace *);
#endif /* backtrace.h */
+72
View File
@@ -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 <config.h>
#include "bitmap.h"
#include <string.h>
/* 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;
}
+99
View File
@@ -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 <limits.h>
#include <stdlib.h>
#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 */
+7
View File
@@ -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.
+2 -1
View File
@@ -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)
+2 -1
View File
@@ -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);
+21
View File
@@ -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.
+12 -8
View File
@@ -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) {
+133 -85
View File
@@ -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 <config.h>
#include "dpif.h"
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <inttypes.h>
@@ -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);
+13 -9
View File
@@ -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 */
+4 -5
View File
@@ -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();
+2 -2
View File
@@ -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);
+30 -21
View File
@@ -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);
}
+30 -4
View File
@@ -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 <stdbool.h>
#include <stdint.h>
#include <string.h>
#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 */
+88 -7
View File
@@ -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 <config.h>
#include "hash.h"
#include <string.h>
/* 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;
}
+3 -5
View File
@@ -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 <stddef.h>
#include <stdint.h>
#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 */
+153
View File
@@ -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 <config.h>
#include "hmap.h"
#include <assert.h>
#include <stdint.h>
#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);
}
}
+235
View File
@@ -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 <stdbool.h>
#include <stdlib.h>
#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 */
+252
View File
@@ -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 <config.h>
#include "leak-checker.h"
#include <inttypes.h>
#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 <errno.h>
#include <fcntl.h>
#include <malloc.h>
#include <sys/stat.h>
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 */
+58
View File
@@ -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 <sys/types.h>
#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 */
+7
View File
@@ -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.
+17 -46
View File
@@ -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 "
+155 -30
View File
@@ -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);
}
}
+11 -3
View File
@@ -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 */
+225 -65
View File
@@ -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 <linux/ethtool.h>
#include <linux/ethtool.h>
#include <linux/rtnetlink.h>
#include <linux/sockios.h>
#include <linux/version.h>
#include <sys/types.h>
@@ -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;
+27 -10
View File
@@ -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 <stdbool.h>
#include <stddef.h>
#include <stdint.h>
/* 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 <stdbool.h>
#include <stdint.h>
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 */
Loaded 100 of 199 files, more files were not shown because too many files have changed in this diff. Show more