Merge remote-tracking branch 'origin/third-party-review'
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..9a7d86c
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,19 @@
+*~
+*.la
+*.lo
+*.o
+.deps/
+.libs/
+Makefile
+Makefile.in
+
+/aclocal.m4
+/autom4te.cache/
+/build-aux/
+/config.*
+/configure
+/libtool
+/stamp-h1
+
+/doxygen.cfg
+/libmnl.pc
diff --git a/Android.bp b/Android.bp
new file mode 100644
index 0000000..2351544
--- /dev/null
+++ b/Android.bp
@@ -0,0 +1,31 @@
+package {
+    default_applicable_licenses: ["external_libmnl_license"],
+}
+
+license {
+    name: "external_libmnl_license",
+    visibility: [":__subpackages__"],
+    license_kinds: ["SPDX-license-identifier-LGPL-2.1"],
+    license_text: ["LICENSE"],
+}
+
+cc_library_shared {
+    name: "libmnl",
+    srcs: [
+        "src/attr.c",
+        "src/callback.c",
+        "src/nlmsg.c",
+        "src/socket.c",
+    ],
+    export_include_dirs: ["include"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wno-gnu-pointer-arith",
+    ],
+    vendor_available: true,
+
+    // b/479773959: Restrict visibility once Soong support is available.
+    // For now, use __subpackages__ to unblock development.
+    visibility: ["//vendor:__subpackages__"],
+}
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..4362b49
--- /dev/null
+++ b/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/LICENSE b/LICENSE
new file mode 120000
index 0000000..d24842f
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1 @@
+COPYING
\ No newline at end of file
diff --git a/METADATA b/METADATA
new file mode 100644
index 0000000..4a78cd4
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,15 @@
+name: "libmnl"
+description: "Minimalistic Netlink Library"
+third_party {
+  url {
+    type: GIT
+    value: "https://git.netfilter.org/libmnl"
+  }
+  version: "libmnl-1.0.5"
+  license_type: RESTRICTED
+  last_upgrade_date {
+    year: 2026
+    month: 1
+    day: 19
+  }
+}
diff --git a/MODULE_LICENSE_LGPL b/MODULE_LICENSE_LGPL
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/MODULE_LICENSE_LGPL
diff --git a/Make_global.am b/Make_global.am
new file mode 100644
index 0000000..7810dab
--- /dev/null
+++ b/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/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..94e6935
--- /dev/null
+++ b/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/OWNERS.android b/OWNERS.android
new file mode 100644
index 0000000..2e8f086
--- /dev/null
+++ b/OWNERS.android
@@ -0,0 +1 @@
+include platform/system/core:main:/janitors/OWNERS
diff --git a/README b/README
new file mode 100644
index 0000000..fbac9d2
--- /dev/null
+++ b/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/autogen.sh b/autogen.sh
new file mode 100755
index 0000000..5e1344a
--- /dev/null
+++ b/autogen.sh
@@ -0,0 +1,4 @@
+#!/bin/sh -e
+
+autoreconf -fi
+rm -Rf autom4te.cache
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..314481d
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,56 @@
+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-bzip2 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/netfilter/Makefile examples/Makefile examples/genl/Makefile examples/kobject/Makefile examples/netfilter/Makefile examples/rtnl/Makefile libmnl.pc 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/doxygen.cfg.in b/doxygen.cfg.in
new file mode 100644
index 0000000..ae31dbe
--- /dev/null
+++ b/doxygen.cfg.in
@@ -0,0 +1,23 @@
+# Difference with default Doxyfile 1.8.20
+PROJECT_NAME           = @PACKAGE@
+PROJECT_NUMBER         = @VERSION@
+OUTPUT_DIRECTORY       = doxygen
+ABBREVIATE_BRIEF       =
+FULL_PATH_NAMES        = NO
+TAB_SIZE               = 8
+OPTIMIZE_OUTPUT_FOR_C  = YES
+INPUT                  = .
+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/doxygen/Makefile.am b/doxygen/Makefile.am
new file mode 100644
index 0000000..29078de
--- /dev/null
+++ b/doxygen/Makefile.am
@@ -0,0 +1,66 @@
+if HAVE_DOXYGEN
+
+doc_srcs = $(shell find $(top_srcdir)/src -name '*.c')
+
+doxyfile.stamp: $(doc_srcs) Makefile.am
+	rm -rf html man
+
+# Test for running under make distcheck.
+# If so, sibling src directory will be empty:
+# move it out of the way and symlink the real one while we run doxygen.
+	[ -f ../src/Makefile.in ] || \
+{ set -x; cd ..; mv src src.distcheck; ln -s $(top_srcdir)/src; }
+
+	cd ..; doxygen doxygen.cfg >/dev/null
+
+	[ ! -d ../src.distcheck ] || \
+{ set -x; cd ..; rm src; mv src.distcheck src; }
+
+# We need to use bash for its associative array facility
+# (`bash -p` prevents import of functions from the environment).
+# The command has to be a single line so the functions work
+# and so `make` gives all lines to `bash -c`
+# (hence ";\" at the end of every line but the last).
+	/bin/bash -p -c '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'
+
+	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
diff --git a/examples/Makefile.am b/examples/Makefile.am
new file mode 100644
index 0000000..e5cb052
--- /dev/null
+++ b/examples/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = genl kobject netfilter rtnl
diff --git a/examples/genl/.gitignore b/examples/genl/.gitignore
new file mode 100644
index 0000000..57f0ca9
--- /dev/null
+++ b/examples/genl/.gitignore
@@ -0,0 +1 @@
+/genl-family-get
diff --git a/examples/genl/Makefile.am b/examples/genl/Makefile.am
new file mode 100644
index 0000000..b4b7954
--- /dev/null
+++ b/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/examples/genl/genl-family-get.c b/examples/genl/genl-family-get.c
new file mode 100644
index 0000000..ba8de12
--- /dev/null
+++ b/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/examples/genl/genl-group-events.c b/examples/genl/genl-group-events.c
new file mode 100644
index 0000000..d5f0a18
--- /dev/null
+++ b/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/examples/kobject/Makefile.am b/examples/kobject/Makefile.am
new file mode 100644
index 0000000..9197f7a
--- /dev/null
+++ b/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/examples/kobject/kobject-event.c b/examples/kobject/kobject-event.c
new file mode 100644
index 0000000..97debdf
--- /dev/null
+++ b/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/examples/netfilter/.gitignore b/examples/netfilter/.gitignore
new file mode 100644
index 0000000..f2f863f
--- /dev/null
+++ b/examples/netfilter/.gitignore
@@ -0,0 +1,3 @@
+/nf-log
+/nf-queue
+/nfct-event
diff --git a/examples/netfilter/Makefile.am b/examples/netfilter/Makefile.am
new file mode 100644
index 0000000..4bae05f
--- /dev/null
+++ b/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/examples/netfilter/nf-log.c b/examples/netfilter/nf-log.c
new file mode 100644
index 0000000..4383b66
--- /dev/null
+++ b/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/examples/netfilter/nf-queue.c b/examples/netfilter/nf-queue.c
new file mode 100644
index 0000000..957e365
--- /dev/null
+++ b/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/examples/netfilter/nfct-create-batch.c b/examples/netfilter/nfct-create-batch.c
new file mode 100644
index 0000000..4675789
--- /dev/null
+++ b/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/examples/netfilter/nfct-daemon.c b/examples/netfilter/nfct-daemon.c
new file mode 100644
index 0000000..d223ac2
--- /dev/null
+++ b/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/examples/netfilter/nfct-dump.c b/examples/netfilter/nfct-dump.c
new file mode 100644
index 0000000..cb8e52c
--- /dev/null
+++ b/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/examples/netfilter/nfct-event.c b/examples/netfilter/nfct-event.c
new file mode 100644
index 0000000..94603d4
--- /dev/null
+++ b/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/examples/rtnl/.gitignore b/examples/rtnl/.gitignore
new file mode 100644
index 0000000..24ac633
--- /dev/null
+++ b/examples/rtnl/.gitignore
@@ -0,0 +1,8 @@
+/rtnl-addr-add
+/rtnl-link-dump
+/rtnl-link-dump2
+/rtnl-link-dump3
+/rtnl-link-event
+/rtnl-link-set
+/rtnl-route-add
+/rtnl-route-dump
diff --git a/examples/rtnl/Makefile.am b/examples/rtnl/Makefile.am
new file mode 100644
index 0000000..dd8a77d
--- /dev/null
+++ b/examples/rtnl/Makefile.am
@@ -0,0 +1,44 @@
+include $(top_srcdir)/Make_global.am
+
+check_PROGRAMS = rtnl-addr-add \
+		 rtnl-addr-dump \
+		 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_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/examples/rtnl/rtnl-addr-add.c b/examples/rtnl/rtnl-addr-add.c
new file mode 100644
index 0000000..f9a69dc
--- /dev/null
+++ b/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/examples/rtnl/rtnl-addr-dump.c b/examples/rtnl/rtnl-addr-dump.c
new file mode 100644
index 0000000..675e9b0
--- /dev/null
+++ b/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/examples/rtnl/rtnl-link-dump.c b/examples/rtnl/rtnl-link-dump.c
new file mode 100644
index 0000000..031346f
--- /dev/null
+++ b/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/examples/rtnl/rtnl-link-dump2.c b/examples/rtnl/rtnl-link-dump2.c
new file mode 100644
index 0000000..890e51a
--- /dev/null
+++ b/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/examples/rtnl/rtnl-link-dump3.c b/examples/rtnl/rtnl-link-dump3.c
new file mode 100644
index 0000000..a381da1
--- /dev/null
+++ b/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/examples/rtnl/rtnl-link-event.c b/examples/rtnl/rtnl-link-event.c
new file mode 100644
index 0000000..123fb88
--- /dev/null
+++ b/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/examples/rtnl/rtnl-link-set.c b/examples/rtnl/rtnl-link-set.c
new file mode 100644
index 0000000..6086c37
--- /dev/null
+++ b/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/examples/rtnl/rtnl-neigh-dump.c b/examples/rtnl/rtnl-neigh-dump.c
new file mode 100644
index 0000000..786e31d
--- /dev/null
+++ b/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 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_GETNEIGH;
+	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/examples/rtnl/rtnl-route-add.c b/examples/rtnl/rtnl-route-add.c
new file mode 100644
index 0000000..97578cd
--- /dev/null
+++ b/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/examples/rtnl/rtnl-route-dump.c b/examples/rtnl/rtnl-route-dump.c
new file mode 100644
index 0000000..02ac6b2
--- /dev/null
+++ b/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/examples/rtnl/rtnl-route-event.c b/examples/rtnl/rtnl-route-event.c
new file mode 100644
index 0000000..6cad9f0
--- /dev/null
+++ b/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/include/Makefile.am b/include/Makefile.am
new file mode 100644
index 0000000..bc92c42
--- /dev/null
+++ b/include/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = libmnl linux
diff --git a/include/config.h b/include/config.h
new file mode 100644
index 0000000..e903671
--- /dev/null
+++ b/include/config.h
@@ -0,0 +1,67 @@
+/* config.h.  Generated from config.h.in by configure.  */
+/* config.h.in.  Generated from configure.ac by autoheader.  */
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#define HAVE_DLFCN_H 1
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdio.h> header file. */
+#define HAVE_STDIO_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* True if compiler supports -fvisibility=hidden */
+#define HAVE_VISIBILITY_HIDDEN 1
+
+/* Define to the sub-directory where libtool stores uninstalled libraries. */
+#define LT_OBJDIR ".libs/"
+
+/* Name of package */
+#define PACKAGE "libmnl"
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT ""
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "libmnl"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "libmnl 1.0.5"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "libmnl"
+
+/* Define to the home page for this package. */
+#define PACKAGE_URL ""
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "1.0.5"
+
+/* Define to 1 if all of the C89 standard headers exist (not just the ones
+   required in a freestanding environment). This macro is provided for
+   backward compatibility; new code need not use it. */
+#define STDC_HEADERS 1
+
+/* Version number of package */
+#define VERSION "1.0.5"
diff --git a/include/libmnl/Makefile.am b/include/libmnl/Makefile.am
new file mode 100644
index 0000000..b03f68a
--- /dev/null
+++ b/include/libmnl/Makefile.am
@@ -0,0 +1 @@
+pkginclude_HEADERS = libmnl.h
diff --git a/include/libmnl/libmnl.h b/include/libmnl/libmnl.h
new file mode 100644
index 0000000..4bd0b92
--- /dev/null
+++ b/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/include/linux/Makefile.am b/include/linux/Makefile.am
new file mode 100644
index 0000000..08c600b
--- /dev/null
+++ b/include/linux/Makefile.am
@@ -0,0 +1,2 @@
+SUBDIRS = netfilter
+noinst_HEADERS = netlink.h socket.h
diff --git a/include/linux/netfilter/Makefile.am b/include/linux/netfilter/Makefile.am
new file mode 100644
index 0000000..64a975e
--- /dev/null
+++ b/include/linux/netfilter/Makefile.am
@@ -0,0 +1 @@
+noinst_HEADERS = nfnetlink_conntrack.h
diff --git a/include/linux/netfilter/nfnetlink_conntrack.h b/include/linux/netfilter/nfnetlink_conntrack.h
new file mode 100644
index 0000000..08fabc6
--- /dev/null
+++ b/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/include/linux/netlink.h b/include/linux/netlink.h
new file mode 100644
index 0000000..ced0e1a
--- /dev/null
+++ b/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/include/linux/socket.h b/include/linux/socket.h
new file mode 100644
index 0000000..8c1e501
--- /dev/null
+++ b/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.pc.in b/libmnl.pc.in
new file mode 100644
index 0000000..8a24315
--- /dev/null
+++ b/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/m4/.gitignore b/m4/.gitignore
new file mode 100644
index 0000000..64d9bbc
--- /dev/null
+++ b/m4/.gitignore
@@ -0,0 +1,2 @@
+/libtool.m4
+/lt*.m4
diff --git a/m4/gcc4_visibility.m4 b/m4/gcc4_visibility.m4
new file mode 100644
index 0000000..214d3f3
--- /dev/null
+++ b/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/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..9aab516
--- /dev/null
+++ b/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/src/attr.c b/src/attr.c
new file mode 100644
index 0000000..838eab0
--- /dev/null
+++ b/src/attr.c
@@ -0,0 +1,722 @@
+/*
+ * (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
+ *
+ * This function returns 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
+ *
+ * This function returns the attribute length that is 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
+ *
+ * This function returns 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
+ *
+ * This function return a 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. Thus, it returns true on success and false on error.
+ *
+ * The len parameter may be negative in the case of malformed messages during
+ * attribute iteration, that is why we use a signed integer.
+ */
+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
+ *
+ * This function returns a pointer to the next attribute after the one passed
+ * as parameter. You have to use mnl_attr_ok() 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 to check if the attribute type is higher than the
+ * maximum supported type. If the attribute type is invalid, this function
+ * returns -1 and errno is explicitly set. On success, this function returns 1.
+ *
+ * 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.
+ */
+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. This function
+ * returns -1 in case of error, and errno is explicitly set.
+ */
+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 to perform a more accurate validation for attributes
+ * whose size is variable. If the size of the attribute is not what we expect,
+ * this functions returns -1 and errno is explicitly set.
+ */
+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 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).
+ *
+ * This function propagates the return value of the callback, which can be
+ * MNL_CB_ERROR, MNL_CB_OK or MNL_CB_STOP.
+ */
+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 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).
+ *
+ * This function propagates the return value of the callback, which can be
+ * MNL_CB_ERROR, MNL_CB_OK or MNL_CB_STOP.
+ */
+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).
+ *
+ * This function propagates the return value of the callback, which can be
+ * MNL_CB_ERROR, MNL_CB_OK or MNL_CB_STOP.
+ */
+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 - returns 8-bit unsigned integer attribute payload
+ * \param attr pointer to netlink attribute
+ *
+ * This function returns the 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 - returns 16-bit unsigned integer attribute payload
+ * \param attr pointer to netlink attribute
+ *
+ * This function returns the 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 - returns 32-bit unsigned integer attribute payload
+ * \param attr pointer to netlink attribute
+ *
+ * This function returns the 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 - returns 64-bit unsigned integer attribute.
+ * \param attr pointer to netlink attribute
+ *
+ * This function returns the 64-bit value of the attribute payload. This
+ * function is align-safe, since accessing 64-bit Netlink attributes is a
+ * common source of alignment issues.
+ */
+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 - returns pointer to string attribute.
+ * \param attr pointer to netlink attribute
+ *
+ * This function returns the payload of string attribute value.
+ */
+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. This function always returns a 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. The function returns true if the attribute could be added
+ * to the message, otherwise false is returned.
+ */
+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. The function returns true if the attribute could be added
+ * to the message, otherwise false is returned.
+ */
+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. The function returns true if the attribute could be added
+ * to the message, otherwise false is returned.
+ * This function updates the length field of the Netlink message (nlmsg_len)
+ * by adding the size (header + payload) of the new attribute.
+ */
+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. The function returns true if the attribute could be added
+ * to the message, otherwise false is returned.
+ * This function updates the length field of the Netlink message (nlmsg_len)
+ * by adding the size (header + payload) of the new attribute.
+ */
+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. The function returns true if the attribute could be added
+ * to the message, otherwise false is returned.
+ * This function updates the length field of the Netlink message (nlmsg_len)
+ * by adding the size (header + payload) of the new attribute.
+ */
+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. The function returns true if the attribute could be added
+ * to the message, otherwise false is returned.
+ * This function updates the length field of the Netlink message (nlmsg_len)
+ * by adding the size (header + payload) of the new attribute.
+ */
+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. The function returns true if the attribute could be added
+ * to the message, otherwise false is returned.
+ */
+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. If the nested attribute cannot be added then NULL,
+ * otherwise valid pointer to the beginning of the nest is returned.
+ */
+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/src/callback.c b/src/callback.c
new file mode 100644
index 0000000..f5349c3
--- /dev/null
+++ b/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/src/internal.h b/src/internal.h
new file mode 100644
index 0000000..d69eaf3
--- /dev/null
+++ b/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/src/libmnl.map b/src/libmnl.map
new file mode 100644
index 0000000..e5920e5
--- /dev/null
+++ b/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/src/nlmsg.c b/src/nlmsg.c
new file mode 100644
index 0000000..ce37cbc
--- /dev/null
+++ b/src/nlmsg.c
@@ -0,0 +1,557 @@
+/*
+ * (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 <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_nlmsg_fprintf_payload(FILE *fd, const struct nlmsghdr *nlh,
+				      size_t extra_header_size)
+{
+	int rem = 0;
+	unsigned int i;
+
+	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) {
+			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);
+			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 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/src/socket.c b/src/socket.c
new file mode 100644
index 0000000..dbfb06c
--- /dev/null
+++ b/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 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);
+}
+
+/**
+ * @}
+ */