Change code base to Nicira's master branch
This commit is contained in:
1 parent
695db2964e
commit
5ff6ec248d
199 files changed
+10152
-1817
No files matched your search
@@ -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>.
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
+112
-11
@@ -1,4 +1,6 @@
|
||||
# Copyright (c) 2008 The Board of Trustees of The Leland Stanford
|
||||
# -*- autoconf -*-
|
||||
|
||||
# Copyright (c) 2008, 2009 The Board of Trustees of The Leland Stanford
|
||||
# Junior University
|
||||
#
|
||||
# We are making the OpenFlow specification and associated documentation
|
||||
@@ -35,8 +37,8 @@ dnl
|
||||
dnl Configure linux kernel source tree
|
||||
AC_DEFUN([OFP_CHECK_LINUX], [
|
||||
AC_ARG_WITH([$1],
|
||||
[AC_HELP_STRING([--with-$1=/path/to/linux-$3],
|
||||
[Specify the linux $3 kernel sources])],
|
||||
[AC_HELP_STRING([--with-$1=/path/to/linux-$2],
|
||||
[Specify the linux $2 kernel sources])],
|
||||
[path="$withval"], [path=])dnl
|
||||
if test -n "$path"; then
|
||||
path=`eval echo "$path"`
|
||||
@@ -44,25 +46,92 @@ AC_DEFUN([OFP_CHECK_LINUX], [
|
||||
AC_MSG_CHECKING([for $path directory])
|
||||
if test -d "$path"; then
|
||||
AC_MSG_RESULT([yes])
|
||||
$4=$path
|
||||
AC_SUBST($4)
|
||||
$3=$path
|
||||
AC_SUBST($3)
|
||||
else
|
||||
AC_MSG_RESULT([no])
|
||||
AC_ERROR([source dir $path doesn't exist])
|
||||
fi
|
||||
|
||||
AC_MSG_CHECKING([for $path kernel version])
|
||||
version=`grep '^PATCHLEVEL = ' "$path/Makefile" | sed 's/PATCHLEVEL = '//`
|
||||
AC_MSG_RESULT([2.$version])
|
||||
if test "2.$version" != '$3'; then
|
||||
AC_ERROR([Linux kernel source in $path is not version $3])
|
||||
patchlevel=`sed -n 's/^PATCHLEVEL = //p' "$path/Makefile"`
|
||||
sublevel=`sed -n 's/^SUBLEVEL = //p' "$path/Makefile"`
|
||||
AC_MSG_RESULT([2.$patchlevel.$sublevel])
|
||||
if test "2.$patchlevel" != '$2'; then
|
||||
AC_ERROR([Linux kernel source in $path is not version $2])
|
||||
fi
|
||||
if ! test -e "$path"/include/linux/version.h || \
|
||||
! test -e "$path"/include/linux/autoconf.h; then
|
||||
AC_MSG_ERROR([Linux kernel source in $path is not configured])
|
||||
fi
|
||||
m4_if($2, [2.6], [OFP_CHECK_LINUX26_COMPAT])
|
||||
fi
|
||||
AM_CONDITIONAL($4, test -n "$path")
|
||||
])
|
||||
|
||||
dnl OFP_GREP_IFELSE(FILE, REGEX, IF-MATCH, IF-NO-MATCH)
|
||||
dnl
|
||||
dnl Greps FILE for REGEX. If it matches, runs IF-MATCH, otherwise IF-NO-MATCH.
|
||||
AC_DEFUN([OFP_GREP_IFELSE], [
|
||||
AC_MSG_CHECKING([whether $2 matches in $1])
|
||||
grep '$2' $1 >/dev/null 2>&1
|
||||
status=$?
|
||||
case $status in
|
||||
0)
|
||||
AC_MSG_RESULT([yes])
|
||||
$3
|
||||
;;
|
||||
1)
|
||||
AC_MSG_RESULT([no])
|
||||
$4
|
||||
;;
|
||||
*)
|
||||
AC_MSG_ERROR([grep exited with status $status])
|
||||
;;
|
||||
esac
|
||||
])
|
||||
|
||||
dnl OFP_DEFINE(NAME)
|
||||
dnl
|
||||
dnl Defines NAME to 1 in kcompat.h.
|
||||
AC_DEFUN([OFP_DEFINE], [
|
||||
echo '#define $1 1' >> datapath/linux-2.6/kcompat.h.new
|
||||
])
|
||||
|
||||
AC_DEFUN([OFP_CHECK_VETH], [
|
||||
AC_MSG_CHECKING([whether to build veth module])
|
||||
if test "$sublevel" = 18; then
|
||||
AC_MSG_RESULT([yes])
|
||||
AC_SUBST([BUILD_VETH], 1)
|
||||
else
|
||||
AC_MSG_RESULT([no])
|
||||
fi
|
||||
])
|
||||
|
||||
dnl OFP_CHECK_LINUX26_COMPAT
|
||||
dnl
|
||||
dnl Runs various Autoconf checks on the Linux 2.6 kernel source in
|
||||
dnl the directory in $KSRC26.
|
||||
AC_DEFUN([OFP_CHECK_LINUX26_COMPAT], [
|
||||
rm -f datapath/linux-2.6/kcompat.h.new
|
||||
mkdir -p datapath/linux-2.6
|
||||
: > datapath/linux-2.6/kcompat.h.new
|
||||
OFP_GREP_IFELSE([$KSRC26/include/linux/skbuff.h], [skb_transport_header],
|
||||
[OFP_DEFINE([HAVE_SKBUFF_HEADER_HELPERS])])
|
||||
OFP_GREP_IFELSE([$KSRC26/include/linux/skbuff.h], [raw],
|
||||
[OFP_DEFINE([HAVE_MAC_RAW])])
|
||||
OFP_GREP_IFELSE([$KSRC26/include/linux/skbuff.h],
|
||||
[skb_copy_from_linear_data_offset],
|
||||
[OFP_DEFINE([HAVE_SKB_COPY_FROM_LINEAR_DATA_OFFSET])])
|
||||
OFP_GREP_IFELSE([$KSRC26/include/net/netlink.h], [NLA_NUL_STRING],
|
||||
[OFP_DEFINE([HAVE_NLA_NUL_STRING])])
|
||||
OFP_CHECK_VETH
|
||||
if cmp -s datapath/linux-2.6/kcompat.h.new \
|
||||
datapath/linux-2.6/kcompat.h >/dev/null 2>&1; then
|
||||
rm datapath/linux-2.6/kcompat.h.new
|
||||
else
|
||||
mv datapath/linux-2.6/kcompat.h.new datapath/linux-2.6/kcompat.h
|
||||
fi
|
||||
AM_CONDITIONAL($5, test -n "$path")
|
||||
])
|
||||
|
||||
dnl Checks for --enable-hw-tables and substitutes HW_TABLES to any
|
||||
@@ -142,4 +211,36 @@ AC_DEFUN([OFP_CHECK_DPKG_BUILDPACKAGE],
|
||||
[AC_CHECK_PROG([HAVE_DPKG_BUILDPACKAGE], [dpkg-buildpackage], [yes], [no])
|
||||
AM_CONDITIONAL([HAVE_DPKG_BUILDPACKAGE],
|
||||
[test $HAVE_DPKG_BUILDPACKAGE = yes])])
|
||||
|
||||
|
||||
dnl ----------------------------------------------------------------------
|
||||
dnl These macros are from GNU PSPP, with the following original license:
|
||||
dnl Copyright (C) 2005, 2006, 2007 Free Software Foundation, Inc.
|
||||
dnl This file is free software; the Free Software Foundation
|
||||
dnl gives unlimited permission to copy and/or distribute it,
|
||||
dnl with or without modifications, as long as this notice is preserved.
|
||||
|
||||
dnl OFP_CHECK_CC_OPTION([OPTION], [ACTION-IF-ACCEPTED], [ACTION-IF-REJECTED])
|
||||
dnl Check whether the given C compiler OPTION is accepted.
|
||||
dnl If so, execute ACTION-IF-ACCEPTED, otherwise ACTION-IF-REJECTED.
|
||||
AC_DEFUN([OFP_CHECK_CC_OPTION],
|
||||
[
|
||||
m4_define([ofp_cv_name], [ofp_cv_[]m4_translit([$1], [-], [_])])dnl
|
||||
AC_CACHE_CHECK([whether $CC accepts $1], [ofp_cv_name],
|
||||
[ofp_save_CFLAGS="$CFLAGS"
|
||||
CFLAGS="$CFLAGS $1"
|
||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM(,)], [ofp_cv_name[]=yes], [ofp_cv_name[]=no])
|
||||
CFLAGS="$ofp_save_CFLAGS"])
|
||||
if test $ofp_cv_name = yes; then
|
||||
m4_if([$2], [], [;], [$2])
|
||||
else
|
||||
m4_if([$3], [], [:], [$3])
|
||||
fi
|
||||
])
|
||||
|
||||
dnl OFP_ENABLE_OPTION([OPTION])
|
||||
dnl Check whether the given C compiler OPTION is accepted.
|
||||
dnl If so, add it to CFLAGS.
|
||||
dnl Example: OFP_ENABLE_OPTION([-Wdeclaration-after-statement])
|
||||
AC_DEFUN([OFP_ENABLE_OPTION],
|
||||
[OFP_CHECK_CC_OPTION([$1], [CFLAGS="$CFLAGS $1"])])
|
||||
dnl ----------------------------------------------------------------------
|
||||
+20
-5
@@ -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
@@ -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
@@ -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
@@ -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)))
|
||||
@@ -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 */
|
||||
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
# Specify the module to build.
|
||||
all_modules += hwtable_nf2
|
||||
build_modules += ofdatapath_netfpga
|
||||
dist_modules += ofdatapath_netfpga
|
||||
|
||||
# Specify the source files that comprise the module.
|
||||
hwtable_nf2_sources = \
|
||||
ofdatapath_netfpga_sources = \
|
||||
hwtable_nf2/nf2_flowtable.c \
|
||||
hwtable_nf2/nf2_procfs.c \
|
||||
hwtable_nf2/nf2_openflow.c \
|
||||
hwtable_nf2/nf2_lib.c
|
||||
hwtable_nf2/nf2_lib.c
|
||||
|
||||
hwtable_nf2_headers = \
|
||||
ofdatapath_netfpga_headers = \
|
||||
hwtable_nf2/nf2.h \
|
||||
hwtable_nf2/nf2_reg.h \
|
||||
hwtable_nf2/nf2_hwapi.h \
|
||||
hwtable_nf2/nf2_flowtable.h \
|
||||
hwtable_nf2/nf2_procfs.h \
|
||||
hwtable_nf2/nf2_openflow.h \
|
||||
hwtable_nf2/nf2_lib.h
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
/Makefile
|
||||
/Makefile.main
|
||||
/attr.c
|
||||
/brcompat.c
|
||||
/chain.c
|
||||
/datapath.c
|
||||
/compat24.c
|
||||
|
||||
@@ -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>
|
||||
#
|
||||
|
||||
@@ -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))
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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))))
|
||||
@@ -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),)
|
||||
|
||||
|
||||
@@ -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 =
|
||||
@@ -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 */
|
||||
@@ -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,24 +1,55 @@
|
||||
#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
|
||||
/* Emulate Linux 2.6.17 and later behavior, in which kfree_skb silently ignores
|
||||
* null pointer arguments. */
|
||||
#define kfree_skb(skb) kfree_skb_maybe_null(skb)
|
||||
static inline void kfree_skb_maybe_null(struct sk_buff *skb)
|
||||
@@ -29,22 +60,21 @@ static inline void kfree_skb_maybe_null(struct sk_buff *skb)
|
||||
#endif
|
||||
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
|
||||
#ifndef CHECKSUM_PARTIAL
|
||||
/* Note that CHECKSUM_PARTIAL is not implemented, but this allows us to at
|
||||
* least test against it: see update_csum() in forward.c. */
|
||||
#undef CHECKSUM_PARTIAL
|
||||
#define CHECKSUM_PARTIAL 3
|
||||
#endif
|
||||
#ifndef CHECKSUM_COMPLETE
|
||||
#define CHECKSUM_COMPLETE CHECKSUM_HW
|
||||
#endif /* linux kernel < 2.6.19 */
|
||||
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22)
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_MAC_RAW
|
||||
#define mac_header mac.raw
|
||||
#define network_header nh.raw
|
||||
#endif
|
||||
|
||||
#if RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(5,2)
|
||||
|
||||
#ifndef HAVE_SKBUFF_HEADER_HELPERS
|
||||
static inline unsigned char *skb_transport_header(const struct sk_buff *skb)
|
||||
{
|
||||
return skb->h.raw;
|
||||
@@ -102,9 +132,6 @@ static inline void skb_copy_to_linear_data(struct sk_buff *skb,
|
||||
{
|
||||
memcpy(skb->data, from, len);
|
||||
}
|
||||
|
||||
#endif /* rhel version < 5.2 */
|
||||
|
||||
#endif /* linux kernel < 2.6.22 */
|
||||
#endif /* !HAVE_SKBUFF_HEADER_HELPERS */
|
||||
|
||||
#endif
|
||||
@@ -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 */
|
||||
@@ -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, ðtool_stats_keys, sizeof(ethtool_stats_keys));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void veth_get_ethtool_stats(struct net_device *dev,
|
||||
struct ethtool_stats *stats, u64 *data)
|
||||
{
|
||||
struct veth_priv *priv;
|
||||
|
||||
priv = netdev_priv(dev);
|
||||
data[0] = priv->peer->ifindex;
|
||||
}
|
||||
|
||||
static u32 veth_get_rx_csum(struct net_device *dev)
|
||||
{
|
||||
struct veth_priv *priv;
|
||||
|
||||
priv = netdev_priv(dev);
|
||||
return priv->ip_summed == CHECKSUM_UNNECESSARY;
|
||||
}
|
||||
|
||||
static int veth_set_rx_csum(struct net_device *dev, u32 data)
|
||||
{
|
||||
struct veth_priv *priv;
|
||||
|
||||
priv = netdev_priv(dev);
|
||||
priv->ip_summed = data ? CHECKSUM_UNNECESSARY : CHECKSUM_NONE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u32 veth_get_tx_csum(struct net_device *dev)
|
||||
{
|
||||
return (dev->features & NETIF_F_NO_CSUM) != 0;
|
||||
}
|
||||
|
||||
static int veth_set_tx_csum(struct net_device *dev, u32 data)
|
||||
{
|
||||
if (data)
|
||||
dev->features |= NETIF_F_NO_CSUM;
|
||||
else
|
||||
dev->features &= ~NETIF_F_NO_CSUM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct ethtool_ops veth_ethtool_ops = {
|
||||
.get_settings = veth_get_settings,
|
||||
.get_drvinfo = veth_get_drvinfo,
|
||||
.get_link = ethtool_op_get_link,
|
||||
.get_rx_csum = veth_get_rx_csum,
|
||||
.set_rx_csum = veth_set_rx_csum,
|
||||
.get_tx_csum = veth_get_tx_csum,
|
||||
.set_tx_csum = veth_set_tx_csum,
|
||||
.get_sg = ethtool_op_get_sg,
|
||||
.set_sg = ethtool_op_set_sg,
|
||||
.get_strings = veth_get_strings,
|
||||
.get_ethtool_stats = veth_get_ethtool_stats,
|
||||
};
|
||||
|
||||
/*
|
||||
* xmit
|
||||
*/
|
||||
|
||||
static int veth_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
{
|
||||
struct net_device *rcv = NULL;
|
||||
struct veth_priv *priv, *rcv_priv;
|
||||
struct veth_net_stats *stats;
|
||||
int length, cpu;
|
||||
|
||||
skb_orphan(skb);
|
||||
|
||||
priv = netdev_priv(dev);
|
||||
rcv = priv->peer;
|
||||
rcv_priv = netdev_priv(rcv);
|
||||
|
||||
cpu = smp_processor_id();
|
||||
stats = per_cpu_ptr(priv->stats, cpu);
|
||||
|
||||
if (!(rcv->flags & IFF_UP))
|
||||
goto outf;
|
||||
|
||||
skb->dev = rcv;
|
||||
skb->pkt_type = PACKET_HOST;
|
||||
skb->protocol = eth_type_trans(skb, rcv);
|
||||
if (dev->features & NETIF_F_NO_CSUM)
|
||||
skb->ip_summed = rcv_priv->ip_summed;
|
||||
|
||||
dst_release(skb->dst);
|
||||
skb->dst = NULL;
|
||||
secpath_reset(skb);
|
||||
nf_reset(skb);
|
||||
|
||||
length = skb->len;
|
||||
|
||||
stats->tx_bytes += length;
|
||||
stats->tx_packets++;
|
||||
|
||||
stats = per_cpu_ptr(rcv_priv->stats, cpu);
|
||||
stats->rx_bytes += length;
|
||||
stats->rx_packets++;
|
||||
|
||||
netif_rx(skb);
|
||||
return 0;
|
||||
|
||||
outf:
|
||||
kfree_skb(skb);
|
||||
stats->tx_dropped++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* general routines
|
||||
*/
|
||||
|
||||
static struct net_device_stats *veth_get_stats(struct net_device *dev)
|
||||
{
|
||||
struct veth_priv *priv;
|
||||
struct net_device_stats *dev_stats;
|
||||
int cpu;
|
||||
struct veth_net_stats *stats;
|
||||
|
||||
priv = netdev_priv(dev);
|
||||
dev_stats = &priv->dev_stats;
|
||||
|
||||
dev_stats->rx_packets = 0;
|
||||
dev_stats->tx_packets = 0;
|
||||
dev_stats->rx_bytes = 0;
|
||||
dev_stats->tx_bytes = 0;
|
||||
dev_stats->tx_dropped = 0;
|
||||
|
||||
for_each_online_cpu(cpu) {
|
||||
stats = per_cpu_ptr(priv->stats, cpu);
|
||||
|
||||
dev_stats->rx_packets += stats->rx_packets;
|
||||
dev_stats->tx_packets += stats->tx_packets;
|
||||
dev_stats->rx_bytes += stats->rx_bytes;
|
||||
dev_stats->tx_bytes += stats->tx_bytes;
|
||||
dev_stats->tx_dropped += stats->tx_dropped;
|
||||
}
|
||||
|
||||
return dev_stats;
|
||||
}
|
||||
|
||||
static int veth_open(struct net_device *dev)
|
||||
{
|
||||
struct veth_priv *priv;
|
||||
|
||||
priv = netdev_priv(dev);
|
||||
if (priv->peer == NULL)
|
||||
return -ENOTCONN;
|
||||
|
||||
if (priv->peer->flags & IFF_UP) {
|
||||
netif_carrier_on(dev);
|
||||
netif_carrier_on(priv->peer);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int veth_dev_init(struct net_device *dev)
|
||||
{
|
||||
struct veth_net_stats *stats;
|
||||
struct veth_priv *priv;
|
||||
|
||||
stats = alloc_percpu(struct veth_net_stats);
|
||||
if (stats == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
priv = netdev_priv(dev);
|
||||
priv->stats = stats;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void veth_dev_free(struct net_device *dev)
|
||||
{
|
||||
struct veth_priv *priv;
|
||||
|
||||
priv = netdev_priv(dev);
|
||||
free_percpu(priv->stats);
|
||||
free_netdev(dev);
|
||||
}
|
||||
|
||||
static void veth_setup(struct net_device *dev)
|
||||
{
|
||||
ether_setup(dev);
|
||||
|
||||
dev->hard_start_xmit = veth_xmit;
|
||||
dev->get_stats = veth_get_stats;
|
||||
dev->open = veth_open;
|
||||
dev->ethtool_ops = &veth_ethtool_ops;
|
||||
dev->features |= NETIF_F_LLTX;
|
||||
dev->init = veth_dev_init;
|
||||
dev->destructor = veth_dev_free;
|
||||
}
|
||||
|
||||
static void veth_change_state(struct net_device *dev)
|
||||
{
|
||||
struct net_device *peer;
|
||||
struct veth_priv *priv;
|
||||
|
||||
priv = netdev_priv(dev);
|
||||
peer = priv->peer;
|
||||
|
||||
if (netif_carrier_ok(peer)) {
|
||||
if (!netif_carrier_ok(dev))
|
||||
netif_carrier_on(dev);
|
||||
} else {
|
||||
if (netif_carrier_ok(dev))
|
||||
netif_carrier_off(dev);
|
||||
}
|
||||
}
|
||||
|
||||
static int veth_device_event(struct notifier_block *unused,
|
||||
unsigned long event, void *ptr)
|
||||
{
|
||||
struct net_device *dev = ptr;
|
||||
|
||||
if (dev->open != veth_open)
|
||||
goto out;
|
||||
|
||||
switch (event) {
|
||||
case NETDEV_CHANGE:
|
||||
veth_change_state(dev);
|
||||
break;
|
||||
}
|
||||
out:
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static struct notifier_block veth_notifier_block __read_mostly = {
|
||||
.notifier_call = veth_device_event,
|
||||
};
|
||||
|
||||
/*
|
||||
* netlink interface
|
||||
*/
|
||||
|
||||
static int veth_newlink(const char *devname, const char *peername)
|
||||
{
|
||||
int err;
|
||||
const char *names[2];
|
||||
struct net_device *devs[2];
|
||||
int i;
|
||||
|
||||
names[0] = devname;
|
||||
names[1] = peername;
|
||||
devs[0] = devs[1] = NULL;
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
struct net_device *dev;
|
||||
|
||||
err = -ENOMEM;
|
||||
devs[i] = alloc_netdev(sizeof(struct veth_priv),
|
||||
names[i], veth_setup);
|
||||
if (!devs[i]) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
dev = devs[i];
|
||||
|
||||
if (strchr(dev->name, '%')) {
|
||||
err = dev_alloc_name(dev, dev->name);
|
||||
if (err < 0)
|
||||
goto err;
|
||||
}
|
||||
random_ether_addr(dev->dev_addr);
|
||||
|
||||
err = register_netdevice(dev);
|
||||
if (err < 0)
|
||||
goto err;
|
||||
|
||||
netif_carrier_off(dev);
|
||||
}
|
||||
|
||||
/*
|
||||
* tie the devices together
|
||||
*/
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
struct veth_priv *priv = netdev_priv(devs[i]);
|
||||
priv->dev = devs[i];
|
||||
priv->peer = devs[!i];
|
||||
if (!i)
|
||||
list_add(&priv->list, &veth_list);
|
||||
else
|
||||
INIT_LIST_HEAD(&priv->list);
|
||||
}
|
||||
return 0;
|
||||
|
||||
err:
|
||||
for (i = 0; i < 2; i++) {
|
||||
if (devs[i]) {
|
||||
if (devs[i]->reg_state != NETREG_UNINITIALIZED)
|
||||
unregister_netdevice(devs[i]);
|
||||
else
|
||||
free_netdev(devs[i]);
|
||||
}
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static void veth_dellink(struct net_device *dev)
|
||||
{
|
||||
struct veth_priv *priv;
|
||||
struct net_device *peer;
|
||||
|
||||
priv = netdev_priv(dev);
|
||||
peer = priv->peer;
|
||||
|
||||
if (!list_empty(&priv->list))
|
||||
list_del(&priv->list);
|
||||
|
||||
priv = netdev_priv(peer);
|
||||
if (!list_empty(&priv->list))
|
||||
list_del(&priv->list);
|
||||
|
||||
unregister_netdevice(dev);
|
||||
unregister_netdevice(peer);
|
||||
}
|
||||
|
||||
/*
|
||||
* sysfs
|
||||
*/
|
||||
|
||||
/*
|
||||
* "show" function for the veth_pairs attribute.
|
||||
* The class parameter is ignored.
|
||||
*/
|
||||
static ssize_t veth_show_veth_pairs(struct class *cls, char *buffer)
|
||||
{
|
||||
int res = 0;
|
||||
struct veth_priv *priv;
|
||||
|
||||
list_for_each_entry(priv, &veth_list, list) {
|
||||
if (res > (PAGE_SIZE - (IFNAMSIZ * 2 + 1))) {
|
||||
/* not enough space for another interface name */
|
||||
if ((PAGE_SIZE - res) > 10)
|
||||
res = PAGE_SIZE - 10;
|
||||
res += sprintf(buffer + res, "++more++");
|
||||
break;
|
||||
}
|
||||
res += sprintf(buffer + res, "%s,%s ",
|
||||
priv->dev->name, priv->peer->name);
|
||||
}
|
||||
res += sprintf(buffer + res, "\n");
|
||||
res++;
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* "store" function for the veth_pairs attribute. This is what
|
||||
* creates and deletes veth pairs.
|
||||
*
|
||||
* The class parameter is ignored.
|
||||
*
|
||||
*/
|
||||
static ssize_t veth_store_veth_pairs(struct class *cls, const char *buffer,
|
||||
size_t count)
|
||||
{
|
||||
int c = *buffer++;
|
||||
int retval;
|
||||
printk("1\n");
|
||||
if (c == '+') {
|
||||
char devname[IFNAMSIZ + 1] = "";
|
||||
char peername[IFNAMSIZ + 1] = "";
|
||||
char *comma = strchr(buffer, ',');
|
||||
printk("2\n");
|
||||
if (!comma)
|
||||
goto err_no_cmd;
|
||||
strncat(devname, buffer,
|
||||
min_t(int, sizeof devname, comma - buffer));
|
||||
strncat(peername, comma + 1,
|
||||
min_t(int, sizeof peername, strcspn(comma + 1, "\n")));
|
||||
printk("3 '%s' '%s'\n", devname, peername);
|
||||
if (!dev_valid_name(devname) || !dev_valid_name(peername))
|
||||
goto err_no_cmd;
|
||||
printk("4\n");
|
||||
rtnl_lock();
|
||||
retval = veth_newlink(devname, peername);
|
||||
rtnl_unlock();
|
||||
return retval ? retval : count;
|
||||
} else if (c == '-') {
|
||||
struct net_device *dev;
|
||||
|
||||
rtnl_lock();
|
||||
dev = dev_get_by_name(buffer);
|
||||
if (!dev)
|
||||
retval = -ENODEV;
|
||||
else if (dev->init != veth_dev_init)
|
||||
retval = -EINVAL;
|
||||
else {
|
||||
veth_dellink(dev);
|
||||
retval = count;
|
||||
}
|
||||
rtnl_unlock();
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
err_no_cmd:
|
||||
printk(KERN_ERR DRV_NAME ": no command found in veth_pairs. Use +ifname,peername or -ifname.\n");
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
/* class attribute for veth_pairs file. This ends up in /sys/class/net */
|
||||
static CLASS_ATTR(veth_pairs, S_IWUSR | S_IRUGO,
|
||||
veth_show_veth_pairs, veth_store_veth_pairs);
|
||||
|
||||
static struct class *netdev_class;
|
||||
|
||||
/*
|
||||
* Initialize sysfs. This sets up the veth_pairs file in
|
||||
* /sys/class/net.
|
||||
*/
|
||||
int veth_create_sysfs(void)
|
||||
{
|
||||
struct net_device *dev = dev_get_by_name("lo");
|
||||
if (!dev)
|
||||
return -ESRCH;
|
||||
netdev_class = dev->class_dev.class;
|
||||
if (!netdev_class)
|
||||
return -ENODEV;
|
||||
|
||||
return class_create_file(netdev_class, &class_attr_veth_pairs);
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove /sys/class/net/veth_pairs.
|
||||
*/
|
||||
void veth_destroy_sysfs(void)
|
||||
{
|
||||
class_remove_file(netdev_class, &class_attr_veth_pairs);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* init/fini
|
||||
*/
|
||||
|
||||
static __init int veth_init(void)
|
||||
{
|
||||
int retval = veth_create_sysfs();
|
||||
if (retval)
|
||||
return retval;
|
||||
register_netdevice_notifier(&veth_notifier_block);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __exit void veth_exit(void)
|
||||
{
|
||||
unregister_netdevice_notifier(&veth_notifier_block);
|
||||
}
|
||||
|
||||
module_init(veth_init);
|
||||
module_exit(veth_exit);
|
||||
|
||||
MODULE_DESCRIPTION("Virtual Ethernet Tunnel");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
+19
-18
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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 *);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
/control
|
||||
/corekeeper
|
||||
/files
|
||||
/nicira-switch
|
||||
/openflow
|
||||
/openflow-common
|
||||
/openflow-common.copyright
|
||||
|
||||
Vendored
+1
-1
@@ -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
|
||||
|
||||
Vendored
+1
@@ -1,2 +1,3 @@
|
||||
_debian/utilities/ofp-parse-leaks usr/bin
|
||||
_debian/utilities/ofp-pki usr/sbin
|
||||
_debian/utilities/vlogconf usr/sbin
|
||||
Vendored
+16
@@ -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
|
||||
|
||||
Vendored
+4
-3
@@ -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).
|
||||
|
||||
@@ -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,4 +1,5 @@
|
||||
noinst_HEADERS += \
|
||||
include/openflow/brcompat-netlink.h \
|
||||
include/openflow/nicira-ext.h \
|
||||
include/openflow/openflow.h \
|
||||
include/openflow/openflow-netlink.h
|
||||
@@ -0,0 +1,64 @@
|
||||
/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
|
||||
* Junior University
|
||||
*
|
||||
* We are making the OpenFlow specification and associated documentation
|
||||
* (Software) available for public use and benefit with the expectation
|
||||
* that others will use, modify and enhance the Software and contribute
|
||||
* those enhancements back to the community. However, since we would
|
||||
* like to make the Software available for broadest use, with as few
|
||||
* restrictions as possible permission is hereby granted, free of
|
||||
* charge, to any person obtaining a copy of this Software to deal in
|
||||
* the Software under the copyrights without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
* The name and trademarks of copyright holder(s) may NOT be used in
|
||||
* advertising or publicity pertaining to the Software or any
|
||||
* derivatives without specific, written prior permission.
|
||||
*/
|
||||
|
||||
#ifndef OPENFLOW_BRCOMPAT_NETLINK_H
|
||||
#define OPENFLOW_BRCOMPAT_NETLINK_H 1
|
||||
|
||||
#define BRC_GENL_FAMILY_NAME "brcompat"
|
||||
|
||||
/* Attributes that can be attached to the datapath's netlink messages. */
|
||||
enum {
|
||||
BRC_GENL_A_UNSPEC,
|
||||
BRC_GENL_A_DP_NAME, /* Datapath name. */
|
||||
BRC_GENL_A_PORT_NAME, /* Interface name. */
|
||||
BRC_GENL_A_ERR_CODE, /* Positive error code. */
|
||||
BRC_GENL_A_MC_GROUP, /* Generic netlink multicast group. */
|
||||
|
||||
__BRC_GENL_A_MAX,
|
||||
BRC_GENL_A_MAX = __BRC_GENL_A_MAX - 1
|
||||
};
|
||||
|
||||
/* Commands that can be executed on the datapath's netlink interface. */
|
||||
enum brc_genl_command {
|
||||
BRC_GENL_C_UNSPEC,
|
||||
BRC_GENL_C_DP_ADD, /* Datapath created. */
|
||||
BRC_GENL_C_DP_DEL, /* Datapath destroyed. */
|
||||
BRC_GENL_C_DP_RESULT, /* Result of datapath command from vswitchd. */
|
||||
BRC_GENL_C_PORT_ADD, /* Port added to datapath. */
|
||||
BRC_GENL_C_PORT_DEL, /* Port removed from datapath. */
|
||||
BRC_GENL_C_QUERY_MC, /* Get multicast group for brcompat. */
|
||||
|
||||
__BRC_GENL_C_MAX,
|
||||
BRC_GENL_C_MAX = __BRC_GENL_C_MAX - 1
|
||||
};
|
||||
#endif /* openflow/brcompat-netlink.h */
|
||||
@@ -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 */
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
Reference in new issue
Block a user