aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMaciej Żenczykowski <maze@google.com>2023-10-27 07:10:11 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2023-10-27 07:10:11 +0000
commit1077f3919194221ba7a0a35acebead3e861a55cc (patch)
tree5b2334c5ceba365679e129403be6d418cc85cd38
parent751dbbb694f3fdaec0f665f88a00d2fd734ec5b9 (diff)
parent03c9e310b47fb9c56fdc517ccfab7363dc92ae9c (diff)
downloadethtool-1077f3919194221ba7a0a35acebead3e861a55cc.tar.gz
ethtool: import of git.netfilter.org/libmnl @ master am: 03c9e310b4
Original change: https://android-review.googlesource.com/c/platform/external/ethtool/+/2804533 Change-Id: I15f7d8117b2d796de59e631034ad11fd2c384429 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
-rw-r--r--LICENSE6
-rw-r--r--libmnl/.gitignore20
-rw-r--r--libmnl/COPYING502
-rw-r--r--libmnl/Make_global.am24
-rw-r--r--libmnl/Makefile.am11
-rw-r--r--libmnl/README28
-rwxr-xr-xlibmnl/autogen.sh4
-rw-r--r--libmnl/configure.ac70
-rw-r--r--libmnl/doxygen/.gitignore4
-rw-r--r--libmnl/doxygen/Makefile.am25
-rw-r--r--libmnl/doxygen/doxygen.cfg.in23
-rw-r--r--libmnl/doxygen/finalize_manpages.sh40
-rw-r--r--libmnl/examples/Makefile.am1
-rw-r--r--libmnl/examples/genl/.gitignore2
-rw-r--r--libmnl/examples/genl/Makefile.am10
-rw-r--r--libmnl/examples/genl/genl-family-get.c241
-rw-r--r--libmnl/examples/genl/genl-group-events.c63
-rw-r--r--libmnl/examples/kobject/.gitignore1
-rw-r--r--libmnl/examples/kobject/Makefile.am6
-rw-r--r--libmnl/examples/kobject/kobject-event.c49
-rw-r--r--libmnl/examples/netfilter/.gitignore6
-rw-r--r--libmnl/examples/netfilter/Makefile.am26
-rw-r--r--libmnl/examples/netfilter/nf-log.c219
-rw-r--r--libmnl/examples/netfilter/nf-queue.c243
-rw-r--r--libmnl/examples/netfilter/nfct-create-batch.c187
-rw-r--r--libmnl/examples/netfilter/nfct-daemon.c365
-rw-r--r--libmnl/examples/netfilter/nfct-dump.c319
-rw-r--r--libmnl/examples/netfilter/nfct-event.c240
-rw-r--r--libmnl/examples/rtnl/.gitignore12
-rw-r--r--libmnl/examples/rtnl/Makefile.am48
-rw-r--r--libmnl/examples/rtnl/rtnl-addr-add.c119
-rw-r--r--libmnl/examples/rtnl/rtnl-addr-dump.c133
-rw-r--r--libmnl/examples/rtnl/rtnl-link-can.c452
-rw-r--r--libmnl/examples/rtnl/rtnl-link-dump.c130
-rw-r--r--libmnl/examples/rtnl/rtnl-link-dump2.c103
-rw-r--r--libmnl/examples/rtnl/rtnl-link-dump3.c103
-rw-r--r--libmnl/examples/rtnl/rtnl-link-event.c95
-rw-r--r--libmnl/examples/rtnl/rtnl-link-set.c84
-rw-r--r--libmnl/examples/rtnl/rtnl-neigh-dump.c158
-rw-r--r--libmnl/examples/rtnl/rtnl-route-add.c127
-rw-r--r--libmnl/examples/rtnl/rtnl-route-dump.c356
-rw-r--r--libmnl/examples/rtnl/rtnl-route-event.c341
-rw-r--r--libmnl/include/Makefile.am1
-rw-r--r--libmnl/include/libmnl/Makefile.am1
-rw-r--r--libmnl/include/libmnl/libmnl.h202
-rw-r--r--libmnl/include/linux/Makefile.am2
-rw-r--r--libmnl/include/linux/can.h298
-rw-r--r--libmnl/include/linux/can/Makefile.am1
-rw-r--r--libmnl/include/linux/can/netlink.h185
-rw-r--r--libmnl/include/linux/netfilter/Makefile.am1
-rw-r--r--libmnl/include/linux/netfilter/nfnetlink_conntrack.h252
-rw-r--r--libmnl/include/linux/netlink.h153
-rw-r--r--libmnl/include/linux/socket.h21
-rw-r--r--libmnl/libmnl.pc.in15
-rw-r--r--libmnl/m4/.gitignore2
-rw-r--r--libmnl/m4/gcc4_visibility.m421
-rw-r--r--libmnl/src/Makefile.am5
-rw-r--r--libmnl/src/attr.c742
-rw-r--r--libmnl/src/callback.c167
-rw-r--r--libmnl/src/internal.h11
-rw-r--r--libmnl/src/libmnl.map79
-rw-r--r--libmnl/src/nlmsg.c587
-rw-r--r--libmnl/src/socket.c351
63 files changed, 8093 insertions, 0 deletions
diff --git a/LICENSE b/LICENSE
index d68dccb..aa72bc6 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,3 +1,9 @@
ethtool is available under the terms of the GNU Public License version 2.
See COPYING for details.
+
+--
+
+libmnl is available under the terms of the GNU LESSER GENERAL PUBLIC LICENSE 2.1.
+
+See libmnl/COPYING for details.
diff --git a/libmnl/.gitignore b/libmnl/.gitignore
new file mode 100644
index 0000000..b6b8d60
--- /dev/null
+++ b/libmnl/.gitignore
@@ -0,0 +1,20 @@
+*~
+*.la
+*.lo
+*.o
+.deps/
+.libs/
+Makefile
+Makefile.in
+
+/aclocal.m4
+/autom4te.cache/
+/build-aux/
+/config.*
+/configure
+/libtool
+/stamp-h1
+
+/libmnl.pc
+
+/libmnl-*.tar.bz2
diff --git a/libmnl/COPYING b/libmnl/COPYING
new file mode 100644
index 0000000..4362b49
--- /dev/null
+++ b/libmnl/COPYING
@@ -0,0 +1,502 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+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 this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+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
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser 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 Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "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
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY 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
+LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey 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 library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/libmnl/Make_global.am b/libmnl/Make_global.am
new file mode 100644
index 0000000..7810dab
--- /dev/null
+++ b/libmnl/Make_global.am
@@ -0,0 +1,24 @@
+# This is _NOT_ the library release version, it's an API version.
+# Extracted from Chapter 6 "Library interface versions" of the libtool docs.
+#
+# <snippet>
+# Here are a set of rules to help you update your library version information:
+#
+# 1. Start with version information of `0:0:0' for each libtool library.
+# 2. Update the version information only immediately before a public release
+# of your software. More frequent updates are unnecessary, and only guarantee
+# that the current interface number gets larger faster.
+# 3. If the library source code has changed at all since the last update,
+# then increment revision (`c:r:a' becomes `c:r+1:a').
+# 4. If any interfaces have been added, removed, or changed since the last
+# update, increment current, and set revision to 0.
+# 5. If any interfaces have been added since the last public release, then
+# increment age.
+# 6. If any interfaces have been removed since the last public release, then
+# set age to 0.
+# </snippet>
+#
+LIBVERSION=2:0:2
+
+AM_CPPFLAGS = ${regular_CPPFLAGS} -I${top_srcdir}/include
+AM_CFLAGS = ${regular_CFLAGS} ${GCC_FVISIBILITY_HIDDEN}
diff --git a/libmnl/Makefile.am b/libmnl/Makefile.am
new file mode 100644
index 0000000..94e6935
--- /dev/null
+++ b/libmnl/Makefile.am
@@ -0,0 +1,11 @@
+include $(top_srcdir)/Make_global.am
+
+ACLOCAL_AMFLAGS = -I m4
+
+SUBDIRS = src include examples doxygen
+DIST_SUBDIRS = src include examples doxygen
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = libmnl.pc
+
+${pkgconfig_DATA}: ${top_builddir}/config.status
diff --git a/libmnl/README b/libmnl/README
new file mode 100644
index 0000000..fbac9d2
--- /dev/null
+++ b/libmnl/README
@@ -0,0 +1,28 @@
+= What is libmnl? =
+
+libmnl is a minimalistic user-space library oriented to Netlink developers.
+There are a lot of common tasks in parsing, validating, constructing of
+both the Netlink header and TLVs that are repetitive and easy to get wrong.
+This library aims to provide simple helpers that allows you to re-use code
+and to avoid re-inventing the wheel. The main features of this library are:
+
+* Small: the shared library requires around 30KB for an x86-based computer.
+* Simple: this library avoids complexity and elaborated abstractions that
+tend to hide Netlink details.
+* Easy to use: the library simplifies the work for Netlink-wise developers.
+It provides functions to make socket handling, message building, validating,
+parsing and sequence tracking, easier.
+* Easy to re-use: you can use the library to build your own abstraction layer
+on top of this library.
+* Decoupling: the interdependency of the main bricks that compose the library
+is reduced, i.e. the library provides many helpers, but the programmer is not
+forced to use them.
+
+= Example files =
+
+You can find several example files under examples/ that you can compile by
+invoking `make check'.
+
+--
+08/sep/2010
+Pablo Neira Ayuso <pablo@netfilter.org>
diff --git a/libmnl/autogen.sh b/libmnl/autogen.sh
new file mode 100755
index 0000000..5e1344a
--- /dev/null
+++ b/libmnl/autogen.sh
@@ -0,0 +1,4 @@
+#!/bin/sh -e
+
+autoreconf -fi
+rm -Rf autom4te.cache
diff --git a/libmnl/configure.ac b/libmnl/configure.ac
new file mode 100644
index 0000000..4698aec
--- /dev/null
+++ b/libmnl/configure.ac
@@ -0,0 +1,70 @@
+dnl Process this file with autoconf to create configure.
+
+AC_INIT([libmnl], [1.0.5])
+AC_CONFIG_AUX_DIR([build-aux])
+AC_CANONICAL_HOST
+AC_CONFIG_MACRO_DIR([m4])
+AC_CONFIG_HEADERS([config.h])
+AM_INIT_AUTOMAKE([foreign tar-pax no-dist-gzip dist-xz 1.6 subdir-objects])
+
+dnl kernel style compile messages
+m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
+
+AC_PROG_CC
+AM_PROG_CC_C_O
+AC_EXEEXT
+AC_DISABLE_STATIC
+LT_INIT
+CHECK_GCC_FVISIBILITY
+case "$host" in
+*-*-linux* | *-*-uclinux*) ;;
+*) AC_MSG_ERROR([Linux only, dude!]);;
+esac
+
+regular_CPPFLAGS="-D_FILE_OFFSET_BITS=64 -D_REENTRANT"
+regular_CFLAGS="-Wall -Waggregate-return -Wmissing-declarations \
+ -Wmissing-prototypes -Wshadow -Wstrict-prototypes \
+ -Wformat=2 -pipe"
+AC_SUBST([regular_CPPFLAGS])
+AC_SUBST([regular_CFLAGS])
+AC_CONFIG_FILES([Makefile
+ src/Makefile
+ include/Makefile
+ include/libmnl/Makefile
+ include/linux/Makefile
+ include/linux/can/Makefile
+ include/linux/netfilter/Makefile
+ examples/Makefile
+ examples/genl/Makefile
+ examples/kobject/Makefile
+ examples/netfilter/Makefile
+ examples/rtnl/Makefile
+ libmnl.pc
+ doxygen/doxygen.cfg
+ doxygen/Makefile])
+
+AC_ARG_WITH([doxygen], [AS_HELP_STRING([--with-doxygen],
+ [create doxygen documentation])],
+ [with_doxygen="$withval"], [with_doxygen=yes])
+
+AS_IF([test "x$with_doxygen" != xno], [
+ AC_CHECK_PROGS([DOXYGEN], [doxygen])
+ AC_CHECK_PROGS([DOT], [dot], [""])
+ AS_IF([test "x$DOT" != "x"],
+ [AC_SUBST(HAVE_DOT, YES)],
+ [AC_SUBST(HAVE_DOT, NO)])
+])
+
+AM_CONDITIONAL([HAVE_DOXYGEN], [test -n "$DOXYGEN"])
+AS_IF([test "x$DOXYGEN" = x], [
+ AS_IF([test "x$with_doxygen" != xno], [
+ dnl Only run doxygen Makefile if doxygen installed
+ AC_MSG_WARN([Doxygen not found - continuing without Doxygen support])
+ with_doxygen=no
+ ])
+])
+AC_OUTPUT
+
+echo "
+libmnl configuration:
+ doxygen: ${with_doxygen}"
diff --git a/libmnl/doxygen/.gitignore b/libmnl/doxygen/.gitignore
new file mode 100644
index 0000000..2196cf8
--- /dev/null
+++ b/libmnl/doxygen/.gitignore
@@ -0,0 +1,4 @@
+doxyfile.stamp
+doxygen.cfg
+html/
+man/
diff --git a/libmnl/doxygen/Makefile.am b/libmnl/doxygen/Makefile.am
new file mode 100644
index 0000000..4770fc7
--- /dev/null
+++ b/libmnl/doxygen/Makefile.am
@@ -0,0 +1,25 @@
+if HAVE_DOXYGEN
+doc_srcs = $(shell find $(top_srcdir)/src -name '*.c')
+
+doxyfile.stamp: $(doc_srcs) Makefile.am
+ rm -rf html man
+ doxygen doxygen.cfg >/dev/null
+ $(SHELL) $(top_srcdir)/doxygen/finalize_manpages.sh
+ touch doxyfile.stamp
+
+CLEANFILES = doxyfile.stamp
+
+all-local: doxyfile.stamp
+clean-local:
+ rm -rf $(top_srcdir)/doxygen/man $(top_srcdir)/doxygen/html
+install-data-local:
+ mkdir -p $(DESTDIR)$(mandir)/man3
+ cp --no-dereference --preserve=links,mode,timestamps man/man3/*.3\
+ $(DESTDIR)$(mandir)/man3/
+
+# make distcheck needs uninstall-local
+uninstall-local:
+ rm -r $(DESTDIR)$(mandir) man html doxyfile.stamp
+endif
+
+EXTRA_DIST = finalize_manpages.sh
diff --git a/libmnl/doxygen/doxygen.cfg.in b/libmnl/doxygen/doxygen.cfg.in
new file mode 100644
index 0000000..24089ac
--- /dev/null
+++ b/libmnl/doxygen/doxygen.cfg.in
@@ -0,0 +1,23 @@
+# Difference with default Doxyfile 1.8.20
+PROJECT_NAME = @PACKAGE@
+PROJECT_NUMBER = @VERSION@
+OUTPUT_DIRECTORY = .
+ABBREVIATE_BRIEF =
+FULL_PATH_NAMES = NO
+TAB_SIZE = 8
+OPTIMIZE_OUTPUT_FOR_C = YES
+INPUT = @top_srcdir@
+FILE_PATTERNS = */src/*.c
+RECURSIVE = YES
+EXCLUDE_SYMBOLS = EXPORT_SYMBOL mnl_nlmsg_batch mnl_socket
+EXAMPLE_PATTERNS =
+INPUT_FILTER = "sed 's/EXPORT_SYMBOL//g'"
+SOURCE_BROWSER = YES
+ALPHABETICAL_INDEX = NO
+SEARCHENGINE = NO
+GENERATE_LATEX = NO
+LATEX_CMD_NAME = latex
+GENERATE_MAN = YES
+MAN_LINKS = YES
+HAVE_DOT = @HAVE_DOT@
+DOT_TRANSPARENT = YES
diff --git a/libmnl/doxygen/finalize_manpages.sh b/libmnl/doxygen/finalize_manpages.sh
new file mode 100644
index 0000000..6f230b1
--- /dev/null
+++ b/libmnl/doxygen/finalize_manpages.sh
@@ -0,0 +1,40 @@
+#
+# We need to use bash for its associative array facility
+#
+[ "$BASH" ] || exec bash $0
+#
+# (`bash -p` prevents import of functions from the environment).
+#
+set -p
+
+declare -A renamed_page
+
+main(){ set -e; cd man/man3; rm -f _*
+ count_real_pages
+ rename_real_pages
+ make_symlinks
+}
+
+count_real_pages(){ page_count=0
+ for i in $(ls -S)
+ do head -n1 $i | grep -E -q '^\.so' && break
+ page_count=$(($page_count + 1))
+ done
+ first_link=$(($page_count + 1))
+}
+
+rename_real_pages(){ for i in $(ls -S | head -n$page_count)
+ do for j in $(ls -S | tail -n+$first_link)
+ do grep -E -q $i$ $j && break
+ done
+ mv -f $i $j
+ renamed_page[$i]=$j
+ done
+}
+
+make_symlinks(){ for j in $(ls -S | tail -n+$first_link)
+ do ln -sf ${renamed_page[$(cat $j | cut -f2 -d/)]} $j
+ done
+}
+
+main
diff --git a/libmnl/examples/Makefile.am b/libmnl/examples/Makefile.am
new file mode 100644
index 0000000..e5cb052
--- /dev/null
+++ b/libmnl/examples/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = genl kobject netfilter rtnl
diff --git a/libmnl/examples/genl/.gitignore b/libmnl/examples/genl/.gitignore
new file mode 100644
index 0000000..a7d8966
--- /dev/null
+++ b/libmnl/examples/genl/.gitignore
@@ -0,0 +1,2 @@
+/genl-family-get
+/genl-group-events \ No newline at end of file
diff --git a/libmnl/examples/genl/Makefile.am b/libmnl/examples/genl/Makefile.am
new file mode 100644
index 0000000..b4b7954
--- /dev/null
+++ b/libmnl/examples/genl/Makefile.am
@@ -0,0 +1,10 @@
+include $(top_srcdir)/Make_global.am
+
+check_PROGRAMS = genl-family-get \
+ genl-group-events
+
+genl_family_get_SOURCES = genl-family-get.c
+genl_family_get_LDADD = ../../src/libmnl.la
+
+genl_group_events_SOURCES = genl-group-events.c
+genl_group_events_LDADD = ../../src/libmnl.la
diff --git a/libmnl/examples/genl/genl-family-get.c b/libmnl/examples/genl/genl-family-get.c
new file mode 100644
index 0000000..ba8de12
--- /dev/null
+++ b/libmnl/examples/genl/genl-family-get.c
@@ -0,0 +1,241 @@
+/* This example is placed in the public domain. */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+
+#include <libmnl/libmnl.h>
+#include <linux/genetlink.h>
+
+static int parse_mc_grps_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ /* skip unsupported attribute in user-space */
+ if (mnl_attr_type_valid(attr, CTRL_ATTR_MCAST_GRP_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case CTRL_ATTR_MCAST_GRP_ID:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ case CTRL_ATTR_MCAST_GRP_NAME:
+ if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ }
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void parse_genl_mc_grps(struct nlattr *nested)
+{
+ struct nlattr *pos;
+
+ mnl_attr_for_each_nested(pos, nested) {
+ struct nlattr *tb[CTRL_ATTR_MCAST_GRP_MAX+1] = {};
+
+ mnl_attr_parse_nested(pos, parse_mc_grps_cb, tb);
+ if (tb[CTRL_ATTR_MCAST_GRP_ID]) {
+ printf("id-0x%x ",
+ mnl_attr_get_u32(tb[CTRL_ATTR_MCAST_GRP_ID]));
+ }
+ if (tb[CTRL_ATTR_MCAST_GRP_NAME]) {
+ printf("name: %s ",
+ mnl_attr_get_str(tb[CTRL_ATTR_MCAST_GRP_NAME]));
+ }
+ printf("\n");
+ }
+}
+
+static int parse_family_ops_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, CTRL_ATTR_OP_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case CTRL_ATTR_OP_ID:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ case CTRL_ATTR_OP_MAX:
+ break;
+ default:
+ return MNL_CB_OK;
+ }
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void parse_genl_family_ops(struct nlattr *nested)
+{
+ struct nlattr *pos;
+
+ mnl_attr_for_each_nested(pos, nested) {
+ struct nlattr *tb[CTRL_ATTR_OP_MAX+1] = {};
+
+ mnl_attr_parse_nested(pos, parse_family_ops_cb, tb);
+ if (tb[CTRL_ATTR_OP_ID]) {
+ printf("id-0x%x ",
+ mnl_attr_get_u32(tb[CTRL_ATTR_OP_ID]));
+ }
+ if (tb[CTRL_ATTR_OP_MAX]) {
+ printf("flags ");
+ }
+ printf("\n");
+ }
+}
+
+static int data_attr_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, CTRL_ATTR_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case CTRL_ATTR_FAMILY_NAME:
+ if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ case CTRL_ATTR_FAMILY_ID:
+ if (mnl_attr_validate(attr, MNL_TYPE_U16) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ case CTRL_ATTR_VERSION:
+ case CTRL_ATTR_HDRSIZE:
+ case CTRL_ATTR_MAXATTR:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ case CTRL_ATTR_OPS:
+ case CTRL_ATTR_MCAST_GROUPS:
+ if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ }
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static int data_cb(const struct nlmsghdr *nlh, void *data)
+{
+ struct nlattr *tb[CTRL_ATTR_MAX+1] = {};
+ struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
+
+ mnl_attr_parse(nlh, sizeof(*genl), data_attr_cb, tb);
+ if (tb[CTRL_ATTR_FAMILY_NAME]) {
+ printf("name=%s\t",
+ mnl_attr_get_str(tb[CTRL_ATTR_FAMILY_NAME]));
+ }
+ if (tb[CTRL_ATTR_FAMILY_ID]) {
+ printf("id=%u\t",
+ mnl_attr_get_u16(tb[CTRL_ATTR_FAMILY_ID]));
+ }
+ if (tb[CTRL_ATTR_VERSION]) {
+ printf("version=%u\t",
+ mnl_attr_get_u32(tb[CTRL_ATTR_VERSION]));
+ }
+ if (tb[CTRL_ATTR_HDRSIZE]) {
+ printf("hdrsize=%u\t",
+ mnl_attr_get_u32(tb[CTRL_ATTR_HDRSIZE]));
+ }
+ if (tb[CTRL_ATTR_MAXATTR]) {
+ printf("maxattr=%u\t",
+ mnl_attr_get_u32(tb[CTRL_ATTR_MAXATTR]));
+ }
+ printf("\n");
+ if (tb[CTRL_ATTR_OPS]) {
+ printf("ops:\n");
+ parse_genl_family_ops(tb[CTRL_ATTR_OPS]);
+ }
+ if (tb[CTRL_ATTR_MCAST_GROUPS]) {
+ printf("grps:\n");
+ parse_genl_mc_grps(tb[CTRL_ATTR_MCAST_GROUPS]);
+ }
+ printf("\n");
+ return MNL_CB_OK;
+}
+
+int main(int argc, char *argv[])
+{
+ struct mnl_socket *nl;
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh;
+ struct genlmsghdr *genl;
+ int ret;
+ unsigned int seq, portid;
+
+ if (argc > 2) {
+ printf("%s [family name]\n", argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ nlh = mnl_nlmsg_put_header(buf);
+ nlh->nlmsg_type = GENL_ID_CTRL;
+ nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+ nlh->nlmsg_seq = seq = time(NULL);
+
+ genl = mnl_nlmsg_put_extra_header(nlh, sizeof(struct genlmsghdr));
+ genl->cmd = CTRL_CMD_GETFAMILY;
+ genl->version = 1;
+
+ mnl_attr_put_u32(nlh, CTRL_ATTR_FAMILY_ID, GENL_ID_CTRL);
+ if (argc >= 2)
+ mnl_attr_put_strz(nlh, CTRL_ATTR_FAMILY_NAME, argv[1]);
+ else
+ nlh->nlmsg_flags |= NLM_F_DUMP;
+
+ nl = mnl_socket_open(NETLINK_GENERIC);
+ if (nl == NULL) {
+ perror("mnl_socket_open");
+ exit(EXIT_FAILURE);
+ }
+
+ if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
+ perror("mnl_socket_bind");
+ exit(EXIT_FAILURE);
+ }
+ portid = mnl_socket_get_portid(nl);
+
+ if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
+ perror("mnl_socket_sendto");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ while (ret > 0) {
+ ret = mnl_cb_run(buf, ret, seq, portid, data_cb, NULL);
+ if (ret <= 0)
+ break;
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ }
+ if (ret == -1) {
+ perror("error");
+ exit(EXIT_FAILURE);
+ }
+
+ mnl_socket_close(nl);
+
+ return 0;
+}
diff --git a/libmnl/examples/genl/genl-group-events.c b/libmnl/examples/genl/genl-group-events.c
new file mode 100644
index 0000000..d5f0a18
--- /dev/null
+++ b/libmnl/examples/genl/genl-group-events.c
@@ -0,0 +1,63 @@
+/* This example is placed in the public domain. */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+
+#include <libmnl/libmnl.h>
+#include <linux/genetlink.h>
+
+static int group;
+
+static int data_cb(const struct nlmsghdr *nlh, void *data)
+{
+ printf("received event type=%d from genetlink group %d\n",
+ nlh->nlmsg_type, group);
+ return MNL_CB_OK;
+}
+
+int main(int argc, char *argv[])
+{
+ struct mnl_socket *nl;
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ int ret;
+
+ if (argc != 2) {
+ printf("%s [group]\n", argv[0]);
+ exit(EXIT_FAILURE);
+ }
+ group = atoi(argv[1]);
+
+ nl = mnl_socket_open(NETLINK_GENERIC);
+ if (nl == NULL) {
+ perror("mnl_socket_open");
+ exit(EXIT_FAILURE);
+ }
+
+ if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
+ perror("mnl_socket_bind");
+ exit(EXIT_FAILURE);
+ }
+
+ if (mnl_socket_setsockopt(nl, NETLINK_ADD_MEMBERSHIP, &group,
+ sizeof(int)) < 0) {
+ perror("mnl_socket_setsockopt");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ while (ret > 0) {
+ ret = mnl_cb_run(buf, ret, 0, 0, data_cb, NULL);
+ if (ret <= 0)
+ break;
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ }
+ if (ret == -1) {
+ perror("error");
+ exit(EXIT_FAILURE);
+ }
+
+ mnl_socket_close(nl);
+
+ return 0;
+}
diff --git a/libmnl/examples/kobject/.gitignore b/libmnl/examples/kobject/.gitignore
new file mode 100644
index 0000000..4d95e59
--- /dev/null
+++ b/libmnl/examples/kobject/.gitignore
@@ -0,0 +1 @@
+/kobject-event
diff --git a/libmnl/examples/kobject/Makefile.am b/libmnl/examples/kobject/Makefile.am
new file mode 100644
index 0000000..9197f7a
--- /dev/null
+++ b/libmnl/examples/kobject/Makefile.am
@@ -0,0 +1,6 @@
+include $(top_srcdir)/Make_global.am
+
+check_PROGRAMS = kobject-event
+
+kobject_event_SOURCES = kobject-event.c
+kobject_event_LDADD = ../../src/libmnl.la
diff --git a/libmnl/examples/kobject/kobject-event.c b/libmnl/examples/kobject/kobject-event.c
new file mode 100644
index 0000000..97debdf
--- /dev/null
+++ b/libmnl/examples/kobject/kobject-event.c
@@ -0,0 +1,49 @@
+/* This example is placed in the public domain. */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+
+#include <libmnl/libmnl.h>
+#include <linux/netlink.h>
+
+int main(int argc, char *argv[])
+{
+ struct mnl_socket *nl;
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ int ret;
+
+ nl = mnl_socket_open(NETLINK_KOBJECT_UEVENT);
+ if (nl == NULL) {
+ perror("mnl_socket_open");
+ exit(EXIT_FAILURE);
+ }
+
+ /* There is one single group in kobject over netlink */
+ if (mnl_socket_bind(nl, (1<<0), MNL_SOCKET_AUTOPID) < 0) {
+ perror("mnl_socket_bind");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ while (ret > 0) {
+ int i;
+
+ /* kobject uses a string based protocol, with no initial
+ * netlink header.
+ */
+ for (i=0; i<ret; i++)
+ printf("%c", buf[i]);
+
+ printf("\n");
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ }
+ if (ret == -1) {
+ perror("error");
+ exit(EXIT_FAILURE);
+ }
+
+ mnl_socket_close(nl);
+
+ return 0;
+}
diff --git a/libmnl/examples/netfilter/.gitignore b/libmnl/examples/netfilter/.gitignore
new file mode 100644
index 0000000..bbeb031
--- /dev/null
+++ b/libmnl/examples/netfilter/.gitignore
@@ -0,0 +1,6 @@
+/nf-log
+/nf-queue
+/nfct-create-batch
+/nfct-daemon
+/nfct-dump
+/nfct-event
diff --git a/libmnl/examples/netfilter/Makefile.am b/libmnl/examples/netfilter/Makefile.am
new file mode 100644
index 0000000..4bae05f
--- /dev/null
+++ b/libmnl/examples/netfilter/Makefile.am
@@ -0,0 +1,26 @@
+include $(top_srcdir)/Make_global.am
+
+check_PROGRAMS = nf-queue \
+ nf-log \
+ nfct-dump \
+ nfct-event \
+ nfct-create-batch \
+ nfct-daemon
+
+nf_queue_SOURCES = nf-queue.c
+nf_queue_LDADD = ../../src/libmnl.la
+
+nf_log_SOURCES = nf-log.c
+nf_log_LDADD = ../../src/libmnl.la
+
+nfct_dump_SOURCES = nfct-dump.c
+nfct_dump_LDADD = ../../src/libmnl.la
+
+nfct_daemon_SOURCES = nfct-daemon.c
+nfct_daemon_LDADD = ../../src/libmnl.la
+
+nfct_event_SOURCES = nfct-event.c
+nfct_event_LDADD = ../../src/libmnl.la
+
+nfct_create_batch_SOURCES = nfct-create-batch.c
+nfct_create_batch_LDADD = ../../src/libmnl.la
diff --git a/libmnl/examples/netfilter/nf-log.c b/libmnl/examples/netfilter/nf-log.c
new file mode 100644
index 0000000..4383b66
--- /dev/null
+++ b/libmnl/examples/netfilter/nf-log.c
@@ -0,0 +1,219 @@
+/* This example is placed in the public domain. */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <time.h>
+#include <arpa/inet.h>
+
+#include <libmnl/libmnl.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/nfnetlink_log.h>
+
+static int parse_attr_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ /* skip unsupported attribute in user-space */
+ if (mnl_attr_type_valid(attr, NFULA_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFULA_MARK:
+ case NFULA_IFINDEX_INDEV:
+ case NFULA_IFINDEX_OUTDEV:
+ case NFULA_IFINDEX_PHYSINDEV:
+ case NFULA_IFINDEX_PHYSOUTDEV:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ case NFULA_TIMESTAMP:
+ if (mnl_attr_validate2(attr, MNL_TYPE_UNSPEC,
+ sizeof(struct nfulnl_msg_packet_timestamp)) < 0) {
+ perror("mnl_attr_validate2");
+ return MNL_CB_ERROR;
+ }
+ break;
+ case NFULA_HWADDR:
+ if (mnl_attr_validate2(attr, MNL_TYPE_UNSPEC,
+ sizeof(struct nfulnl_msg_packet_hw)) < 0) {
+ perror("mnl_attr_validate2");
+ return MNL_CB_ERROR;
+ }
+ break;
+ case NFULA_PREFIX:
+ if (mnl_attr_validate(attr, MNL_TYPE_NUL_STRING) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ case NFULA_PAYLOAD:
+ break;
+ }
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static int log_cb(const struct nlmsghdr *nlh, void *data)
+{
+ struct nlattr *tb[NFULA_MAX+1] = {};
+ struct nfulnl_msg_packet_hdr *ph = NULL;
+ const char *prefix = NULL;
+ uint32_t mark = 0;
+
+ mnl_attr_parse(nlh, sizeof(struct nfgenmsg), parse_attr_cb, tb);
+ if (tb[NFULA_PACKET_HDR])
+ ph = mnl_attr_get_payload(tb[NFULA_PACKET_HDR]);
+ if (tb[NFULA_PREFIX])
+ prefix = mnl_attr_get_str(tb[NFULA_PREFIX]);
+ if (tb[NFULA_MARK])
+ mark = ntohl(mnl_attr_get_u32(tb[NFULA_MARK]));
+
+ printf("log received (prefix=\"%s\" hw=0x%04x hook=%u mark=%u)\n",
+ prefix ? prefix : "", ntohs(ph->hw_protocol), ph->hook,
+ mark);
+
+ return MNL_CB_OK;
+}
+
+static struct nlmsghdr *
+nflog_build_cfg_pf_request(char *buf, uint8_t command)
+{
+ struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf);
+ nlh->nlmsg_type = (NFNL_SUBSYS_ULOG << 8) | NFULNL_MSG_CONFIG;
+ nlh->nlmsg_flags = NLM_F_REQUEST;
+
+ struct nfgenmsg *nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg));
+ nfg->nfgen_family = AF_INET;
+ nfg->version = NFNETLINK_V0;
+
+ struct nfulnl_msg_config_cmd cmd = {
+ .command = command,
+ };
+ mnl_attr_put(nlh, NFULA_CFG_CMD, sizeof(cmd), &cmd);
+
+ return nlh;
+}
+
+static struct nlmsghdr *
+nflog_build_cfg_request(char *buf, uint8_t command, int qnum)
+{
+ struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf);
+ nlh->nlmsg_type = (NFNL_SUBSYS_ULOG << 8) | NFULNL_MSG_CONFIG;
+ nlh->nlmsg_flags = NLM_F_REQUEST;
+
+ struct nfgenmsg *nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg));
+ nfg->nfgen_family = AF_INET;
+ nfg->version = NFNETLINK_V0;
+ nfg->res_id = htons(qnum);
+
+ struct nfulnl_msg_config_cmd cmd = {
+ .command = command,
+ };
+ mnl_attr_put(nlh, NFULA_CFG_CMD, sizeof(cmd), &cmd);
+
+ return nlh;
+}
+
+static struct nlmsghdr *
+nflog_build_cfg_params(char *buf, uint8_t mode, int range, int qnum)
+{
+ struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf);
+ nlh->nlmsg_type = (NFNL_SUBSYS_ULOG << 8) | NFULNL_MSG_CONFIG;
+ nlh->nlmsg_flags = NLM_F_REQUEST;
+
+ struct nfgenmsg *nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg));
+ nfg->nfgen_family = AF_UNSPEC;
+ nfg->version = NFNETLINK_V0;
+ nfg->res_id = htons(qnum);
+
+ struct nfulnl_msg_config_mode params = {
+ .copy_range = htonl(range),
+ .copy_mode = mode,
+ };
+ mnl_attr_put(nlh, NFULA_CFG_MODE, sizeof(params), &params);
+
+ return nlh;
+}
+
+int main(int argc, char *argv[])
+{
+ struct mnl_socket *nl;
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh;
+ int ret;
+ unsigned int portid, qnum;
+
+ if (argc != 2) {
+ printf("Usage: %s [queue_num]\n", argv[0]);
+ exit(EXIT_FAILURE);
+ }
+ qnum = atoi(argv[1]);
+
+ nl = mnl_socket_open(NETLINK_NETFILTER);
+ if (nl == NULL) {
+ perror("mnl_socket_open");
+ exit(EXIT_FAILURE);
+ }
+
+ if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
+ perror("mnl_socket_bind");
+ exit(EXIT_FAILURE);
+ }
+ portid = mnl_socket_get_portid(nl);
+
+ nlh = nflog_build_cfg_pf_request(buf, NFULNL_CFG_CMD_PF_UNBIND);
+
+ if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
+ perror("mnl_socket_sendto");
+ exit(EXIT_FAILURE);
+ }
+
+ nlh = nflog_build_cfg_pf_request(buf, NFULNL_CFG_CMD_PF_BIND);
+
+ if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
+ perror("mnl_socket_sendto");
+ exit(EXIT_FAILURE);
+ }
+
+ nlh = nflog_build_cfg_request(buf, NFULNL_CFG_CMD_BIND, qnum);
+
+ if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
+ perror("mnl_socket_sendto");
+ exit(EXIT_FAILURE);
+ }
+
+ nlh = nflog_build_cfg_params(buf, NFULNL_COPY_PACKET, 0xFFFF, qnum);
+
+ if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
+ perror("mnl_socket_sendto");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ if (ret == -1) {
+ perror("mnl_socket_recvfrom");
+ exit(EXIT_FAILURE);
+ }
+ while (ret > 0) {
+ ret = mnl_cb_run(buf, ret, 0, portid, log_cb, NULL);
+ if (ret < 0){
+ perror("mnl_cb_run");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ if (ret == -1) {
+ perror("mnl_socket_recvfrom");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ mnl_socket_close(nl);
+
+ return 0;
+}
diff --git a/libmnl/examples/netfilter/nf-queue.c b/libmnl/examples/netfilter/nf-queue.c
new file mode 100644
index 0000000..957e365
--- /dev/null
+++ b/libmnl/examples/netfilter/nf-queue.c
@@ -0,0 +1,243 @@
+/* This example is placed in the public domain. */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <time.h>
+#include <arpa/inet.h>
+
+#include <libmnl/libmnl.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/nfnetlink_queue.h>
+
+static int parse_attr_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ /* skip unsupported attribute in user-space */
+ if (mnl_attr_type_valid(attr, NFQA_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFQA_MARK:
+ case NFQA_IFINDEX_INDEV:
+ case NFQA_IFINDEX_OUTDEV:
+ case NFQA_IFINDEX_PHYSINDEV:
+ case NFQA_IFINDEX_PHYSOUTDEV:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ case NFQA_TIMESTAMP:
+ if (mnl_attr_validate2(attr, MNL_TYPE_UNSPEC,
+ sizeof(struct nfqnl_msg_packet_timestamp)) < 0) {
+ perror("mnl_attr_validate2");
+ return MNL_CB_ERROR;
+ }
+ break;
+ case NFQA_HWADDR:
+ if (mnl_attr_validate2(attr, MNL_TYPE_UNSPEC,
+ sizeof(struct nfqnl_msg_packet_hw)) < 0) {
+ perror("mnl_attr_validate2");
+ return MNL_CB_ERROR;
+ }
+ break;
+ case NFQA_PAYLOAD:
+ break;
+ }
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static int queue_cb(const struct nlmsghdr *nlh, void *data)
+{
+ struct nlattr *tb[NFQA_MAX+1] = {};
+ struct nfqnl_msg_packet_hdr *ph = NULL;
+ uint32_t id = 0;
+
+ mnl_attr_parse(nlh, sizeof(struct nfgenmsg), parse_attr_cb, tb);
+ if (tb[NFQA_PACKET_HDR]) {
+ ph = mnl_attr_get_payload(tb[NFQA_PACKET_HDR]);
+ id = ntohl(ph->packet_id);
+
+ printf("packet received (id=%u hw=0x%04x hook=%u)\n",
+ id, ntohs(ph->hw_protocol), ph->hook);
+ }
+
+ return MNL_CB_OK + id;
+}
+
+static struct nlmsghdr *
+nfq_build_cfg_pf_request(char *buf, uint8_t command)
+{
+ struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf);
+ nlh->nlmsg_type = (NFNL_SUBSYS_QUEUE << 8) | NFQNL_MSG_CONFIG;
+ nlh->nlmsg_flags = NLM_F_REQUEST;
+
+ struct nfgenmsg *nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg));
+ nfg->nfgen_family = AF_UNSPEC;
+ nfg->version = NFNETLINK_V0;
+
+ struct nfqnl_msg_config_cmd cmd = {
+ .command = command,
+ .pf = htons(AF_INET),
+ };
+ mnl_attr_put(nlh, NFQA_CFG_CMD, sizeof(cmd), &cmd);
+
+ return nlh;
+}
+
+static struct nlmsghdr *
+nfq_build_cfg_request(char *buf, uint8_t command, int queue_num)
+{
+ struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf);
+ nlh->nlmsg_type = (NFNL_SUBSYS_QUEUE << 8) | NFQNL_MSG_CONFIG;
+ nlh->nlmsg_flags = NLM_F_REQUEST;
+
+ struct nfgenmsg *nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg));
+ nfg->nfgen_family = AF_UNSPEC;
+ nfg->version = NFNETLINK_V0;
+ nfg->res_id = htons(queue_num);
+
+ struct nfqnl_msg_config_cmd cmd = {
+ .command = command,
+ .pf = htons(AF_INET),
+ };
+ mnl_attr_put(nlh, NFQA_CFG_CMD, sizeof(cmd), &cmd);
+
+ return nlh;
+}
+
+static struct nlmsghdr *
+nfq_build_cfg_params(char *buf, uint8_t mode, int range, int queue_num)
+{
+ struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf);
+ nlh->nlmsg_type = (NFNL_SUBSYS_QUEUE << 8) | NFQNL_MSG_CONFIG;
+ nlh->nlmsg_flags = NLM_F_REQUEST;
+
+ struct nfgenmsg *nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg));
+ nfg->nfgen_family = AF_UNSPEC;
+ nfg->version = NFNETLINK_V0;
+ nfg->res_id = htons(queue_num);
+
+ struct nfqnl_msg_config_params params = {
+ .copy_range = htonl(range),
+ .copy_mode = mode,
+ };
+ mnl_attr_put(nlh, NFQA_CFG_PARAMS, sizeof(params), &params);
+
+ return nlh;
+}
+
+static struct nlmsghdr *
+nfq_build_verdict(char *buf, int id, int queue_num, int verd)
+{
+ struct nlmsghdr *nlh;
+ struct nfgenmsg *nfg;
+
+ nlh = mnl_nlmsg_put_header(buf);
+ nlh->nlmsg_type = (NFNL_SUBSYS_QUEUE << 8) | NFQNL_MSG_VERDICT;
+ nlh->nlmsg_flags = NLM_F_REQUEST;
+ nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg));
+ nfg->nfgen_family = AF_UNSPEC;
+ nfg->version = NFNETLINK_V0;
+ nfg->res_id = htons(queue_num);
+
+ struct nfqnl_msg_verdict_hdr vh = {
+ .verdict = htonl(verd),
+ .id = htonl(id),
+ };
+ mnl_attr_put(nlh, NFQA_VERDICT_HDR, sizeof(vh), &vh);
+
+ return nlh;
+}
+
+int main(int argc, char *argv[])
+{
+ struct mnl_socket *nl;
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh;
+ int ret;
+ unsigned int portid, queue_num;
+
+ if (argc != 2) {
+ printf("Usage: %s [queue_num]\n", argv[0]);
+ exit(EXIT_FAILURE);
+ }
+ queue_num = atoi(argv[1]);
+
+ nl = mnl_socket_open(NETLINK_NETFILTER);
+ if (nl == NULL) {
+ perror("mnl_socket_open");
+ exit(EXIT_FAILURE);
+ }
+
+ if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
+ perror("mnl_socket_bind");
+ exit(EXIT_FAILURE);
+ }
+ portid = mnl_socket_get_portid(nl);
+
+ nlh = nfq_build_cfg_pf_request(buf, NFQNL_CFG_CMD_PF_UNBIND);
+
+ if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
+ perror("mnl_socket_sendto");
+ exit(EXIT_FAILURE);
+ }
+
+ nlh = nfq_build_cfg_pf_request(buf, NFQNL_CFG_CMD_PF_BIND);
+
+ if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
+ perror("mnl_socket_sendto");
+ exit(EXIT_FAILURE);
+ }
+
+ nlh = nfq_build_cfg_request(buf, NFQNL_CFG_CMD_BIND, queue_num);
+
+ if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
+ perror("mnl_socket_sendto");
+ exit(EXIT_FAILURE);
+ }
+
+ nlh = nfq_build_cfg_params(buf, NFQNL_COPY_PACKET, 0xFFFF, queue_num);
+
+ if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
+ perror("mnl_socket_sendto");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ if (ret == -1) {
+ perror("mnl_socket_recvfrom");
+ exit(EXIT_FAILURE);
+ }
+ while (ret > 0) {
+ uint32_t id;
+
+ ret = mnl_cb_run(buf, ret, 0, portid, queue_cb, NULL);
+ if (ret < 0){
+ perror("mnl_cb_run");
+ exit(EXIT_FAILURE);
+ }
+
+ id = ret - MNL_CB_OK;
+ nlh = nfq_build_verdict(buf, id, queue_num, NF_ACCEPT);
+ if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
+ perror("mnl_socket_sendto");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ if (ret == -1) {
+ perror("mnl_socket_recvfrom");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ mnl_socket_close(nl);
+
+ return 0;
+}
diff --git a/libmnl/examples/netfilter/nfct-create-batch.c b/libmnl/examples/netfilter/nfct-create-batch.c
new file mode 100644
index 0000000..4675789
--- /dev/null
+++ b/libmnl/examples/netfilter/nfct-create-batch.c
@@ -0,0 +1,187 @@
+/* This example is placed in the public domain. */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <time.h>
+#include <sys/select.h>
+#include <string.h>
+
+#include <libmnl/libmnl.h>
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/nfnetlink_conntrack.h>
+#include <linux/netfilter/nf_conntrack_common.h>
+#include <linux/netfilter/nf_conntrack_tcp.h>
+
+static void put_msg(char *buf, uint16_t i, int seq)
+{
+ struct nlmsghdr *nlh;
+ struct nfgenmsg *nfh;
+ struct nlattr *nest1, *nest2;
+
+ nlh = mnl_nlmsg_put_header(buf);
+ nlh->nlmsg_type = (NFNL_SUBSYS_CTNETLINK << 8) | IPCTNL_MSG_CT_NEW;
+ nlh->nlmsg_flags = NLM_F_REQUEST|NLM_F_CREATE|NLM_F_EXCL|NLM_F_ACK;
+ nlh->nlmsg_seq = seq;
+
+ nfh = mnl_nlmsg_put_extra_header(nlh, sizeof(struct nfgenmsg));
+ nfh->nfgen_family = AF_INET;
+ nfh->version = NFNETLINK_V0;
+ nfh->res_id = 0;
+
+ nest1 = mnl_attr_nest_start(nlh, CTA_TUPLE_ORIG);
+ nest2 = mnl_attr_nest_start(nlh, CTA_TUPLE_IP);
+ mnl_attr_put_u32(nlh, CTA_IP_V4_SRC, inet_addr("1.1.1.1"));
+ mnl_attr_put_u32(nlh, CTA_IP_V4_DST, inet_addr("2.2.2.2"));
+ mnl_attr_nest_end(nlh, nest2);
+
+ nest2 = mnl_attr_nest_start(nlh, CTA_TUPLE_PROTO);
+ mnl_attr_put_u8(nlh, CTA_PROTO_NUM, IPPROTO_TCP);
+ mnl_attr_put_u16(nlh, CTA_PROTO_SRC_PORT, htons(i));
+ mnl_attr_put_u16(nlh, CTA_PROTO_DST_PORT, htons(1025));
+ mnl_attr_nest_end(nlh, nest2);
+ mnl_attr_nest_end(nlh, nest1);
+
+ nest1 = mnl_attr_nest_start(nlh, CTA_TUPLE_REPLY);
+ nest2 = mnl_attr_nest_start(nlh, CTA_TUPLE_IP);
+ mnl_attr_put_u32(nlh, CTA_IP_V4_SRC, inet_addr("2.2.2.2"));
+ mnl_attr_put_u32(nlh, CTA_IP_V4_DST, inet_addr("1.1.1.1"));
+ mnl_attr_nest_end(nlh, nest2);
+
+ nest2 = mnl_attr_nest_start(nlh, CTA_TUPLE_PROTO);
+ mnl_attr_put_u8(nlh, CTA_PROTO_NUM, IPPROTO_TCP);
+ mnl_attr_put_u16(nlh, CTA_PROTO_SRC_PORT, htons(1025));
+ mnl_attr_put_u16(nlh, CTA_PROTO_DST_PORT, htons(i));
+ mnl_attr_nest_end(nlh, nest2);
+ mnl_attr_nest_end(nlh, nest1);
+
+ nest1 = mnl_attr_nest_start(nlh, CTA_PROTOINFO);
+ nest2 = mnl_attr_nest_start(nlh, CTA_PROTOINFO_TCP);
+ mnl_attr_put_u8(nlh, CTA_PROTOINFO_TCP_STATE, TCP_CONNTRACK_SYN_SENT);
+ mnl_attr_nest_end(nlh, nest2);
+ mnl_attr_nest_end(nlh, nest1);
+
+ mnl_attr_put_u32(nlh, CTA_STATUS, htonl(IPS_CONFIRMED));
+ mnl_attr_put_u32(nlh, CTA_TIMEOUT, htonl(1000));
+}
+
+static int cb_err(const struct nlmsghdr *nlh, void *data)
+{
+ struct nlmsgerr *err = mnl_nlmsg_get_payload(nlh);
+ if (err->error != 0)
+ printf("message with seq %u has failed: %s\n",
+ nlh->nlmsg_seq, strerror(-err->error));
+ return MNL_CB_OK;
+}
+
+static mnl_cb_t cb_ctl_array[NLMSG_MIN_TYPE] = {
+ [NLMSG_ERROR] = cb_err,
+};
+
+static void
+send_batch(struct mnl_socket *nl, struct mnl_nlmsg_batch *b, int portid)
+{
+ int ret, fd = mnl_socket_get_fd(nl);
+ size_t len = mnl_nlmsg_batch_size(b);
+ char rcv_buf[MNL_SOCKET_BUFFER_SIZE];
+
+ ret = mnl_socket_sendto(nl, mnl_nlmsg_batch_head(b), len);
+ if (ret == -1) {
+ perror("mnl_socket_sendto");
+ exit(EXIT_FAILURE);
+ }
+
+ /* receive and digest all the acknowledgments from the kernel. */
+ struct timeval tv = {
+ .tv_sec = 0,
+ .tv_usec = 0
+ };
+ fd_set readfds;
+ FD_ZERO(&readfds);
+ FD_SET(fd, &readfds);
+
+ ret = select(fd+1, &readfds, NULL, NULL, &tv);
+ if (ret == -1) {
+ perror("select");
+ exit(EXIT_FAILURE);
+ }
+ while (ret > 0 && FD_ISSET(fd, &readfds)) {
+ ret = mnl_socket_recvfrom(nl, rcv_buf, sizeof(rcv_buf));
+ if (ret == -1) {
+ perror("mnl_socket_recvfrom");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = mnl_cb_run2(rcv_buf, ret, 0, portid,
+ NULL, NULL, cb_ctl_array,
+ MNL_ARRAY_SIZE(cb_ctl_array));
+ if (ret == -1) {
+ perror("mnl_cb_run2");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = select(fd+1, &readfds, NULL, NULL, &tv);
+ if (ret == -1) {
+ perror("select");
+ exit(EXIT_FAILURE);
+ }
+ FD_ZERO(&readfds);
+ FD_SET(fd, &readfds);
+ }
+}
+
+int main(void)
+{
+ struct mnl_socket *nl;
+ char snd_buf[MNL_SOCKET_BUFFER_SIZE*2];
+ struct mnl_nlmsg_batch *b;
+ int j;
+ unsigned int seq, portid;
+ uint16_t i;
+
+ nl = mnl_socket_open(NETLINK_NETFILTER);
+ if (nl == NULL) {
+ perror("mnl_socket_open");
+ exit(EXIT_FAILURE);
+ }
+ if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
+ perror("mnl_socket_bind");
+ exit(EXIT_FAILURE);
+ }
+ portid = mnl_socket_get_portid(nl);
+
+ /* The buffer that we use to batch messages is MNL_SOCKET_BUFFER_SIZE
+ * multiplied by 2 bytes long, but we limit the batch to half of it
+ * since the last message that does not fit the batch goes over the
+ * upper boundary, if you break this rule, expect memory corruptions. */
+ b = mnl_nlmsg_batch_start(snd_buf, MNL_SOCKET_BUFFER_SIZE);
+ if (b == NULL) {
+ perror("mnl_nlmsg_batch_start");
+ exit(EXIT_FAILURE);
+ }
+
+ seq = time(NULL);
+ for (i=1024, j=0; i<65535; i++, j++) {
+ put_msg(mnl_nlmsg_batch_current(b), i, seq+j);
+
+ /* is there room for more messages in this batch?
+ * if so, continue. */
+ if (mnl_nlmsg_batch_next(b))
+ continue;
+
+ send_batch(nl, b, portid);
+
+ /* this moves the last message that did not fit into the
+ * batch to the head of it. */
+ mnl_nlmsg_batch_reset(b);
+ }
+
+ /* check if there is any message in the batch not sent yet. */
+ if (!mnl_nlmsg_batch_is_empty(b))
+ send_batch(nl, b, portid);
+
+ mnl_nlmsg_batch_stop(b);
+ mnl_socket_close(nl);
+
+ return 0;
+}
diff --git a/libmnl/examples/netfilter/nfct-daemon.c b/libmnl/examples/netfilter/nfct-daemon.c
new file mode 100644
index 0000000..d223ac2
--- /dev/null
+++ b/libmnl/examples/netfilter/nfct-daemon.c
@@ -0,0 +1,365 @@
+/* A very simple skeleton code that implements a daemon that collects
+ * conntrack statistics from ctnetlink.
+ *
+ * This example is placed in the public domain.
+ */
+#include <endian.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <inttypes.h>
+#include <errno.h>
+#include <sys/select.h>
+
+#include <libmnl/libmnl.h>
+#include <linux/netlink.h>
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/nfnetlink_conntrack.h>
+
+#include <sys/queue.h>
+
+struct nstats {
+ LIST_ENTRY(nstats) list;
+
+ uint8_t family;
+
+ union {
+ struct in_addr ip;
+ struct in6_addr ip6;
+ };
+ uint64_t pkts, bytes;
+};
+
+static LIST_HEAD(nstats_head, nstats) nstats_head;
+
+static int parse_counters_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, CTA_COUNTERS_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case CTA_COUNTERS_PACKETS:
+ case CTA_COUNTERS_BYTES:
+ if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ }
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void parse_counters(const struct nlattr *nest, struct nstats *ns)
+{
+ struct nlattr *tb[CTA_COUNTERS_MAX+1] = {};
+
+ mnl_attr_parse_nested(nest, parse_counters_cb, tb);
+ if (tb[CTA_COUNTERS_PACKETS])
+ ns->pkts += be64toh(mnl_attr_get_u64(tb[CTA_COUNTERS_PACKETS]));
+
+ if (tb[CTA_COUNTERS_BYTES])
+ ns->bytes += be64toh(mnl_attr_get_u64(tb[CTA_COUNTERS_BYTES]));
+}
+
+static int parse_ip_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, CTA_IP_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case CTA_IP_V4_SRC:
+ case CTA_IP_V4_DST:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ case CTA_IP_V6_SRC:
+ case CTA_IP_V6_DST:
+ if (mnl_attr_validate2(attr, MNL_TYPE_BINARY,
+ sizeof(struct in6_addr)) < 0) {
+ perror("mnl_attr_validate2");
+ return MNL_CB_ERROR;
+ }
+ break;
+ }
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void parse_ip(const struct nlattr *nest, struct nstats *ns)
+{
+ struct nlattr *tb[CTA_IP_MAX+1] = {};
+
+ mnl_attr_parse_nested(nest, parse_ip_cb, tb);
+ if (tb[CTA_IP_V4_SRC]) {
+ struct in_addr *in = mnl_attr_get_payload(tb[CTA_IP_V4_SRC]);
+ ns->ip = *in;
+ ns->family = AF_INET;
+ }
+ if (tb[CTA_IP_V6_SRC]) {
+ struct in6_addr *in = mnl_attr_get_payload(tb[CTA_IP_V6_SRC]);
+ ns->ip6 = *in;
+ ns->family = AF_INET6;
+ }
+}
+
+static int parse_tuple_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, CTA_TUPLE_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case CTA_TUPLE_IP:
+ if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ }
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void parse_tuple(const struct nlattr *nest, struct nstats *ns)
+{
+ struct nlattr *tb[CTA_TUPLE_MAX+1] = {};
+
+ mnl_attr_parse_nested(nest, parse_tuple_cb, tb);
+ if (tb[CTA_TUPLE_IP])
+ parse_ip(tb[CTA_TUPLE_IP], ns);
+}
+
+static int data_attr_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, CTA_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case CTA_TUPLE_ORIG:
+ case CTA_COUNTERS_ORIG:
+ case CTA_COUNTERS_REPLY:
+ if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ }
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static int data_cb(const struct nlmsghdr *nlh, void *data)
+{
+ struct nlattr *tb[CTA_MAX+1] = {};
+ struct nfgenmsg *nfg = mnl_nlmsg_get_payload(nlh);
+ struct nstats ns = {}, *cur, *new;
+
+ mnl_attr_parse(nlh, sizeof(*nfg), data_attr_cb, tb);
+ if (tb[CTA_TUPLE_ORIG])
+ parse_tuple(tb[CTA_TUPLE_ORIG], &ns);
+
+ if (tb[CTA_COUNTERS_ORIG])
+ parse_counters(tb[CTA_COUNTERS_ORIG], &ns);
+
+ if (tb[CTA_COUNTERS_REPLY])
+ parse_counters(tb[CTA_COUNTERS_REPLY], &ns);
+
+ /* Look up for existing statistics object ... */
+ LIST_FOREACH(cur, &nstats_head, list) {
+ if (memcmp(&ns.ip6, &cur->ip6, sizeof(struct in6_addr)) == 0) {
+ /* ... and sum counters */
+ cur->pkts += ns.pkts;
+ cur->bytes += ns.bytes;
+ return MNL_CB_OK;
+ }
+ }
+
+ /* ... if it does not exist, add new stats object */
+ new = calloc(1, sizeof(struct nstats));
+ if (!new)
+ return MNL_CB_OK;
+
+ new->family = ns.family;
+ new->ip6 = ns.ip6;
+ new->pkts = ns.pkts;
+ new->bytes = ns.bytes;
+
+ LIST_INSERT_HEAD(&nstats_head, new, list);
+
+ return MNL_CB_OK;
+}
+
+static int handle(struct mnl_socket *nl)
+{
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ int ret;
+
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ if (ret == -1) {
+ /* It only happens if NETLINK_NO_ENOBUFS is not set, it means
+ * we are leaking statistics.
+ */
+ if (errno == ENOBUFS) {
+ fprintf(stderr, "The daemon has hit ENOBUFS, you can "
+ "increase the size of your receiver "
+ "buffer to mitigate this or enable "
+ "reliable delivery.\n");
+ } else {
+ perror("mnl_socket_recvfrom");
+ }
+ return -1;
+ }
+
+ ret = mnl_cb_run(buf, ret, 0, 0, data_cb, NULL);
+ if (ret == -1) {
+ perror("mnl_cb_run");
+ return -1;
+ } else if (ret <= MNL_CB_STOP)
+ return 0;
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ struct mnl_socket *nl;
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh;
+ struct nfgenmsg *nfh;
+ struct nstats *cur;
+ struct timeval tv = {};
+ int ret, secs, on = 1, buffersize = (1 << 22);
+
+ if (argc != 2) {
+ printf("Usage: %s <poll-secs>\n", argv[0]);
+ exit(EXIT_FAILURE);
+ }
+ secs = atoi(argv[1]);
+
+ LIST_INIT(&nstats_head);
+
+ printf("Polling every %d seconds from kernel...\n", secs);
+
+ /* Set high priority for this process, less chances to overrun
+ * the netlink receiver buffer since the scheduler gives this process
+ * more chances to run.
+ */
+ nice(-20);
+
+ /* Open netlink socket to operate with netfilter */
+ nl = mnl_socket_open(NETLINK_NETFILTER);
+ if (nl == NULL) {
+ perror("mnl_socket_open");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Subscribe to destroy events to avoid leaking counters. The same
+ * socket is used to periodically atomically dump and reset counters.
+ */
+ if (mnl_socket_bind(nl, NF_NETLINK_CONNTRACK_DESTROY,
+ MNL_SOCKET_AUTOPID) < 0) {
+ perror("mnl_socket_bind");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Set netlink receiver buffer to 16 MBytes, to avoid packet drops */
+ setsockopt(mnl_socket_get_fd(nl), SOL_SOCKET, SO_RCVBUFFORCE,
+ &buffersize, sizeof(socklen_t));
+
+ /* The two tweaks below enable reliable event delivery, packets may
+ * be dropped if the netlink receiver buffer overruns. This happens ...
+ *
+ * a) if the kernel spams this user-space process until the receiver
+ * is filled.
+ *
+ * or:
+ *
+ * b) if the user-space process does not pull messages from the
+ * receiver buffer so often.
+ */
+ mnl_socket_setsockopt(nl, NETLINK_BROADCAST_ERROR, &on, sizeof(int));
+ mnl_socket_setsockopt(nl, NETLINK_NO_ENOBUFS, &on, sizeof(int));
+
+ nlh = mnl_nlmsg_put_header(buf);
+ /* Counters are atomically zeroed in each dump */
+ nlh->nlmsg_type = (NFNL_SUBSYS_CTNETLINK << 8) |
+ IPCTNL_MSG_CT_GET_CTRZERO;
+ nlh->nlmsg_flags = NLM_F_REQUEST|NLM_F_DUMP;
+
+ nfh = mnl_nlmsg_put_extra_header(nlh, sizeof(struct nfgenmsg));
+ nfh->nfgen_family = AF_INET;
+ nfh->version = NFNETLINK_V0;
+ nfh->res_id = 0;
+
+ /* Filter by mark: We only want to dump entries whose mark is zero */
+ mnl_attr_put_u32(nlh, CTA_MARK, htonl(0));
+ mnl_attr_put_u32(nlh, CTA_MARK_MASK, htonl(0xffffffff));
+
+ while (1) {
+ int fd_max = mnl_socket_get_fd(nl);
+ fd_set readfds;
+
+ /* Every N seconds ... */
+ if (tv.tv_sec == 0 && tv.tv_usec == 0) {
+ /* ... request a fresh dump of the table from kernel */
+ ret = mnl_socket_sendto(nl, nlh, nlh->nlmsg_len);
+ if (ret == -1) {
+ perror("mnl_socket_sendto");
+ return -1;
+ }
+ tv.tv_sec = secs;
+ tv.tv_usec = 0;
+
+ /* print the content of the list */
+ LIST_FOREACH(cur, &nstats_head, list) {
+ char out[INET6_ADDRSTRLEN];
+
+ if (inet_ntop(cur->family, &cur->ip, out, sizeof(out)))
+ printf("src=%s ", out);
+
+ printf("counters %"PRIu64" %"PRIu64"\n",
+ cur->pkts, cur->bytes);
+ }
+ }
+
+ FD_ZERO(&readfds);
+ FD_SET(mnl_socket_get_fd(nl), &readfds);
+
+ ret = select(fd_max+1, &readfds, NULL, NULL, &tv);
+ if (ret < 0) {
+ if (errno == EINTR)
+ continue;
+
+ perror("select");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Handled event and periodic atomic-dump-and-reset messages */
+ if (FD_ISSET(mnl_socket_get_fd(nl), &readfds)) {
+ if (handle(nl) < 0)
+ return EXIT_FAILURE;
+ }
+ }
+
+ mnl_socket_close(nl);
+
+ return 0;
+}
diff --git a/libmnl/examples/netfilter/nfct-dump.c b/libmnl/examples/netfilter/nfct-dump.c
new file mode 100644
index 0000000..cb8e52c
--- /dev/null
+++ b/libmnl/examples/netfilter/nfct-dump.c
@@ -0,0 +1,319 @@
+/* This example is placed in the public domain. */
+#include <endian.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+#include <arpa/inet.h>
+#include <inttypes.h>
+
+#include <libmnl/libmnl.h>
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/nfnetlink_conntrack.h>
+
+static int parse_counters_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, CTA_COUNTERS_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case CTA_COUNTERS_PACKETS:
+ case CTA_COUNTERS_BYTES:
+ if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ }
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void print_counters(const struct nlattr *nest)
+{
+ struct nlattr *tb[CTA_COUNTERS_MAX+1] = {};
+
+ mnl_attr_parse_nested(nest, parse_counters_cb, tb);
+ if (tb[CTA_COUNTERS_PACKETS]) {
+ printf("packets=%"PRIu64" ",
+ be64toh(mnl_attr_get_u64(tb[CTA_COUNTERS_PACKETS])));
+ }
+ if (tb[CTA_COUNTERS_BYTES]) {
+ printf("bytes=%"PRIu64" ",
+ be64toh(mnl_attr_get_u64(tb[CTA_COUNTERS_BYTES])));
+ }
+}
+
+static int parse_ip_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, CTA_IP_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case CTA_IP_V4_SRC:
+ case CTA_IP_V4_DST:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ case CTA_IP_V6_SRC:
+ case CTA_IP_V6_DST:
+ if (mnl_attr_validate2(attr, MNL_TYPE_BINARY,
+ sizeof(struct in6_addr)) < 0) {
+ perror("mnl_attr_validate2");
+ return MNL_CB_ERROR;
+ }
+ break;
+ }
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void print_ip(const struct nlattr *nest)
+{
+ struct nlattr *tb[CTA_IP_MAX+1] = {};
+
+ mnl_attr_parse_nested(nest, parse_ip_cb, tb);
+ if (tb[CTA_IP_V4_SRC]) {
+ struct in_addr *in = mnl_attr_get_payload(tb[CTA_IP_V4_SRC]);
+ printf("src=%s ", inet_ntoa(*in));
+ }
+ if (tb[CTA_IP_V4_DST]) {
+ struct in_addr *in = mnl_attr_get_payload(tb[CTA_IP_V4_DST]);
+ printf("dst=%s ", inet_ntoa(*in));
+ }
+ if (tb[CTA_IP_V6_SRC]) {
+ struct in6_addr *in = mnl_attr_get_payload(tb[CTA_IP_V6_SRC]);
+ char out[INET6_ADDRSTRLEN];
+
+ if (!inet_ntop(AF_INET6, in, out, sizeof(out)))
+ printf("src=%s ", out);
+ }
+ if (tb[CTA_IP_V6_DST]) {
+ struct in6_addr *in = mnl_attr_get_payload(tb[CTA_IP_V6_DST]);
+ char out[INET6_ADDRSTRLEN];
+
+ if (!inet_ntop(AF_INET6, in, out, sizeof(out)))
+ printf("dst=%s ", out);
+ }
+}
+
+static int parse_proto_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, CTA_PROTO_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case CTA_PROTO_NUM:
+ case CTA_PROTO_ICMP_TYPE:
+ case CTA_PROTO_ICMP_CODE:
+ if (mnl_attr_validate(attr, MNL_TYPE_U8) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ case CTA_PROTO_SRC_PORT:
+ case CTA_PROTO_DST_PORT:
+ case CTA_PROTO_ICMP_ID:
+ if (mnl_attr_validate(attr, MNL_TYPE_U16) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ }
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void print_proto(const struct nlattr *nest)
+{
+ struct nlattr *tb[CTA_PROTO_MAX+1] = {};
+
+ mnl_attr_parse_nested(nest, parse_proto_cb, tb);
+ if (tb[CTA_PROTO_NUM]) {
+ printf("proto=%u ", mnl_attr_get_u8(tb[CTA_PROTO_NUM]));
+ }
+ if (tb[CTA_PROTO_SRC_PORT]) {
+ printf("sport=%u ",
+ ntohs(mnl_attr_get_u16(tb[CTA_PROTO_SRC_PORT])));
+ }
+ if (tb[CTA_PROTO_DST_PORT]) {
+ printf("dport=%u ",
+ ntohs(mnl_attr_get_u16(tb[CTA_PROTO_DST_PORT])));
+ }
+ if (tb[CTA_PROTO_ICMP_ID]) {
+ printf("id=%u ",
+ ntohs(mnl_attr_get_u16(tb[CTA_PROTO_ICMP_ID])));
+ }
+ if (tb[CTA_PROTO_ICMP_TYPE]) {
+ printf("type=%u ", mnl_attr_get_u8(tb[CTA_PROTO_ICMP_TYPE]));
+ }
+ if (tb[CTA_PROTO_ICMP_CODE]) {
+ printf("code=%u ", mnl_attr_get_u8(tb[CTA_PROTO_ICMP_CODE]));
+ }
+}
+
+static int parse_tuple_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, CTA_TUPLE_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case CTA_TUPLE_IP:
+ if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ case CTA_TUPLE_PROTO:
+ if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ }
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void print_tuple(const struct nlattr *nest)
+{
+ struct nlattr *tb[CTA_TUPLE_MAX+1] = {};
+
+ mnl_attr_parse_nested(nest, parse_tuple_cb, tb);
+ if (tb[CTA_TUPLE_IP]) {
+ print_ip(tb[CTA_TUPLE_IP]);
+ }
+ if (tb[CTA_TUPLE_PROTO]) {
+ print_proto(tb[CTA_TUPLE_PROTO]);
+ }
+}
+
+static int data_attr_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, CTA_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case CTA_TUPLE_ORIG:
+ case CTA_COUNTERS_ORIG:
+ case CTA_COUNTERS_REPLY:
+ if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ case CTA_TIMEOUT:
+ case CTA_MARK:
+ case CTA_SECMARK:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ }
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static int data_cb(const struct nlmsghdr *nlh, void *data)
+{
+ struct nlattr *tb[CTA_MAX+1] = {};
+ struct nfgenmsg *nfg = mnl_nlmsg_get_payload(nlh);
+
+ mnl_attr_parse(nlh, sizeof(*nfg), data_attr_cb, tb);
+ if (tb[CTA_TUPLE_ORIG])
+ print_tuple(tb[CTA_TUPLE_ORIG]);
+
+ if (tb[CTA_MARK])
+ printf("mark=%u ", ntohl(mnl_attr_get_u32(tb[CTA_MARK])));
+
+ if (tb[CTA_SECMARK])
+ printf("secmark=%u ", ntohl(mnl_attr_get_u32(tb[CTA_SECMARK])));
+
+ if (tb[CTA_COUNTERS_ORIG]) {
+ printf("original ");
+ print_counters(tb[CTA_COUNTERS_ORIG]);
+ }
+
+ if (tb[CTA_COUNTERS_REPLY]) {
+ printf("reply ");
+ print_counters(tb[CTA_COUNTERS_REPLY]);
+ }
+
+ printf("\n");
+ return MNL_CB_OK;
+}
+
+int main(void)
+{
+ char buf[MNL_SOCKET_DUMP_SIZE];
+ struct mnl_socket *nl;
+ struct nlmsghdr *nlh;
+ struct nfgenmsg *nfh;
+ uint32_t seq, portid;
+ int ret;
+
+ nl = mnl_socket_open(NETLINK_NETFILTER);
+ if (nl == NULL) {
+ perror("mnl_socket_open");
+ exit(EXIT_FAILURE);
+ }
+
+ if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
+ perror("mnl_socket_bind");
+ exit(EXIT_FAILURE);
+ }
+
+ nlh = mnl_nlmsg_put_header(buf);
+ nlh->nlmsg_type = (NFNL_SUBSYS_CTNETLINK << 8) | IPCTNL_MSG_CT_GET;
+ nlh->nlmsg_flags = NLM_F_REQUEST|NLM_F_DUMP;
+ nlh->nlmsg_seq = seq = time(NULL);
+
+ nfh = mnl_nlmsg_put_extra_header(nlh, sizeof(struct nfgenmsg));
+ nfh->nfgen_family = AF_INET;
+ nfh->version = NFNETLINK_V0;
+ nfh->res_id = 0;
+
+ ret = mnl_socket_sendto(nl, nlh, nlh->nlmsg_len);
+ if (ret == -1) {
+ perror("mnl_socket_sendto");
+ exit(EXIT_FAILURE);
+ }
+ portid = mnl_socket_get_portid(nl);
+
+ while (1) {
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ if (ret == -1) {
+ perror("mnl_socket_recvfrom");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = mnl_cb_run(buf, ret, seq, portid, data_cb, NULL);
+ if (ret == -1) {
+ perror("mnl_cb_run");
+ exit(EXIT_FAILURE);
+ } else if (ret <= MNL_CB_STOP)
+ break;
+ }
+
+ mnl_socket_close(nl);
+
+ return 0;
+}
diff --git a/libmnl/examples/netfilter/nfct-event.c b/libmnl/examples/netfilter/nfct-event.c
new file mode 100644
index 0000000..94603d4
--- /dev/null
+++ b/libmnl/examples/netfilter/nfct-event.c
@@ -0,0 +1,240 @@
+/* This example is placed in the public domain. */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+
+#include <libmnl/libmnl.h>
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/nfnetlink_conntrack.h>
+
+static int parse_ip_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, CTA_IP_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case CTA_IP_V4_SRC:
+ case CTA_IP_V4_DST:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ }
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void print_ip(const struct nlattr *nest)
+{
+ struct nlattr *tb[CTA_IP_MAX+1] = {};
+
+ mnl_attr_parse_nested(nest, parse_ip_cb, tb);
+ if (tb[CTA_IP_V4_SRC]) {
+ struct in_addr *in = mnl_attr_get_payload(tb[CTA_IP_V4_SRC]);
+ printf("src=%s ", inet_ntoa(*in));
+ }
+ if (tb[CTA_IP_V4_DST]) {
+ struct in_addr *in = mnl_attr_get_payload(tb[CTA_IP_V4_DST]);
+ printf("dst=%s ", inet_ntoa(*in));
+ }
+}
+
+static int parse_proto_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, CTA_PROTO_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case CTA_PROTO_NUM:
+ case CTA_PROTO_ICMP_TYPE:
+ case CTA_PROTO_ICMP_CODE:
+ if (mnl_attr_validate(attr, MNL_TYPE_U8) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ case CTA_PROTO_SRC_PORT:
+ case CTA_PROTO_DST_PORT:
+ case CTA_PROTO_ICMP_ID:
+ if (mnl_attr_validate(attr, MNL_TYPE_U16) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ }
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void print_proto(const struct nlattr *nest)
+{
+ struct nlattr *tb[CTA_PROTO_MAX+1] = {};
+
+ mnl_attr_parse_nested(nest, parse_proto_cb, tb);
+ if (tb[CTA_PROTO_NUM]) {
+ printf("proto=%u ", mnl_attr_get_u8(tb[CTA_PROTO_NUM]));
+ }
+ if (tb[CTA_PROTO_SRC_PORT]) {
+ printf("sport=%u ",
+ ntohs(mnl_attr_get_u16(tb[CTA_PROTO_SRC_PORT])));
+ }
+ if (tb[CTA_PROTO_DST_PORT]) {
+ printf("dport=%u ",
+ ntohs(mnl_attr_get_u16(tb[CTA_PROTO_DST_PORT])));
+ }
+ if (tb[CTA_PROTO_ICMP_ID]) {
+ printf("id=%u ",
+ ntohs(mnl_attr_get_u16(tb[CTA_PROTO_ICMP_ID])));
+ }
+ if (tb[CTA_PROTO_ICMP_TYPE]) {
+ printf("type=%u ", mnl_attr_get_u8(tb[CTA_PROTO_ICMP_TYPE]));
+ }
+ if (tb[CTA_PROTO_ICMP_CODE]) {
+ printf("code=%u ", mnl_attr_get_u8(tb[CTA_PROTO_ICMP_CODE]));
+ }
+}
+
+static int parse_tuple_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, CTA_TUPLE_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case CTA_TUPLE_IP:
+ if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ case CTA_TUPLE_PROTO:
+ if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ }
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void print_tuple(const struct nlattr *nest)
+{
+ struct nlattr *tb[CTA_TUPLE_MAX+1] = {};
+
+ mnl_attr_parse_nested(nest, parse_tuple_cb, tb);
+ if (tb[CTA_TUPLE_IP]) {
+ print_ip(tb[CTA_TUPLE_IP]);
+ }
+ if (tb[CTA_TUPLE_PROTO]) {
+ print_proto(tb[CTA_TUPLE_PROTO]);
+ }
+}
+
+static int data_attr_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, CTA_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case CTA_TUPLE_ORIG:
+ if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ case CTA_TIMEOUT:
+ case CTA_MARK:
+ case CTA_SECMARK:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ }
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static int data_cb(const struct nlmsghdr *nlh, void *data)
+{
+ struct nlattr *tb[CTA_MAX+1] = {};
+ struct nfgenmsg *nfg = mnl_nlmsg_get_payload(nlh);
+
+ switch(nlh->nlmsg_type & 0xFF) {
+ case IPCTNL_MSG_CT_NEW:
+ if (nlh->nlmsg_flags & (NLM_F_CREATE|NLM_F_EXCL))
+ printf("%9s ", "[NEW] ");
+ else
+ printf("%9s ", "[UPDATE] ");
+ break;
+ case IPCTNL_MSG_CT_DELETE:
+ printf("%9s ", "[DESTROY] ");
+ break;
+ }
+
+ mnl_attr_parse(nlh, sizeof(*nfg), data_attr_cb, tb);
+ if (tb[CTA_TUPLE_ORIG]) {
+ print_tuple(tb[CTA_TUPLE_ORIG]);
+ }
+ if (tb[CTA_MARK]) {
+ printf("mark=%u ", ntohl(mnl_attr_get_u32(tb[CTA_MARK])));
+ }
+ if (tb[CTA_SECMARK]) {
+ printf("secmark=%u ", ntohl(mnl_attr_get_u32(tb[CTA_SECMARK])));
+ }
+ printf("\n");
+ return MNL_CB_OK;
+}
+
+int main(void)
+{
+ struct mnl_socket *nl;
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ int ret;
+
+ nl = mnl_socket_open(NETLINK_NETFILTER);
+ if (nl == NULL) {
+ perror("mnl_socket_open");
+ exit(EXIT_FAILURE);
+ }
+
+ if (mnl_socket_bind(nl, NF_NETLINK_CONNTRACK_NEW |
+ NF_NETLINK_CONNTRACK_UPDATE |
+ NF_NETLINK_CONNTRACK_DESTROY,
+ MNL_SOCKET_AUTOPID) < 0) {
+ perror("mnl_socket_bind");
+ exit(EXIT_FAILURE);
+ }
+
+ while (1) {
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ if (ret == -1) {
+ perror("mnl_socket_recvfrom");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = mnl_cb_run(buf, ret, 0, 0, data_cb, NULL);
+ if (ret == -1) {
+ perror("mnl_cb_run");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ mnl_socket_close(nl);
+
+ return 0;
+}
diff --git a/libmnl/examples/rtnl/.gitignore b/libmnl/examples/rtnl/.gitignore
new file mode 100644
index 0000000..4b16c53
--- /dev/null
+++ b/libmnl/examples/rtnl/.gitignore
@@ -0,0 +1,12 @@
+/rtnl-addr-add
+/rtnl-addr-dump
+/rtnl-link-can
+/rtnl-link-dump
+/rtnl-link-dump2
+/rtnl-link-dump3
+/rtnl-link-event
+/rtnl-link-set
+/rtnl-neigh-dump
+/rtnl-route-event
+/rtnl-route-add
+/rtnl-route-dump
diff --git a/libmnl/examples/rtnl/Makefile.am b/libmnl/examples/rtnl/Makefile.am
new file mode 100644
index 0000000..5a28e09
--- /dev/null
+++ b/libmnl/examples/rtnl/Makefile.am
@@ -0,0 +1,48 @@
+include $(top_srcdir)/Make_global.am
+
+check_PROGRAMS = rtnl-addr-add \
+ rtnl-addr-dump \
+ rtnl-link-can \
+ rtnl-link-dump rtnl-link-dump2 rtnl-link-dump3 \
+ rtnl-link-event \
+ rtnl-link-set \
+ rtnl-route-add \
+ rtnl-route-dump \
+ rtnl-route-event \
+ rtnl-neigh-dump
+
+rtnl_addr_add_SOURCES = rtnl-addr-add.c
+rtnl_addr_add_LDADD = ../../src/libmnl.la
+
+rtnl_link_can_SOURCES = rtnl-link-can.c
+rtnl_link_can_LDADD = ../../src/libmnl.la
+
+rtnl_addr_dump_SOURCES = rtnl-addr-dump.c
+rtnl_addr_dump_LDADD = ../../src/libmnl.la
+
+rtnl_link_dump_SOURCES = rtnl-link-dump.c
+rtnl_link_dump_LDADD = ../../src/libmnl.la
+
+rtnl_link_dump2_SOURCES = rtnl-link-dump2.c
+rtnl_link_dump2_LDADD = ../../src/libmnl.la
+
+rtnl_link_dump3_SOURCES = rtnl-link-dump3.c
+rtnl_link_dump3_LDADD = ../../src/libmnl.la
+
+rtnl_route_add_SOURCES = rtnl-route-add.c
+rtnl_route_add_LDADD = ../../src/libmnl.la
+
+rtnl_link_event_SOURCES = rtnl-link-event.c
+rtnl_link_event_LDADD = ../../src/libmnl.la
+
+rtnl_link_set_SOURCES = rtnl-link-set.c
+rtnl_link_set_LDADD = ../../src/libmnl.la
+
+rtnl_route_dump_SOURCES = rtnl-route-dump.c
+rtnl_route_dump_LDADD = ../../src/libmnl.la
+
+rtnl_route_event_SOURCES = rtnl-route-event.c
+rtnl_route_event_LDADD = ../../src/libmnl.la
+
+rtnl_neigh_dump_SOURCES = rtnl-neigh-dump.c
+rtnl_neigh_dump_LDADD = ../../src/libmnl.la
diff --git a/libmnl/examples/rtnl/rtnl-addr-add.c b/libmnl/examples/rtnl/rtnl-addr-add.c
new file mode 100644
index 0000000..f9a69dc
--- /dev/null
+++ b/libmnl/examples/rtnl/rtnl-addr-add.c
@@ -0,0 +1,119 @@
+/* This example is placed in the public domain. */
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <time.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <strings.h>
+#include <net/if.h>
+
+#include <libmnl/libmnl.h>
+#include <linux/if_link.h>
+#include <linux/rtnetlink.h>
+
+int main(int argc, char *argv[])
+{
+ struct mnl_socket *nl;
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh;
+ struct ifaddrmsg *ifm;
+ uint32_t seq, portid;
+ union {
+ in_addr_t ip;
+ struct in6_addr ip6;
+ } addr;
+ int ret, family = AF_INET;
+
+ uint32_t prefix;
+ int iface;
+
+
+ if (argc <= 3) {
+ printf("Usage: %s iface destination cidr\n", argv[0]);
+ printf("Example: %s eth0 10.0.1.12 32\n", argv[0]);
+ printf(" %s eth0 ffff::10.0.1.12 128\n", argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ iface = if_nametoindex(argv[1]);
+ if (iface == 0) {
+ perror("if_nametoindex");
+ exit(EXIT_FAILURE);
+ }
+
+ if (!inet_pton(AF_INET, argv[2], &addr)) {
+ if (!inet_pton(AF_INET6, argv[2], &addr)) {
+ perror("inet_pton");
+ exit(EXIT_FAILURE);
+ }
+ family = AF_INET6;
+ }
+
+ if (sscanf(argv[3], "%u", &prefix) == 0) {
+ perror("sscanf");
+ exit(EXIT_FAILURE);
+ }
+
+ nlh = mnl_nlmsg_put_header(buf);
+ nlh->nlmsg_type = RTM_NEWADDR;
+
+ nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE | NLM_F_ACK;
+ nlh->nlmsg_seq = seq = time(NULL);
+
+ ifm = mnl_nlmsg_put_extra_header(nlh, sizeof(struct ifaddrmsg));
+
+ ifm->ifa_family = family;
+ ifm->ifa_prefixlen = prefix;
+ ifm->ifa_flags = IFA_F_PERMANENT;
+
+ ifm->ifa_scope = RT_SCOPE_UNIVERSE;
+ ifm->ifa_index = iface;
+
+ /*
+ * The exact meaning of IFA_LOCAL and IFA_ADDRESS depend
+ * on the address family being used and the device type.
+ * For broadcast devices (like the interfaces we use),
+ * for IPv4 we specify both and they are used interchangeably.
+ * For IPv6, only IFA_ADDRESS needs to be set.
+ */
+ if (family == AF_INET) {
+ mnl_attr_put_u32(nlh, IFA_LOCAL, addr.ip);
+ mnl_attr_put_u32(nlh, IFA_ADDRESS, addr.ip);
+ } else {
+ mnl_attr_put(nlh, IFA_ADDRESS, sizeof(struct in6_addr), &addr);
+ }
+
+ nl = mnl_socket_open(NETLINK_ROUTE);
+ if (nl == NULL) {
+ perror("mnl_socket_open");
+ exit(EXIT_FAILURE);
+ }
+
+ if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
+ perror("mnl_socket_bind");
+ exit(EXIT_FAILURE);
+ }
+ portid = mnl_socket_get_portid(nl);
+
+ if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
+ perror("mnl_socket_sendto");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ if (ret < 0) {
+ perror("mnl_socket_recvfrom");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL);
+ if (ret < 0) {
+ perror("mnl_cb_run");
+ exit(EXIT_FAILURE);
+ }
+
+ mnl_socket_close(nl);
+
+ return 0;
+}
diff --git a/libmnl/examples/rtnl/rtnl-addr-dump.c b/libmnl/examples/rtnl/rtnl-addr-dump.c
new file mode 100644
index 0000000..675e9b0
--- /dev/null
+++ b/libmnl/examples/rtnl/rtnl-addr-dump.c
@@ -0,0 +1,133 @@
+/* This example is placed in the public domain. */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+#include <arpa/inet.h>
+
+#include <libmnl/libmnl.h>
+#include <linux/if.h>
+#include <linux/if_link.h>
+#include <linux/rtnetlink.h>
+
+static int data_attr_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ /* skip unsupported attribute in user-space */
+ if (mnl_attr_type_valid(attr, IFA_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case IFA_ADDRESS:
+ if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ }
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static int data_cb(const struct nlmsghdr *nlh, void *data)
+{
+ struct nlattr *tb[IFA_MAX + 1] = {};
+ struct ifaddrmsg *ifa = mnl_nlmsg_get_payload(nlh);
+
+ printf("index=%d family=%d ", ifa->ifa_index, ifa->ifa_family);
+
+ mnl_attr_parse(nlh, sizeof(*ifa), data_attr_cb, tb);
+ printf("addr=");
+ if (tb[IFA_ADDRESS]) {
+ void *addr = mnl_attr_get_payload(tb[IFA_ADDRESS]);
+ char out[INET6_ADDRSTRLEN];
+
+ if (inet_ntop(ifa->ifa_family, addr, out, sizeof(out)))
+ printf("%s ", out);
+ }
+ printf("scope=");
+ switch(ifa->ifa_scope) {
+ case 0:
+ printf("global ");
+ break;
+ case 200:
+ printf("site ");
+ break;
+ case 253:
+ printf("link ");
+ break;
+ case 254:
+ printf("host ");
+ break;
+ case 255:
+ printf("nowhere ");
+ break;
+ default:
+ printf("%d ", ifa->ifa_scope);
+ break;
+ }
+
+ printf("\n");
+ return MNL_CB_OK;
+}
+
+int main(int argc, char *argv[])
+{
+ char buf[MNL_SOCKET_DUMP_SIZE];
+ unsigned int seq, portid;
+ struct mnl_socket *nl;
+ struct nlmsghdr *nlh;
+ struct rtgenmsg *rt;
+ int ret;
+
+ if (argc != 2) {
+ fprintf(stderr, "Usage: %s <inet|inet6>\n", argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ nlh = mnl_nlmsg_put_header(buf);
+ nlh->nlmsg_type = RTM_GETADDR;
+ nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
+ nlh->nlmsg_seq = seq = time(NULL);
+ rt = mnl_nlmsg_put_extra_header(nlh, sizeof(struct rtgenmsg));
+ if (strcmp(argv[1], "inet") == 0)
+ rt->rtgen_family = AF_INET;
+ else if (strcmp(argv[1], "inet6") == 0)
+ rt->rtgen_family = AF_INET6;
+
+ nl = mnl_socket_open(NETLINK_ROUTE);
+ if (nl == NULL) {
+ perror("mnl_socket_open");
+ exit(EXIT_FAILURE);
+ }
+
+ if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
+ perror("mnl_socket_bind");
+ exit(EXIT_FAILURE);
+ }
+ portid = mnl_socket_get_portid(nl);
+
+ if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
+ perror("mnl_socket_sendto");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ while (ret > 0) {
+ ret = mnl_cb_run(buf, ret, seq, portid, data_cb, NULL);
+ if (ret <= MNL_CB_STOP)
+ break;
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ }
+ if (ret == -1) {
+ perror("error");
+ exit(EXIT_FAILURE);
+ }
+
+ mnl_socket_close(nl);
+
+ return 0;
+}
diff --git a/libmnl/examples/rtnl/rtnl-link-can.c b/libmnl/examples/rtnl/rtnl-link-can.c
new file mode 100644
index 0000000..8ed70d1
--- /dev/null
+++ b/libmnl/examples/rtnl/rtnl-link-can.c
@@ -0,0 +1,452 @@
+/* This example is placed in the public domain. */
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+
+#include <libmnl/libmnl.h>
+#include <linux/can/netlink.h>
+#include <linux/if.h>
+#include <linux/if_link.h>
+#include <linux/rtnetlink.h>
+
+static void incomplete_command(void) __attribute__((noreturn));
+
+#define NEXT_ARG() \
+ do { \
+ if (argc <= 0) incomplete_command(); \
+ argv++; \
+ argc--; \
+ } while (0)
+
+static void duparg2(const char *key, const char *arg)
+{
+ fprintf(stderr,
+ "Error: either \"%s\" is duplicate, or \"%s\" is a garbage.\n",
+ key, arg);
+ exit(-1);
+}
+
+static void incomplete_command(void)
+{
+ fprintf(stderr, "Command line is not complete. Try option \"help\"\n");
+ exit(EXIT_FAILURE);
+}
+
+/* Returns false if 'prefix' is a not empty prefix of 'string'.
+ */
+static bool matches(const char *prefix, const char *string)
+{
+ if (!*prefix)
+ return true;
+
+ while (*string && *prefix == *string) {
+ prefix++;
+ string++;
+ }
+
+ return !!*prefix;
+}
+
+static int get_u16(__u16 *val, const char *arg, int base)
+{
+ unsigned long res;
+ char *ptr;
+
+ if (!arg || !*arg)
+ return -1;
+
+ res = strtoul(arg, &ptr, base);
+
+ /* empty string or trailing non-digits */
+ if (!ptr || ptr == arg || *ptr)
+ return -1;
+
+ /* overflow */
+ if (res == ULONG_MAX && errno == ERANGE)
+ return -1;
+
+ if (res > 0xFFFFUL)
+ return -1;
+
+ *val = res;
+ return 0;
+}
+
+static int get_u32(__u32 *val, const char *arg, int base)
+{
+ unsigned long res;
+ char *ptr;
+
+ if (!arg || !*arg)
+ return -1;
+
+ res = strtoul(arg, &ptr, base);
+
+ /* empty string or trailing non-digits */
+ if (!ptr || ptr == arg || *ptr)
+ return -1;
+
+ /* overflow */
+ if (res == ULONG_MAX && errno == ERANGE)
+ return -1;
+
+ /* in case UL > 32 bits */
+ if (res > 0xFFFFFFFFUL)
+ return -1;
+
+ *val = res;
+ return 0;
+}
+
+static int get_float(float *val, const char *arg)
+{
+ float res;
+ char *ptr;
+
+ if (!arg || !*arg)
+ return -1;
+
+ res = strtof(arg, &ptr);
+ if (!ptr || ptr == arg || *ptr)
+ return -1;
+
+ *val = res;
+ return 0;
+}
+
+static void set_ctrlmode(char *name, char *arg,
+ struct can_ctrlmode *cm, __u32 flags)
+{
+ if (strcmp(arg, "on") == 0) {
+ cm->flags |= flags;
+ } else if (strcmp(arg, "off") != 0) {
+ fprintf(stderr,
+ "Error: argument of \"%s\" must be \"on\" or \"off\", not \"%s\"\n",
+ name, arg);
+ exit(EXIT_FAILURE);
+ }
+
+ cm->mask |= flags;
+}
+
+static void invarg(const char *msg, const char *arg)
+{
+ fprintf(stderr, "Error: argument \"%s\" is wrong: %s\n", arg, msg);
+ exit(-1);
+}
+
+static void print_usage(FILE *f)
+{
+ fprintf(f,
+ "Usage: ip link set DEVICE type can\n"
+ "\t[ bitrate BITRATE [ sample-point SAMPLE-POINT] ] |\n"
+ "\t[ tq TQ prop-seg PROP_SEG phase-seg1 PHASE-SEG1\n \t phase-seg2 PHASE-SEG2 [ sjw SJW ] ]\n"
+ "\n"
+ "\t[ dbitrate BITRATE [ dsample-point SAMPLE-POINT] ] |\n"
+ "\t[ dtq TQ dprop-seg PROP_SEG dphase-seg1 PHASE-SEG1\n \t dphase-seg2 PHASE-SEG2 [ dsjw SJW ] ]\n"
+ "\n"
+ "\t[ loopback { on | off } ]\n"
+ "\t[ listen-only { on | off } ]\n"
+ "\t[ triple-sampling { on | off } ]\n"
+ "\t[ one-shot { on | off } ]\n"
+ "\t[ berr-reporting { on | off } ]\n"
+ "\t[ fd { on | off } ]\n"
+ "\t[ fd-non-iso { on | off } ]\n"
+ "\t[ presume-ack { on | off } ]\n"
+ "\t[ cc-len8-dlc { on | off } ]\n"
+ "\n"
+ "\t[ restart-ms TIME-MS ]\n"
+ "\t[ restart ]\n"
+ "\n"
+ "\t[ termination { 0..65535 } ]\n"
+ "\n"
+ "\tWhere: BITRATE := { 1..1000000 }\n"
+ "\t SAMPLE-POINT := { 0.000..0.999 }\n"
+ "\t TQ := { NUMBER }\n"
+ "\t PROP-SEG := { 1..8 }\n"
+ "\t PHASE-SEG1 := { 1..8 }\n"
+ "\t PHASE-SEG2 := { 1..8 }\n"
+ "\t SJW := { 1..4 }\n"
+ "\t RESTART-MS := { 0 | NUMBER }\n"
+ );
+}
+
+static void usage(void)
+{
+ print_usage(stderr);
+}
+
+static int iplink_set_can_parse(int argc, char **argv, struct nlmsghdr *nlh)
+{
+ struct can_bittiming bt = {}, dbt = {};
+ struct can_ctrlmode cm = {};
+
+ while (argc > 0) {
+ if (matches(*argv, "bitrate") == 0) {
+ NEXT_ARG();
+ if (get_u32(&bt.bitrate, *argv, 0))
+ invarg("invalid \"bitrate\" value\n", *argv);
+ } else if (matches(*argv, "sample-point") == 0) {
+ float sp;
+
+ NEXT_ARG();
+ if (get_float(&sp, *argv))
+ invarg("invalid \"sample-point\" value\n",
+ *argv);
+
+ bt.sample_point = (__u32)(sp * 1000);
+ } else if (matches(*argv, "tq") == 0) {
+ NEXT_ARG();
+ if (get_u32(&bt.tq, *argv, 0))
+ invarg("invalid \"tq\" value\n", *argv);
+ } else if (matches(*argv, "prop-seg") == 0) {
+ NEXT_ARG();
+ if (get_u32(&bt.prop_seg, *argv, 0))
+ invarg("invalid \"prop-seg\" value\n", *argv);
+ } else if (matches(*argv, "phase-seg1") == 0) {
+ NEXT_ARG();
+ if (get_u32(&bt.phase_seg1, *argv, 0))
+ invarg("invalid \"phase-seg1\" value\n", *argv);
+ } else if (matches(*argv, "phase-seg2") == 0) {
+ NEXT_ARG();
+ if (get_u32(&bt.phase_seg2, *argv, 0))
+ invarg("invalid \"phase-seg2\" value\n", *argv);
+ } else if (matches(*argv, "sjw") == 0) {
+ NEXT_ARG();
+ if (get_u32(&bt.sjw, *argv, 0))
+ invarg("invalid \"sjw\" value\n", *argv);
+ } else if (matches(*argv, "dbitrate") == 0) {
+ NEXT_ARG();
+ if (get_u32(&dbt.bitrate, *argv, 0))
+ invarg("invalid \"dbitrate\" value\n", *argv);
+ } else if (matches(*argv, "dsample-point") == 0) {
+ float sp;
+
+ NEXT_ARG();
+ if (get_float(&sp, *argv))
+ invarg("invalid \"dsample-point\" value\n", *argv);
+ dbt.sample_point = (__u32)(sp * 1000);
+ } else if (matches(*argv, "dtq") == 0) {
+ NEXT_ARG();
+ if (get_u32(&dbt.tq, *argv, 0))
+ invarg("invalid \"dtq\" value\n", *argv);
+ } else if (matches(*argv, "dprop-seg") == 0) {
+ NEXT_ARG();
+ if (get_u32(&dbt.prop_seg, *argv, 0))
+ invarg("invalid \"dprop-seg\" value\n", *argv);
+ } else if (matches(*argv, "dphase-seg1") == 0) {
+ NEXT_ARG();
+ if (get_u32(&dbt.phase_seg1, *argv, 0))
+ invarg("invalid \"dphase-seg1\" value\n", *argv);
+ } else if (matches(*argv, "dphase-seg2") == 0) {
+ NEXT_ARG();
+ if (get_u32(&dbt.phase_seg2, *argv, 0))
+ invarg("invalid \"dphase-seg2\" value\n", *argv);
+ } else if (matches(*argv, "dsjw") == 0) {
+ NEXT_ARG();
+ if (get_u32(&dbt.sjw, *argv, 0))
+ invarg("invalid \"dsjw\" value\n", *argv);
+ } else if (matches(*argv, "loopback") == 0) {
+ NEXT_ARG();
+ set_ctrlmode("loopback", *argv, &cm,
+ CAN_CTRLMODE_LOOPBACK);
+ } else if (matches(*argv, "listen-only") == 0) {
+ NEXT_ARG();
+ set_ctrlmode("listen-only", *argv, &cm,
+ CAN_CTRLMODE_LISTENONLY);
+ } else if (matches(*argv, "triple-sampling") == 0) {
+ NEXT_ARG();
+ set_ctrlmode("triple-sampling", *argv, &cm,
+ CAN_CTRLMODE_3_SAMPLES);
+ } else if (matches(*argv, "one-shot") == 0) {
+ NEXT_ARG();
+ set_ctrlmode("one-shot", *argv, &cm,
+ CAN_CTRLMODE_ONE_SHOT);
+ } else if (matches(*argv, "berr-reporting") == 0) {
+ NEXT_ARG();
+ set_ctrlmode("berr-reporting", *argv, &cm,
+ CAN_CTRLMODE_BERR_REPORTING);
+ } else if (matches(*argv, "fd") == 0) {
+ NEXT_ARG();
+ set_ctrlmode("fd", *argv, &cm,
+ CAN_CTRLMODE_FD);
+ } else if (matches(*argv, "fd-non-iso") == 0) {
+ NEXT_ARG();
+ set_ctrlmode("fd-non-iso", *argv, &cm,
+ CAN_CTRLMODE_FD_NON_ISO);
+ } else if (matches(*argv, "presume-ack") == 0) {
+ NEXT_ARG();
+ set_ctrlmode("presume-ack", *argv, &cm,
+ CAN_CTRLMODE_PRESUME_ACK);
+#if defined(CAN_CTRLMODE_CC_LEN8_DLC)
+ } else if (matches(*argv, "cc-len8-dlc") == 0) {
+ NEXT_ARG();
+ set_ctrlmode("cc-len8-dlc", *argv, &cm,
+ CAN_CTRLMODE_CC_LEN8_DLC);
+#endif
+ } else if (matches(*argv, "restart") == 0) {
+ __u32 val = 1;
+
+ mnl_attr_put(nlh, IFLA_CAN_RESTART, sizeof(val), &val);
+ } else if (matches(*argv, "restart-ms") == 0) {
+ __u32 val;
+
+ NEXT_ARG();
+ if (get_u32(&val, *argv, 0))
+ invarg("invalid \"restart-ms\" value\n", *argv);
+
+ mnl_attr_put(nlh, IFLA_CAN_RESTART_MS, sizeof(val), &val);
+ } else if (matches(*argv, "termination") == 0) {
+ __u16 val;
+
+ NEXT_ARG();
+ if (get_u16(&val, *argv, 0))
+ invarg("invalid \"termination\" value\n",
+ *argv);
+
+ mnl_attr_put(nlh, IFLA_CAN_TERMINATION, sizeof(val), &val);
+ } else {
+ fprintf(stderr, "unknown option \"%s\"\n", *argv);
+ usage();
+ return -1;
+ }
+
+ NEXT_ARG();
+ }
+
+ if (bt.bitrate || bt.tq)
+ mnl_attr_put(nlh, IFLA_CAN_BITTIMING, sizeof(bt), &bt);
+
+ if (cm.mask)
+ mnl_attr_put(nlh, IFLA_CAN_CTRLMODE, sizeof(cm), &cm);
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct mnl_socket *nl;
+ struct nlmsghdr *nlh;
+ struct ifinfomsg *ifm;
+ int ret;
+ unsigned int seq, portid;
+ struct nlattr *linkinfo, *data;
+ const char *signatures[] = {
+ "ip", "link", "set", ""
+ };
+ char *type = NULL;
+ char *dev = NULL;
+ int i;
+
+ NEXT_ARG();
+ for (i = 0; argc > 0 && signatures[i][0];) {
+ if (matches(*argv, signatures[i]))
+ incomplete_command();
+
+ NEXT_ARG();
+ i++;
+ }
+
+ if (argc == 0)
+ incomplete_command();
+
+ nlh = mnl_nlmsg_put_header(buf);
+ nlh->nlmsg_type = RTM_NEWLINK;
+ nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+ nlh->nlmsg_seq = seq = time(NULL);
+ ifm = mnl_nlmsg_put_extra_header(nlh, sizeof(*ifm));
+ ifm->ifi_family = AF_UNSPEC;
+ ifm->ifi_change = 0;
+ ifm->ifi_flags = 0;
+
+ while (argc > 0) {
+ if (matches(*argv, "up") == 0) {
+ ifm->ifi_change |= IFF_UP;
+ ifm->ifi_flags |= IFF_UP;
+ } else if (matches(*argv, "down") == 0) {
+ ifm->ifi_change |= IFF_UP;
+ ifm->ifi_flags &= ~IFF_UP;
+ } else if (matches(*argv, "type") == 0) {
+ NEXT_ARG();
+ type = *argv;
+ NEXT_ARG();
+ break;
+ } else if (matches(*argv, "help") == 0) {
+ usage();
+ exit(EXIT_FAILURE);
+ } else {
+ if (matches(*argv, "dev") == 0)
+ NEXT_ARG();
+
+ if (dev)
+ duparg2("dev", *argv);
+
+ dev = *argv;
+ }
+
+ NEXT_ARG();
+ }
+
+ if (dev)
+ mnl_attr_put_str(nlh, IFLA_IFNAME, dev);
+
+ if (type) {
+ if (matches(type, "can")) {
+ fprintf(stderr, "unknown type \"%s\"\n", type);
+ usage();
+ exit(EXIT_FAILURE);
+ }
+
+ linkinfo = mnl_attr_nest_start(nlh, IFLA_LINKINFO);
+ mnl_attr_put_str(nlh, IFLA_INFO_KIND, "can");
+ data = mnl_attr_nest_start(nlh, IFLA_INFO_DATA);
+
+ if (iplink_set_can_parse(argc, argv, nlh))
+ return -1;
+
+ mnl_attr_nest_end(nlh, data);
+ mnl_attr_nest_end(nlh, linkinfo);
+ }
+
+ nl = mnl_socket_open(NETLINK_ROUTE);
+ if (nl == NULL) {
+ perror("mnl_socket_open");
+ exit(EXIT_FAILURE);
+ }
+
+ if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
+ perror("mnl_socket_bind");
+ exit(EXIT_FAILURE);
+ }
+
+ portid = mnl_socket_get_portid(nl);
+
+ mnl_nlmsg_fprintf(stdout, nlh, nlh->nlmsg_len,
+ sizeof(struct ifinfomsg));
+
+ if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
+ perror("mnl_socket_sendto");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ if (ret == -1) {
+ perror("mnl_socket_recvfrom");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL);
+ if (ret == -1) {
+ perror("mnl_cb_run");
+ exit(EXIT_FAILURE);
+ }
+
+ mnl_socket_close(nl);
+
+ return 0;
+}
diff --git a/libmnl/examples/rtnl/rtnl-link-dump.c b/libmnl/examples/rtnl/rtnl-link-dump.c
new file mode 100644
index 0000000..031346f
--- /dev/null
+++ b/libmnl/examples/rtnl/rtnl-link-dump.c
@@ -0,0 +1,130 @@
+/* This example is placed in the public domain. */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+#include <arpa/inet.h>
+
+#include <libmnl/libmnl.h>
+#include <linux/if.h>
+#include <linux/if_link.h>
+#include <linux/rtnetlink.h>
+
+static int data_attr_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ /* skip unsupported attribute in user-space */
+ if (mnl_attr_type_valid(attr, IFLA_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case IFLA_ADDRESS:
+ if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ case IFLA_MTU:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ case IFLA_IFNAME:
+ if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ }
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static int data_cb(const struct nlmsghdr *nlh, void *data)
+{
+ struct nlattr *tb[IFLA_MAX+1] = {};
+ struct ifinfomsg *ifm = mnl_nlmsg_get_payload(nlh);
+
+ printf("index=%d type=%d flags=%d family=%d ",
+ ifm->ifi_index, ifm->ifi_type,
+ ifm->ifi_flags, ifm->ifi_family);
+
+ if (ifm->ifi_flags & IFF_RUNNING)
+ printf("[RUNNING] ");
+ else
+ printf("[NOT RUNNING] ");
+
+ mnl_attr_parse(nlh, sizeof(*ifm), data_attr_cb, tb);
+ if (tb[IFLA_MTU]) {
+ printf("mtu=%d ", mnl_attr_get_u32(tb[IFLA_MTU]));
+ }
+ if (tb[IFLA_IFNAME]) {
+ printf("name=%s ", mnl_attr_get_str(tb[IFLA_IFNAME]));
+ }
+ if (tb[IFLA_ADDRESS]) {
+ uint8_t *hwaddr = mnl_attr_get_payload(tb[IFLA_ADDRESS]);
+ int i;
+
+ printf("hwaddr=");
+ for (i=0; i<mnl_attr_get_payload_len(tb[IFLA_ADDRESS]); i++) {
+ printf("%.2x", hwaddr[i] & 0xff);
+ if (i+1 != mnl_attr_get_payload_len(tb[IFLA_ADDRESS]))
+ printf(":");
+ }
+ }
+ printf("\n");
+ return MNL_CB_OK;
+}
+
+int main(void)
+{
+ char buf[MNL_SOCKET_DUMP_SIZE];
+ unsigned int seq, portid;
+ struct mnl_socket *nl;
+ struct nlmsghdr *nlh;
+ struct rtgenmsg *rt;
+ int ret;
+
+ nlh = mnl_nlmsg_put_header(buf);
+ nlh->nlmsg_type = RTM_GETLINK;
+ nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
+ nlh->nlmsg_seq = seq = time(NULL);
+ rt = mnl_nlmsg_put_extra_header(nlh, sizeof(struct rtgenmsg));
+ rt->rtgen_family = AF_PACKET;
+
+ nl = mnl_socket_open(NETLINK_ROUTE);
+ if (nl == NULL) {
+ perror("mnl_socket_open");
+ exit(EXIT_FAILURE);
+ }
+
+ if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
+ perror("mnl_socket_bind");
+ exit(EXIT_FAILURE);
+ }
+ portid = mnl_socket_get_portid(nl);
+
+ if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
+ perror("mnl_socket_sendto");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ while (ret > 0) {
+ ret = mnl_cb_run(buf, ret, seq, portid, data_cb, NULL);
+ if (ret <= MNL_CB_STOP)
+ break;
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ }
+ if (ret == -1) {
+ perror("error");
+ exit(EXIT_FAILURE);
+ }
+
+ mnl_socket_close(nl);
+
+ return 0;
+}
diff --git a/libmnl/examples/rtnl/rtnl-link-dump2.c b/libmnl/examples/rtnl/rtnl-link-dump2.c
new file mode 100644
index 0000000..890e51a
--- /dev/null
+++ b/libmnl/examples/rtnl/rtnl-link-dump2.c
@@ -0,0 +1,103 @@
+/* This example is placed in the public domain. */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+
+#include <libmnl/libmnl.h>
+#include <linux/if.h>
+#include <linux/if_link.h>
+#include <linux/rtnetlink.h>
+
+static int data_attr_cb(const struct nlattr *attr, void *data)
+{
+ /* skip unsupported attribute in user-space */
+ if (mnl_attr_type_valid(attr, IFLA_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(mnl_attr_get_type(attr)) {
+ case IFLA_MTU:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ printf("mtu=%d ", mnl_attr_get_u32(attr));
+ break;
+ case IFLA_IFNAME:
+ if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ printf("name=%s ", mnl_attr_get_str(attr));
+ break;
+ }
+ return MNL_CB_OK;
+}
+
+static int data_cb(const struct nlmsghdr *nlh, void *data)
+{
+ struct ifinfomsg *ifm = mnl_nlmsg_get_payload(nlh);
+
+ printf("index=%d type=%d flags=%d family=%d ",
+ ifm->ifi_index, ifm->ifi_type,
+ ifm->ifi_flags, ifm->ifi_family);
+
+ if (ifm->ifi_flags & IFF_RUNNING)
+ printf("[RUNNING] ");
+ else
+ printf("[NOT RUNNING] ");
+
+ mnl_attr_parse(nlh, sizeof(*ifm), data_attr_cb, NULL);
+ printf("\n");
+ return MNL_CB_OK;
+}
+
+int main(void)
+{
+ char buf[MNL_SOCKET_DUMP_SIZE];
+ unsigned int seq, portid;
+ struct mnl_socket *nl;
+ struct nlmsghdr *nlh;
+ struct rtgenmsg *rt;
+ int ret;
+
+ nlh = mnl_nlmsg_put_header(buf);
+ nlh->nlmsg_type = RTM_GETLINK;
+ nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
+ nlh->nlmsg_seq = seq = time(NULL);
+ rt = mnl_nlmsg_put_extra_header(nlh, sizeof(struct rtgenmsg));
+ rt->rtgen_family = AF_PACKET;
+
+ nl = mnl_socket_open(NETLINK_ROUTE);
+ if (nl == NULL) {
+ perror("mnl_socket_open");
+ exit(EXIT_FAILURE);
+ }
+
+ if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
+ perror("mnl_socket_bind");
+ exit(EXIT_FAILURE);
+ }
+ portid = mnl_socket_get_portid(nl);
+
+ if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
+ perror("mnl_socket_sendto");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ while (ret > 0) {
+ ret = mnl_cb_run(buf, ret, seq, portid, data_cb, NULL);
+ if (ret <= MNL_CB_STOP)
+ break;
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ }
+ if (ret == -1) {
+ perror("error");
+ exit(EXIT_FAILURE);
+ }
+
+ mnl_socket_close(nl);
+
+ return 0;
+}
diff --git a/libmnl/examples/rtnl/rtnl-link-dump3.c b/libmnl/examples/rtnl/rtnl-link-dump3.c
new file mode 100644
index 0000000..a381da1
--- /dev/null
+++ b/libmnl/examples/rtnl/rtnl-link-dump3.c
@@ -0,0 +1,103 @@
+/* This example is placed in the public domain. */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+
+#include <libmnl/libmnl.h>
+#include <linux/if.h>
+#include <linux/if_link.h>
+#include <linux/rtnetlink.h>
+
+static int data_cb(const struct nlmsghdr *nlh, void *data)
+{
+ struct ifinfomsg *ifm = mnl_nlmsg_get_payload(nlh);
+ struct nlattr *attr;
+
+ printf("index=%d type=%d flags=%d family=%d ",
+ ifm->ifi_index, ifm->ifi_type,
+ ifm->ifi_flags, ifm->ifi_family);
+
+ if (ifm->ifi_flags & IFF_RUNNING)
+ printf("[RUNNING] ");
+ else
+ printf("[NOT RUNNING] ");
+
+ mnl_attr_for_each(attr, nlh, sizeof(*ifm)) {
+ int type = mnl_attr_get_type(attr);
+
+ /* skip unsupported attribute in user-space */
+ if (mnl_attr_type_valid(attr, IFLA_MAX) < 0)
+ continue;
+
+ switch(type) {
+ case IFLA_MTU:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ printf("mtu=%d ", mnl_attr_get_u32(attr));
+ break;
+ case IFLA_IFNAME:
+ if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ printf("name=%s ", mnl_attr_get_str(attr));
+ break;
+ }
+ }
+ printf("\n");
+
+ return MNL_CB_OK;
+}
+
+int main(void)
+{
+ char buf[MNL_SOCKET_DUMP_SIZE];
+ unsigned int seq, portid;
+ struct mnl_socket *nl;
+ struct nlmsghdr *nlh;
+ struct rtgenmsg *rt;
+ int ret;
+
+ nlh = mnl_nlmsg_put_header(buf);
+ nlh->nlmsg_type = RTM_GETLINK;
+ nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
+ nlh->nlmsg_seq = seq = time(NULL);
+ rt = mnl_nlmsg_put_extra_header(nlh, sizeof(struct rtgenmsg));
+ rt->rtgen_family = AF_PACKET;
+
+ nl = mnl_socket_open(NETLINK_ROUTE);
+ if (nl == NULL) {
+ perror("mnl_socket_open");
+ exit(EXIT_FAILURE);
+ }
+
+ if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
+ perror("mnl_socket_bind");
+ exit(EXIT_FAILURE);
+ }
+ portid = mnl_socket_get_portid(nl);
+
+ if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
+ perror("mnl_socket_sendto");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ while (ret > 0) {
+ ret = mnl_cb_run(buf, ret, seq, portid, data_cb, NULL);
+ if (ret <= MNL_CB_STOP)
+ break;
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ }
+ if (ret == -1) {
+ perror("error");
+ exit(EXIT_FAILURE);
+ }
+
+ mnl_socket_close(nl);
+
+ return 0;
+}
diff --git a/libmnl/examples/rtnl/rtnl-link-event.c b/libmnl/examples/rtnl/rtnl-link-event.c
new file mode 100644
index 0000000..123fb88
--- /dev/null
+++ b/libmnl/examples/rtnl/rtnl-link-event.c
@@ -0,0 +1,95 @@
+/* This example is placed in the public domain. */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <libmnl/libmnl.h>
+#include <linux/if.h>
+#include <linux/if_link.h>
+#include <linux/rtnetlink.h>
+
+static int data_attr_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ /* skip unsupported attribute in user-space */
+ if (mnl_attr_type_valid(attr, IFLA_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case IFLA_MTU:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ case IFLA_IFNAME:
+ if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ }
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static int data_cb(const struct nlmsghdr *nlh, void *data)
+{
+ struct nlattr *tb[IFLA_MAX+1] = {};
+ struct ifinfomsg *ifm = mnl_nlmsg_get_payload(nlh);
+
+ printf("index=%d type=%d flags=%d family=%d ",
+ ifm->ifi_index, ifm->ifi_type,
+ ifm->ifi_flags, ifm->ifi_family);
+
+ if (ifm->ifi_flags & IFF_RUNNING)
+ printf("[RUNNING] ");
+ else
+ printf("[NOT RUNNING] ");
+
+ mnl_attr_parse(nlh, sizeof(*ifm), data_attr_cb, tb);
+ if (tb[IFLA_MTU]) {
+ printf("mtu=%d ", mnl_attr_get_u32(tb[IFLA_MTU]));
+ }
+ if (tb[IFLA_IFNAME]) {
+ printf("name=%s", mnl_attr_get_str(tb[IFLA_IFNAME]));
+ }
+ printf("\n");
+ return MNL_CB_OK;
+}
+
+int main(void)
+{
+ struct mnl_socket *nl;
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ int ret;
+
+ nl = mnl_socket_open(NETLINK_ROUTE);
+ if (nl == NULL) {
+ perror("mnl_socket_open");
+ exit(EXIT_FAILURE);
+ }
+
+ if (mnl_socket_bind(nl, RTMGRP_LINK, MNL_SOCKET_AUTOPID) < 0) {
+ perror("mnl_socket_bind");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ while (ret > 0) {
+ ret = mnl_cb_run(buf, ret, 0, 0, data_cb, NULL);
+ if (ret <= 0)
+ break;
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ }
+ if (ret == -1) {
+ perror("error");
+ exit(EXIT_FAILURE);
+ }
+
+ mnl_socket_close(nl);
+
+ return 0;
+}
diff --git a/libmnl/examples/rtnl/rtnl-link-set.c b/libmnl/examples/rtnl/rtnl-link-set.c
new file mode 100644
index 0000000..6086c37
--- /dev/null
+++ b/libmnl/examples/rtnl/rtnl-link-set.c
@@ -0,0 +1,84 @@
+/* This example is placed in the public domain. */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <time.h>
+
+#include <libmnl/libmnl.h>
+#include <linux/if.h>
+#include <linux/if_link.h>
+#include <linux/rtnetlink.h>
+
+int main(int argc, char *argv[])
+{
+ struct mnl_socket *nl;
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh;
+ struct ifinfomsg *ifm;
+ int ret;
+ unsigned int seq, portid, change = 0, flags = 0;
+
+ if (argc != 3) {
+ printf("Usage: %s [ifname] [up|down]\n", argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ if (strncasecmp(argv[2], "up", strlen("up")) == 0) {
+ change |= IFF_UP;
+ flags |= IFF_UP;
+ } else if (strncasecmp(argv[2], "down", strlen("down")) == 0) {
+ change |= IFF_UP;
+ flags &= ~IFF_UP;
+ } else {
+ fprintf(stderr, "%s is not `up' nor `down'\n", argv[2]);
+ exit(EXIT_FAILURE);
+ }
+
+ nlh = mnl_nlmsg_put_header(buf);
+ nlh->nlmsg_type = RTM_NEWLINK;
+ nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+ nlh->nlmsg_seq = seq = time(NULL);
+ ifm = mnl_nlmsg_put_extra_header(nlh, sizeof(*ifm));
+ ifm->ifi_family = AF_UNSPEC;
+ ifm->ifi_change = change;
+ ifm->ifi_flags = flags;
+
+ mnl_attr_put_str(nlh, IFLA_IFNAME, argv[1]);
+
+ nl = mnl_socket_open(NETLINK_ROUTE);
+ if (nl == NULL) {
+ perror("mnl_socket_open");
+ exit(EXIT_FAILURE);
+ }
+
+ if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
+ perror("mnl_socket_bind");
+ exit(EXIT_FAILURE);
+ }
+ portid = mnl_socket_get_portid(nl);
+
+ mnl_nlmsg_fprintf(stdout, nlh, nlh->nlmsg_len,
+ sizeof(struct ifinfomsg));
+
+ if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
+ perror("mnl_socket_sendto");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ if (ret == -1) {
+ perror("mnl_socket_recvfrom");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL);
+ if (ret == -1){
+ perror("mnl_cb_run");
+ exit(EXIT_FAILURE);
+ }
+
+ mnl_socket_close(nl);
+
+ return 0;
+}
diff --git a/libmnl/examples/rtnl/rtnl-neigh-dump.c b/libmnl/examples/rtnl/rtnl-neigh-dump.c
new file mode 100644
index 0000000..8dd5f7e
--- /dev/null
+++ b/libmnl/examples/rtnl/rtnl-neigh-dump.c
@@ -0,0 +1,158 @@
+/* This example is placed in the public domain. */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+#include <arpa/inet.h>
+
+#include <libmnl/libmnl.h>
+#include <linux/if.h>
+#include <linux/if_link.h>
+#include <linux/rtnetlink.h>
+
+static int data_attr_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ /* skip unsupported attribute in user-space */
+ if (mnl_attr_type_valid(attr, NDA_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NDA_DST:
+ case NDA_LLADDR:
+ if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ }
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static int data_cb(const struct nlmsghdr *nlh, void *data)
+{
+ struct nlattr *tb[NDA_MAX + 1] = {};
+ struct ndmsg *ndm = mnl_nlmsg_get_payload(nlh);
+
+ printf("index=%d family=%d ", ndm->ndm_ifindex, ndm->ndm_family);
+
+ mnl_attr_parse(nlh, sizeof(*ndm), data_attr_cb, tb);
+ printf("dst=");
+ if (tb[NDA_DST]) {
+ void *addr = mnl_attr_get_payload(tb[NDA_DST]);
+ char out[INET6_ADDRSTRLEN];
+
+ if (inet_ntop(ndm->ndm_family, addr, out, sizeof(out)))
+ printf("%s ", out);
+ }
+
+ mnl_attr_parse(nlh, sizeof(*ndm), data_attr_cb, tb);
+ printf("lladdr=");
+ if (tb[NDA_LLADDR]) {
+ void *addr = mnl_attr_get_payload(tb[NDA_LLADDR]);
+ unsigned char lladdr[6] = {0};
+
+ if (memcpy(&lladdr, addr, 6))
+ printf("%02x:%02x:%02x:%02x:%02x:%02x ",
+ lladdr[0], lladdr[1], lladdr[2],
+ lladdr[3], lladdr[4], lladdr[5]);
+ }
+
+ printf("state=");
+ switch(ndm->ndm_state) {
+ case NUD_INCOMPLETE:
+ printf("incomplete ");
+ break;
+ case NUD_REACHABLE:
+ printf("reachable ");
+ break;
+ case NUD_STALE:
+ printf("stale ");
+ break;
+ case NUD_DELAY:
+ printf("delay ");
+ break;
+ case NUD_PROBE:
+ printf("probe ");
+ break;
+ case NUD_FAILED:
+ printf("failed ");
+ break;
+ case NUD_NOARP:
+ printf("noarp ");
+ break;
+ case NUD_PERMANENT:
+ printf("permanent ");
+ break;
+ default:
+ printf("%d ", ndm->ndm_state);
+ break;
+ }
+
+ printf("\n");
+ return MNL_CB_OK;
+}
+
+int main(int argc, char *argv[])
+{
+ char buf[MNL_SOCKET_DUMP_SIZE];
+ unsigned int seq, portid;
+ struct mnl_socket *nl;
+ struct nlmsghdr *nlh;
+ struct ndmsg *nd;
+ int ret;
+
+ if (argc != 2) {
+ fprintf(stderr, "Usage: %s <inet|inet6>\n", argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ nlh = mnl_nlmsg_put_header(buf);
+ nlh->nlmsg_type = RTM_GETNEIGH;
+ nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
+ nlh->nlmsg_seq = seq = time(NULL);
+
+ nd = mnl_nlmsg_put_extra_header(nlh, sizeof(struct ndmsg));
+ if (strcmp(argv[1], "inet") == 0)
+ nd->ndm_family = AF_INET;
+ else if (strcmp(argv[1], "inet6") == 0)
+ nd->ndm_family = AF_INET6;
+
+ nl = mnl_socket_open(NETLINK_ROUTE);
+ if (nl == NULL) {
+ perror("mnl_socket_open");
+ exit(EXIT_FAILURE);
+ }
+
+ if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
+ perror("mnl_socket_bind");
+ exit(EXIT_FAILURE);
+ }
+ portid = mnl_socket_get_portid(nl);
+
+ if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
+ perror("mnl_socket_sendto");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ while (ret > 0) {
+ ret = mnl_cb_run(buf, ret, seq, portid, data_cb, NULL);
+ if (ret <= MNL_CB_STOP)
+ break;
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ }
+
+ if (ret == -1) {
+ perror("error");
+ exit(EXIT_FAILURE);
+ }
+
+ mnl_socket_close(nl);
+
+ return 0;
+}
diff --git a/libmnl/examples/rtnl/rtnl-route-add.c b/libmnl/examples/rtnl/rtnl-route-add.c
new file mode 100644
index 0000000..97578cd
--- /dev/null
+++ b/libmnl/examples/rtnl/rtnl-route-add.c
@@ -0,0 +1,127 @@
+/* This example is placed in the public domain. */
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <time.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <strings.h>
+#include <net/if.h>
+
+#include <libmnl/libmnl.h>
+#include <linux/if_link.h>
+#include <linux/rtnetlink.h>
+
+int main(int argc, char *argv[])
+{
+ struct mnl_socket *nl;
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh;
+ struct rtmsg *rtm;
+ uint32_t prefix, seq, portid;
+ union {
+ in_addr_t ip;
+ struct in6_addr ip6;
+ } dst;
+ union {
+ in_addr_t ip;
+ struct in6_addr ip6;
+ } gw;
+ int iface, ret, family = AF_INET;
+
+ if (argc <= 3) {
+ printf("Usage: %s iface destination cidr [gateway]\n", argv[0]);
+ printf("Example: %s eth0 10.0.1.12 32 10.0.1.11\n", argv[0]);
+ printf(" %s eth0 ffff::10.0.1.12 128 fdff::1\n", argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ iface = if_nametoindex(argv[1]);
+ if (iface == 0) {
+ perror("if_nametoindex");
+ exit(EXIT_FAILURE);
+ }
+
+ if (!inet_pton(AF_INET, argv[2], &dst)) {
+ if (!inet_pton(AF_INET6, argv[2], &dst)) {
+ perror("inet_pton");
+ exit(EXIT_FAILURE);
+ }
+ family = AF_INET6;
+ }
+
+ if (sscanf(argv[3], "%u", &prefix) == 0) {
+ perror("sscanf");
+ exit(EXIT_FAILURE);
+ }
+
+ if (argc == 5 && !inet_pton(family, argv[4], &gw)) {
+ perror("inet_pton");
+ exit(EXIT_FAILURE);
+ }
+
+ nlh = mnl_nlmsg_put_header(buf);
+ nlh->nlmsg_type = RTM_NEWROUTE;
+ nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK;
+ nlh->nlmsg_seq = seq = time(NULL);
+
+ rtm = mnl_nlmsg_put_extra_header(nlh, sizeof(struct rtmsg));
+ rtm->rtm_family = family;
+ rtm->rtm_dst_len = prefix;
+ rtm->rtm_src_len = 0;
+ rtm->rtm_tos = 0;
+ rtm->rtm_protocol = RTPROT_STATIC;
+ rtm->rtm_table = RT_TABLE_MAIN;
+ rtm->rtm_type = RTN_UNICAST;
+ /* is there any gateway? */
+ rtm->rtm_scope = (argc == 4) ? RT_SCOPE_LINK : RT_SCOPE_UNIVERSE;
+ rtm->rtm_flags = 0;
+
+ if (family == AF_INET)
+ mnl_attr_put_u32(nlh, RTA_DST, dst.ip);
+ else
+ mnl_attr_put(nlh, RTA_DST, sizeof(struct in6_addr), &dst);
+
+ mnl_attr_put_u32(nlh, RTA_OIF, iface);
+ if (argc == 5) {
+ if (family == AF_INET)
+ mnl_attr_put_u32(nlh, RTA_GATEWAY, gw.ip);
+ else {
+ mnl_attr_put(nlh, RTA_GATEWAY, sizeof(struct in6_addr),
+ &gw.ip6);
+ }
+ }
+
+ nl = mnl_socket_open(NETLINK_ROUTE);
+ if (nl == NULL) {
+ perror("mnl_socket_open");
+ exit(EXIT_FAILURE);
+ }
+
+ if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
+ perror("mnl_socket_bind");
+ exit(EXIT_FAILURE);
+ }
+ portid = mnl_socket_get_portid(nl);
+
+ if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
+ perror("mnl_socket_sendto");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ if (ret < 0) {
+ perror("mnl_socket_recvfrom");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL);
+ if (ret < 0) {
+ perror("mnl_cb_run");
+ exit(EXIT_FAILURE);
+ }
+
+ mnl_socket_close(nl);
+
+ return 0;
+}
diff --git a/libmnl/examples/rtnl/rtnl-route-dump.c b/libmnl/examples/rtnl/rtnl-route-dump.c
new file mode 100644
index 0000000..02ac6b2
--- /dev/null
+++ b/libmnl/examples/rtnl/rtnl-route-dump.c
@@ -0,0 +1,356 @@
+/* This example is placed in the public domain. */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+#include <arpa/inet.h>
+
+#include <libmnl/libmnl.h>
+#include <linux/if.h>
+#include <linux/if_link.h>
+#include <linux/rtnetlink.h>
+
+static int data_attr_cb2(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+
+ /* skip unsupported attribute in user-space */
+ if (mnl_attr_type_valid(attr, RTAX_MAX) < 0)
+ return MNL_CB_OK;
+
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+
+ tb[mnl_attr_get_type(attr)] = attr;
+ return MNL_CB_OK;
+}
+
+static void attributes_show_ipv4(struct nlattr *tb[])
+{
+ if (tb[RTA_TABLE]) {
+ printf("table=%u ", mnl_attr_get_u32(tb[RTA_TABLE]));
+ }
+ if (tb[RTA_DST]) {
+ struct in_addr *addr = mnl_attr_get_payload(tb[RTA_DST]);
+ printf("dst=%s ", inet_ntoa(*addr));
+ }
+ if (tb[RTA_SRC]) {
+ struct in_addr *addr = mnl_attr_get_payload(tb[RTA_SRC]);
+ printf("src=%s ", inet_ntoa(*addr));
+ }
+ if (tb[RTA_OIF]) {
+ printf("oif=%u ", mnl_attr_get_u32(tb[RTA_OIF]));
+ }
+ if (tb[RTA_FLOW]) {
+ printf("flow=%u ", mnl_attr_get_u32(tb[RTA_FLOW]));
+ }
+ if (tb[RTA_PREFSRC]) {
+ struct in_addr *addr = mnl_attr_get_payload(tb[RTA_PREFSRC]);
+ printf("prefsrc=%s ", inet_ntoa(*addr));
+ }
+ if (tb[RTA_GATEWAY]) {
+ struct in_addr *addr = mnl_attr_get_payload(tb[RTA_GATEWAY]);
+ printf("gw=%s ", inet_ntoa(*addr));
+ }
+ if (tb[RTA_PRIORITY]) {
+ printf("prio=%u ", mnl_attr_get_u32(tb[RTA_PRIORITY]));
+ }
+ if (tb[RTA_METRICS]) {
+ int i;
+ struct nlattr *tbx[RTAX_MAX+1] = {};
+
+ mnl_attr_parse_nested(tb[RTA_METRICS], data_attr_cb2, tbx);
+
+ for (i=0; i<RTAX_MAX; i++) {
+ if (tbx[i]) {
+ printf("metrics[%d]=%u ",
+ i, mnl_attr_get_u32(tbx[i]));
+ }
+ }
+ }
+}
+
+/* like inet_ntoa(), not reentrant */
+static const char *inet6_ntoa(struct in6_addr in6)
+{
+ static char buf[INET6_ADDRSTRLEN];
+
+ return inet_ntop(AF_INET6, &in6.s6_addr, buf, sizeof(buf));
+}
+
+static void attributes_show_ipv6(struct nlattr *tb[])
+{
+ if (tb[RTA_TABLE]) {
+ printf("table=%u ", mnl_attr_get_u32(tb[RTA_TABLE]));
+ }
+ if (tb[RTA_DST]) {
+ struct in6_addr *addr = mnl_attr_get_payload(tb[RTA_DST]);
+ printf("dst=%s ", inet6_ntoa(*addr));
+ }
+ if (tb[RTA_SRC]) {
+ struct in6_addr *addr = mnl_attr_get_payload(tb[RTA_SRC]);
+ printf("src=%s ", inet6_ntoa(*addr));
+ }
+ if (tb[RTA_OIF]) {
+ printf("oif=%u ", mnl_attr_get_u32(tb[RTA_OIF]));
+ }
+ if (tb[RTA_FLOW]) {
+ printf("flow=%u ", mnl_attr_get_u32(tb[RTA_FLOW]));
+ }
+ if (tb[RTA_PREFSRC]) {
+ struct in6_addr *addr = mnl_attr_get_payload(tb[RTA_PREFSRC]);
+ printf("prefsrc=%s ", inet6_ntoa(*addr));
+ }
+ if (tb[RTA_GATEWAY]) {
+ struct in6_addr *addr = mnl_attr_get_payload(tb[RTA_GATEWAY]);
+ printf("gw=%s ", inet6_ntoa(*addr));
+ }
+ if (tb[RTA_PRIORITY]) {
+ printf("prio=%u ", mnl_attr_get_u32(tb[RTA_PRIORITY]));
+ }
+ if (tb[RTA_METRICS]) {
+ int i;
+ struct nlattr *tbx[RTAX_MAX+1] = {};
+
+ mnl_attr_parse_nested(tb[RTA_METRICS], data_attr_cb2, tbx);
+
+ for (i=0; i<RTAX_MAX; i++) {
+ if (tbx[i]) {
+ printf("metrics[%d]=%u ",
+ i, mnl_attr_get_u32(tbx[i]));
+ }
+ }
+ }
+}
+
+static int data_ipv4_attr_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ /* skip unsupported attribute in user-space */
+ if (mnl_attr_type_valid(attr, RTA_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case RTA_TABLE:
+ case RTA_DST:
+ case RTA_SRC:
+ case RTA_OIF:
+ case RTA_FLOW:
+ case RTA_PREFSRC:
+ case RTA_GATEWAY:
+ case RTA_PRIORITY:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ case RTA_METRICS:
+ if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ }
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static int data_ipv6_attr_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ /* skip unsupported attribute in user-space */
+ if (mnl_attr_type_valid(attr, RTA_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case RTA_TABLE:
+ case RTA_OIF:
+ case RTA_FLOW:
+ case RTA_PRIORITY:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ case RTA_DST:
+ case RTA_SRC:
+ case RTA_PREFSRC:
+ case RTA_GATEWAY:
+ if (mnl_attr_validate2(attr, MNL_TYPE_BINARY,
+ sizeof(struct in6_addr)) < 0) {
+ perror("mnl_attr_validate2");
+ return MNL_CB_ERROR;
+ }
+ break;
+ case RTA_METRICS:
+ if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ }
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static int data_cb(const struct nlmsghdr *nlh, void *data)
+{
+ struct nlattr *tb[RTA_MAX+1] = {};
+ struct rtmsg *rm = mnl_nlmsg_get_payload(nlh);
+
+ /* protocol family = AF_INET | AF_INET6 */
+ printf("family=%u ", rm->rtm_family);
+
+ /* destination CIDR, eg. 24 or 32 for IPv4 */
+ printf("dst_len=%u ", rm->rtm_dst_len);
+
+ /* source CIDR */
+ printf("src_len=%u ", rm->rtm_src_len);
+
+ /* type of service (TOS), eg. 0 */
+ printf("tos=%u ", rm->rtm_tos);
+
+ /* table id:
+ * RT_TABLE_UNSPEC = 0
+ *
+ * ... user defined values ...
+ *
+ * RT_TABLE_COMPAT = 252
+ * RT_TABLE_DEFAULT = 253
+ * RT_TABLE_MAIN = 254
+ * RT_TABLE_LOCAL = 255
+ * RT_TABLE_MAX = 0xFFFFFFFF
+ *
+ * Synonimous attribute: RTA_TABLE.
+ */
+ printf("table=%u ", rm->rtm_table);
+
+ /* type:
+ * RTN_UNSPEC = 0
+ * RTN_UNICAST = 1
+ * RTN_LOCAL = 2
+ * RTN_BROADCAST = 3
+ * RTN_ANYCAST = 4
+ * RTN_MULTICAST = 5
+ * RTN_BLACKHOLE = 6
+ * RTN_UNREACHABLE = 7
+ * RTN_PROHIBIT = 8
+ * RTN_THROW = 9
+ * RTN_NAT = 10
+ * RTN_XRESOLVE = 11
+ * __RTN_MAX = 12
+ */
+ printf("type=%u ", rm->rtm_type);
+
+ /* scope:
+ * RT_SCOPE_UNIVERSE = 0 : everywhere in the universe
+ *
+ * ... user defined values ...
+ *
+ * RT_SCOPE_SITE = 200
+ * RT_SCOPE_LINK = 253 : destination attached to link
+ * RT_SCOPE_HOST = 254 : local address
+ * RT_SCOPE_NOWHERE = 255 : not existing destination
+ */
+ printf("scope=%u ", rm->rtm_scope);
+
+ /* protocol:
+ * RTPROT_UNSPEC = 0
+ * RTPROT_REDIRECT = 1
+ * RTPROT_KERNEL = 2 : route installed by kernel
+ * RTPROT_BOOT = 3 : route installed during boot
+ * RTPROT_STATIC = 4 : route installed by administrator
+ *
+ * Values >= RTPROT_STATIC are not interpreted by kernel, they are
+ * just user-defined.
+ */
+ printf("proto=%u ", rm->rtm_protocol);
+
+ /* flags:
+ * RTM_F_NOTIFY = 0x100: notify user of route change
+ * RTM_F_CLONED = 0x200: this route is cloned
+ * RTM_F_EQUALIZE = 0x400: Multipath equalizer: NI
+ * RTM_F_PREFIX = 0x800: Prefix addresses
+ */
+ printf("flags=%x ", rm->rtm_flags);
+
+ switch(rm->rtm_family) {
+ case AF_INET:
+ mnl_attr_parse(nlh, sizeof(*rm), data_ipv4_attr_cb, tb);
+ attributes_show_ipv4(tb);
+ break;
+ case AF_INET6:
+ mnl_attr_parse(nlh, sizeof(*rm), data_ipv6_attr_cb, tb);
+ attributes_show_ipv6(tb);
+ break;
+ }
+
+ printf("\n");
+ return MNL_CB_OK;
+}
+
+int main(int argc, char *argv[])
+{
+ char buf[MNL_SOCKET_DUMP_SIZE];
+ unsigned int seq, portid;
+ struct mnl_socket *nl;
+ struct nlmsghdr *nlh;
+ struct rtmsg *rtm;
+ int ret;
+
+ if (argc != 2) {
+ fprintf(stderr, "Usage: %s <inet|inet6>\n", argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ nlh = mnl_nlmsg_put_header(buf);
+ nlh->nlmsg_type = RTM_GETROUTE;
+ nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
+ nlh->nlmsg_seq = seq = time(NULL);
+ rtm = mnl_nlmsg_put_extra_header(nlh, sizeof(struct rtmsg));
+
+ if (strcmp(argv[1], "inet") == 0)
+ rtm->rtm_family = AF_INET;
+ else if (strcmp(argv[1], "inet6") == 0)
+ rtm->rtm_family = AF_INET6;
+
+ nl = mnl_socket_open(NETLINK_ROUTE);
+ if (nl == NULL) {
+ perror("mnl_socket_open");
+ exit(EXIT_FAILURE);
+ }
+
+ if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
+ perror("mnl_socket_bind");
+ exit(EXIT_FAILURE);
+ }
+ portid = mnl_socket_get_portid(nl);
+
+ if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
+ perror("mnl_socket_sendto");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ while (ret > 0) {
+ ret = mnl_cb_run(buf, ret, seq, portid, data_cb, NULL);
+ if (ret <= MNL_CB_STOP)
+ break;
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ }
+ if (ret == -1) {
+ perror("error");
+ exit(EXIT_FAILURE);
+ }
+
+ mnl_socket_close(nl);
+
+ return 0;
+}
diff --git a/libmnl/examples/rtnl/rtnl-route-event.c b/libmnl/examples/rtnl/rtnl-route-event.c
new file mode 100644
index 0000000..6cad9f0
--- /dev/null
+++ b/libmnl/examples/rtnl/rtnl-route-event.c
@@ -0,0 +1,341 @@
+/* This example is placed in the public domain. */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+#include <arpa/inet.h>
+
+#include <libmnl/libmnl.h>
+#include <linux/if.h>
+#include <linux/if_link.h>
+#include <linux/rtnetlink.h>
+
+static int data_attr_cb2(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+
+ /* skip unsupported attribute in user-space */
+ if (mnl_attr_type_valid(attr, RTAX_MAX) < 0)
+ return MNL_CB_OK;
+
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+
+ tb[mnl_attr_get_type(attr)] = attr;
+ return MNL_CB_OK;
+}
+
+static void attributes_show_ipv4(struct nlattr *tb[])
+{
+ if (tb[RTA_TABLE]) {
+ printf("table=%u ", mnl_attr_get_u32(tb[RTA_TABLE]));
+ }
+ if (tb[RTA_DST]) {
+ struct in_addr *addr = mnl_attr_get_payload(tb[RTA_DST]);
+ printf("dst=%s ", inet_ntoa(*addr));
+ }
+ if (tb[RTA_SRC]) {
+ struct in_addr *addr = mnl_attr_get_payload(tb[RTA_SRC]);
+ printf("src=%s ", inet_ntoa(*addr));
+ }
+ if (tb[RTA_OIF]) {
+ printf("oif=%u ", mnl_attr_get_u32(tb[RTA_OIF]));
+ }
+ if (tb[RTA_FLOW]) {
+ printf("flow=%u ", mnl_attr_get_u32(tb[RTA_FLOW]));
+ }
+ if (tb[RTA_PREFSRC]) {
+ struct in_addr *addr = mnl_attr_get_payload(tb[RTA_PREFSRC]);
+ printf("prefsrc=%s ", inet_ntoa(*addr));
+ }
+ if (tb[RTA_GATEWAY]) {
+ struct in_addr *addr = mnl_attr_get_payload(tb[RTA_GATEWAY]);
+ printf("gw=%s ", inet_ntoa(*addr));
+ }
+ if (tb[RTA_PRIORITY]) {
+ printf("prio=%u ", mnl_attr_get_u32(tb[RTA_PRIORITY]));
+ }
+ if (tb[RTA_METRICS]) {
+ int i;
+ struct nlattr *tbx[RTAX_MAX+1] = {};
+
+ mnl_attr_parse_nested(tb[RTA_METRICS], data_attr_cb2, tbx);
+
+ for (i=0; i<RTAX_MAX; i++) {
+ if (tbx[i]) {
+ printf("metrics[%d]=%u ",
+ i, mnl_attr_get_u32(tbx[i]));
+ }
+ }
+ }
+}
+
+/* like inet_ntoa(), not reentrant */
+static const char *inet6_ntoa(struct in6_addr in6)
+{
+ static char buf[INET6_ADDRSTRLEN];
+
+ return inet_ntop(AF_INET6, &in6.s6_addr, buf, sizeof(buf));
+}
+
+static void attributes_show_ipv6(struct nlattr *tb[])
+{
+ if (tb[RTA_TABLE]) {
+ printf("table=%u ", mnl_attr_get_u32(tb[RTA_TABLE]));
+ }
+ if (tb[RTA_DST]) {
+ struct in6_addr *addr = mnl_attr_get_payload(tb[RTA_DST]);
+ printf("dst=%s ", inet6_ntoa(*addr));
+ }
+ if (tb[RTA_SRC]) {
+ struct in6_addr *addr = mnl_attr_get_payload(tb[RTA_SRC]);
+ printf("src=%s ", inet6_ntoa(*addr));
+ }
+ if (tb[RTA_OIF]) {
+ printf("oif=%u ", mnl_attr_get_u32(tb[RTA_OIF]));
+ }
+ if (tb[RTA_FLOW]) {
+ printf("flow=%u ", mnl_attr_get_u32(tb[RTA_FLOW]));
+ }
+ if (tb[RTA_PREFSRC]) {
+ struct in6_addr *addr = mnl_attr_get_payload(tb[RTA_PREFSRC]);
+ printf("prefsrc=%s ", inet6_ntoa(*addr));
+ }
+ if (tb[RTA_GATEWAY]) {
+ struct in6_addr *addr = mnl_attr_get_payload(tb[RTA_GATEWAY]);
+ printf("gw=%s ", inet6_ntoa(*addr));
+ }
+ if (tb[RTA_PRIORITY]) {
+ printf("prio=%u ", mnl_attr_get_u32(tb[RTA_PRIORITY]));
+ }
+ if (tb[RTA_METRICS]) {
+ int i;
+ struct nlattr *tbx[RTAX_MAX+1] = {};
+
+ mnl_attr_parse_nested(tb[RTA_METRICS], data_attr_cb2, tbx);
+
+ for (i=0; i<RTAX_MAX; i++) {
+ if (tbx[i]) {
+ printf("metrics[%d]=%u ",
+ i, mnl_attr_get_u32(tbx[i]));
+ }
+ }
+ }
+}
+
+static int data_ipv4_attr_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ /* skip unsupported attribute in user-space */
+ if (mnl_attr_type_valid(attr, RTA_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case RTA_TABLE:
+ case RTA_DST:
+ case RTA_SRC:
+ case RTA_OIF:
+ case RTA_FLOW:
+ case RTA_PREFSRC:
+ case RTA_GATEWAY:
+ case RTA_PRIORITY:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ case RTA_METRICS:
+ if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ }
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static int data_ipv6_attr_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ /* skip unsupported attribute in user-space */
+ if (mnl_attr_type_valid(attr, RTA_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case RTA_TABLE:
+ case RTA_OIF:
+ case RTA_FLOW:
+ case RTA_PRIORITY:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ case RTA_DST:
+ case RTA_SRC:
+ case RTA_PREFSRC:
+ case RTA_GATEWAY:
+ if (mnl_attr_validate2(attr, MNL_TYPE_BINARY,
+ sizeof(struct in6_addr)) < 0) {
+ perror("mnl_attr_validate2");
+ return MNL_CB_ERROR;
+ }
+ break;
+ case RTA_METRICS:
+ if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ }
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static int data_cb(const struct nlmsghdr *nlh, void *data)
+{
+ struct nlattr *tb[RTA_MAX+1] = {};
+ struct rtmsg *rm = mnl_nlmsg_get_payload(nlh);
+
+ switch(nlh->nlmsg_type) {
+ case RTM_NEWROUTE:
+ printf("[NEW] ");
+ break;
+ case RTM_DELROUTE:
+ printf("[DEL] ");
+ break;
+ }
+
+ /* protocol family = AF_INET | AF_INET6 */
+ printf("family=%u ", rm->rtm_family);
+
+ /* destination CIDR, eg. 24 or 32 for IPv4 */
+ printf("dst_len=%u ", rm->rtm_dst_len);
+
+ /* source CIDR */
+ printf("src_len=%u ", rm->rtm_src_len);
+
+ /* type of service (TOS), eg. 0 */
+ printf("tos=%u ", rm->rtm_tos);
+
+ /* table id:
+ * RT_TABLE_UNSPEC = 0
+ *
+ * ... user defined values ...
+ *
+ * RT_TABLE_COMPAT = 252
+ * RT_TABLE_DEFAULT = 253
+ * RT_TABLE_MAIN = 254
+ * RT_TABLE_LOCAL = 255
+ * RT_TABLE_MAX = 0xFFFFFFFF
+ *
+ * Synonimous attribute: RTA_TABLE.
+ */
+ printf("table=%u ", rm->rtm_table);
+
+ /* type:
+ * RTN_UNSPEC = 0
+ * RTN_UNICAST = 1
+ * RTN_LOCAL = 2
+ * RTN_BROADCAST = 3
+ * RTN_ANYCAST = 4
+ * RTN_MULTICAST = 5
+ * RTN_BLACKHOLE = 6
+ * RTN_UNREACHABLE = 7
+ * RTN_PROHIBIT = 8
+ * RTN_THROW = 9
+ * RTN_NAT = 10
+ * RTN_XRESOLVE = 11
+ * __RTN_MAX = 12
+ */
+ printf("type=%u ", rm->rtm_type);
+
+ /* scope:
+ * RT_SCOPE_UNIVERSE = 0 : everywhere in the universe
+ *
+ * ... user defined values ...
+ *
+ * RT_SCOPE_SITE = 200
+ * RT_SCOPE_LINK = 253 : destination attached to link
+ * RT_SCOPE_HOST = 254 : local address
+ * RT_SCOPE_NOWHERE = 255 : not existing destination
+ */
+ printf("scope=%u ", rm->rtm_scope);
+
+ /* protocol:
+ * RTPROT_UNSPEC = 0
+ * RTPROT_REDIRECT = 1
+ * RTPROT_KERNEL = 2 : route installed by kernel
+ * RTPROT_BOOT = 3 : route installed during boot
+ * RTPROT_STATIC = 4 : route installed by administrator
+ *
+ * Values >= RTPROT_STATIC are not interpreted by kernel, they are
+ * just user-defined.
+ */
+ printf("proto=%u ", rm->rtm_protocol);
+
+ /* flags:
+ * RTM_F_NOTIFY = 0x100: notify user of route change
+ * RTM_F_CLONED = 0x200: this route is cloned
+ * RTM_F_EQUALIZE = 0x400: Multipath equalizer: NI
+ * RTM_F_PREFIX = 0x800: Prefix addresses
+ */
+ printf("flags=%x ", rm->rtm_flags);
+
+ switch(rm->rtm_family) {
+ case AF_INET:
+ mnl_attr_parse(nlh, sizeof(*rm), data_ipv4_attr_cb, tb);
+ attributes_show_ipv4(tb);
+ break;
+ case AF_INET6:
+ mnl_attr_parse(nlh, sizeof(*rm), data_ipv6_attr_cb, tb);
+ attributes_show_ipv6(tb);
+ break;
+ }
+
+ printf("\n");
+ return MNL_CB_OK;
+}
+
+int main(int argc, char *argv[])
+{
+ struct mnl_socket *nl;
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ int ret;
+
+ nl = mnl_socket_open(NETLINK_ROUTE);
+ if (nl == NULL) {
+ perror("mnl_socket_open");
+ exit(EXIT_FAILURE);
+ }
+
+ if (mnl_socket_bind(nl, RTMGRP_IPV4_ROUTE | RTMGRP_IPV6_ROUTE,
+ MNL_SOCKET_AUTOPID) < 0) {
+ perror("mnl_socket_bind");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ while (ret > 0) {
+ ret = mnl_cb_run(buf, ret, 0, 0, data_cb, NULL);
+ if (ret <= MNL_CB_STOP)
+ break;
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ }
+ if (ret == -1) {
+ perror("error");
+ exit(EXIT_FAILURE);
+ }
+
+ mnl_socket_close(nl);
+
+ return 0;
+}
diff --git a/libmnl/include/Makefile.am b/libmnl/include/Makefile.am
new file mode 100644
index 0000000..bc92c42
--- /dev/null
+++ b/libmnl/include/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = libmnl linux
diff --git a/libmnl/include/libmnl/Makefile.am b/libmnl/include/libmnl/Makefile.am
new file mode 100644
index 0000000..b03f68a
--- /dev/null
+++ b/libmnl/include/libmnl/Makefile.am
@@ -0,0 +1 @@
+pkginclude_HEADERS = libmnl.h
diff --git a/libmnl/include/libmnl/libmnl.h b/libmnl/include/libmnl/libmnl.h
new file mode 100644
index 0000000..4bd0b92
--- /dev/null
+++ b/libmnl/include/libmnl/libmnl.h
@@ -0,0 +1,202 @@
+#ifndef _LIBMNL_H_
+#define _LIBMNL_H_
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <sys/socket.h> /* for sa_family_t */
+#include <linux/netlink.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Netlink socket API
+ */
+
+#define MNL_SOCKET_AUTOPID 0
+#define MNL_SOCKET_BUFFER_SIZE (sysconf(_SC_PAGESIZE) < 8192L ? sysconf(_SC_PAGESIZE) : 8192L)
+#define MNL_SOCKET_DUMP_SIZE 32768
+
+struct mnl_socket;
+
+extern struct mnl_socket *mnl_socket_open(int bus);
+extern struct mnl_socket *mnl_socket_open2(int bus, int flags);
+extern struct mnl_socket *mnl_socket_fdopen(int fd);
+extern int mnl_socket_bind(struct mnl_socket *nl, unsigned int groups, pid_t pid);
+extern int mnl_socket_close(struct mnl_socket *nl);
+extern int mnl_socket_get_fd(const struct mnl_socket *nl);
+extern unsigned int mnl_socket_get_portid(const struct mnl_socket *nl);
+extern ssize_t mnl_socket_sendto(const struct mnl_socket *nl, const void *req, size_t siz);
+extern ssize_t mnl_socket_recvfrom(const struct mnl_socket *nl, void *buf, size_t siz);
+extern int mnl_socket_setsockopt(const struct mnl_socket *nl, int type, void *buf, socklen_t len);
+extern int mnl_socket_getsockopt(const struct mnl_socket *nl, int type, void *buf, socklen_t *len);
+
+/*
+ * Netlink message API
+ */
+
+#define MNL_ALIGNTO 4
+#define MNL_ALIGN(len) (((len)+MNL_ALIGNTO-1) & ~(MNL_ALIGNTO-1))
+#define MNL_NLMSG_HDRLEN MNL_ALIGN(sizeof(struct nlmsghdr))
+
+extern size_t mnl_nlmsg_size(size_t len);
+extern size_t mnl_nlmsg_get_payload_len(const struct nlmsghdr *nlh);
+
+/* Netlink message header builder */
+extern struct nlmsghdr *mnl_nlmsg_put_header(void *buf);
+extern void *mnl_nlmsg_put_extra_header(struct nlmsghdr *nlh, size_t size);
+
+/* Netlink message iterators */
+extern bool mnl_nlmsg_ok(const struct nlmsghdr *nlh, int len);
+extern struct nlmsghdr *mnl_nlmsg_next(const struct nlmsghdr *nlh, int *len);
+
+/* Netlink sequence tracking */
+extern bool mnl_nlmsg_seq_ok(const struct nlmsghdr *nlh, unsigned int seq);
+
+/* Netlink portID checking */
+extern bool mnl_nlmsg_portid_ok(const struct nlmsghdr *nlh, unsigned int portid);
+
+/* Netlink message getters */
+extern void *mnl_nlmsg_get_payload(const struct nlmsghdr *nlh);
+extern void *mnl_nlmsg_get_payload_offset(const struct nlmsghdr *nlh, size_t offset);
+extern void *mnl_nlmsg_get_payload_tail(const struct nlmsghdr *nlh);
+
+/* Netlink message printer */
+extern void mnl_nlmsg_fprintf(FILE *fd, const void *data, size_t datalen, size_t extra_header_size);
+
+/* Message batch helpers */
+struct mnl_nlmsg_batch;
+extern struct mnl_nlmsg_batch *mnl_nlmsg_batch_start(void *buf, size_t bufsiz);
+extern bool mnl_nlmsg_batch_next(struct mnl_nlmsg_batch *b);
+extern void mnl_nlmsg_batch_stop(struct mnl_nlmsg_batch *b);
+extern size_t mnl_nlmsg_batch_size(struct mnl_nlmsg_batch *b);
+extern void mnl_nlmsg_batch_reset(struct mnl_nlmsg_batch *b);
+extern void *mnl_nlmsg_batch_head(struct mnl_nlmsg_batch *b);
+extern void *mnl_nlmsg_batch_current(struct mnl_nlmsg_batch *b);
+extern bool mnl_nlmsg_batch_is_empty(struct mnl_nlmsg_batch *b);
+
+/*
+ * Netlink attributes API
+ */
+#define MNL_ATTR_HDRLEN MNL_ALIGN(sizeof(struct nlattr))
+
+/* TLV attribute getters */
+extern uint16_t mnl_attr_get_type(const struct nlattr *attr);
+extern uint16_t mnl_attr_get_len(const struct nlattr *attr);
+extern uint16_t mnl_attr_get_payload_len(const struct nlattr *attr);
+extern void *mnl_attr_get_payload(const struct nlattr *attr);
+extern uint8_t mnl_attr_get_u8(const struct nlattr *attr);
+extern uint16_t mnl_attr_get_u16(const struct nlattr *attr);
+extern uint32_t mnl_attr_get_u32(const struct nlattr *attr);
+extern uint64_t mnl_attr_get_u64(const struct nlattr *attr);
+extern const char *mnl_attr_get_str(const struct nlattr *attr);
+
+/* TLV attribute putters */
+extern void mnl_attr_put(struct nlmsghdr *nlh, uint16_t type, size_t len, const void *data);
+extern void mnl_attr_put_u8(struct nlmsghdr *nlh, uint16_t type, uint8_t data);
+extern void mnl_attr_put_u16(struct nlmsghdr *nlh, uint16_t type, uint16_t data);
+extern void mnl_attr_put_u32(struct nlmsghdr *nlh, uint16_t type, uint32_t data);
+extern void mnl_attr_put_u64(struct nlmsghdr *nlh, uint16_t type, uint64_t data);
+extern void mnl_attr_put_str(struct nlmsghdr *nlh, uint16_t type, const char *data);
+extern void mnl_attr_put_strz(struct nlmsghdr *nlh, uint16_t type, const char *data);
+
+/* TLV attribute putters with buffer boundary checkings */
+extern bool mnl_attr_put_check(struct nlmsghdr *nlh, size_t buflen, uint16_t type, size_t len, const void *data);
+extern bool mnl_attr_put_u8_check(struct nlmsghdr *nlh, size_t buflen, uint16_t type, uint8_t data);
+extern bool mnl_attr_put_u16_check(struct nlmsghdr *nlh, size_t buflen, uint16_t type, uint16_t data);
+extern bool mnl_attr_put_u32_check(struct nlmsghdr *nlh, size_t buflen, uint16_t type, uint32_t data);
+extern bool mnl_attr_put_u64_check(struct nlmsghdr *nlh, size_t buflen, uint16_t type, uint64_t data);
+extern bool mnl_attr_put_str_check(struct nlmsghdr *nlh, size_t buflen, uint16_t type, const char *data);
+extern bool mnl_attr_put_strz_check(struct nlmsghdr *nlh, size_t buflen, uint16_t type, const char *data);
+
+/* TLV attribute nesting */
+extern struct nlattr *mnl_attr_nest_start(struct nlmsghdr *nlh, uint16_t type);
+extern struct nlattr *mnl_attr_nest_start_check(struct nlmsghdr *nlh, size_t buflen, uint16_t type);
+extern void mnl_attr_nest_end(struct nlmsghdr *nlh, struct nlattr *start);
+extern void mnl_attr_nest_cancel(struct nlmsghdr *nlh, struct nlattr *start);
+
+/* TLV validation */
+extern int mnl_attr_type_valid(const struct nlattr *attr, uint16_t maxtype);
+
+enum mnl_attr_data_type {
+ MNL_TYPE_UNSPEC,
+ MNL_TYPE_U8,
+ MNL_TYPE_U16,
+ MNL_TYPE_U32,
+ MNL_TYPE_U64,
+ MNL_TYPE_STRING,
+ MNL_TYPE_FLAG,
+ MNL_TYPE_MSECS,
+ MNL_TYPE_NESTED,
+ MNL_TYPE_NESTED_COMPAT,
+ MNL_TYPE_NUL_STRING,
+ MNL_TYPE_BINARY,
+ MNL_TYPE_MAX,
+};
+
+extern int mnl_attr_validate(const struct nlattr *attr, enum mnl_attr_data_type type);
+extern int mnl_attr_validate2(const struct nlattr *attr, enum mnl_attr_data_type type, size_t len);
+
+/* TLV iterators */
+extern bool mnl_attr_ok(const struct nlattr *attr, int len);
+extern struct nlattr *mnl_attr_next(const struct nlattr *attr);
+
+#define mnl_attr_for_each(attr, nlh, offset) \
+ for ((attr) = mnl_nlmsg_get_payload_offset((nlh), (offset)); \
+ mnl_attr_ok((attr), (char *)mnl_nlmsg_get_payload_tail(nlh) - (char *)(attr)); \
+ (attr) = mnl_attr_next(attr))
+
+#define mnl_attr_for_each_nested(attr, nest) \
+ for ((attr) = mnl_attr_get_payload(nest); \
+ mnl_attr_ok((attr), (char *)mnl_attr_get_payload(nest) + mnl_attr_get_payload_len(nest) - (char *)(attr)); \
+ (attr) = mnl_attr_next(attr))
+
+#define mnl_attr_for_each_payload(payload, payload_size) \
+ for ((attr) = (payload); \
+ mnl_attr_ok((attr), (char *)(payload) + payload_size - (char *)(attr)); \
+ (attr) = mnl_attr_next(attr))
+
+/* TLV callback-based attribute parsers */
+typedef int (*mnl_attr_cb_t)(const struct nlattr *attr, void *data);
+
+extern int mnl_attr_parse(const struct nlmsghdr *nlh, unsigned int offset, mnl_attr_cb_t cb, void *data);
+extern int mnl_attr_parse_nested(const struct nlattr *attr, mnl_attr_cb_t cb, void *data);
+extern int mnl_attr_parse_payload(const void *payload, size_t payload_len, mnl_attr_cb_t cb, void *data);
+
+/*
+ * callback API
+ */
+#define MNL_CB_ERROR -1
+#define MNL_CB_STOP 0
+#define MNL_CB_OK 1
+
+typedef int (*mnl_cb_t)(const struct nlmsghdr *nlh, void *data);
+
+extern int mnl_cb_run(const void *buf, size_t numbytes, unsigned int seq,
+ unsigned int portid, mnl_cb_t cb_data, void *data);
+
+extern int mnl_cb_run2(const void *buf, size_t numbytes, unsigned int seq,
+ unsigned int portid, mnl_cb_t cb_data, void *data,
+ const mnl_cb_t *cb_ctl_array,
+ unsigned int cb_ctl_array_len);
+
+/*
+ * other declarations
+ */
+
+#ifndef SOL_NETLINK
+#define SOL_NETLINK 270
+#endif
+
+#ifndef MNL_ARRAY_SIZE
+#define MNL_ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0]))
+#endif
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif
diff --git a/libmnl/include/linux/Makefile.am b/libmnl/include/linux/Makefile.am
new file mode 100644
index 0000000..ee0993d
--- /dev/null
+++ b/libmnl/include/linux/Makefile.am
@@ -0,0 +1,2 @@
+SUBDIRS = can netfilter
+noinst_HEADERS = can.h netlink.h socket.h
diff --git a/libmnl/include/linux/can.h b/libmnl/include/linux/can.h
new file mode 100644
index 0000000..2c6a3ee
--- /dev/null
+++ b/libmnl/include/linux/can.h
@@ -0,0 +1,298 @@
+/* SPDX-License-Identifier: ((GPL-2.0-only WITH Linux-syscall-note) OR BSD-3-Clause) */
+/*
+ * linux/can.h
+ *
+ * Definitions for CAN network layer (socket addr / CAN frame / CAN filter)
+ *
+ * Authors: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
+ * Urs Thuermann <urs.thuermann@volkswagen.de>
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef _UAPI_CAN_H
+#define _UAPI_CAN_H
+
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/stddef.h> /* for offsetof */
+
+/* controller area network (CAN) kernel definitions */
+
+/* special address description flags for the CAN_ID */
+#define CAN_EFF_FLAG 0x80000000U /* EFF/SFF is set in the MSB */
+#define CAN_RTR_FLAG 0x40000000U /* remote transmission request */
+#define CAN_ERR_FLAG 0x20000000U /* error message frame */
+
+/* valid bits in CAN ID for frame formats */
+#define CAN_SFF_MASK 0x000007FFU /* standard frame format (SFF) */
+#define CAN_EFF_MASK 0x1FFFFFFFU /* extended frame format (EFF) */
+#define CAN_ERR_MASK 0x1FFFFFFFU /* omit EFF, RTR, ERR flags */
+#define CANXL_PRIO_MASK CAN_SFF_MASK /* 11 bit priority mask */
+
+/*
+ * Controller Area Network Identifier structure
+ *
+ * bit 0-28 : CAN identifier (11/29 bit)
+ * bit 29 : error message frame flag (0 = data frame, 1 = error message)
+ * bit 30 : remote transmission request flag (1 = rtr frame)
+ * bit 31 : frame format flag (0 = standard 11 bit, 1 = extended 29 bit)
+ */
+typedef __u32 canid_t;
+
+#define CAN_SFF_ID_BITS 11
+#define CAN_EFF_ID_BITS 29
+#define CANXL_PRIO_BITS CAN_SFF_ID_BITS
+
+/*
+ * Controller Area Network Error Message Frame Mask structure
+ *
+ * bit 0-28 : error class mask (see include/uapi/linux/can/error.h)
+ * bit 29-31 : set to zero
+ */
+typedef __u32 can_err_mask_t;
+
+/* CAN payload length and DLC definitions according to ISO 11898-1 */
+#define CAN_MAX_DLC 8
+#define CAN_MAX_RAW_DLC 15
+#define CAN_MAX_DLEN 8
+
+/* CAN FD payload length and DLC definitions according to ISO 11898-7 */
+#define CANFD_MAX_DLC 15
+#define CANFD_MAX_DLEN 64
+
+/*
+ * CAN XL payload length and DLC definitions according to ISO 11898-1
+ * CAN XL DLC ranges from 0 .. 2047 => data length from 1 .. 2048 byte
+ */
+#define CANXL_MIN_DLC 0
+#define CANXL_MAX_DLC 2047
+#define CANXL_MAX_DLC_MASK 0x07FF
+#define CANXL_MIN_DLEN 1
+#define CANXL_MAX_DLEN 2048
+
+/**
+ * struct can_frame - Classical CAN frame structure (aka CAN 2.0B)
+ * @can_id: CAN ID of the frame and CAN_*_FLAG flags, see canid_t definition
+ * @len: CAN frame payload length in byte (0 .. 8)
+ * @can_dlc: deprecated name for CAN frame payload length in byte (0 .. 8)
+ * @__pad: padding
+ * @__res0: reserved / padding
+ * @len8_dlc: optional DLC value (9 .. 15) at 8 byte payload length
+ * len8_dlc contains values from 9 .. 15 when the payload length is
+ * 8 bytes but the DLC value (see ISO 11898-1) is greater then 8.
+ * CAN_CTRLMODE_CC_LEN8_DLC flag has to be enabled in CAN driver.
+ * @data: CAN frame payload (up to 8 byte)
+ */
+struct can_frame {
+ canid_t can_id; /* 32 bit CAN_ID + EFF/RTR/ERR flags */
+ union {
+ /* CAN frame payload length in byte (0 .. CAN_MAX_DLEN)
+ * was previously named can_dlc so we need to carry that
+ * name for legacy support
+ */
+ __u8 len;
+ __u8 can_dlc; /* deprecated */
+ } __attribute__((packed)); /* disable padding added in some ABIs */
+ __u8 __pad; /* padding */
+ __u8 __res0; /* reserved / padding */
+ __u8 len8_dlc; /* optional DLC for 8 byte payload length (9 .. 15) */
+ __u8 data[CAN_MAX_DLEN] __attribute__((aligned(8)));
+};
+
+/*
+ * defined bits for canfd_frame.flags
+ *
+ * The use of struct canfd_frame implies the FD Frame (FDF) bit to
+ * be set in the CAN frame bitstream on the wire. The FDF bit switch turns
+ * the CAN controllers bitstream processor into the CAN FD mode which creates
+ * two new options within the CAN FD frame specification:
+ *
+ * Bit Rate Switch - to indicate a second bitrate is/was used for the payload
+ * Error State Indicator - represents the error state of the transmitting node
+ *
+ * As the CANFD_ESI bit is internally generated by the transmitting CAN
+ * controller only the CANFD_BRS bit is relevant for real CAN controllers when
+ * building a CAN FD frame for transmission. Setting the CANFD_ESI bit can make
+ * sense for virtual CAN interfaces to test applications with echoed frames.
+ *
+ * The struct can_frame and struct canfd_frame intentionally share the same
+ * layout to be able to write CAN frame content into a CAN FD frame structure.
+ * When this is done the former differentiation via CAN_MTU / CANFD_MTU gets
+ * lost. CANFD_FDF allows programmers to mark CAN FD frames in the case of
+ * using struct canfd_frame for mixed CAN / CAN FD content (dual use).
+ * Since the introduction of CAN XL the CANFD_FDF flag is set in all CAN FD
+ * frame structures provided by the CAN subsystem of the Linux kernel.
+ */
+#define CANFD_BRS 0x01 /* bit rate switch (second bitrate for payload data) */
+#define CANFD_ESI 0x02 /* error state indicator of the transmitting node */
+#define CANFD_FDF 0x04 /* mark CAN FD for dual use of struct canfd_frame */
+
+/**
+ * struct canfd_frame - CAN flexible data rate frame structure
+ * @can_id: CAN ID of the frame and CAN_*_FLAG flags, see canid_t definition
+ * @len: frame payload length in byte (0 .. CANFD_MAX_DLEN)
+ * @flags: additional flags for CAN FD
+ * @__res0: reserved / padding
+ * @__res1: reserved / padding
+ * @data: CAN FD frame payload (up to CANFD_MAX_DLEN byte)
+ */
+struct canfd_frame {
+ canid_t can_id; /* 32 bit CAN_ID + EFF/RTR/ERR flags */
+ __u8 len; /* frame payload length in byte */
+ __u8 flags; /* additional flags for CAN FD */
+ __u8 __res0; /* reserved / padding */
+ __u8 __res1; /* reserved / padding */
+ __u8 data[CANFD_MAX_DLEN] __attribute__((aligned(8)));
+};
+
+/*
+ * defined bits for canxl_frame.flags
+ *
+ * The canxl_frame.flags element contains two bits CANXL_XLF and CANXL_SEC
+ * and shares the relative position of the struct can[fd]_frame.len element.
+ * The CANXL_XLF bit ALWAYS needs to be set to indicate a valid CAN XL frame.
+ * As a side effect setting this bit intentionally breaks the length checks
+ * for Classical CAN and CAN FD frames.
+ *
+ * Undefined bits in canxl_frame.flags are reserved and shall be set to zero.
+ */
+#define CANXL_XLF 0x80 /* mandatory CAN XL frame flag (must always be set!) */
+#define CANXL_SEC 0x01 /* Simple Extended Content (security/segmentation) */
+
+/**
+ * struct canxl_frame - CAN with e'X'tended frame 'L'ength frame structure
+ * @prio: 11 bit arbitration priority with zero'ed CAN_*_FLAG flags
+ * @flags: additional flags for CAN XL
+ * @sdt: SDU (service data unit) type
+ * @len: frame payload length in byte (CANXL_MIN_DLEN .. CANXL_MAX_DLEN)
+ * @af: acceptance field
+ * @data: CAN XL frame payload (CANXL_MIN_DLEN .. CANXL_MAX_DLEN byte)
+ *
+ * @prio shares the same position as @can_id from struct can[fd]_frame.
+ */
+struct canxl_frame {
+ canid_t prio; /* 11 bit priority for arbitration (canid_t) */
+ __u8 flags; /* additional flags for CAN XL */
+ __u8 sdt; /* SDU (service data unit) type */
+ __u16 len; /* frame payload length in byte */
+ __u32 af; /* acceptance field */
+ __u8 data[CANXL_MAX_DLEN];
+};
+
+#define CAN_MTU (sizeof(struct can_frame))
+#define CANFD_MTU (sizeof(struct canfd_frame))
+#define CANXL_MTU (sizeof(struct canxl_frame))
+#define CANXL_HDR_SIZE (offsetof(struct canxl_frame, data))
+#define CANXL_MIN_MTU (CANXL_HDR_SIZE + 64)
+#define CANXL_MAX_MTU CANXL_MTU
+
+/* particular protocols of the protocol family PF_CAN */
+#define CAN_RAW 1 /* RAW sockets */
+#define CAN_BCM 2 /* Broadcast Manager */
+#define CAN_TP16 3 /* VAG Transport Protocol v1.6 */
+#define CAN_TP20 4 /* VAG Transport Protocol v2.0 */
+#define CAN_MCNET 5 /* Bosch MCNet */
+#define CAN_ISOTP 6 /* ISO 15765-2 Transport Protocol */
+#define CAN_J1939 7 /* SAE J1939 */
+#define CAN_NPROTO 8
+
+#define SOL_CAN_BASE 100
+
+/*
+ * This typedef was introduced in Linux v3.1-rc2
+ * (commit 6602a4b net: Make userland include of netlink.h more sane)
+ * in <linux/socket.h>. It must be duplicated here to make the CAN
+ * headers self-contained.
+ */
+typedef unsigned short __kernel_sa_family_t;
+
+/**
+ * struct sockaddr_can - the sockaddr structure for CAN sockets
+ * @can_family: address family number AF_CAN.
+ * @can_ifindex: CAN network interface index.
+ * @can_addr: protocol specific address information
+ */
+struct sockaddr_can {
+ __kernel_sa_family_t can_family;
+ int can_ifindex;
+ union {
+ /* transport protocol class address information (e.g. ISOTP) */
+ struct { canid_t rx_id, tx_id; } tp;
+
+ /* J1939 address information */
+ struct {
+ /* 8 byte name when using dynamic addressing */
+ __u64 name;
+
+ /* pgn:
+ * 8 bit: PS in PDU2 case, else 0
+ * 8 bit: PF
+ * 1 bit: DP
+ * 1 bit: reserved
+ */
+ __u32 pgn;
+
+ /* 1 byte address */
+ __u8 addr;
+ } j1939;
+
+ /* reserved for future CAN protocols address information */
+ } can_addr;
+};
+
+/**
+ * struct can_filter - CAN ID based filter in can_register().
+ * @can_id: relevant bits of CAN ID which are not masked out.
+ * @can_mask: CAN mask (see description)
+ *
+ * Description:
+ * A filter matches, when
+ *
+ * <received_can_id> & mask == can_id & mask
+ *
+ * The filter can be inverted (CAN_INV_FILTER bit set in can_id) or it can
+ * filter for error message frames (CAN_ERR_FLAG bit set in mask).
+ */
+struct can_filter {
+ canid_t can_id;
+ canid_t can_mask;
+};
+
+#define CAN_INV_FILTER 0x20000000U /* to be set in can_filter.can_id */
+#define CAN_RAW_FILTER_MAX 512 /* maximum number of can_filter set via setsockopt() */
+
+#endif /* !_UAPI_CAN_H */
diff --git a/libmnl/include/linux/can/Makefile.am b/libmnl/include/linux/can/Makefile.am
new file mode 100644
index 0000000..2d02887
--- /dev/null
+++ b/libmnl/include/linux/can/Makefile.am
@@ -0,0 +1 @@
+noinst_HEADERS = netlink.h
diff --git a/libmnl/include/linux/can/netlink.h b/libmnl/include/linux/can/netlink.h
new file mode 100644
index 0000000..02ec32d
--- /dev/null
+++ b/libmnl/include/linux/can/netlink.h
@@ -0,0 +1,185 @@
+/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */
+/*
+ * linux/can/netlink.h
+ *
+ * Definitions for the CAN netlink interface
+ *
+ * Copyright (c) 2009 Wolfgang Grandegger <wg@grandegger.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the version 2 of the GNU General Public License
+ * as published by the Free Software Foundation
+ *
+ * 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.
+ */
+
+#ifndef _UAPI_CAN_NETLINK_H
+#define _UAPI_CAN_NETLINK_H
+
+#include <linux/types.h>
+
+/*
+ * CAN bit-timing parameters
+ *
+ * For further information, please read chapter "8 BIT TIMING
+ * REQUIREMENTS" of the "Bosch CAN Specification version 2.0"
+ * at http://www.semiconductors.bosch.de/pdf/can2spec.pdf.
+ */
+struct can_bittiming {
+ __u32 bitrate; /* Bit-rate in bits/second */
+ __u32 sample_point; /* Sample point in one-tenth of a percent */
+ __u32 tq; /* Time quanta (TQ) in nanoseconds */
+ __u32 prop_seg; /* Propagation segment in TQs */
+ __u32 phase_seg1; /* Phase buffer segment 1 in TQs */
+ __u32 phase_seg2; /* Phase buffer segment 2 in TQs */
+ __u32 sjw; /* Synchronisation jump width in TQs */
+ __u32 brp; /* Bit-rate prescaler */
+};
+
+/*
+ * CAN hardware-dependent bit-timing constant
+ *
+ * Used for calculating and checking bit-timing parameters
+ */
+struct can_bittiming_const {
+ char name[16]; /* Name of the CAN controller hardware */
+ __u32 tseg1_min; /* Time segment 1 = prop_seg + phase_seg1 */
+ __u32 tseg1_max;
+ __u32 tseg2_min; /* Time segment 2 = phase_seg2 */
+ __u32 tseg2_max;
+ __u32 sjw_max; /* Synchronisation jump width */
+ __u32 brp_min; /* Bit-rate prescaler */
+ __u32 brp_max;
+ __u32 brp_inc;
+};
+
+/*
+ * CAN clock parameters
+ */
+struct can_clock {
+ __u32 freq; /* CAN system clock frequency in Hz */
+};
+
+/*
+ * CAN operational and error states
+ */
+enum can_state {
+ CAN_STATE_ERROR_ACTIVE = 0, /* RX/TX error count < 96 */
+ CAN_STATE_ERROR_WARNING, /* RX/TX error count < 128 */
+ CAN_STATE_ERROR_PASSIVE, /* RX/TX error count < 256 */
+ CAN_STATE_BUS_OFF, /* RX/TX error count >= 256 */
+ CAN_STATE_STOPPED, /* Device is stopped */
+ CAN_STATE_SLEEPING, /* Device is sleeping */
+ CAN_STATE_MAX
+};
+
+/*
+ * CAN bus error counters
+ */
+struct can_berr_counter {
+ __u16 txerr;
+ __u16 rxerr;
+};
+
+/*
+ * CAN controller mode
+ */
+struct can_ctrlmode {
+ __u32 mask;
+ __u32 flags;
+};
+
+#define CAN_CTRLMODE_LOOPBACK 0x01 /* Loopback mode */
+#define CAN_CTRLMODE_LISTENONLY 0x02 /* Listen-only mode */
+#define CAN_CTRLMODE_3_SAMPLES 0x04 /* Triple sampling mode */
+#define CAN_CTRLMODE_ONE_SHOT 0x08 /* One-Shot mode */
+#define CAN_CTRLMODE_BERR_REPORTING 0x10 /* Bus-error reporting */
+#define CAN_CTRLMODE_FD 0x20 /* CAN FD mode */
+#define CAN_CTRLMODE_PRESUME_ACK 0x40 /* Ignore missing CAN ACKs */
+#define CAN_CTRLMODE_FD_NON_ISO 0x80 /* CAN FD in non-ISO mode */
+#define CAN_CTRLMODE_CC_LEN8_DLC 0x100 /* Classic CAN DLC option */
+#define CAN_CTRLMODE_TDC_AUTO 0x200 /* CAN transiver automatically calculates TDCV */
+#define CAN_CTRLMODE_TDC_MANUAL 0x400 /* TDCV is manually set up by user */
+
+/*
+ * CAN device statistics
+ */
+struct can_device_stats {
+ __u32 bus_error; /* Bus errors */
+ __u32 error_warning; /* Changes to error warning state */
+ __u32 error_passive; /* Changes to error passive state */
+ __u32 bus_off; /* Changes to bus off state */
+ __u32 arbitration_lost; /* Arbitration lost errors */
+ __u32 restarts; /* CAN controller re-starts */
+};
+
+/*
+ * CAN netlink interface
+ */
+enum {
+ IFLA_CAN_UNSPEC,
+ IFLA_CAN_BITTIMING,
+ IFLA_CAN_BITTIMING_CONST,
+ IFLA_CAN_CLOCK,
+ IFLA_CAN_STATE,
+ IFLA_CAN_CTRLMODE,
+ IFLA_CAN_RESTART_MS,
+ IFLA_CAN_RESTART,
+ IFLA_CAN_BERR_COUNTER,
+ IFLA_CAN_DATA_BITTIMING,
+ IFLA_CAN_DATA_BITTIMING_CONST,
+ IFLA_CAN_TERMINATION,
+ IFLA_CAN_TERMINATION_CONST,
+ IFLA_CAN_BITRATE_CONST,
+ IFLA_CAN_DATA_BITRATE_CONST,
+ IFLA_CAN_BITRATE_MAX,
+ IFLA_CAN_TDC,
+ IFLA_CAN_CTRLMODE_EXT,
+
+ /* add new constants above here */
+ __IFLA_CAN_MAX,
+ IFLA_CAN_MAX = __IFLA_CAN_MAX - 1
+};
+
+/*
+ * CAN FD Transmitter Delay Compensation (TDC)
+ *
+ * Please refer to struct can_tdc_const and can_tdc in
+ * include/linux/can/bittiming.h for further details.
+ */
+enum {
+ IFLA_CAN_TDC_UNSPEC,
+ IFLA_CAN_TDC_TDCV_MIN, /* u32 */
+ IFLA_CAN_TDC_TDCV_MAX, /* u32 */
+ IFLA_CAN_TDC_TDCO_MIN, /* u32 */
+ IFLA_CAN_TDC_TDCO_MAX, /* u32 */
+ IFLA_CAN_TDC_TDCF_MIN, /* u32 */
+ IFLA_CAN_TDC_TDCF_MAX, /* u32 */
+ IFLA_CAN_TDC_TDCV, /* u32 */
+ IFLA_CAN_TDC_TDCO, /* u32 */
+ IFLA_CAN_TDC_TDCF, /* u32 */
+
+ /* add new constants above here */
+ __IFLA_CAN_TDC,
+ IFLA_CAN_TDC_MAX = __IFLA_CAN_TDC - 1
+};
+
+/*
+ * IFLA_CAN_CTRLMODE_EXT nest: controller mode extended parameters
+ */
+enum {
+ IFLA_CAN_CTRLMODE_UNSPEC,
+ IFLA_CAN_CTRLMODE_SUPPORTED, /* u32 */
+
+ /* add new constants above here */
+ __IFLA_CAN_CTRLMODE,
+ IFLA_CAN_CTRLMODE_MAX = __IFLA_CAN_CTRLMODE - 1
+};
+
+/* u16 termination range: 1..65535 Ohms */
+#define CAN_TERMINATION_DISABLED 0
+
+#endif /* !_UAPI_CAN_NETLINK_H */
diff --git a/libmnl/include/linux/netfilter/Makefile.am b/libmnl/include/linux/netfilter/Makefile.am
new file mode 100644
index 0000000..64a975e
--- /dev/null
+++ b/libmnl/include/linux/netfilter/Makefile.am
@@ -0,0 +1 @@
+noinst_HEADERS = nfnetlink_conntrack.h
diff --git a/libmnl/include/linux/netfilter/nfnetlink_conntrack.h b/libmnl/include/linux/netfilter/nfnetlink_conntrack.h
new file mode 100644
index 0000000..08fabc6
--- /dev/null
+++ b/libmnl/include/linux/netfilter/nfnetlink_conntrack.h
@@ -0,0 +1,252 @@
+#ifndef _IPCONNTRACK_NETLINK_H
+#define _IPCONNTRACK_NETLINK_H
+#include <linux/netfilter/nfnetlink.h>
+
+enum cntl_msg_types {
+ IPCTNL_MSG_CT_NEW,
+ IPCTNL_MSG_CT_GET,
+ IPCTNL_MSG_CT_DELETE,
+ IPCTNL_MSG_CT_GET_CTRZERO,
+ IPCTNL_MSG_CT_GET_STATS_CPU,
+ IPCTNL_MSG_CT_GET_STATS,
+ IPCTNL_MSG_CT_GET_DYING,
+ IPCTNL_MSG_CT_GET_UNCONFIRMED,
+
+ IPCTNL_MSG_MAX
+};
+
+enum ctnl_exp_msg_types {
+ IPCTNL_MSG_EXP_NEW,
+ IPCTNL_MSG_EXP_GET,
+ IPCTNL_MSG_EXP_DELETE,
+ IPCTNL_MSG_EXP_GET_STATS_CPU,
+
+ IPCTNL_MSG_EXP_MAX
+};
+
+
+enum ctattr_type {
+ CTA_UNSPEC,
+ CTA_TUPLE_ORIG,
+ CTA_TUPLE_REPLY,
+ CTA_STATUS,
+ CTA_PROTOINFO,
+ CTA_HELP,
+ CTA_NAT_SRC,
+#define CTA_NAT CTA_NAT_SRC /* backwards compatibility */
+ CTA_TIMEOUT,
+ CTA_MARK,
+ CTA_COUNTERS_ORIG,
+ CTA_COUNTERS_REPLY,
+ CTA_USE,
+ CTA_ID,
+ CTA_NAT_DST,
+ CTA_TUPLE_MASTER,
+ CTA_NAT_SEQ_ADJ_ORIG,
+ CTA_NAT_SEQ_ADJ_REPLY,
+ CTA_SECMARK, /* obsolete */
+ CTA_ZONE,
+ CTA_SECCTX,
+ CTA_TIMESTAMP,
+ CTA_MARK_MASK,
+ CTA_LABELS,
+ CTA_LABELS_MASK,
+ __CTA_MAX
+};
+#define CTA_MAX (__CTA_MAX - 1)
+
+enum ctattr_tuple {
+ CTA_TUPLE_UNSPEC,
+ CTA_TUPLE_IP,
+ CTA_TUPLE_PROTO,
+ __CTA_TUPLE_MAX
+};
+#define CTA_TUPLE_MAX (__CTA_TUPLE_MAX - 1)
+
+enum ctattr_ip {
+ CTA_IP_UNSPEC,
+ CTA_IP_V4_SRC,
+ CTA_IP_V4_DST,
+ CTA_IP_V6_SRC,
+ CTA_IP_V6_DST,
+ __CTA_IP_MAX
+};
+#define CTA_IP_MAX (__CTA_IP_MAX - 1)
+
+enum ctattr_l4proto {
+ CTA_PROTO_UNSPEC,
+ CTA_PROTO_NUM,
+ CTA_PROTO_SRC_PORT,
+ CTA_PROTO_DST_PORT,
+ CTA_PROTO_ICMP_ID,
+ CTA_PROTO_ICMP_TYPE,
+ CTA_PROTO_ICMP_CODE,
+ CTA_PROTO_ICMPV6_ID,
+ CTA_PROTO_ICMPV6_TYPE,
+ CTA_PROTO_ICMPV6_CODE,
+ __CTA_PROTO_MAX
+};
+#define CTA_PROTO_MAX (__CTA_PROTO_MAX - 1)
+
+enum ctattr_protoinfo {
+ CTA_PROTOINFO_UNSPEC,
+ CTA_PROTOINFO_TCP,
+ CTA_PROTOINFO_DCCP,
+ CTA_PROTOINFO_SCTP,
+ __CTA_PROTOINFO_MAX
+};
+#define CTA_PROTOINFO_MAX (__CTA_PROTOINFO_MAX - 1)
+
+enum ctattr_protoinfo_tcp {
+ CTA_PROTOINFO_TCP_UNSPEC,
+ CTA_PROTOINFO_TCP_STATE,
+ CTA_PROTOINFO_TCP_WSCALE_ORIGINAL,
+ CTA_PROTOINFO_TCP_WSCALE_REPLY,
+ CTA_PROTOINFO_TCP_FLAGS_ORIGINAL,
+ CTA_PROTOINFO_TCP_FLAGS_REPLY,
+ __CTA_PROTOINFO_TCP_MAX
+};
+#define CTA_PROTOINFO_TCP_MAX (__CTA_PROTOINFO_TCP_MAX - 1)
+
+enum ctattr_protoinfo_dccp {
+ CTA_PROTOINFO_DCCP_UNSPEC,
+ CTA_PROTOINFO_DCCP_STATE,
+ CTA_PROTOINFO_DCCP_ROLE,
+ CTA_PROTOINFO_DCCP_HANDSHAKE_SEQ,
+ __CTA_PROTOINFO_DCCP_MAX,
+};
+#define CTA_PROTOINFO_DCCP_MAX (__CTA_PROTOINFO_DCCP_MAX - 1)
+
+enum ctattr_protoinfo_sctp {
+ CTA_PROTOINFO_SCTP_UNSPEC,
+ CTA_PROTOINFO_SCTP_STATE,
+ CTA_PROTOINFO_SCTP_VTAG_ORIGINAL,
+ CTA_PROTOINFO_SCTP_VTAG_REPLY,
+ __CTA_PROTOINFO_SCTP_MAX
+};
+#define CTA_PROTOINFO_SCTP_MAX (__CTA_PROTOINFO_SCTP_MAX - 1)
+
+enum ctattr_counters {
+ CTA_COUNTERS_UNSPEC,
+ CTA_COUNTERS_PACKETS, /* 64bit counters */
+ CTA_COUNTERS_BYTES, /* 64bit counters */
+ CTA_COUNTERS32_PACKETS, /* old 32bit counters, unused */
+ CTA_COUNTERS32_BYTES, /* old 32bit counters, unused */
+ __CTA_COUNTERS_MAX
+};
+#define CTA_COUNTERS_MAX (__CTA_COUNTERS_MAX - 1)
+
+enum ctattr_tstamp {
+ CTA_TIMESTAMP_UNSPEC,
+ CTA_TIMESTAMP_START,
+ CTA_TIMESTAMP_STOP,
+ __CTA_TIMESTAMP_MAX
+};
+#define CTA_TIMESTAMP_MAX (__CTA_TIMESTAMP_MAX - 1)
+
+enum ctattr_nat {
+ CTA_NAT_UNSPEC,
+ CTA_NAT_V4_MINIP,
+#define CTA_NAT_MINIP CTA_NAT_V4_MINIP
+ CTA_NAT_V4_MAXIP,
+#define CTA_NAT_MAXIP CTA_NAT_V4_MAXIP
+ CTA_NAT_PROTO,
+ CTA_NAT_V6_MINIP,
+ CTA_NAT_V6_MAXIP,
+ __CTA_NAT_MAX
+};
+#define CTA_NAT_MAX (__CTA_NAT_MAX - 1)
+
+enum ctattr_protonat {
+ CTA_PROTONAT_UNSPEC,
+ CTA_PROTONAT_PORT_MIN,
+ CTA_PROTONAT_PORT_MAX,
+ __CTA_PROTONAT_MAX
+};
+#define CTA_PROTONAT_MAX (__CTA_PROTONAT_MAX - 1)
+
+enum ctattr_natseq {
+ CTA_NAT_SEQ_UNSPEC,
+ CTA_NAT_SEQ_CORRECTION_POS,
+ CTA_NAT_SEQ_OFFSET_BEFORE,
+ CTA_NAT_SEQ_OFFSET_AFTER,
+ __CTA_NAT_SEQ_MAX
+};
+#define CTA_NAT_SEQ_MAX (__CTA_NAT_SEQ_MAX - 1)
+
+enum ctattr_expect {
+ CTA_EXPECT_UNSPEC,
+ CTA_EXPECT_MASTER,
+ CTA_EXPECT_TUPLE,
+ CTA_EXPECT_MASK,
+ CTA_EXPECT_TIMEOUT,
+ CTA_EXPECT_ID,
+ CTA_EXPECT_HELP_NAME,
+ CTA_EXPECT_ZONE,
+ CTA_EXPECT_FLAGS,
+ CTA_EXPECT_CLASS,
+ CTA_EXPECT_NAT,
+ CTA_EXPECT_FN,
+ __CTA_EXPECT_MAX
+};
+#define CTA_EXPECT_MAX (__CTA_EXPECT_MAX - 1)
+
+enum ctattr_expect_nat {
+ CTA_EXPECT_NAT_UNSPEC,
+ CTA_EXPECT_NAT_DIR,
+ CTA_EXPECT_NAT_TUPLE,
+ __CTA_EXPECT_NAT_MAX
+};
+#define CTA_EXPECT_NAT_MAX (__CTA_EXPECT_NAT_MAX - 1)
+
+enum ctattr_help {
+ CTA_HELP_UNSPEC,
+ CTA_HELP_NAME,
+ CTA_HELP_INFO,
+ __CTA_HELP_MAX
+};
+#define CTA_HELP_MAX (__CTA_HELP_MAX - 1)
+
+enum ctattr_secctx {
+ CTA_SECCTX_UNSPEC,
+ CTA_SECCTX_NAME,
+ __CTA_SECCTX_MAX
+};
+#define CTA_SECCTX_MAX (__CTA_SECCTX_MAX - 1)
+
+enum ctattr_stats_cpu {
+ CTA_STATS_UNSPEC,
+ CTA_STATS_SEARCHED,
+ CTA_STATS_FOUND,
+ CTA_STATS_NEW,
+ CTA_STATS_INVALID,
+ CTA_STATS_IGNORE,
+ CTA_STATS_DELETE,
+ CTA_STATS_DELETE_LIST,
+ CTA_STATS_INSERT,
+ CTA_STATS_INSERT_FAILED,
+ CTA_STATS_DROP,
+ CTA_STATS_EARLY_DROP,
+ CTA_STATS_ERROR,
+ CTA_STATS_SEARCH_RESTART,
+ __CTA_STATS_MAX,
+};
+#define CTA_STATS_MAX (__CTA_STATS_MAX - 1)
+
+enum ctattr_stats_global {
+ CTA_STATS_GLOBAL_UNSPEC,
+ CTA_STATS_GLOBAL_ENTRIES,
+ __CTA_STATS_GLOBAL_MAX,
+};
+#define CTA_STATS_GLOBAL_MAX (__CTA_STATS_GLOBAL_MAX - 1)
+
+enum ctattr_expect_stats {
+ CTA_STATS_EXP_UNSPEC,
+ CTA_STATS_EXP_NEW,
+ CTA_STATS_EXP_CREATE,
+ CTA_STATS_EXP_DELETE,
+ __CTA_STATS_EXP_MAX,
+};
+#define CTA_STATS_EXP_MAX (__CTA_STATS_EXP_MAX - 1)
+
+#endif /* _IPCONNTRACK_NETLINK_H */
diff --git a/libmnl/include/linux/netlink.h b/libmnl/include/linux/netlink.h
new file mode 100644
index 0000000..ced0e1a
--- /dev/null
+++ b/libmnl/include/linux/netlink.h
@@ -0,0 +1,153 @@
+#ifndef __LINUX_NETLINK_H
+#define __LINUX_NETLINK_H
+
+#include <linux/socket.h> /* for __kernel_sa_family_t */
+#include <linux/types.h>
+
+#define NETLINK_ROUTE 0 /* Routing/device hook */
+#define NETLINK_UNUSED 1 /* Unused number */
+#define NETLINK_USERSOCK 2 /* Reserved for user mode socket protocols */
+#define NETLINK_FIREWALL 3 /* Unused number, formerly ip_queue */
+#define NETLINK_SOCK_DIAG 4 /* socket monitoring */
+#define NETLINK_NFLOG 5 /* netfilter/iptables ULOG */
+#define NETLINK_XFRM 6 /* ipsec */
+#define NETLINK_SELINUX 7 /* SELinux event notifications */
+#define NETLINK_ISCSI 8 /* Open-iSCSI */
+#define NETLINK_AUDIT 9 /* auditing */
+#define NETLINK_FIB_LOOKUP 10
+#define NETLINK_CONNECTOR 11
+#define NETLINK_NETFILTER 12 /* netfilter subsystem */
+#define NETLINK_IP6_FW 13
+#define NETLINK_DNRTMSG 14 /* DECnet routing messages */
+#define NETLINK_KOBJECT_UEVENT 15 /* Kernel messages to userspace */
+#define NETLINK_GENERIC 16
+/* leave room for NETLINK_DM (DM Events) */
+#define NETLINK_SCSITRANSPORT 18 /* SCSI Transports */
+#define NETLINK_ECRYPTFS 19
+#define NETLINK_RDMA 20
+#define NETLINK_CRYPTO 21 /* Crypto layer */
+
+#define NETLINK_INET_DIAG NETLINK_SOCK_DIAG
+
+#define MAX_LINKS 32
+
+struct sockaddr_nl {
+ __kernel_sa_family_t nl_family; /* AF_NETLINK */
+ unsigned short nl_pad; /* zero */
+ __u32 nl_pid; /* port ID */
+ __u32 nl_groups; /* multicast groups mask */
+};
+
+struct nlmsghdr {
+ __u32 nlmsg_len; /* Length of message including header */
+ __u16 nlmsg_type; /* Message content */
+ __u16 nlmsg_flags; /* Additional flags */
+ __u32 nlmsg_seq; /* Sequence number */
+ __u32 nlmsg_pid; /* Sending process port ID */
+};
+
+/* Flags values */
+
+#define NLM_F_REQUEST 1 /* It is request message. */
+#define NLM_F_MULTI 2 /* Multipart message, terminated by NLMSG_DONE */
+#define NLM_F_ACK 4 /* Reply with ack, with zero or error code */
+#define NLM_F_ECHO 8 /* Echo this request */
+#define NLM_F_DUMP_INTR 16 /* Dump was inconsistent due to sequence change */
+
+/* Modifiers to GET request */
+#define NLM_F_ROOT 0x100 /* specify tree root */
+#define NLM_F_MATCH 0x200 /* return all matching */
+#define NLM_F_ATOMIC 0x400 /* atomic GET */
+#define NLM_F_DUMP (NLM_F_ROOT|NLM_F_MATCH)
+
+/* Modifiers to NEW request */
+#define NLM_F_REPLACE 0x100 /* Override existing */
+#define NLM_F_EXCL 0x200 /* Do not touch, if it exists */
+#define NLM_F_CREATE 0x400 /* Create, if it does not exist */
+#define NLM_F_APPEND 0x800 /* Add to end of list */
+
+/*
+ 4.4BSD ADD NLM_F_CREATE|NLM_F_EXCL
+ 4.4BSD CHANGE NLM_F_REPLACE
+
+ True CHANGE NLM_F_CREATE|NLM_F_REPLACE
+ Append NLM_F_CREATE
+ Check NLM_F_EXCL
+ */
+
+#define NLMSG_ALIGNTO 4U
+#define NLMSG_ALIGN(len) ( ((len)+NLMSG_ALIGNTO-1) & ~(NLMSG_ALIGNTO-1) )
+#define NLMSG_HDRLEN ((int) NLMSG_ALIGN(sizeof(struct nlmsghdr)))
+#define NLMSG_LENGTH(len) ((len)+NLMSG_ALIGN(NLMSG_HDRLEN))
+#define NLMSG_SPACE(len) NLMSG_ALIGN(NLMSG_LENGTH(len))
+#define NLMSG_DATA(nlh) ((void*)(((char*)nlh) + NLMSG_LENGTH(0)))
+#define NLMSG_NEXT(nlh,len) ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), \
+ (struct nlmsghdr*)(((char*)(nlh)) + NLMSG_ALIGN((nlh)->nlmsg_len)))
+#define NLMSG_OK(nlh,len) ((len) >= (int)sizeof(struct nlmsghdr) && \
+ (nlh)->nlmsg_len >= sizeof(struct nlmsghdr) && \
+ (nlh)->nlmsg_len <= (len))
+#define NLMSG_PAYLOAD(nlh,len) ((nlh)->nlmsg_len - NLMSG_SPACE((len)))
+
+#define NLMSG_NOOP 0x1 /* Nothing. */
+#define NLMSG_ERROR 0x2 /* Error */
+#define NLMSG_DONE 0x3 /* End of a dump */
+#define NLMSG_OVERRUN 0x4 /* Data lost */
+
+#define NLMSG_MIN_TYPE 0x10 /* < 0x10: reserved control messages */
+
+struct nlmsgerr {
+ int error;
+ struct nlmsghdr msg;
+};
+
+#define NETLINK_ADD_MEMBERSHIP 1
+#define NETLINK_DROP_MEMBERSHIP 2
+#define NETLINK_PKTINFO 3
+#define NETLINK_BROADCAST_ERROR 4
+#define NETLINK_NO_ENOBUFS 5
+
+struct nl_pktinfo {
+ __u32 group;
+};
+
+#define NET_MAJOR 36 /* Major 36 is reserved for networking */
+
+enum {
+ NETLINK_UNCONNECTED = 0,
+ NETLINK_CONNECTED,
+};
+
+/*
+ * <------- NLA_HDRLEN ------> <-- NLA_ALIGN(payload)-->
+ * +---------------------+- - -+- - - - - - - - - -+- - -+
+ * | Header | Pad | Payload | Pad |
+ * | (struct nlattr) | ing | | ing |
+ * +---------------------+- - -+- - - - - - - - - -+- - -+
+ * <-------------- nlattr->nla_len -------------->
+ */
+
+struct nlattr {
+ __u16 nla_len;
+ __u16 nla_type;
+};
+
+/*
+ * nla_type (16 bits)
+ * +---+---+-------------------------------+
+ * | N | O | Attribute Type |
+ * +---+---+-------------------------------+
+ * N := Carries nested attributes
+ * O := Payload stored in network byte order
+ *
+ * Note: The N and O flag are mutually exclusive.
+ */
+#define NLA_F_NESTED (1 << 15)
+#define NLA_F_NET_BYTEORDER (1 << 14)
+#define NLA_TYPE_MASK ~(NLA_F_NESTED | NLA_F_NET_BYTEORDER)
+
+#define NLA_ALIGNTO 4
+#define NLA_ALIGN(len) (((len) + NLA_ALIGNTO - 1) & ~(NLA_ALIGNTO - 1))
+#define NLA_HDRLEN ((int) NLA_ALIGN(sizeof(struct nlattr)))
+
+
+#endif /* __LINUX_NETLINK_H */
diff --git a/libmnl/include/linux/socket.h b/libmnl/include/linux/socket.h
new file mode 100644
index 0000000..8c1e501
--- /dev/null
+++ b/libmnl/include/linux/socket.h
@@ -0,0 +1,21 @@
+#ifndef _LINUX_SOCKET_H
+#define _LINUX_SOCKET_H
+
+/*
+ * Desired design of maximum size and alignment (see RFC2553)
+ */
+#define _K_SS_MAXSIZE 128 /* Implementation specific max size */
+#define _K_SS_ALIGNSIZE (__alignof__ (struct sockaddr *))
+ /* Implementation specific desired alignment */
+
+typedef unsigned short __kernel_sa_family_t;
+
+struct __kernel_sockaddr_storage {
+ __kernel_sa_family_t ss_family; /* address family */
+ /* Following field(s) are implementation specific */
+ char __data[_K_SS_MAXSIZE - sizeof(unsigned short)];
+ /* space to achieve desired size, */
+ /* _SS_MAXSIZE value minus size of ss_family */
+} __attribute__ ((aligned(_K_SS_ALIGNSIZE))); /* force desired alignment */
+
+#endif /* _LINUX_SOCKET_H */
diff --git a/libmnl/libmnl.pc.in b/libmnl/libmnl.pc.in
new file mode 100644
index 0000000..8a24315
--- /dev/null
+++ b/libmnl/libmnl.pc.in
@@ -0,0 +1,15 @@
+# libmnl pkg-config file
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libmnl
+Description: Minimalistic Netlink communication library
+URL: http://netfilter.org/projects/libmnl/
+Version: @VERSION@
+Requires:
+Conflicts:
+Libs: -L${libdir} -lmnl
+Cflags: -I${includedir}
diff --git a/libmnl/m4/.gitignore b/libmnl/m4/.gitignore
new file mode 100644
index 0000000..64d9bbc
--- /dev/null
+++ b/libmnl/m4/.gitignore
@@ -0,0 +1,2 @@
+/libtool.m4
+/lt*.m4
diff --git a/libmnl/m4/gcc4_visibility.m4 b/libmnl/m4/gcc4_visibility.m4
new file mode 100644
index 0000000..214d3f3
--- /dev/null
+++ b/libmnl/m4/gcc4_visibility.m4
@@ -0,0 +1,21 @@
+
+# GCC 4.x -fvisibility=hidden
+
+AC_DEFUN([CHECK_GCC_FVISIBILITY], [
+ AC_LANG_PUSH([C])
+ saved_CFLAGS="$CFLAGS"
+ CFLAGS="$saved_CFLAGS -fvisibility=hidden"
+ AC_CACHE_CHECK([whether compiler accepts -fvisibility=hidden],
+ [ac_cv_fvisibility_hidden], AC_COMPILE_IFELSE(
+ [AC_LANG_SOURCE()],
+ [ac_cv_fvisibility_hidden=yes],
+ [ac_cv_fvisibility_hidden=no]
+ ))
+ if test "$ac_cv_fvisibility_hidden" = "yes"; then
+ AC_DEFINE([HAVE_VISIBILITY_HIDDEN], [1],
+ [True if compiler supports -fvisibility=hidden])
+ AC_SUBST([GCC_FVISIBILITY_HIDDEN], [-fvisibility=hidden])
+ fi
+ CFLAGS="$saved_CFLAGS"
+ AC_LANG_POP([C])
+])
diff --git a/libmnl/src/Makefile.am b/libmnl/src/Makefile.am
new file mode 100644
index 0000000..9aab516
--- /dev/null
+++ b/libmnl/src/Makefile.am
@@ -0,0 +1,5 @@
+include $(top_srcdir)/Make_global.am
+lib_LTLIBRARIES = libmnl.la
+
+libmnl_la_LDFLAGS = -Wl,--version-script=$(srcdir)/libmnl.map -version-info $(LIBVERSION)
+libmnl_la_SOURCES = socket.c callback.c nlmsg.c attr.c internal.h libmnl.map
diff --git a/libmnl/src/attr.c b/libmnl/src/attr.c
new file mode 100644
index 0000000..bc39df4
--- /dev/null
+++ b/libmnl/src/attr.c
@@ -0,0 +1,742 @@
+/*
+ * (C) 2008-2012 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+#include <limits.h> /* for INT_MAX */
+#include <libmnl/libmnl.h>
+#include <string.h>
+#include <errno.h>
+#include "internal.h"
+
+/**
+ * \defgroup attr Netlink attribute helpers
+ *
+ * Netlink Type-Length-Value (TLV) attribute:
+ * \verbatim
+ |<-- 2 bytes -->|<-- 2 bytes -->|<-- variable -->|
+ -------------------------------------------------
+ | length | type | value |
+ -------------------------------------------------
+ |<--------- header ------------>|<-- payload --->|
+\endverbatim
+ * The payload of the Netlink message contains sequences of attributes that are
+ * expressed in TLV format.
+ *
+ * @{
+ */
+
+/**
+ * mnl_attr_get_type - get type of netlink attribute
+ * \param attr pointer to netlink attribute
+ *
+ * \return the attribute type
+ */
+EXPORT_SYMBOL uint16_t mnl_attr_get_type(const struct nlattr *attr)
+{
+ return attr->nla_type & NLA_TYPE_MASK;
+}
+
+/**
+ * mnl_attr_get_len - get length of netlink attribute
+ * \param attr pointer to netlink attribute
+ *
+ * \return the attribute length
+ *
+ * The attribute length is the length of the attribute header plus the
+ * attribute payload.
+ *
+ */
+EXPORT_SYMBOL uint16_t mnl_attr_get_len(const struct nlattr *attr)
+{
+ return attr->nla_len;
+}
+
+/**
+ * mnl_attr_get_payload_len - get the attribute payload-value length
+ * \param attr pointer to netlink attribute
+ *
+ * \return the attribute payload-value length
+ */
+EXPORT_SYMBOL uint16_t mnl_attr_get_payload_len(const struct nlattr *attr)
+{
+ return attr->nla_len - MNL_ATTR_HDRLEN;
+}
+
+/**
+ * mnl_attr_get_payload - get pointer to the attribute payload
+ * \param attr pointer to netlink attribute
+ *
+ * \return pointer to the attribute payload
+ */
+EXPORT_SYMBOL void *mnl_attr_get_payload(const struct nlattr *attr)
+{
+ return (void *)attr + MNL_ATTR_HDRLEN;
+}
+
+/**
+ * mnl_attr_ok - check if there is room for an attribute in a buffer
+ * \param attr attribute that we want to check if there is room for
+ * \param len remaining bytes in a buffer that contains the attribute
+ *
+ * This function is used to check that a buffer, which is supposed to contain
+ * an attribute, has enough room for the attribute that it stores, i.e. this
+ * function can be used to verify that an attribute is neither malformed nor
+ * truncated.
+ *
+ * This function does not set errno in case of error since it is intended
+ * for iterations.
+ *
+ * The len parameter may be negative in the case of malformed messages during
+ * attribute iteration, that is why we use a signed integer.
+ *
+ * \return true if there is room for the attribute, false otherwise
+ */
+EXPORT_SYMBOL bool mnl_attr_ok(const struct nlattr *attr, int len)
+{
+ return len >= (int)sizeof(struct nlattr) &&
+ attr->nla_len >= sizeof(struct nlattr) &&
+ (int)attr->nla_len <= len;
+}
+
+/**
+ * mnl_attr_next - get the next attribute in the payload of a netlink message
+ * \param attr pointer to the current attribute
+ *
+ * \return a pointer to the next attribute after the one passed in
+ *
+ * You have to use mnl_attr_ok() on the returned attribute to ensure that the
+ * next attribute is valid.
+ *
+ */
+EXPORT_SYMBOL struct nlattr *mnl_attr_next(const struct nlattr *attr)
+{
+ return (struct nlattr *)((void *)attr + MNL_ALIGN(attr->nla_len));
+}
+
+/**
+ * mnl_attr_type_valid - check if the attribute type is valid
+ * \param attr pointer to attribute to be checked
+ * \param max maximum attribute type
+ *
+ * This function allows one to check if the attribute type is higher than the
+ * maximum supported type.
+ *
+ * Strict attribute checking in user-space is not a good idea since you may
+ * run an old application with a newer kernel that supports new attributes.
+ * This leads to backward compatibility breakages in user-space. Better check
+ * if you support an attribute, if not, skip it.
+ *
+ * On an error, errno is explicitly set.
+ *
+ * \return 1 if the attribute is valid, -1 otherwise
+ *
+ */
+EXPORT_SYMBOL int mnl_attr_type_valid(const struct nlattr *attr, uint16_t max)
+{
+ if (mnl_attr_get_type(attr) > max) {
+ errno = EOPNOTSUPP;
+ return -1;
+ }
+ return 1;
+}
+
+static int __mnl_attr_validate(const struct nlattr *attr,
+ enum mnl_attr_data_type type, size_t exp_len)
+{
+ uint16_t attr_len = mnl_attr_get_payload_len(attr);
+ const char *attr_data = mnl_attr_get_payload(attr);
+
+ if (attr_len < exp_len) {
+ errno = ERANGE;
+ return -1;
+ }
+ switch(type) {
+ case MNL_TYPE_FLAG:
+ if (attr_len > 0) {
+ errno = ERANGE;
+ return -1;
+ }
+ break;
+ case MNL_TYPE_NUL_STRING:
+ if (attr_len == 0) {
+ errno = ERANGE;
+ return -1;
+ }
+ if (attr_data[attr_len-1] != '\0') {
+ errno = EINVAL;
+ return -1;
+ }
+ break;
+ case MNL_TYPE_STRING:
+ if (attr_len == 0) {
+ errno = ERANGE;
+ return -1;
+ }
+ break;
+ case MNL_TYPE_NESTED:
+ /* empty nested attributes are OK. */
+ if (attr_len == 0)
+ break;
+ /* if not empty, they must contain one header, eg. flag */
+ if (attr_len < MNL_ATTR_HDRLEN) {
+ errno = ERANGE;
+ return -1;
+ }
+ break;
+ default:
+ /* make gcc happy. */
+ break;
+ }
+ if (exp_len && attr_len > exp_len) {
+ errno = ERANGE;
+ return -1;
+ }
+ return 0;
+}
+
+static const size_t mnl_attr_data_type_len[MNL_TYPE_MAX] = {
+ [MNL_TYPE_U8] = sizeof(uint8_t),
+ [MNL_TYPE_U16] = sizeof(uint16_t),
+ [MNL_TYPE_U32] = sizeof(uint32_t),
+ [MNL_TYPE_U64] = sizeof(uint64_t),
+ [MNL_TYPE_MSECS] = sizeof(uint64_t),
+};
+
+/**
+ * mnl_attr_validate - validate netlink attribute (simplified version)
+ * \param attr pointer to netlink attribute that we want to validate
+ * \param type data type (see enum mnl_attr_data_type)
+ *
+ * The validation is based on the data type. Specifically, it checks that
+ * integers (u8, u16, u32 and u64) have enough room for them.
+ *
+ * On an error, errno is explicitly set.
+ *
+ * \return 0 on success, -1 on error
+ */
+EXPORT_SYMBOL int mnl_attr_validate(const struct nlattr *attr, enum mnl_attr_data_type type)
+{
+ int exp_len;
+
+ if (type >= MNL_TYPE_MAX) {
+ errno = EINVAL;
+ return -1;
+ }
+ exp_len = mnl_attr_data_type_len[type];
+ return __mnl_attr_validate(attr, type, exp_len);
+}
+
+/**
+ * mnl_attr_validate2 - validate netlink attribute (extended version)
+ * \param attr pointer to netlink attribute that we want to validate
+ * \param type attribute type (see enum mnl_attr_data_type)
+ * \param exp_len expected attribute data size
+ *
+ * This function allows one to perform a more accurate validation for attributes
+ * whose size is variable.
+ *
+ * On an error, errno is explicitly set.
+ *
+ * \return 0 if the attribute is valid and fits within the expected length, -1
+ * otherwise
+ */
+EXPORT_SYMBOL int mnl_attr_validate2(const struct nlattr *attr,
+ enum mnl_attr_data_type type,
+ size_t exp_len)
+{
+ if (type >= MNL_TYPE_MAX) {
+ errno = EINVAL;
+ return -1;
+ }
+ return __mnl_attr_validate(attr, type, exp_len);
+}
+
+/**
+ * mnl_attr_parse - parse attributes
+ * \param nlh pointer to netlink message
+ * \param offset offset to start parsing from (if payload is after any header)
+ * \param cb callback function that is called for each attribute
+ * \param data pointer to data that is passed to the callback function
+ *
+ * This function allows you to iterate over the sequence of attributes that
+ * compose the Netlink message. You can then put the attribute in an array as it
+ * usually happens at this stage or you can use any other data structure (such
+ * as lists or trees).
+ *
+ * \return propagated value from callback, one of MNL_CB_ERROR, MNL_CB_STOP
+ * or MNL_CB_OK
+ */
+EXPORT_SYMBOL int mnl_attr_parse(const struct nlmsghdr *nlh,
+ unsigned int offset, mnl_attr_cb_t cb,
+ void *data)
+{
+ int ret = MNL_CB_OK;
+ const struct nlattr *attr;
+
+ mnl_attr_for_each(attr, nlh, offset)
+ if ((ret = cb(attr, data)) <= MNL_CB_STOP)
+ return ret;
+ return ret;
+}
+
+/**
+ * mnl_attr_parse_nested - parse attributes inside a nest
+ * \param nested pointer to netlink attribute that contains a nest
+ * \param cb callback function that is called for each attribute in the nest
+ * \param data pointer to data passed to the callback function
+ *
+ * This function allows you to iterate over the sequence of attributes that
+ * compose the Netlink message. You can then put the attribute in an array as it
+ * usually happens at this stage or you can use any other data structure (such
+ * as lists or trees).
+ *
+* \return propagated value from callback, one of MNL_CB_ERROR, MNL_CB_STOP
+* or MNL_CB_OK
+ */
+EXPORT_SYMBOL int mnl_attr_parse_nested(const struct nlattr *nested,
+ mnl_attr_cb_t cb, void *data)
+{
+ int ret = MNL_CB_OK;
+ const struct nlattr *attr;
+
+ mnl_attr_for_each_nested(attr, nested)
+ if ((ret = cb(attr, data)) <= MNL_CB_STOP)
+ return ret;
+ return ret;
+}
+
+/**
+ * mnl_attr_parse_payload - parse attributes in payload of Netlink message
+ * \param payload pointer to payload of the Netlink message
+ * \param payload_len payload length that contains the attributes
+ * \param cb callback function that is called for each attribute
+ * \param data pointer to data that is passed to the callback function
+ *
+ * This function takes a pointer to the area that contains the attributes,
+ * commonly known as the payload of the Netlink message. Thus, you have to
+ * pass a pointer to the Netlink message payload, instead of the entire
+ * message.
+ *
+ * This function allows you to iterate over the sequence of attributes that are
+ * located at some payload offset. You can then put the attributes in one array
+ * as usual, or you can use any other data structure (such as lists or trees).
+ *
+ * \return propagated value from callback, one of MNL_CB_ERROR, MNL_CB_STOP
+ * or MNL_CB_OK
+ */
+EXPORT_SYMBOL int mnl_attr_parse_payload(const void *payload,
+ size_t payload_len,
+ mnl_attr_cb_t cb, void *data)
+{
+ int ret = MNL_CB_OK;
+ const struct nlattr *attr;
+
+ mnl_attr_for_each_payload(payload, payload_len)
+ if ((ret = cb(attr, data)) <= MNL_CB_STOP)
+ return ret;
+ return ret;
+}
+
+/**
+ * mnl_attr_get_u8 - get 8-bit unsigned integer attribute payload
+ * \param attr pointer to netlink attribute
+ *
+ * \return 8-bit value of the attribute payload
+ */
+EXPORT_SYMBOL uint8_t mnl_attr_get_u8(const struct nlattr *attr)
+{
+ return *((uint8_t *)mnl_attr_get_payload(attr));
+}
+
+/**
+ * mnl_attr_get_u16 - get 16-bit unsigned integer attribute payload
+ * \param attr pointer to netlink attribute
+ *
+ * \return 16-bit value of the attribute payload
+ */
+EXPORT_SYMBOL uint16_t mnl_attr_get_u16(const struct nlattr *attr)
+{
+ return *((uint16_t *)mnl_attr_get_payload(attr));
+}
+
+/**
+ * mnl_attr_get_u32 - get 32-bit unsigned integer attribute payload
+ * \param attr pointer to netlink attribute
+ *
+ * \return 32-bit value of the attribute payload
+ */
+EXPORT_SYMBOL uint32_t mnl_attr_get_u32(const struct nlattr *attr)
+{
+ return *((uint32_t *)mnl_attr_get_payload(attr));
+}
+
+/**
+ * mnl_attr_get_u64 - get 64-bit unsigned integer attribute
+ * \param attr pointer to netlink attribute
+ *
+ * This function reads the 64-bit nlattr payload in an alignment safe manner.
+ *
+ * \return 64-bit value of the attribute payload
+ */
+EXPORT_SYMBOL uint64_t mnl_attr_get_u64(const struct nlattr *attr)
+{
+ uint64_t tmp;
+ memcpy(&tmp, mnl_attr_get_payload(attr), sizeof(tmp));
+ return tmp;
+}
+
+/**
+ * mnl_attr_get_str - get pointer to string attribute
+ * \param attr pointer to netlink attribute
+ *
+ * \return string pointer of the attribute payload
+ */
+EXPORT_SYMBOL const char *mnl_attr_get_str(const struct nlattr *attr)
+{
+ return mnl_attr_get_payload(attr);
+}
+
+/**
+ * mnl_attr_put - add an attribute to netlink message
+ * \param nlh pointer to the netlink message
+ * \param type netlink attribute type that you want to add
+ * \param len netlink attribute payload length
+ * \param data pointer to the data that will be stored by the new attribute
+ *
+ * This function updates the length field of the Netlink message (nlmsg_len)
+ * by adding the size (header + payload) of the new attribute.
+ */
+EXPORT_SYMBOL void mnl_attr_put(struct nlmsghdr *nlh, uint16_t type,
+ size_t len, const void *data)
+{
+ struct nlattr *attr = mnl_nlmsg_get_payload_tail(nlh);
+ uint16_t payload_len = MNL_ALIGN(sizeof(struct nlattr)) + len;
+ int pad;
+
+ attr->nla_type = type;
+ attr->nla_len = payload_len;
+ memcpy(mnl_attr_get_payload(attr), data, len);
+ pad = MNL_ALIGN(len) - len;
+ if (pad > 0)
+ memset(mnl_attr_get_payload(attr) + len, 0, pad);
+
+ nlh->nlmsg_len += MNL_ALIGN(payload_len);
+}
+
+/**
+ * mnl_attr_put_u8 - add 8-bit unsigned integer attribute to netlink message
+ * \param nlh pointer to the netlink message
+ * \param type netlink attribute type
+ * \param data 8-bit unsigned integer data that is stored by the new attribute
+ *
+ * This function updates the length field of the Netlink message (nlmsg_len)
+ * by adding the size (header + payload) of the new attribute.
+ */
+EXPORT_SYMBOL void mnl_attr_put_u8(struct nlmsghdr *nlh, uint16_t type,
+ uint8_t data)
+{
+ mnl_attr_put(nlh, type, sizeof(uint8_t), &data);
+}
+
+/**
+ * mnl_attr_put_u16 - add 16-bit unsigned integer attribute to netlink message
+ * \param nlh pointer to the netlink message
+ * \param type netlink attribute type
+ * \param data 16-bit unsigned integer data that is stored by the new attribute
+ *
+ * This function updates the length field of the Netlink message (nlmsg_len)
+ * by adding the size (header + payload) of the new attribute.
+ */
+EXPORT_SYMBOL void mnl_attr_put_u16(struct nlmsghdr *nlh, uint16_t type,
+ uint16_t data)
+{
+ mnl_attr_put(nlh, type, sizeof(uint16_t), &data);
+}
+
+/**
+ * mnl_attr_put_u32 - add 32-bit unsigned integer attribute to netlink message
+ * \param nlh pointer to the netlink message
+ * \param type netlink attribute type
+ * \param data 32-bit unsigned integer data that is stored by the new attribute
+ *
+ * This function updates the length field of the Netlink message (nlmsg_len)
+ * by adding the size (header + payload) of the new attribute.
+ */
+EXPORT_SYMBOL void mnl_attr_put_u32(struct nlmsghdr *nlh, uint16_t type,
+ uint32_t data)
+{
+ mnl_attr_put(nlh, type, sizeof(uint32_t), &data);
+}
+
+/**
+ * mnl_attr_put_u64 - add 64-bit unsigned integer attribute to netlink message
+ * \param nlh pointer to the netlink message
+ * \param type netlink attribute type
+ * \param data 64-bit unsigned integer data that is stored by the new attribute
+ *
+ * This function updates the length field of the Netlink message (nlmsg_len)
+ * by adding the size (header + payload) of the new attribute.
+ */
+EXPORT_SYMBOL void mnl_attr_put_u64(struct nlmsghdr *nlh, uint16_t type,
+ uint64_t data)
+{
+ mnl_attr_put(nlh, type, sizeof(uint64_t), &data);
+}
+
+/**
+ * mnl_attr_put_str - add string attribute to netlink message
+ * \param nlh pointer to the netlink message
+ * \param type netlink attribute type
+ * \param data pointer to string data that is stored by the new attribute
+ *
+ * This function updates the length field of the Netlink message (nlmsg_len)
+ * by adding the size (header + payload) of the new attribute.
+ */
+EXPORT_SYMBOL void mnl_attr_put_str(struct nlmsghdr *nlh, uint16_t type,
+ const char *data)
+{
+ mnl_attr_put(nlh, type, strlen(data), data);
+}
+
+/**
+ * mnl_attr_put_strz - add string attribute to netlink message
+ * \param nlh pointer to the netlink message
+ * \param type netlink attribute type
+ * \param data pointer to string data that is stored by the new attribute
+ *
+ * This function is similar to mnl_attr_put_str, but it includes the
+ * NUL/zero ('\0') terminator at the end of the string.
+ *
+ * This function updates the length field of the Netlink message (nlmsg_len)
+ * by adding the size (header + payload) of the new attribute.
+ */
+EXPORT_SYMBOL void mnl_attr_put_strz(struct nlmsghdr *nlh, uint16_t type,
+ const char *data)
+{
+ mnl_attr_put(nlh, type, strlen(data)+1, data);
+}
+
+/**
+ * mnl_attr_nest_start - start an attribute nest
+ * \param nlh pointer to the netlink message
+ * \param type netlink attribute type
+ *
+ * This function adds the attribute header that identifies the beginning of
+ * an attribute nest.
+ *
+ * \return valid pointer to the beginning of the nest
+ */
+EXPORT_SYMBOL struct nlattr *mnl_attr_nest_start(struct nlmsghdr *nlh,
+ uint16_t type)
+{
+ struct nlattr *start = mnl_nlmsg_get_payload_tail(nlh);
+
+ /* set start->nla_len in mnl_attr_nest_end() */
+ start->nla_type = NLA_F_NESTED | type;
+ nlh->nlmsg_len += MNL_ALIGN(sizeof(struct nlattr));
+
+ return start;
+}
+
+/**
+ * mnl_attr_put_check - add an attribute to netlink message
+ * \param nlh pointer to the netlink message
+ * \param buflen size of buffer which stores the message
+ * \param type netlink attribute type that you want to add
+ * \param len netlink attribute payload length
+ * \param data pointer to the data that will be stored by the new attribute
+ *
+ * This function first checks that the data can be added to the message
+ * (fits into the buffer) and then updates the length field of the Netlink
+ * message (nlmsg_len) by adding the size (header + payload) of the new
+ * attribute.
+ *
+ * \return true if the attribute could be added, false otherwise
+ */
+EXPORT_SYMBOL bool mnl_attr_put_check(struct nlmsghdr *nlh, size_t buflen,
+ uint16_t type, size_t len,
+ const void *data)
+{
+ if (nlh->nlmsg_len + MNL_ATTR_HDRLEN + MNL_ALIGN(len) > buflen)
+ return false;
+ mnl_attr_put(nlh, type, len, data);
+ return true;
+}
+
+/**
+ * mnl_attr_put_u8_check - add 8-bit unsigned int attribute to netlink message
+ * \param nlh pointer to the netlink message
+ * \param buflen size of buffer which stores the message
+ * \param type netlink attribute type
+ * \param data 8-bit unsigned integer data that is stored by the new attribute
+ *
+ * This function first checks that the data can be added to the message
+ * (fits into the buffer) and then updates the length field of the Netlink
+ * message (nlmsg_len) by adding the size (header + payload) of the new
+ * attribute.
+ *
+ * \return true if the attribute could be added, false otherwise
+ */
+EXPORT_SYMBOL bool mnl_attr_put_u8_check(struct nlmsghdr *nlh, size_t buflen,
+ uint16_t type, uint8_t data)
+{
+ return mnl_attr_put_check(nlh, buflen, type, sizeof(uint8_t), &data);
+}
+
+/**
+ * mnl_attr_put_u16_check - add 16-bit unsigned int attribute to netlink message
+ * \param nlh pointer to the netlink message
+ * \param buflen size of buffer which stores the message
+ * \param type netlink attribute type
+ * \param data 16-bit unsigned integer data that is stored by the new attribute
+ *
+ * This function first checks that the data can be added to the message
+ * (fits into the buffer) and then updates the length field of the Netlink
+ * message (nlmsg_len) by adding the size (header + payload) of the new
+ * attribute.
+ *
+ * \return true if the attribute could be added, false otherwise
+ */
+EXPORT_SYMBOL bool mnl_attr_put_u16_check(struct nlmsghdr *nlh, size_t buflen,
+ uint16_t type, uint16_t data)
+{
+ return mnl_attr_put_check(nlh, buflen, type, sizeof(uint16_t), &data);
+}
+
+/**
+ * mnl_attr_put_u32_check - add 32-bit unsigned int attribute to netlink message
+ * \param nlh pointer to the netlink message
+ * \param buflen size of buffer which stores the message
+ * \param type netlink attribute type
+ * \param data 32-bit unsigned integer data that is stored by the new attribute
+ *
+ * This function first checks that the data can be added to the message
+ * (fits into the buffer) and then updates the length field of the Netlink
+ * message (nlmsg_len) by adding the size (header + payload) of the new
+ * attribute.
+ *
+ * \return true if the attribute could be added, false otherwise
+ */
+EXPORT_SYMBOL bool mnl_attr_put_u32_check(struct nlmsghdr *nlh, size_t buflen,
+ uint16_t type, uint32_t data)
+{
+ return mnl_attr_put_check(nlh, buflen, type, sizeof(uint32_t), &data);
+}
+
+/**
+ * mnl_attr_put_u64_check - add 64-bit unsigned int attribute to netlink message
+ * \param nlh pointer to the netlink message
+ * \param buflen size of buffer which stores the message
+ * \param type netlink attribute type
+ * \param data 64-bit unsigned integer data that is stored by the new attribute
+ *
+ * This function first checks that the data can be added to the message
+ * (fits into the buffer) and then updates the length field of the Netlink
+ * message (nlmsg_len) by adding the size (header + payload) of the new
+ * attribute.
+ *
+ * \return true if the attribute could be added, false otherwise
+ */
+EXPORT_SYMBOL bool mnl_attr_put_u64_check(struct nlmsghdr *nlh, size_t buflen,
+ uint16_t type, uint64_t data)
+{
+ return mnl_attr_put_check(nlh, buflen, type, sizeof(uint64_t), &data);
+}
+
+/**
+ * mnl_attr_put_str_check - add string attribute to netlink message
+ * \param nlh pointer to the netlink message
+ * \param buflen size of buffer which stores the message
+ * \param type netlink attribute type
+ * \param data pointer to string data that is stored by the new attribute
+ *
+ * This function first checks that the data can be added to the message
+ * (fits into the buffer) and then updates the length field of the Netlink
+ * message (nlmsg_len) by adding the size (header + payload) of the new
+ * attribute.
+ *
+ * \return true if the attribute could be added, false otherwise
+ */
+EXPORT_SYMBOL bool mnl_attr_put_str_check(struct nlmsghdr *nlh, size_t buflen,
+ uint16_t type, const char *data)
+{
+ return mnl_attr_put_check(nlh, buflen, type, strlen(data), data);
+}
+
+/**
+ * mnl_attr_put_strz_check - add string attribute to netlink message
+ * \param nlh pointer to the netlink message
+ * \param buflen size of buffer which stores the message
+ * \param type netlink attribute type
+ * \param data pointer to string data that is stored by the new attribute
+ *
+ * This function is similar to mnl_attr_put_str, but it includes the
+ * NUL/zero ('\0') terminator at the end of the string.
+ *
+ * This function first checks that the data can be added to the message
+ * (fits into the buffer) and then updates the length field of the Netlink
+ * message (nlmsg_len) by adding the size (header + payload) of the new
+ * attribute.
+ *
+ * \return true if the attribute could be added, false otherwise
+ */
+EXPORT_SYMBOL bool mnl_attr_put_strz_check(struct nlmsghdr *nlh, size_t buflen,
+ uint16_t type, const char *data)
+{
+ return mnl_attr_put_check(nlh, buflen, type, strlen(data)+1, data);
+}
+
+/**
+ * mnl_attr_nest_start_check - start an attribute nest
+ * \param buflen size of buffer which stores the message
+ * \param nlh pointer to the netlink message
+ * \param type netlink attribute type
+ *
+ * This function adds the attribute header that identifies the beginning of
+ * an attribute nest.
+ *
+ * \return NULL if the attribute cannot be added, otherwise a pointer to the
+ * beginning of the nest
+ */
+EXPORT_SYMBOL struct nlattr *mnl_attr_nest_start_check(struct nlmsghdr *nlh,
+ size_t buflen,
+ uint16_t type)
+{
+ if (nlh->nlmsg_len + MNL_ATTR_HDRLEN > buflen)
+ return NULL;
+ return mnl_attr_nest_start(nlh, type);
+}
+
+/**
+ * mnl_attr_nest_end - end an attribute nest
+ * \param nlh pointer to the netlink message
+ * \param start pointer to the attribute nest returned by mnl_attr_nest_start()
+ *
+ * This function updates the attribute header that identifies the nest.
+ */
+EXPORT_SYMBOL void mnl_attr_nest_end(struct nlmsghdr *nlh,
+ struct nlattr *start)
+{
+ start->nla_len = mnl_nlmsg_get_payload_tail(nlh) - (void *)start;
+}
+
+/**
+ * mnl_attr_nest_cancel - cancel an attribute nest
+ * \param nlh pointer to the netlink message
+ * \param start pointer to the attribute nest returned by mnl_attr_nest_start()
+ *
+ * This function updates the attribute header that identifies the nest.
+ */
+EXPORT_SYMBOL void mnl_attr_nest_cancel(struct nlmsghdr *nlh,
+ struct nlattr *start)
+{
+ nlh->nlmsg_len -= mnl_nlmsg_get_payload_tail(nlh) - (void *)start;
+}
+
+/**
+ * @}
+ */
diff --git a/libmnl/src/callback.c b/libmnl/src/callback.c
new file mode 100644
index 0000000..f5349c3
--- /dev/null
+++ b/libmnl/src/callback.c
@@ -0,0 +1,167 @@
+/*
+ * (C) 2008-2010 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <errno.h>
+#include <libmnl/libmnl.h>
+#include "internal.h"
+
+static int mnl_cb_noop(const struct nlmsghdr *nlh, void *data)
+{
+ return MNL_CB_OK;
+}
+
+static int mnl_cb_error(const struct nlmsghdr *nlh, void *data)
+{
+ const struct nlmsgerr *err = mnl_nlmsg_get_payload(nlh);
+
+ if (nlh->nlmsg_len < mnl_nlmsg_size(sizeof(struct nlmsgerr))) {
+ errno = EBADMSG;
+ return MNL_CB_ERROR;
+ }
+ /* Netlink subsystems returns the errno value with different signess */
+ if (err->error < 0)
+ errno = -err->error;
+ else
+ errno = err->error;
+
+ return err->error == 0 ? MNL_CB_STOP : MNL_CB_ERROR;
+}
+
+static int mnl_cb_stop(const struct nlmsghdr *nlh, void *data)
+{
+ return MNL_CB_STOP;
+}
+
+static const mnl_cb_t default_cb_array[NLMSG_MIN_TYPE] = {
+ [NLMSG_NOOP] = mnl_cb_noop,
+ [NLMSG_ERROR] = mnl_cb_error,
+ [NLMSG_DONE] = mnl_cb_stop,
+ [NLMSG_OVERRUN] = mnl_cb_noop,
+};
+
+static inline int __mnl_cb_run(const void *buf, size_t numbytes,
+ unsigned int seq, unsigned int portid,
+ mnl_cb_t cb_data, void *data,
+ const mnl_cb_t *cb_ctl_array,
+ unsigned int cb_ctl_array_len)
+{
+ int ret = MNL_CB_OK, len = numbytes;
+ const struct nlmsghdr *nlh = buf;
+
+ while (mnl_nlmsg_ok(nlh, len)) {
+ /* check message source */
+ if (!mnl_nlmsg_portid_ok(nlh, portid)) {
+ errno = ESRCH;
+ return -1;
+ }
+ /* perform sequence tracking */
+ if (!mnl_nlmsg_seq_ok(nlh, seq)) {
+ errno = EPROTO;
+ return -1;
+ }
+
+ /* dump was interrupted */
+ if (nlh->nlmsg_flags & NLM_F_DUMP_INTR) {
+ errno = EINTR;
+ return -1;
+ }
+
+ /* netlink data message handling */
+ if (nlh->nlmsg_type >= NLMSG_MIN_TYPE) {
+ if (cb_data){
+ ret = cb_data(nlh, data);
+ if (ret <= MNL_CB_STOP)
+ goto out;
+ }
+ } else if (nlh->nlmsg_type < cb_ctl_array_len) {
+ if (cb_ctl_array && cb_ctl_array[nlh->nlmsg_type]) {
+ ret = cb_ctl_array[nlh->nlmsg_type](nlh, data);
+ if (ret <= MNL_CB_STOP)
+ goto out;
+ }
+ } else if (default_cb_array[nlh->nlmsg_type]) {
+ ret = default_cb_array[nlh->nlmsg_type](nlh, data);
+ if (ret <= MNL_CB_STOP)
+ goto out;
+ }
+ nlh = mnl_nlmsg_next(nlh, &len);
+ }
+out:
+ return ret;
+}
+
+/**
+ * \defgroup callback Callback helpers
+ * @{
+ */
+
+/**
+ * mnl_cb_run2 - callback runqueue for netlink messages
+ * \param buf buffer that contains the netlink messages
+ * \param numbytes number of bytes stored in the buffer
+ * \param seq sequence number that we expect to receive
+ * \param portid Netlink PortID that we expect to receive
+ * \param cb_data callback handler for data messages
+ * \param data pointer to data that will be passed to the data callback handler
+ * \param cb_ctl_array array of custom callback handlers from control messages
+ * \param cb_ctl_array_len array length of custom control callback handlers
+ *
+ * You can set the cb_ctl_array to NULL if you want to use the default control
+ * callback handlers, in that case, the parameter cb_ctl_array_len is not
+ * checked.
+ *
+ * Your callback may return three possible values:
+ * - MNL_CB_ERROR (<=-1): an error has occurred. Stop callback runqueue.
+ * - MNL_CB_STOP (=0): stop callback runqueue.
+ * - MNL_CB_OK (>=1): no problem has occurred.
+ *
+ * This function propagates the callback return value. On error, it returns
+ * -1 and errno is explicitly set. If the portID is not the expected, errno
+ * is set to ESRCH. If the sequence number is not the expected, errno is set
+ * to EPROTO. If the dump was interrupted, errno is set to EINTR and you should
+ * request a new fresh dump again.
+ */
+EXPORT_SYMBOL int mnl_cb_run2(const void *buf, size_t numbytes,
+ unsigned int seq, unsigned int portid,
+ mnl_cb_t cb_data, void *data,
+ const mnl_cb_t *cb_ctl_array,
+ unsigned int cb_ctl_array_len)
+{
+ return __mnl_cb_run(buf, numbytes, seq, portid, cb_data, data,
+ cb_ctl_array, cb_ctl_array_len);
+}
+
+/**
+ * mnl_cb_run - callback runqueue for netlink messages (simplified version)
+ * \param buf buffer that contains the netlink messages
+ * \param numbytes number of bytes stored in the buffer
+ * \param seq sequence number that we expect to receive
+ * \param portid Netlink PortID that we expect to receive
+ * \param cb_data callback handler for data messages
+ * \param data pointer to data that will be passed to the data callback handler
+ *
+ * This function is like mnl_cb_run2() but it does not allow you to set
+ * the control callback handlers.
+ *
+ * Your callback may return three possible values:
+ * - MNL_CB_ERROR (<=-1): an error has occurred. Stop callback runqueue.
+ * - MNL_CB_STOP (=0): stop callback runqueue.
+ * - MNL_CB_OK (>=1): no problems has occurred.
+ *
+ * This function propagates the callback return value.
+ */
+EXPORT_SYMBOL int mnl_cb_run(const void *buf, size_t numbytes, unsigned int seq,
+ unsigned int portid, mnl_cb_t cb_data, void *data)
+{
+ return __mnl_cb_run(buf, numbytes, seq, portid, cb_data, data, NULL, 0);
+}
+
+/**
+ * @}
+ */
diff --git a/libmnl/src/internal.h b/libmnl/src/internal.h
new file mode 100644
index 0000000..d69eaf3
--- /dev/null
+++ b/libmnl/src/internal.h
@@ -0,0 +1,11 @@
+#ifndef INTERNAL_H
+#define INTERNAL_H 1
+
+#include "config.h"
+#ifdef HAVE_VISIBILITY_HIDDEN
+# define EXPORT_SYMBOL __attribute__((visibility("default")))
+#else
+# define EXPORT_SYMBOL
+#endif
+
+#endif
diff --git a/libmnl/src/libmnl.map b/libmnl/src/libmnl.map
new file mode 100644
index 0000000..e5920e5
--- /dev/null
+++ b/libmnl/src/libmnl.map
@@ -0,0 +1,79 @@
+LIBMNL_1.0 {
+global:
+ mnl_attr_get_len;
+ mnl_attr_get_payload;
+ mnl_attr_get_payload_len;
+ mnl_attr_get_str;
+ mnl_attr_get_type;
+ mnl_attr_get_u16;
+ mnl_attr_get_u32;
+ mnl_attr_get_u64;
+ mnl_attr_get_u8;
+ mnl_attr_nest_end;
+ mnl_attr_nest_start;
+ mnl_attr_nest_start_check;
+ mnl_attr_nest_cancel;
+ mnl_attr_next;
+ mnl_attr_ok;
+ mnl_attr_parse;
+ mnl_attr_parse_nested;
+ mnl_attr_put;
+ mnl_attr_put_str;
+ mnl_attr_put_strz;
+ mnl_attr_put_u16;
+ mnl_attr_put_u32;
+ mnl_attr_put_u64;
+ mnl_attr_put_u8;
+ mnl_attr_put_check;
+ mnl_attr_put_str_check;
+ mnl_attr_put_strz_check;
+ mnl_attr_put_u16_check;
+ mnl_attr_put_u32_check;
+ mnl_attr_put_u64_check;
+ mnl_attr_put_u8_check;
+ mnl_attr_type_valid;
+ mnl_attr_validate;
+ mnl_attr_validate2;
+ mnl_cb_run;
+ mnl_cb_run2;
+ mnl_nlmsg_fprintf;
+ mnl_nlmsg_get_payload;
+ mnl_nlmsg_get_payload_len;
+ mnl_nlmsg_get_payload_offset;
+ mnl_nlmsg_get_payload_tail;
+ mnl_nlmsg_next;
+ mnl_nlmsg_ok;
+ mnl_nlmsg_portid_ok;
+ mnl_nlmsg_put_extra_header;
+ mnl_nlmsg_put_header;
+ mnl_nlmsg_seq_ok;
+ mnl_nlmsg_size;
+ mnl_nlmsg_batch_start;
+ mnl_nlmsg_batch_stop;
+ mnl_nlmsg_batch_next;
+ mnl_nlmsg_batch_size;
+ mnl_nlmsg_batch_reset;
+ mnl_nlmsg_batch_current;
+ mnl_nlmsg_batch_head;
+ mnl_nlmsg_batch_is_empty;
+ mnl_socket_bind;
+ mnl_socket_close;
+ mnl_socket_get_fd;
+ mnl_socket_get_portid;
+ mnl_socket_getsockopt;
+ mnl_socket_open;
+ mnl_socket_recvfrom;
+ mnl_socket_sendto;
+ mnl_socket_setsockopt;
+
+local: *;
+};
+
+LIBMNL_1.1 {
+ mnl_attr_parse_payload;
+} LIBMNL_1.0;
+
+LIBMNL_1.2 {
+ mnl_socket_open2;
+ mnl_socket_fdopen;
+} LIBMNL_1.1;
diff --git a/libmnl/src/nlmsg.c b/libmnl/src/nlmsg.c
new file mode 100644
index 0000000..c634501
--- /dev/null
+++ b/libmnl/src/nlmsg.c
@@ -0,0 +1,587 @@
+/*
+ * (C) 2008-2010 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <libmnl/libmnl.h>
+#include "internal.h"
+
+/**
+ * \defgroup nlmsg Netlink message helpers
+ *
+ * Netlink message:
+ * \verbatim
+ |<----------------- 4 bytes ------------------->|
+ |<----- 2 bytes ------>|<------- 2 bytes ------>|
+ |-----------------------------------------------|
+ | Message length (including header) |
+ |-----------------------------------------------|
+ | Message type | Message flags |
+ |-----------------------------------------------|
+ | Message sequence number |
+ |-----------------------------------------------|
+ | Netlink PortID |
+ |-----------------------------------------------|
+ | |
+ . Payload .
+ |_______________________________________________|
+\endverbatim
+ *
+ * There is usually an extra header after the the Netlink header (at the
+ * beginning of the payload). This extra header is specific of the Netlink
+ * subsystem. After this extra header, it comes the sequence of attributes
+ * that are expressed in Type-Length-Value (TLV) format.
+ *
+ * @{
+ */
+
+/**
+ * mnl_nlmsg_size - calculate the size of Netlink message (without alignment)
+ * \param len length of the Netlink payload
+ *
+ * This function returns the size of a netlink message (header plus payload)
+ * without alignment.
+ */
+EXPORT_SYMBOL size_t mnl_nlmsg_size(size_t len)
+{
+ return len + MNL_NLMSG_HDRLEN;
+}
+
+/**
+ * mnl_nlmsg_get_payload_len - get the length of the Netlink payload
+ * \param nlh pointer to the header of the Netlink message
+ *
+ * This function returns the Length of the netlink payload, ie. the length
+ * of the full message minus the size of the Netlink header.
+ */
+EXPORT_SYMBOL size_t mnl_nlmsg_get_payload_len(const struct nlmsghdr *nlh)
+{
+ return nlh->nlmsg_len - MNL_NLMSG_HDRLEN;
+}
+
+/**
+ * mnl_nlmsg_put_header - reserve and prepare room for Netlink header
+ * \param buf memory already allocated to store the Netlink header
+ *
+ * This function sets to zero the room that is required to put the Netlink
+ * header in the memory buffer passed as parameter. This function also
+ * initializes the nlmsg_len field to the size of the Netlink header. This
+ * function returns a pointer to the Netlink header structure.
+ */
+EXPORT_SYMBOL struct nlmsghdr *mnl_nlmsg_put_header(void *buf)
+{
+ int len = MNL_ALIGN(sizeof(struct nlmsghdr));
+ struct nlmsghdr *nlh = buf;
+
+ memset(buf, 0, len);
+ nlh->nlmsg_len = len;
+ return nlh;
+}
+
+/**
+ * mnl_nlmsg_put_extra_header - reserve and prepare room for an extra header
+ * \param nlh pointer to Netlink header
+ * \param size size of the extra header that we want to put
+ *
+ * This function sets to zero the room that is required to put the extra
+ * header after the initial Netlink header. This function also increases
+ * the nlmsg_len field. You have to invoke mnl_nlmsg_put_header() before
+ * you call this function. This function returns a pointer to the extra
+ * header.
+ */
+EXPORT_SYMBOL void *mnl_nlmsg_put_extra_header(struct nlmsghdr *nlh,
+ size_t size)
+{
+ char *ptr = (char *)nlh + nlh->nlmsg_len;
+ size_t len = MNL_ALIGN(size);
+ nlh->nlmsg_len += len;
+ memset(ptr, 0, len);
+ return ptr;
+}
+
+/**
+ * mnl_nlmsg_get_payload - get a pointer to the payload of the netlink message
+ * \param nlh pointer to a netlink header
+ *
+ * This function returns a pointer to the payload of the netlink message.
+ */
+EXPORT_SYMBOL void *mnl_nlmsg_get_payload(const struct nlmsghdr *nlh)
+{
+ return (void *)nlh + MNL_NLMSG_HDRLEN;
+}
+
+/**
+ * mnl_nlmsg_get_payload_offset - get a pointer to the payload of the message
+ * \param nlh pointer to a netlink header
+ * \param offset offset to the payload of the attributes TLV set
+ *
+ * This function returns a pointer to the payload of the netlink message plus
+ * a given offset.
+ */
+EXPORT_SYMBOL void *mnl_nlmsg_get_payload_offset(const struct nlmsghdr *nlh,
+ size_t offset)
+{
+ return (void *)nlh + MNL_NLMSG_HDRLEN + MNL_ALIGN(offset);
+}
+
+/**
+ * mnl_nlmsg_ok - check a there is room for netlink message
+ * \param nlh netlink message that we want to check
+ * \param len remaining bytes in a buffer that contains the netlink message
+ *
+ * This function is used to check that a buffer that contains a netlink
+ * message has enough room for the netlink message that it stores, ie. this
+ * function can be used to verify that a netlink message is not malformed nor
+ * truncated.
+ *
+ * This function does not set errno in case of error since it is intended
+ * for iterations. Thus, it returns true on success and false on error.
+ *
+ * The len parameter may become negative in malformed messages during message
+ * iteration, that is why we use a signed integer.
+ */
+EXPORT_SYMBOL bool mnl_nlmsg_ok(const struct nlmsghdr *nlh, int len)
+{
+ return len >= (int)sizeof(struct nlmsghdr) &&
+ nlh->nlmsg_len >= sizeof(struct nlmsghdr) &&
+ (int)nlh->nlmsg_len <= len;
+}
+
+/**
+ * mnl_nlmsg_next - get the next netlink message in a multipart message
+ * \param nlh current netlink message that we are handling
+ * \param len length of the remaining bytes in the buffer (passed by reference).
+ *
+ * This function returns a pointer to the next netlink message that is part
+ * of a multi-part netlink message. Netlink can batch several messages into
+ * one buffer so that the receiver has to iterate over the whole set of
+ * Netlink messages.
+ *
+ * You have to use mnl_nlmsg_ok() to check if the next Netlink message is
+ * valid.
+ */
+EXPORT_SYMBOL struct nlmsghdr *mnl_nlmsg_next(const struct nlmsghdr *nlh,
+ int *len)
+{
+ *len -= MNL_ALIGN(nlh->nlmsg_len);
+ return (struct nlmsghdr *)((void *)nlh + MNL_ALIGN(nlh->nlmsg_len));
+}
+
+/**
+ * mnl_nlmsg_get_payload_tail - get the ending of the netlink message
+ * \param nlh pointer to netlink message
+ *
+ * This function returns a pointer to the netlink message tail. This is useful
+ * to build a message since we continue adding attributes at the end of the
+ * message.
+ */
+EXPORT_SYMBOL void *mnl_nlmsg_get_payload_tail(const struct nlmsghdr *nlh)
+{
+ return (void *)nlh + MNL_ALIGN(nlh->nlmsg_len);
+}
+
+/**
+ * mnl_nlmsg_seq_ok - perform sequence tracking
+ * \param nlh current netlink message that we are handling
+ * \param seq last sequence number used to send a message
+ *
+ * This functions returns true if the sequence tracking is fulfilled, otherwise
+ * false is returned. We skip the tracking for netlink messages whose sequence
+ * number is zero since it is usually reserved for event-based kernel
+ * notifications. On the other hand, if seq is set but the message sequence
+ * number is not set (i.e. this is an event message coming from kernel-space),
+ * then we also skip the tracking. This approach is good if we use the same
+ * socket to send commands to kernel-space (that we want to track) and to
+ * listen to events (that we do not track).
+ */
+EXPORT_SYMBOL bool mnl_nlmsg_seq_ok(const struct nlmsghdr *nlh,
+ unsigned int seq)
+{
+ return nlh->nlmsg_seq && seq ? nlh->nlmsg_seq == seq : true;
+}
+
+/**
+ * mnl_nlmsg_portid_ok - perform portID origin check
+ * \param nlh current netlink message that we are handling
+ * \param portid netlink portid that we want to check
+ *
+ * This functions returns true if the origin is fulfilled, otherwise
+ * false is returned. We skip the tracking for netlink message whose portID
+ * is zero since it is reserved for event-based kernel notifications. On the
+ * other hand, if portid is set but the message PortID is not (i.e. this
+ * is an event message coming from kernel-space), then we also skip the
+ * tracking. This approach is good if we use the same socket to send commands
+ * to kernel-space (that we want to track) and to listen to events (that we
+ * do not track).
+ */
+EXPORT_SYMBOL bool mnl_nlmsg_portid_ok(const struct nlmsghdr *nlh,
+ unsigned int portid)
+{
+ return nlh->nlmsg_pid && portid ? nlh->nlmsg_pid == portid : true;
+}
+
+static void mnl_nlmsg_fprintf_header(FILE *fd, const struct nlmsghdr *nlh)
+{
+ fprintf(fd, "----------------\t------------------\n");
+ fprintf(fd, "| %.010u |\t| message length |\n", nlh->nlmsg_len);
+ fprintf(fd, "| %.05u | %c%c%c%c |\t| type | flags |\n",
+ nlh->nlmsg_type,
+ nlh->nlmsg_flags & NLM_F_REQUEST ? 'R' : '-',
+ nlh->nlmsg_flags & NLM_F_MULTI ? 'M' : '-',
+ nlh->nlmsg_flags & NLM_F_ACK ? 'A' : '-',
+ nlh->nlmsg_flags & NLM_F_ECHO ? 'E' : '-');
+ fprintf(fd, "| %.010u |\t| sequence number|\n", nlh->nlmsg_seq);
+ fprintf(fd, "| %.010u |\t| port ID |\n", nlh->nlmsg_pid);
+ fprintf(fd, "----------------\t------------------\n");
+}
+
+static void mnl_fprintf_attr_color(FILE *fd, const struct nlattr *attr)
+{
+ fprintf(fd, "|%c[%d;%dm"
+ "%.5u"
+ "%c[%dm"
+ "|"
+ "%c[%d;%dm"
+ "%c%c"
+ "%c[%dm"
+ "|"
+ "%c[%d;%dm"
+ "%.5u"
+ "%c[%dm|\t",
+ 27, 1, 31,
+ attr->nla_len,
+ 27, 0,
+ 27, 1, 32,
+ attr->nla_type & NLA_F_NESTED ? 'N' : '-',
+ attr->nla_type & NLA_F_NET_BYTEORDER ? 'B' : '-',
+ 27, 0,
+ 27, 1, 34,
+ attr->nla_type & NLA_TYPE_MASK,
+ 27, 0);
+}
+
+static void mnl_fprintf_attr_raw(FILE *fd, const struct nlattr *attr)
+{
+ fprintf(fd, "|"
+ "%.5u"
+ "|"
+ "%c%c"
+ "|"
+ "%.5u"
+ "|\t",
+ attr->nla_len,
+ attr->nla_type & NLA_F_NESTED ? 'N' : '-',
+ attr->nla_type & NLA_F_NET_BYTEORDER ? 'B' : '-',
+ attr->nla_type & NLA_TYPE_MASK);
+}
+
+static void mnl_nlmsg_fprintf_payload(FILE *fd, const struct nlmsghdr *nlh,
+ size_t extra_header_size)
+{
+ int colorize = 0;
+ unsigned int i;
+ int rem = 0;
+ int fdnum;
+
+ fdnum = fileno(fd);
+ if (fdnum != -1)
+ colorize = isatty(fdnum);
+
+ for (i=sizeof(struct nlmsghdr); i<nlh->nlmsg_len; i+=4) {
+ char *b = (char *) nlh;
+ struct nlattr *attr = (struct nlattr *) (b+i);
+
+ /* netlink control message. */
+ if (nlh->nlmsg_type < NLMSG_MIN_TYPE) {
+ fprintf(fd, "| %.2x %.2x %.2x %.2x |\t",
+ 0xff & b[i], 0xff & b[i+1],
+ 0xff & b[i+2], 0xff & b[i+3]);
+ fprintf(fd, "| |\n");
+ /* special handling for the extra header. */
+ } else if (extra_header_size > 0) {
+ extra_header_size -= 4;
+ fprintf(fd, "| %.2x %.2x %.2x %.2x |\t",
+ 0xff & b[i], 0xff & b[i+1],
+ 0xff & b[i+2], 0xff & b[i+3]);
+ fprintf(fd, "| extra header |\n");
+ /* this seems like an attribute header. */
+ } else if (rem == 0 && (attr->nla_type & NLA_TYPE_MASK) != 0) {
+ if (colorize) {
+ mnl_fprintf_attr_color(fd, attr);
+ } else {
+ mnl_fprintf_attr_raw(fd, attr);
+ }
+ fprintf(fd, "|len |flags| type|\n");
+
+ if (!(attr->nla_type & NLA_F_NESTED)) {
+ rem = NLA_ALIGN(attr->nla_len) -
+ sizeof(struct nlattr);
+ }
+ /* this is the attribute payload. */
+ } else if (rem > 0) {
+ rem -= 4;
+ fprintf(fd, "| %.2x %.2x %.2x %.2x |\t",
+ 0xff & b[i], 0xff & b[i+1],
+ 0xff & b[i+2], 0xff & b[i+3]);
+ fprintf(fd, "| data |");
+ fprintf(fd, "\t %c %c %c %c\n",
+ isprint(b[i]) ? b[i] : ' ',
+ isprint(b[i+1]) ? b[i+1] : ' ',
+ isprint(b[i+2]) ? b[i+2] : ' ',
+ isprint(b[i+3]) ? b[i+3] : ' ');
+ }
+ }
+ fprintf(fd, "----------------\t------------------\n");
+}
+
+/**
+ * mnl_nlmsg_fprintf - print netlink message to file
+ * \param fd pointer to file type
+ * \param data pointer to the buffer that contains messages to be printed
+ * \param datalen length of data stored in the buffer
+ * \param extra_header_size size of the extra header (if any)
+ *
+ * This function prints the netlink header to a file handle.
+ * It may be useful for debugging purposes. One example of the output
+ * is the following:
+ *
+ *\verbatim
+---------------- ------------------
+| 0000000040 | | message length |
+| 00016 | R-A- | | type | flags |
+| 1289148991 | | sequence number|
+| 0000000000 | | port ID |
+---------------- ------------------
+| 00 00 00 00 | | extra header |
+| 00 00 00 00 | | extra header |
+| 01 00 00 00 | | extra header |
+| 01 00 00 00 | | extra header |
+|00008|--|00003| |len |flags| type|
+| 65 74 68 30 | | data | e t h 0
+---------------- ------------------
+\endverbatim
+ *
+ * This example above shows the netlink message that is send to kernel-space
+ * to set up the link interface eth0. The netlink and attribute header data
+ * are displayed in base 10 whereas the extra header and the attribute payload
+ * are expressed in base 16. The possible flags in the netlink header are:
+ *
+ * - R, that indicates that NLM_F_REQUEST is set.
+ * - M, that indicates that NLM_F_MULTI is set.
+ * - A, that indicates that NLM_F_ACK is set.
+ * - E, that indicates that NLM_F_ECHO is set.
+ *
+ * The lack of one flag is displayed with '-'. On the other hand, the possible
+ * attribute flags available are:
+ *
+ * - N, that indicates that NLA_F_NESTED is set.
+ * - B, that indicates that NLA_F_NET_BYTEORDER is set.
+ */
+EXPORT_SYMBOL void mnl_nlmsg_fprintf(FILE *fd, const void *data, size_t datalen,
+ size_t extra_header_size)
+{
+ const struct nlmsghdr *nlh = data;
+ int len = datalen;
+
+ while (mnl_nlmsg_ok(nlh, len)) {
+ mnl_nlmsg_fprintf_header(fd, nlh);
+ mnl_nlmsg_fprintf_payload(fd, nlh, extra_header_size);
+ nlh = mnl_nlmsg_next(nlh, &len);
+ }
+}
+
+/**
+ * @}
+ */
+
+/**
+ * \defgroup batch Netlink message batch helpers
+ *
+ * This library provides helpers to batch several messages into one single
+ * datagram. These helpers do not perform strict memory boundary checkings.
+ *
+ * The following figure represents a Netlink message batch:
+ *\verbatim
+ |<-------------- MNL_SOCKET_BUFFER_SIZE ------------->|
+ |<-------------------- batch ------------------>| |
+ |-----------|-----------|-----------|-----------|-----------|
+ |<- nlmsg ->|<- nlmsg ->|<- nlmsg ->|<- nlmsg ->|<- nlmsg ->|
+ |-----------|-----------|-----------|-----------|-----------|
+ ^ ^
+ | |
+ message N message N+1
+\endverbatim
+ *
+ * To start the batch, you have to call mnl_nlmsg_batch_start() and you can
+ * use mnl_nlmsg_batch_stop() to release it.
+ *
+ * You have to invoke mnl_nlmsg_batch_next() to get room for a new message
+ * in the batch. If this function returns NULL, it means that the last
+ * message that was added (message N+1 in the figure above) does not fit the
+ * batch. Thus, you have to send the batch (which includes until message N)
+ * and, then, you have to call mnl_nlmsg_batch_reset() to re-initialize
+ * the batch (this moves message N+1 to the head of the buffer). For that
+ * reason, the buffer that you have to use to store the batch must be double
+ * of MNL_SOCKET_BUFFER_SIZE to ensure that the last message (message N+1)
+ * that did not fit into the batch is written inside valid memory boundaries.
+ *
+ * @{
+ */
+
+struct mnl_nlmsg_batch {
+ /* the buffer that is used to store the batch. */
+ void *buf;
+ size_t limit;
+ size_t buflen;
+ /* the current netlink message in the batch. */
+ void *cur;
+ bool overflow;
+};
+
+/**
+ * mnl_nlmsg_batch_start - initialize a batch
+ * \param buf pointer to the buffer that will store this batch
+ * \param limit maximum size of the batch (should be MNL_SOCKET_BUFFER_SIZE).
+ *
+ * The buffer that you pass must be double of MNL_SOCKET_BUFFER_SIZE. The
+ * limit must be half of the buffer size, otherwise expect funny memory
+ * corruptions 8-).
+ *
+ * You can allocate the buffer that you use to store the batch in the stack or
+ * the heap, no restrictions in this regard. This function returns NULL on
+ * error.
+ */
+EXPORT_SYMBOL struct mnl_nlmsg_batch *mnl_nlmsg_batch_start(void *buf,
+ size_t limit)
+{
+ struct mnl_nlmsg_batch *b;
+
+ b = malloc(sizeof(struct mnl_nlmsg_batch));
+ if (b == NULL)
+ return NULL;
+
+ b->buf = buf;
+ b->limit = limit;
+ b->buflen = 0;
+ b->cur = buf;
+ b->overflow = false;
+
+ return b;
+}
+
+/**
+ * mnl_nlmsg_batch_stop - release a batch
+ * \param b pointer to batch
+ *
+ * This function releases the batch allocated by mnl_nlmsg_batch_start().
+ */
+EXPORT_SYMBOL void mnl_nlmsg_batch_stop(struct mnl_nlmsg_batch *b)
+{
+ free(b);
+}
+
+/**
+ * mnl_nlmsg_batch_next - get room for the next message in the batch
+ * \param b pointer to batch
+ *
+ * This function returns false if the last message did not fit into the
+ * batch. Otherwise, it prepares the batch to provide room for the new
+ * Netlink message in the batch and returns true.
+ *
+ * You have to put at least one message in the batch before calling this
+ * function, otherwise your application is likely to crash.
+ */
+EXPORT_SYMBOL bool mnl_nlmsg_batch_next(struct mnl_nlmsg_batch *b)
+{
+ struct nlmsghdr *nlh = b->cur;
+
+ if (b->buflen + nlh->nlmsg_len > b->limit) {
+ b->overflow = true;
+ return false;
+ }
+ b->cur = b->buf + b->buflen + nlh->nlmsg_len;
+ b->buflen += nlh->nlmsg_len;
+ return true;
+}
+
+/**
+ * mnl_nlmsg_batch_reset - reset the batch
+ * \param b pointer to batch
+ *
+ * This function allows you to reset a batch, so you can reuse it to create a
+ * new one. This function moves the last message which does not fit the batch to
+ * the head of the buffer, if any.
+ */
+EXPORT_SYMBOL void mnl_nlmsg_batch_reset(struct mnl_nlmsg_batch *b)
+{
+ if (b->overflow) {
+ struct nlmsghdr *nlh = b->cur;
+ memcpy(b->buf, b->cur, nlh->nlmsg_len);
+ b->buflen = nlh->nlmsg_len;
+ b->cur = b->buf + b->buflen;
+ b->overflow = false;
+ } else {
+ b->buflen = 0;
+ b->cur = b->buf;
+ }
+}
+
+/**
+ * mnl_nlmsg_batch_size - get current size of the batch
+ * \param b pointer to batch
+ *
+ * This function returns the current size of the batch.
+ */
+EXPORT_SYMBOL size_t mnl_nlmsg_batch_size(struct mnl_nlmsg_batch *b)
+{
+ return b->buflen;
+}
+
+/**
+ * mnl_nlmsg_batch_head - get head of this batch
+ * \param b pointer to batch
+ *
+ * This function returns a pointer to the head of the batch, which is the
+ * beginning of the buffer that is used.
+ */
+EXPORT_SYMBOL void *mnl_nlmsg_batch_head(struct mnl_nlmsg_batch *b)
+{
+ return b->buf;
+}
+
+/**
+ * mnl_nlmsg_batch_current - returns current position in the batch
+ * \param b pointer to batch
+ *
+ * This function returns a pointer to the current position in the buffer
+ * that is used to store the batch.
+ */
+EXPORT_SYMBOL void *mnl_nlmsg_batch_current(struct mnl_nlmsg_batch *b)
+{
+ return b->cur;
+}
+
+/**
+ * mnl_nlmsg_batch_is_empty - check if there is any message in the batch
+ * \param b pointer to batch
+ *
+ * This function returns true if the batch is empty.
+ */
+EXPORT_SYMBOL bool mnl_nlmsg_batch_is_empty(struct mnl_nlmsg_batch *b)
+{
+ return b->buflen == 0;
+}
+
+/**
+ * @}
+ */
diff --git a/libmnl/src/socket.c b/libmnl/src/socket.c
new file mode 100644
index 0000000..85b6bcc
--- /dev/null
+++ b/libmnl/src/socket.c
@@ -0,0 +1,351 @@
+/*
+ * (C) 2008-2010 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <libmnl/libmnl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+#include <errno.h>
+#include "internal.h"
+
+/**
+ * \mainpage
+ *
+ * libmnl is a minimalistic user-space library oriented to Netlink developers.
+ * There are a lot of common tasks in parsing, validating, constructing of
+ * both the Netlink header and TLVs that are repetitive and easy to get wrong.
+ * This library aims to provide simple helpers that allows you to avoid
+ * re-inventing the wheel in common Netlink tasks.
+ *
+ * \verbatim
+"Simplify, simplify" -- Henry David Thoureau. Walden (1854)
+\endverbatim
+ *
+ * The acronym libmnl stands for LIBrary Minimalistic NetLink.
+ *
+ * libmnl homepage is:
+ * http://www.netfilter.org/projects/libmnl/
+ *
+ * \section features Main Features
+ * - Small: the shared library requires around 30KB for an x86-based computer.
+ * - Simple: this library avoids complex abstractions that tend to hide Netlink
+ * details. It avoids elaborated object-oriented infrastructure and complex
+ * callback-based workflow.
+ * - Easy to use: the library simplifies the work for Netlink-wise developers.
+ * It provides functions to make socket handling, message building,
+ * validating, parsing and sequence tracking, easier.
+ * - Easy to re-use: you can use this library to build your own abstraction
+ * layer upon this library, if you want to provide another library that
+ * hides Netlink details to your users.
+ * - Decoupling: the interdependency of the main bricks that compose this
+ * library is reduced, i.e. the library provides many helpers, but the
+ * programmer is not forced to use them.
+ *
+ * \section licensing Licensing terms
+ * This library is released under the LGPLv2.1 or any later (at your option).
+ *
+ * \section Dependencies
+ * You have to install the Linux kernel headers that you want to use to develop
+ * your application. Moreover, this library requires that you have some basics
+ * on Netlink.
+ *
+ * \section scm Git Tree
+ * The current development version of libmnl can be accessed at:
+ * https://git.netfilter.org/libmnl/
+ *
+ * \section using Using libmnl
+ * You can access several example files under examples/ in the libmnl source
+ * code tree.
+ */
+
+struct mnl_socket {
+ int fd;
+ struct sockaddr_nl addr;
+};
+
+/**
+ * \defgroup socket Netlink socket helpers
+ * @{
+ */
+
+/**
+ * mnl_socket_get_fd - obtain file descriptor from netlink socket
+ * \param nl netlink socket obtained via mnl_socket_open()
+ *
+ * This function returns the file descriptor of a given netlink socket.
+ */
+EXPORT_SYMBOL int mnl_socket_get_fd(const struct mnl_socket *nl)
+{
+ return nl->fd;
+}
+
+/**
+ * mnl_socket_get_portid - obtain Netlink PortID from netlink socket
+ * \param nl netlink socket obtained via mnl_socket_open()
+ *
+ * This function returns the Netlink PortID of a given netlink socket.
+ * It's a common mistake to assume that this PortID equals the process ID
+ * which is not always true. This is the case if you open more than one
+ * socket that is binded to the same Netlink subsystem from the same process.
+ */
+EXPORT_SYMBOL unsigned int mnl_socket_get_portid(const struct mnl_socket *nl)
+{
+ return nl->addr.nl_pid;
+}
+
+static struct mnl_socket *__mnl_socket_open(int bus, int flags)
+{
+ struct mnl_socket *nl;
+
+ nl = calloc(1, sizeof(struct mnl_socket));
+ if (nl == NULL)
+ return NULL;
+
+ nl->fd = socket(AF_NETLINK, SOCK_RAW | flags, bus);
+ if (nl->fd == -1) {
+ free(nl);
+ return NULL;
+ }
+
+ return nl;
+}
+
+/**
+ * mnl_socket_open - open a netlink socket
+ * \param bus the netlink socket bus ID (see NETLINK_* constants)
+ *
+ * On error, it returns NULL and errno is appropriately set. Otherwise, it
+ * returns a valid pointer to the mnl_socket structure.
+ */
+EXPORT_SYMBOL struct mnl_socket *mnl_socket_open(int bus)
+{
+ return __mnl_socket_open(bus, 0);
+}
+
+/**
+ * mnl_socket_open2 - open a netlink socket with appropriate flags
+ * \param bus the netlink socket bus ID (see NETLINK_* constants)
+ * \param flags the netlink socket flags (see SOCK_* constants in socket(2))
+ *
+ * This is similar to mnl_socket_open(), but allows one to set flags like
+ * SOCK_CLOEXEC at socket creation time (useful for multi-threaded programs
+ * performing exec calls).
+ *
+ * On error, it returns NULL and errno is appropriately set. Otherwise, it
+ * returns a valid pointer to the mnl_socket structure.
+ */
+EXPORT_SYMBOL struct mnl_socket *mnl_socket_open2(int bus, int flags)
+{
+ return __mnl_socket_open(bus, flags);
+}
+
+/**
+ * mnl_socket_fdopen - associates a mnl_socket object with pre-existing socket.
+ * \param fd pre-existing socket descriptor.
+ *
+ * On error, it returns NULL and errno is appropriately set. Otherwise, it
+ * returns a valid pointer to the mnl_socket structure. It also sets the portID
+ * if the socket fd is already bound and it is AF_NETLINK.
+ *
+ * Note that mnl_socket_get_portid() returns 0 if this function is used with
+ * non-netlink socket.
+ */
+EXPORT_SYMBOL struct mnl_socket *mnl_socket_fdopen(int fd)
+{
+ int ret;
+ struct mnl_socket *nl;
+ struct sockaddr_nl addr;
+ socklen_t addr_len = sizeof(struct sockaddr_nl);
+
+ ret = getsockname(fd, (struct sockaddr *) &addr, &addr_len);
+ if (ret == -1)
+ return NULL;
+
+ nl = calloc(1, sizeof(struct mnl_socket));
+ if (nl == NULL)
+ return NULL;
+
+ nl->fd = fd;
+ if (addr.nl_family == AF_NETLINK)
+ nl->addr = addr;
+
+ return nl;
+}
+
+/**
+ * mnl_socket_bind - bind netlink socket
+ * \param nl netlink socket obtained via mnl_socket_open()
+ * \param groups the group of message you're interested in
+ * \param pid the port ID you want to use (use zero for automatic selection)
+ *
+ * On error, this function returns -1 and errno is appropriately set. On
+ * success, 0 is returned. You can use MNL_SOCKET_AUTOPID which is 0 for
+ * automatic port ID selection.
+ */
+EXPORT_SYMBOL int mnl_socket_bind(struct mnl_socket *nl, unsigned int groups,
+ pid_t pid)
+{
+ int ret;
+ socklen_t addr_len;
+
+ nl->addr.nl_family = AF_NETLINK;
+ nl->addr.nl_groups = groups;
+ nl->addr.nl_pid = pid;
+
+ ret = bind(nl->fd, (struct sockaddr *) &nl->addr, sizeof (nl->addr));
+ if (ret < 0)
+ return ret;
+
+ addr_len = sizeof(nl->addr);
+ ret = getsockname(nl->fd, (struct sockaddr *) &nl->addr, &addr_len);
+ if (ret < 0)
+ return ret;
+
+ if (addr_len != sizeof(nl->addr)) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (nl->addr.nl_family != AF_NETLINK) {
+ errno = EINVAL;
+ return -1;
+ }
+ return 0;
+}
+
+/**
+ * mnl_socket_sendto - send a netlink message of a certain size
+ * \param nl netlink socket obtained via mnl_socket_open()
+ * \param buf buffer containing the netlink message to be sent
+ * \param len number of bytes in the buffer that you want to send
+ *
+ * On error, it returns -1 and errno is appropriately set. Otherwise, it
+ * returns the number of bytes sent.
+ */
+EXPORT_SYMBOL ssize_t mnl_socket_sendto(const struct mnl_socket *nl,
+ const void *buf, size_t len)
+{
+ static const struct sockaddr_nl snl = {
+ .nl_family = AF_NETLINK
+ };
+ return sendto(nl->fd, buf, len, 0,
+ (struct sockaddr *) &snl, sizeof(snl));
+}
+
+/**
+ * mnl_socket_recvfrom - receive a netlink message
+ * \param nl netlink socket obtained via mnl_socket_open()
+ * \param buf buffer that you want to use to store the netlink message
+ * \param bufsiz size of the buffer passed to store the netlink message
+ *
+ * On error, it returns -1 and errno is appropriately set. If errno is set
+ * to ENOSPC, it means that the buffer that you have passed to store the
+ * netlink message is too small, so you have received a truncated message.
+ * To avoid this, you have to allocate a buffer of MNL_SOCKET_BUFFER_SIZE
+ * (which is 8KB, see linux/netlink.h for more information). Using this
+ * buffer size ensures that your buffer is big enough to store the netlink
+ * message without truncating it.
+ */
+EXPORT_SYMBOL ssize_t mnl_socket_recvfrom(const struct mnl_socket *nl,
+ void *buf, size_t bufsiz)
+{
+ ssize_t ret;
+ struct sockaddr_nl addr;
+ struct iovec iov = {
+ .iov_base = buf,
+ .iov_len = bufsiz,
+ };
+ struct msghdr msg = {
+ .msg_name = &addr,
+ .msg_namelen = sizeof(struct sockaddr_nl),
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ .msg_control = NULL,
+ .msg_controllen = 0,
+ .msg_flags = 0,
+ };
+ ret = recvmsg(nl->fd, &msg, 0);
+ if (ret == -1)
+ return ret;
+
+ if (msg.msg_flags & MSG_TRUNC) {
+ errno = ENOSPC;
+ return -1;
+ }
+ if (msg.msg_namelen != sizeof(struct sockaddr_nl)) {
+ errno = EINVAL;
+ return -1;
+ }
+ return ret;
+}
+
+/**
+ * mnl_socket_close - close a given netlink socket
+ * \param nl netlink socket obtained via mnl_socket_open()
+ *
+ * On error, this function returns -1 and errno is appropriately set.
+ * On success, it returns 0.
+ */
+EXPORT_SYMBOL int mnl_socket_close(struct mnl_socket *nl)
+{
+ int ret = close(nl->fd);
+ free(nl);
+ return ret;
+}
+
+/**
+ * mnl_socket_setsockopt - set Netlink socket option
+ * \param nl netlink socket obtained via mnl_socket_open()
+ * \param type type of Netlink socket options
+ * \param buf the buffer that contains the data about this option
+ * \param len the size of the buffer passed
+ *
+ * This function allows you to set some Netlink socket option. As of writing
+ * this (see linux/netlink.h), the existing options are:
+ *
+ * - \#define NETLINK_ADD_MEMBERSHIP 1
+ * - \#define NETLINK_DROP_MEMBERSHIP 2
+ * - \#define NETLINK_PKTINFO 3
+ * - \#define NETLINK_BROADCAST_ERROR 4
+ * - \#define NETLINK_NO_ENOBUFS 5
+ *
+ * In the early days, Netlink only supported 32 groups expressed in a
+ * 32-bits mask. However, since 2.6.14, Netlink may have up to 2^32 multicast
+ * groups but you have to use setsockopt() with NETLINK_ADD_MEMBERSHIP to
+ * join a given multicast group. This function internally calls setsockopt()
+ * to join a given netlink multicast group. You can still use mnl_bind()
+ * and the 32-bit mask to join a set of Netlink multicast groups.
+ *
+ * On error, this function returns -1 and errno is appropriately set.
+ */
+EXPORT_SYMBOL int mnl_socket_setsockopt(const struct mnl_socket *nl, int type,
+ void *buf, socklen_t len)
+{
+ return setsockopt(nl->fd, SOL_NETLINK, type, buf, len);
+}
+
+/**
+ * mnl_socket_getsockopt - get a Netlink socket option
+ * \param nl netlink socket obtained via mnl_socket_open()
+ * \param type type of Netlink socket options
+ * \param buf pointer to the buffer to store the value of this option
+ * \param len size of the information written in the buffer
+ *
+ * On error, this function returns -1 and errno is appropriately set.
+ */
+EXPORT_SYMBOL int mnl_socket_getsockopt(const struct mnl_socket *nl, int type,
+ void *buf, socklen_t *len)
+{
+ return getsockopt(nl->fd, SOL_NETLINK, type, buf, len);
+}
+
+/**
+ * @}
+ */