diff --git a/.gitignore b/.gitignore index 17a67a0..f9fe4bd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,28 +1,25 @@ .deps .libs .dirstamp -*.in *.la *.lo *.o *.swp Makefile +Makefile.in +defs.h.in +defs.h.in~ /lib/stamp-h1 /libnl-1.pc -/doc/Doxyfile /lib/defs.h cscope.* /aclocal.m4 /autom4te.cache -/compile +/build-aux/ /config.* /configure -/depcomp /libtool -/ltmain.sh -/install-sh -/missing - /*.pc + diff --git a/COPYING b/COPYING index 371ec20..4362b49 100644 --- a/COPYING +++ b/COPYING @@ -1,9 +1,8 @@ - GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. - 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + 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. @@ -23,8 +22,7 @@ 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. +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 @@ -89,9 +87,9 @@ 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 +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. @@ -138,8 +136,8 @@ 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. +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 @@ -305,10 +303,10 @@ of these things: 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. + 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 @@ -386,10 +384,9 @@ 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. +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 @@ -407,11 +404,11 @@ 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. +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. @@ -459,3 +456,47 @@ 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. + + + Copyright (C) + + 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. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/ChangeLog b/ChangeLog index 6dc334b..cdd78a8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,5 @@ ChangeLog discontinued, git history can be found here: -http://git.kernel.org/?p=libs/netlink/libnl.git +http://git.infradead.org/users/tgr/libnl.git Summary of Changes from 1.0-pre6 to 1.0-pre7 ================================================ diff --git a/Makefile.am b/Makefile.am index 5931a9e..bc4266d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -2,19 +2,21 @@ ACLOCAL_AMFLAGS = -I m4 -OPT_DIRS = +SUBDIRS = include lib man python tests + +pkgconfig_DATA = libnl-3.0.pc \ + libnl-route-3.0.pc \ + libnl-genl-3.0.pc \ + libnl-nf-3.0.pc if ENABLE_CLI -OPT_DIRS += src +SUBDIRS += src +pkgconfig_DATA += libnl-cli-3.0.pc endif -SUBDIRS = include lib doc man $(OPT_DIRS) -pkgconfig_DATA = libnl-3.0.pc +pkgsysconfdir = ${sysconfdir}/libnl +pkgsysconf_DATA = etc/pktloc etc/classid -sysconfdir = @sysconfdir@/libnl -sysconf_DATA = etc/pktloc etc/classid - -.PHONY: cscope -cscope: - cscope -b -q -R -Iinclude -slib -ssrc; +EXTRA_DIST = \ + $(pkgsysconf_DATA) diff --git a/autogen.sh b/autogen.sh index a569614..c3538bb 100755 --- a/autogen.sh +++ b/autogen.sh @@ -2,3 +2,5 @@ autoreconf -fi; rm -Rf autom4te.cache; + +doc/autogen.sh diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..7797c3d --- /dev/null +++ b/configure.ac @@ -0,0 +1,163 @@ +# +# configure.in +# +# 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 version 2.1 +# of the License. +# +# Copyright (c) 2003-2013 Thomas Graf +# + + +# copied from glib +m4_define([libnl_major_version], [3]) +m4_define([libnl_minor_version], [2]) +m4_define([libnl_micro_version], [21]) + + +# The following explanation may help to understand the above rules a bit +# better: consider that there are three possible kinds of reactions from +# users of your library to changes in a shared library: +# +# 1. Programs using the previous version may use the new version as drop-in +# replacement, and programs using the new version can also work with the +# previous one. In other words, no recompiling nor relinking is needed. +# In this case, bump revision only, don't touch current nor age. +# +# 2. Programs using the previous version may use the new version as drop-in +# replacement, but programs using the new version may use APIs not +# present in the previous one. In other words, a program linking against +# the new version may fail with “unresolved symbols” if linking against +# the old version at runtime: set revision to 0, bump current and age. +# +# 3. Programs may need to be changed, recompiled, relinked in order to use +# the new version. Bump current, set revision and age to 0. + +m4_define([libnl_lt_current], [216]) +m4_define([libnl_lt_revision], [1]) +m4_define([libnl_lt_age], [16]) + +m4_define([libnl_version], + [libnl_major_version.libnl_minor_version.libnl_micro_version]) + +AC_INIT(libnl, [libnl_version], [], [], [http://www.infradead.org/~tgr/libnl/]) +AC_CONFIG_HEADERS([lib/defs.h]) +AC_CONFIG_AUX_DIR([build-aux]) +AC_CONFIG_MACRO_DIR([m4]) +AM_INIT_AUTOMAKE([-Wall foreign subdir-objects]) +m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES(yes)], []) +m4_ifdef([AM_PROG_AR], [AM_PROG_AR]) + +MAJ_VERSION=libnl_major_version +AC_SUBST(MAJ_VERSION) +MIN_VERSION=libnl_minor_version +AC_SUBST(MIN_VERSION) +MIC_VERSION=libnl_micro_version +AC_SUBST(MIC_VERSION) +LIBNL_VERSION=libnl_version +AC_SUBST(LIBNL_VERSION) + +LT_CURRENT=libnl_lt_current +AC_SUBST(LT_CURRENT) +LT_REVISION=libnl_lt_revision +AC_SUBST(LT_REVISION) +LT_AGE=libnl_lt_age +AC_SUBST(LT_AGE) + +AC_PROG_CC +AM_PROG_CC_C_O +AC_PROG_INSTALL +AM_PROG_LIBTOOL +AC_CHECK_PROGS(FLEX, 'flex') +AC_CHECK_PROGS(YACC, 'bison -y') + +AC_C_CONST +AC_C_INLINE + +AM_PATH_CHECK() + +AC_ARG_WITH([pkgconfigdir], AS_HELP_STRING([--with-pkgconfigdir=PATH], + [Path to the pkgconfig directory [[LIBDIR/pkgconfig]]]), + [pkgconfigdir="$withval"], [pkgconfigdir='${libdir}/pkgconfig']) +AC_SUBST([pkgconfigdir]) + +AC_ARG_ENABLE([cli], + AS_HELP_STRING([--disable-cli], [Do not build command line interface utils]), + [enable_cli="$enableval"], [enable_cli="yes"]) +AM_CONDITIONAL([ENABLE_CLI], [test "$enable_cli" = "yes"]) + +AC_ARG_ENABLE([pthreads], + AS_HELP_STRING([--disable-pthreads], [Disable pthreads support]), + [enable_pthreads="$enableval"], [enable_pthreads="yes"]) +AM_CONDITIONAL([DISABLE_PTHREADS], [test "$enable_pthreads" = "no"]) + +AC_CHECK_LIB([m], [pow], [], AC_MSG_ERROR([libm is required])) + +if test "x$enable_pthreads" = "xno"; then + AC_DEFINE([DISABLE_PTHREADS], [1], [Define to 1 to disable pthreads]) +else + AC_CHECK_LIB([pthread], [pthread_mutex_lock], [], AC_MSG_ERROR([libpthread is required])) +fi + +AC_CONFIG_SUBDIRS([doc]) + +AC_CONFIG_FILES([ +Makefile +libnl-3.0.pc +libnl-route-3.0.pc +libnl-genl-3.0.pc +libnl-nf-3.0.pc +libnl-dect-3.0.pc +libnl-cli-3.0.pc +lib/Makefile +include/Makefile +src/Makefile +src/lib/Makefile +tests/Makefile +man/Makefile +python/Makefile +python/setup.py +python/netlink/Makefile +python/netlink/route/Makefile +include/netlink/version.h +]) + +ac_errcount=0 +if test -z "$YACC"; then + AC_MSG_WARN(bison not found. Please install before continuing.) + ac_errcount=$((ac_errcount + 1)) +fi +if test -z "$FLEX"; then + AC_MSG_WARN(flex not found. Please install before continuing.) + ac_errcount=$((ac_errcount + 1)) +fi +if test $ac_errcount -gt 0; then + AC_MSG_ERROR(Required packages are missing. Please install them and rerun ./configure) +fi + +AC_OUTPUT + +echo "-------------------------------------------------------------------------------" +echo " NOTE" +echo "" +echo " There have been some changes starting with 3.2 regarding where and how libnl" +echo " is being installed on the system in order to allow multiple libnl versions" +echo " to be installed in parallel:" +echo "" +echo " - Headers will be installed in ${includedir}/libnl${MAJ_VERSION}, therefore" +echo " you will need to add \"-I/usr/include/libnl${MAJ_VERSION}\" to CFLAGS" +echo "" +echo " - The library basename was renamed to libnl-${MAJ_VERSION}, i.e. the SO names become" +echo " libnl-${MAJ_VERSION}.so., libnl-route-${MAJ_VERSION}.so, etc." +echo "" +echo " - libtool versioning was assumed, to ease detection of compatible library" +echo " versions." +echo "" +echo " If you are using pkg-config for detecting and linking against the library " +echo " things will continue magically as if nothing every happened. If you are " +echo " linking manually you need to adapt your Makefiles or switch to using " +echo " pkg-config files." +echo "" +echo "-------------------------------------------------------------------------------" + diff --git a/configure.in b/configure.in deleted file mode 100644 index 43150a7..0000000 --- a/configure.in +++ /dev/null @@ -1,48 +0,0 @@ -# -# configure.in -# -# 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 version 2.1 -# of the License. -# -# Copyright (c) 2003-2011 Thomas Graf -# - -AC_INIT(libnl, 3.0, tgraf@suug.ch) -AC_CONFIG_HEADERS([lib/defs.h]) -AC_CONFIG_MACRO_DIR([m4]) -AM_INIT_AUTOMAKE([-Wall foreign subdir-objects]) -m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES(yes)], []) - -MAJ_VERSION=3 -AC_SUBST([MAJ_VERSION]) -MIN_VERSION=0 -AC_SUBST([MIN_VERSION]) - -AC_PROG_CC -AM_PROG_CC_C_O -AC_PROG_INSTALL -AM_PROG_LIBTOOL -AM_PROG_LEX -AC_PROG_YACC - -AC_C_CONST -AC_C_INLINE - -AC_ARG_WITH([pkgconfigdir], AS_HELP_STRING([--with-pkgconfigdir=PATH], - [Path to the pkgconfig directory [[LIBDIR/pkgconfig]]]), - [pkgconfigdir="$withval"], [pkgconfigdir='${libdir}/pkgconfig']) -AC_SUBST([pkgconfigdir]) - -AC_ARG_ENABLE([cli], - AS_HELP_STRING([--disable-cli], [Do not build command line interface utils]), - [enable_cli="$enableval"], [enable_cli="yes"]) -AM_CONDITIONAL([ENABLE_CLI], [test "$enable_cli" = "yes"]) - -AC_CHECK_LIB([m], [pow], [], AC_MSG_ERROR([libm is required])) - -AC_CONFIG_FILES([Makefile doc/Doxyfile doc/Makefile lib/Makefile - include/Makefile src/Makefile src/lib/Makefile man/Makefile - libnl-3.0.pc include/netlink/version.h]) -AC_OUTPUT diff --git a/doc/.gitignore b/doc/.gitignore new file mode 100644 index 0000000..f7cb70d --- /dev/null +++ b/doc/.gitignore @@ -0,0 +1,8 @@ +*.html +libnl.dict +Doxyfile +/aclocal.m4 +/autom4te.cache/ +/build-aux/ +/config.* +/configure diff --git a/doc/AUTHORS b/doc/AUTHORS new file mode 100644 index 0000000..26e3cb4 --- /dev/null +++ b/doc/AUTHORS @@ -0,0 +1 @@ +Thomas Graf diff --git a/src/COPYING b/doc/COPYING similarity index 99% rename from src/COPYING rename to doc/COPYING index 4432540..94a9ed0 100644 --- a/src/COPYING +++ b/doc/COPYING @@ -1,12 +1,11 @@ - - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. - Preamble + Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. @@ -69,7 +68,7 @@ patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. - TERMS AND CONDITIONS + TERMS AND CONDITIONS 0. Definitions. @@ -77,7 +76,7 @@ modification follow. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. - + "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. @@ -510,7 +509,7 @@ actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. - + If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties @@ -619,9 +618,9 @@ an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. - END OF TERMS AND CONDITIONS + END OF TERMS AND CONDITIONS - How to Apply These Terms to Your New Programs + How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it @@ -673,4 +672,3 @@ may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . - diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in index aafebbe..7a0640c 100644 --- a/doc/Doxyfile.in +++ b/doc/Doxyfile.in @@ -1,14 +1,14 @@ -# Doxyfile 1.7.1 +# Doxyfile 1.8.1.1 # This file describes the settings to be used by the documentation system -# doxygen (www.doxygen.org) for a project +# doxygen (www.doxygen.org) for a project. # -# All text after a hash (#) is considered a comment and will be ignored +# All text after a hash (#) is considered a comment and will be ignored. # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] -# Values that contain spaces should be placed between quotes (" ") +# Values that contain spaces should be placed between quotes (" "). #--------------------------------------------------------------------------- # Project related configuration options @@ -22,8 +22,9 @@ DOXYFILE_ENCODING = UTF-8 -# The PROJECT_NAME tag is a single word (or a sequence of words surrounded -# by quotes) that should identify the project. +# The PROJECT_NAME tag is a single word (or sequence of words) that should +# identify the project. Note that if you do not use Doxywizard you need +# to put quotes around the project name if it contains spaces. PROJECT_NAME = libnl @@ -33,6 +34,19 @@ PROJECT_NAME = libnl PROJECT_NUMBER = @PACKAGE_VERSION@ +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer +# a quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = + +# With the PROJECT_LOGO tag one can specify an logo or icon that is +# included in the documentation. The maximum height of the logo should not +# exceed 55 pixels and the maximum width should not exceed 200 pixels. +# Doxygen will copy the logo to the output directory. + +PROJECT_LOGO = + # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location @@ -57,7 +71,7 @@ CREATE_SUBDIRS = NO # Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, # Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English # messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, -# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, +# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, # Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. OUTPUT_LANGUAGE = English @@ -126,7 +140,7 @@ STRIP_FROM_PATH = STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter -# (but less readable) file names. This can be useful is your file systems +# (but less readable) file names. This can be useful if your file system # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO @@ -179,7 +193,21 @@ TAB_SIZE = 8 # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. -ALIASES = arg=\param +ALIASES = arg=\param \ + "ref_asciidoc{3}=\3" \ + "ref_core{2}=\ref_asciidoc{core,\1,\2 (Netlink Core Library Development Guide)}" \ + "ref_route{2}=\ref_asciidoc{route,\1,\2 (Netlink Routing Development Guide)}" \ + "core_doc{2}=\ref_core{\1,\2}" \ + "route_doc{2}=\ref_route{\1,\2}" \ + "callback=\par Callback Invocation:\n" \ + "lowlevel=\copydoc low_level_api" + +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding +# "class=itcl::class" will allow you to use the command class in the +# itcl::class meaning. + +TCL_SUBST = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. @@ -219,11 +247,20 @@ OPTIMIZE_OUTPUT_VHDL = NO EXTENSION_MAPPING = +# If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all +# comments according to the Markdown format, which allows for more readable +# documentation. See http://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you +# can mix doxygen, HTML, and XML commands with Markdown formatting. +# Disable only in case of backward compatibilities issues. + +MARKDOWN_SUPPORT = YES + # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should # set this tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); v.s. -# func(std::string) {}). This also make the inheritance and collaboration +# func(std::string) {}). This also makes the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = NO @@ -241,7 +278,7 @@ SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate getter # and setter methods for a property. Setting this option to YES (the default) -# will make doxygen to replace the get and set methods by a property in the +# will make doxygen replace the get and set methods by a property in the # documentation. This will only work if the methods are indeed getting or # setting a simple type. If this is not the case, or you want to show the # methods anyway, you should set this option to NO. @@ -263,6 +300,22 @@ DISTRIBUTE_GROUP_DOC = NO SUBGROUPING = YES +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and +# unions are shown inside the group in which they are included (e.g. using +# @ingroup) instead of on a separate page (for HTML and Man pages) or +# section (for LaTeX and RTF). + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and +# unions with only public data fields will be shown inline in the documentation +# of the scope in which they are defined (i.e. file, namespace, or group +# documentation), provided this scope is documented. If set to NO (the default), +# structs, classes, and unions are shown on a separate page (for HTML and Man +# pages) or section (for LaTeX and RTF). + +INLINE_SIMPLE_STRUCTS = NO + # When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum # is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct @@ -279,16 +332,27 @@ TYPEDEF_HIDES_STRUCT = NO # For small to medium size projects (<1000 input files) the default value is # probably good enough. For larger projects a too small cache size can cause # doxygen to be busy swapping symbols to and from disk most of the time -# causing a significant performance penality. +# causing a significant performance penalty. # If the system has enough physical memory increasing the cache will improve the # performance by keeping more symbols in memory. Note that the value works on -# a logarithmic scale so increasing the size by one will rougly double the +# a logarithmic scale so increasing the size by one will roughly double the # memory usage. The cache size is given by this formula: # 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, -# corresponding to a cache size of 2^16 = 65536 symbols +# corresponding to a cache size of 2^16 = 65536 symbols. SYMBOL_CACHE_SIZE = 0 +# Similar to the SYMBOL_CACHE_SIZE the size of the symbol lookup cache can be +# set using LOOKUP_CACHE_SIZE. This cache is used to resolve symbols given +# their name and scope. Since this can be an expensive process and often the +# same symbol appear multiple times in the code, doxygen keeps a cache of +# pre-resolved symbols. If the cache is too small doxygen will become slower. +# If the cache is too large, memory is wasted. The cache size is given by this +# formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range is 0..9, the default is 0, +# corresponding to a cache size of 2^16 = 65536 symbols. + +LOOKUP_CACHE_SIZE = 0 + #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- @@ -305,6 +369,10 @@ EXTRACT_ALL = NO EXTRACT_PRIVATE = YES +# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal scope will be included in the documentation. + +EXTRACT_PACKAGE = NO + # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. @@ -327,7 +395,7 @@ EXTRACT_LOCAL_METHODS = YES # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base # name of the file that contains the anonymous namespace. By default -# anonymous namespace are hidden. +# anonymous namespaces are hidden. EXTRACT_ANON_NSPACES = NO @@ -438,6 +506,15 @@ SORT_GROUP_NAMES = YES SORT_BY_SCOPE_NAME = NO +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to +# do proper type resolution of all parameters of a function it will reject a +# match between the prototype and the implementation of a member function even +# if there is only one candidate or it is obvious which candidate to choose +# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen +# will still accept a match between prototype and implementation in such cases. + +STRICT_PROTO_MATCHING = NO + # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. @@ -468,10 +545,10 @@ GENERATE_DEPRECATEDLIST= YES ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines -# the initial value of a variable or define consists of for it to appear in +# the initial value of a variable or macro consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. -# The appearance of the initializer of individual variables and defines in the +# The appearance of the initializer of individual variables and macros in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. @@ -483,17 +560,11 @@ MAX_INITIALIZER_LINES = 30 SHOW_USED_FILES = NO -# If the sources in your project are distributed over multiple directories -# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy -# in the documentation. The default is NO. - -SHOW_DIRECTORIES = YES - # Set the SHOW_FILES tag to NO to disable the generation of the Files page. # This will remove the Files entry from the Quick Index and from the # Folder Tree View (if specified). The default is YES. -SHOW_FILES = YES +SHOW_FILES = NO # Set the SHOW_NAMESPACES tag to NO to disable the generation of the # Namespaces page. @@ -514,12 +585,22 @@ FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated -# output files in an output format independent way. The create the layout file +# output files in an output format independent way. To create the layout file # that represents doxygen's defaults, run doxygen with the -l option. # You can optionally specify a file name after the option, if omitted # DoxygenLayout.xml will be used as the name of the layout file. -LAYOUT_FILE = +LAYOUT_FILE = DoxygenLayout.xml + +# The CITE_BIB_FILES tag can be used to specify one or more bib files +# containing the references data. This must be a list of .bib files. The +# .bib extension is automatically appended if omitted. Using this command +# requires the bibtex tool to be installed. See also +# http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style +# of the bibliography can be controlled using LATEX_BIB_STYLE. To use this +# feature you need bibtex and perl available in the search path. + +CITE_BIB_FILES = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages @@ -549,7 +630,7 @@ WARN_IF_UNDOCUMENTED = NO WARN_IF_DOC_ERROR = YES -# This WARN_NO_PARAMDOC option can be abled to get warnings for +# The WARN_NO_PARAMDOC option can be enabled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of @@ -581,10 +662,11 @@ WARN_LOGFILE = # directories like "/usr/src/myproject". Separate the files or directories # with spaces. -INPUT = ../lib \ - ../src/lib \ - ../include/netlink \ - src +INPUT = @top_srcdir@/../lib \ + @top_srcdir@/../src/lib \ + @top_srcdir@/../include/netlink \ + @top_srcdir@/../src \ + @top_srcdir@/../doc/src # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is @@ -598,8 +680,9 @@ INPUT_ENCODING = UTF-8 # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: -# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx -# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 +# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh +# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py +# *.f90 *.f *.for *.vhd *.vhdl FILE_PATTERNS = *.c \ *.h @@ -610,14 +693,16 @@ FILE_PATTERNS = *.c \ RECURSIVE = YES -# The EXCLUDE tag can be used to specify files and/or directories that should +# The EXCLUDE tag can be used to specify files and/or directories that should be # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. +# Note that relative paths are relative to the directory from which doxygen is +# run. EXCLUDE = SCCS -# The EXCLUDE_SYMLINKS tag can be used select whether or not files or -# directories that are symbolic links (a Unix filesystem feature) are excluded +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO @@ -642,7 +727,7 @@ EXCLUDE_SYMBOLS = # directories that contain example code fragments that are included (see # the \include command). -EXAMPLE_PATH = src/examples +EXAMPLE_PATH = @top_srcdir@/src # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp @@ -662,7 +747,7 @@ EXAMPLE_RECURSIVE = NO # directories that contain image that are included in the documentation (see # the \image command). -IMAGE_PATH = src/img +IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program @@ -681,8 +766,8 @@ INPUT_FILTER = # filter if there is a match. # The filters are a list of the form: # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further -# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER -# is applied to all files. +# info on how filters are used. If FILTER_PATTERNS is empty or if +# non of the patterns match the file name, INPUT_FILTER is applied. FILTER_PATTERNS = @@ -692,6 +777,14 @@ FILTER_PATTERNS = FILTER_SOURCE_FILES = NO +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) +# and it is also possible to disable source filtering for a specific pattern +# using *.ext= (so without naming a filter). This option only has effect when +# FILTER_SOURCE_FILES is enabled. + +FILTER_SOURCE_PATTERNS = + #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- @@ -710,7 +803,7 @@ INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code -# fragments. Normal C and C++ comments will always remain visible. +# fragments. Normal C, C++ and Fortran comments will always remain visible. STRIP_CODE_COMMENTS = NO @@ -784,7 +877,7 @@ GENERATE_HTML = YES # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. -HTML_OUTPUT = html +HTML_OUTPUT = api # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank @@ -794,7 +887,14 @@ HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a -# standard header. +# standard header. Note that when using a custom header you are responsible +# for the proper inclusion of any scripts and style sheets that doxygen +# needs, which is dependent on the configuration options used. +# It is advised to generate a default header using "doxygen -w html +# header.html footer.html stylesheet.css YourConfigFile" and then modify +# that header. Note that the header is subject to change so you typically +# have to redo this when upgrading to a newer version of doxygen or when +# changing the value of configuration settings such as GENERATE_TREEVIEW! HTML_HEADER = @@ -804,22 +904,26 @@ HTML_HEADER = HTML_FOOTER = -# If the HTML_TIMESTAMP tag is set to YES then the generated HTML -# documentation will contain the timesstamp. - -HTML_TIMESTAMP = NO - # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If the tag is left blank doxygen # will generate a default style sheet. Note that doxygen will try to copy # the style sheet file to the HTML output directory, so don't put your own -# stylesheet in the HTML output directory as well, or it will be erased! +# style sheet in the HTML output directory as well, or it will be erased! -HTML_STYLESHEET = libnl.css +HTML_STYLESHEET = @srcdir@/libnl.css + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that +# the files will be copied as-is; there are no commands or markers available. + +HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. -# Doxygen will adjust the colors in the stylesheet and background images +# Doxygen will adjust the colors in the style sheet and background images # according to this color. Hue is specified as an angle on a colorwheel, # see http://en.wikipedia.org/wiki/Hue for more information. # For instance the value 0 represents red, 60 is yellow, 120 is green, @@ -849,20 +953,23 @@ HTML_COLORSTYLE_GAMMA = 80 HTML_TIMESTAMP = YES -# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, -# files or namespaces will be aligned in HTML using tables. If set to -# NO a bullet list will be used. - -HTML_ALIGN_MEMBERS = YES - # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the -# page has loaded. For this to work a browser that supports -# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox -# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). +# page has loaded. HTML_DYNAMIC_SECTIONS = YES +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of +# entries shown in the various tree structured indices initially; the user +# can expand and collapse entries dynamically later on. Doxygen will expand +# the tree to such a level that at most the specified number of entries are +# visible (unless a fully collapsed tree already exceeds this amount). +# So setting the number of entries 1 will produce a full collapsed tree by +# default. 0 is a special value representing an infinite number of entries +# and will result in a full expanded tree by default. + +HTML_INDEX_NUM_ENTRIES = 100 + # If the GENERATE_DOCSET tag is set to YES, additional index files # will be generated that can be used as input for Apple's Xcode 3 # integrated development environment, introduced with OSX 10.5 (Leopard). @@ -1012,19 +1119,16 @@ GENERATE_ECLIPSEHELP = NO # the directory name containing the HTML and XML files should also have # this name. -ECLIPSE_DOC_ID = org.doxygen.Project +ECLIPSE_DOC_ID = org.infradead.libnl -# The DISABLE_INDEX tag can be used to turn on/off the condensed index at -# top of each HTML page. The value NO (the default) enables the index and -# the value YES disables it. +# The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) +# at top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. Since the tabs have the same information as the +# navigation tree you can set this option to NO if you already set +# GENERATE_TREEVIEW to YES. DISABLE_INDEX = NO -# This tag can be used to set the number of enum values (range [1..20]) -# that doxygen will group on one line in the generated HTML documentation. - -ENUM_VALUES_PER_LINE = 1 - # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. # If the tag value is set to YES, a side panel will be generated @@ -1032,19 +1136,23 @@ ENUM_VALUES_PER_LINE = 1 # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). # Windows users are probably better off using the HTML help feature. +# Since the tree basically has the same information as the tab index you +# could consider to set DISABLE_INDEX to NO when enabling this option. -GENERATE_TREEVIEW = NO +GENERATE_TREEVIEW = YES -# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, -# and Class Hierarchy pages using a tree view instead of an ordered list. +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values +# (range [0,1..20]) that doxygen will group on one line in the generated HTML +# documentation. Note that a value of 0 will completely suppress the enum +# values from appearing in the overview section. -USE_INLINE_TREES = YES +ENUM_VALUES_PER_LINE = 1 # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. -TREEVIEW_WIDTH = 250 +TREEVIEW_WIDTH = 205 # When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open # links to external symbols imported via tag files in a separate window. @@ -1067,6 +1175,32 @@ FORMULA_FONTSIZE = 10 FORMULA_TRANSPARENT = YES +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax +# (see http://www.mathjax.org) which uses client side Javascript for the +# rendering instead of using prerendered bitmaps. Use this if you do not +# have LaTeX installed or if you want to formulas look prettier in the HTML +# output. When enabled you may also need to install MathJax separately and +# configure the path to it using the MATHJAX_RELPATH option. + +USE_MATHJAX = NO + +# When MathJax is enabled you need to specify the location relative to the +# HTML output directory using the MATHJAX_RELPATH option. The destination +# directory should contain the MathJax.js script. For instance, if the mathjax +# directory is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to +# the MathJax Content Delivery Network so you can quickly see the result without +# installing MathJax. +# However, it is strongly recommended to install a local +# copy of MathJax from http://www.mathjax.org before deployment. + +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest + +# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension +# names that should be enabled during MathJax rendering. + +MATHJAX_EXTENSIONS = + # When the SEARCHENGINE tag is enabled doxygen will generate a search box # for the HTML output. The underlying search engine uses javascript # and DHTML and should work on any modern browser. Note that when using @@ -1082,7 +1216,7 @@ SEARCHENGINE = NO # using Javascript. Doxygen will generate the search PHP script and index # file to put on the web server. The advantage of the server # based approach is that it scales better to large projects and allows -# full text search. The disadvances is that it is more difficult to setup +# full text search. The disadvantages are that it is more difficult to setup # and does not have live searching capabilities. SERVER_BASED_SEARCH = NO @@ -1123,7 +1257,7 @@ MAKEINDEX_CMD_NAME = makeindex COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used -# by the printer. Possible values are: a4, a4wide, letter, legal and +# by the printer. Possible values are: a4, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = a4wide @@ -1140,6 +1274,13 @@ EXTRA_PACKAGES = LATEX_HEADER = +# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for +# the generated latex document. The footer should contain everything after +# the last chapter. If it is left blank doxygen will generate a +# standard footer. Notice: only use this tag if you know what you are doing! + +LATEX_FOOTER = + # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references @@ -1173,6 +1314,12 @@ LATEX_HIDE_INDICES = NO LATEX_SOURCE_CODE = NO +# The LATEX_BIB_STYLE tag can be used to specify the style to use for the +# bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See +# http://en.wikipedia.org/wiki/BibTeX for more info. + +LATEX_BIB_STYLE = plain + #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- @@ -1204,7 +1351,7 @@ COMPACT_RTF = NO RTF_HYPERLINKS = NO -# Load stylesheet definitions from file. Syntax is similar to doxygen's +# Load style sheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. @@ -1349,7 +1496,7 @@ MACRO_EXPANSION = NO EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files -# in the INCLUDE_PATH (see below) will be search if a #include is found. +# pointed to by INCLUDE_PATH will be searched when a #include is found. SEARCH_INCLUDES = NO @@ -1379,15 +1526,15 @@ PREDEFINED = # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. -# Use the PREDEFINED tag if you want to use a different macro definition. +# Use the PREDEFINED tag if you want to use a different macro definition that +# overrules the definition found in the source code. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then -# doxygen's preprocessor will remove all function-like macros that are alone -# on a line, have an all uppercase name, and do not end with a semicolon. Such -# function macros are typically used for boiler-plate code, and will confuse -# the parser if not removed. +# doxygen's preprocessor will remove all references to function-like macros +# that are alone on a line, have an all uppercase name, and do not end with a +# semicolon, because these will confuse the parser if not removed. SKIP_FUNCTION_MACROS = YES @@ -1395,22 +1542,18 @@ SKIP_FUNCTION_MACROS = YES # Configuration::additions related to external references #--------------------------------------------------------------------------- -# The TAGFILES option can be used to specify one or more tagfiles. -# Optionally an initial location of the external documentation -# can be added for each tagfile. The format of a tag file without -# this location is as follows: +# The TAGFILES option can be used to specify one or more tagfiles. For each +# tag file the location of the external documentation should be added. The +# format of a tag file without this location is as follows: # # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # # TAGFILES = file1=loc1 "file2 = loc2" ... -# where "loc1" and "loc2" can be relative or absolute paths or -# URLs. If a location is present for each tag, the installdox tool -# does not have to be run to correct the links. -# Note that each tag file must have a unique name -# (where the name does NOT include the path) -# If a tag file is not located in the directory in which doxygen -# is run, you must also specify the path to the tagfile here. +# where "loc1" and "loc2" can be relative or absolute paths +# or URLs. Note that each tag file must have a unique name (where the name does +# NOT include the path). If a tag file is not located in the directory in which +# doxygen is run, you must also specify the path to the tagfile here. TAGFILES = @@ -1443,9 +1586,8 @@ PERL_PATH = /usr/bin/perl # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that -# this option is superseded by the HAVE_DOT option below. This is only a -# fallback. It is recommended to install and use dot, since it yields more -# powerful graphs. +# this option also works with HAVE_DOT disabled, but it is recommended to +# install and use dot, since it yields more powerful graphs. CLASS_DIAGRAMS = NO @@ -1479,14 +1621,12 @@ HAVE_DOT = YES DOT_NUM_THREADS = 0 -# By default doxygen will write a font called FreeSans.ttf to the output -# directory and reference it in all dot files that doxygen generates. This -# font does not include all possible unicode characters however, so when you need -# these (or just want a differently looking font) you can specify the font name -# using DOT_FONTNAME. You need need to make sure dot is able to find the font, -# which can be done by putting it in a standard location or by setting the -# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory -# containing the font. +# By default doxygen will use the Helvetica font for all dot files that +# doxygen generates. When you want a differently looking font you can specify +# the font name using DOT_FONTNAME. You need to make sure dot is able to find +# the font, which can be done by putting it in a standard location or by setting +# the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the +# directory containing the font. DOT_FONTNAME = FreeSans.ttf @@ -1495,17 +1635,16 @@ DOT_FONTNAME = FreeSans.ttf DOT_FONTSIZE = 10 -# By default doxygen will tell dot to use the output directory to look for the -# FreeSans.ttf font (which doxygen will put there itself). If you specify a -# different font using DOT_FONTNAME you can set the path where dot -# can find it using this tag. +# By default doxygen will tell dot to use the Helvetica font. +# If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to +# set the path where dot can find it. DOT_FONTPATH = # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the -# the CLASS_DIAGRAMS tag to NO. +# CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = NO @@ -1527,6 +1666,15 @@ GROUP_GRAPHS = NO UML_LOOK = YES +# If the UML_LOOK tag is enabled, the fields and methods are shown inside +# the class node. If there are many fields or methods and many nodes the +# graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS +# threshold limits the number of items for each type to make the size more +# managable. Set this to 0 for no limit. Note that the threshold may be +# exceeded by 50% before the limit is enforced. + +UML_LIMIT_NUM_FIELDS = 10 + # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. @@ -1563,11 +1711,11 @@ CALL_GRAPH = YES CALLER_GRAPH = YES # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen -# will graphical hierarchy of all classes instead of a textual one. +# will generate a graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES -# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES +# If the DIRECTORY_GRAPH and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. @@ -1575,11 +1723,22 @@ GRAPHICAL_HIERARCHY = YES DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images -# generated by dot. Possible values are png, jpg, or gif -# If left blank png will be used. +# generated by dot. Possible values are svg, png, jpg, or gif. +# If left blank png will be used. If you choose svg you need to set +# HTML_FILE_EXTENSION to xhtml in order to make the SVG files +# visible in IE 9+ (other browsers do not have this requirement). DOT_IMAGE_FORMAT = png +# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to +# enable generation of interactive SVG images that allow zooming and panning. +# Note that this requires a modern browser other than Internet Explorer. +# Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you +# need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files +# visible. Older versions of IE do not have SVG support. + +INTERACTIVE_SVG = NO + # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. @@ -1591,6 +1750,12 @@ DOT_PATH = DOTFILE_DIRS = +# The MSCFILE_DIRS tag can be used to specify one or more directories that +# contain msc files that are included in the documentation (see the +# \mscfile command). + +MSCFILE_DIRS = + # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of # nodes that will be shown in the graph. If the number of nodes in a graph # becomes larger than this value, doxygen will truncate the graph, which is @@ -1599,7 +1764,7 @@ DOTFILE_DIRS = # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. -DOT_GRAPH_MAX_NODES = 50 +DOT_GRAPH_MAX_NODES = 100 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable diff --git a/doc/DoxygenLayout.xml b/doc/DoxygenLayout.xml new file mode 100644 index 0000000..589d0f1 --- /dev/null +++ b/doc/DoxygenLayout.xml @@ -0,0 +1,187 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/Makefile.am b/doc/Makefile.am index 040ff87..338f077 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -1,9 +1,73 @@ # -*- Makefile -*- -.PHONY: gendoc +.PHONY: gendoc api_ref asciidoc -gendoc: +ASCIIDOCOPTS=-a pygments -a language=c -a icons \ + -a toc2 \ + -a numbered \ + -a imagesdir="./images/" \ + -a iconsdir="./images/icons" \ + -a stylesdir="${abs_srcdir}/stylesheets/" + +EXTRA_DIST = \ + core.txt \ + core.html \ + route.txt \ + route.html \ + index.txt \ + index.html \ + libnl.css \ + stylesheets \ + images \ + api + +dist-hook: + rm -f $(distdir)/aclocal.m4 + rm -f $(distdir)/configure + rm -f $(distdir)/configure.in + rm -rf $(distdir)/m4 + rm -f $(distdir)/README + rm -f $(distdir)/missing + rm -f $(distdir)/Doxyfile.in + rm -f $(distdir)/Makefile.am + rm -f $(distdir)/Makefile.in + +link_doc: +if LINK_DOC + ./gen-tags.sh > libnl.dict +else + @echo "Warning: Linking to API reference is disabled, check configure output" +endif + + +%.html: %.txt link_doc + ./resolve-asciidoc-refs.py $< > asciidoc.tmp + asciidoc $(ASCIIDOCOPTS) -o $@ asciidoc.tmp +if LINK_DOC + ./doxygen-link.py libnl.dict $@ > asciidoc.tmp + mv asciidoc.tmp $@ +endif + +asciidoc: core.html route.html index.html + +api_ref: doxygen Doxyfile; -distclean-local: - rm -f html/*; +gendoc: +if GENERATE_DOC +if HAVE_DOXYGEN + $(MAKE) api_ref +else + @echo "Warning: Building of API reference (doxygen) is disabled, check autoconf logs" +endif +if HAVE_ASCIIDOC + $(MAKE) asciidoc +else + @echo "Warning: Building of asciidoc files is disabled, check autoconf logs" +endif +else + @echo "Warning: Building of documentation disabled by user or autoconf" +endif + +clean-local: + rm -f api/* libnl.dict *.html; diff --git a/doc/README b/doc/README new file mode 100644 index 0000000..0784e13 --- /dev/null +++ b/doc/README @@ -0,0 +1,10 @@ +Requirements to build documentation + +mscgen + http://www.mcternan.me.uk/mscgen/ + +mscgen-filter-1.2 + http://code.google.com/p/asciidoc-mscgen-filter/ + +asciidoc > 8.6.x +doxygen > 1.8.0 diff --git a/doc/api/.gitignore b/doc/api/.gitignore new file mode 100644 index 0000000..e57ca88 --- /dev/null +++ b/doc/api/.gitignore @@ -0,0 +1,8 @@ +*.html +*.png +*.css +*.map +*.md5 +*.js +formula.repository +jquery.js diff --git a/doc/autogen.sh b/doc/autogen.sh new file mode 100755 index 0000000..a569614 --- /dev/null +++ b/doc/autogen.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +autoreconf -fi; +rm -Rf autom4te.cache; diff --git a/doc/configure.ac b/doc/configure.ac new file mode 100644 index 0000000..4c3a52f --- /dev/null +++ b/doc/configure.ac @@ -0,0 +1,107 @@ +# +# configure.in +# +# 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 version 2.1 +# of the License. +# +# Copyright (c) 2003-2013 Thomas Graf +# + +AC_INIT(libnl-doc, [3.2.21], [http://www.infradead.org/~tgr/libnl/]) +AC_CONFIG_MACRO_DIR([m4]) +AC_CONFIG_AUX_DIR([build-aux]) +AM_INIT_AUTOMAKE([foreign]) +m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES(yes)], []) + +m4_include([m4/ax_python.m4]) + +# +# Generating the documentation +# +AC_ARG_ENABLE([doc], + AS_HELP_STRING([--disable-doc], [Do not generate documentation]), + [generate_doc="$enableval"], [generate_doc=auto]) + +if test "x$generate_doc" != "xno"; then + AC_PROG_SED + AC_PROG_EGREP + + AX_PYTHON + + AC_CHECK_PROG(HAVE_DOXYGEN, [doxygen], yes, no) + if test "x$HAVE_DOXYGEN" = "xno" -a "x$generate_doc" = "xyes"; then + AC_MSG_ERROR([*** doxygen package required to generate documentation]) + fi + + AC_CHECK_PROG(HAVE_DOT, [dot], yes, no) + if test "x$HAVE_DOT" = "xno"; then + if test "x$generate_doc" = "xyes"; then + AC_MSG_ERROR([*** graphviz package required to generate documentation]) + else + AC_MSG_WARN([*** graphviz not found, disabling building of API reference]) + HAVE_DOXYGEN=no + fi + fi + + AC_CHECK_PROG(HAVE_ASCIIDOC, [asciidoc], yes, no) + if test "x$HAVE_ASCIIDOC" = "xno"; then + if test "x$generate_doc" = "xyes"; then + AC_MSG_ERROR([*** asciidoc package required to generate documentation]) + else + AC_MSG_WARN([*** asciidoc not found, disabling building of guides]) + fi + fi + + AC_CHECK_PROG(HAVE_SOURCE_HIGHLIGHT, [source-highlight], yes, no) + if test "x$HAVE_SOURCE_HIGHLIGHT" = "xno"; then + if test "x$generate_doc" = "xyes"; then + AC_MSG_ERROR([*** source-highlight required to generate documentation]) + else + AC_MSG_WARN([*** source-highlight not found, disabling building of guides]) + HAVE_ASCIIDOC=no + fi + fi + + AC_CHECK_PROG(HAVE_MSCGEN, [mscgen], yes, no) + if test "x$HAVE_MSCGEN" = "xno"; then + AC_MSG_WARN([*** mscgen not found, get it at http://www.mcternan.me.uk/mscgen/]) + if test "x$generate_doc" = "xyes"; then + AC_MSG_ERROR([*** mscgen package required to generate documentation]) + else + AC_MSG_WARN([*** Disabling building of guides]) + HAVE_ASCIIDOC=no + HAVE_DOXYGEN=no + fi + fi + + AC_CHECK_PROG(HAVE_PYGMENTIZE, [pygmentize], yes, no) + if test "x$HAVE_PYGMENTIZE" = "xno"; then + if test "x$generate_doc" = "xyes"; then + AC_MSG_ERROR([*** pygmentize package required to generate documentation]) + else + AC_MSG_WARN([*** Disabling building of guides]) + HAVE_ASCIIDOC=no + fi + fi + + link_doc=yes + if test "x$HAVE_DOXYGEN" = "xno"; then + AC_MSG_WARN([*** Disabling API linking due to missing doxygen package]) + link_doc=no + fi +fi + +AM_CONDITIONAL([LINK_DOC], [test "x$link_doc" = "xyes"]) +AM_CONDITIONAL([HAVE_DOXYGEN], [test "x$HAVE_DOXYGEN" = "xyes"]) +AM_CONDITIONAL([HAVE_ASCIIDOC], [test "x$HAVE_ASCIIDOC" = "xyes"]) + +AM_CONDITIONAL([GENERATE_DOC], [test "x$generate_doc" != "xno"]) + +AC_CONFIG_FILES([ +Doxyfile +Makefile +]) + +AC_OUTPUT diff --git a/doc/core.txt b/doc/core.txt new file mode 100644 index 0000000..1e951d0 --- /dev/null +++ b/doc/core.txt @@ -0,0 +1,3017 @@ +//// + vim.syntax: asciidoc + + Copyright (c) 2011 Thomas Graf +//// + +Netlink Library (libnl) +======================= +Thomas Graf +3.2, May 9 2011: +:numbered: + +== Introduction + +The core library contains the fundamentals required to communicate +over netlink sockets. It deals with connecting and disconnectng of +sockets, sending and receiving of data, construction and parsing of +messages, provides a customizeable receiving state machine, and +provides a abstract data type framework which eases the implementation +of object based netlink protocols where objects are added, removed, or +modified using a netlink based protocol. + +.Library Hierarchy + +The suite is split into multiple libraries: + +image:library_overview.png["Library Hierarchy"] + +link:core.html[Netlink Library] (libnl):: +Socket handling, sending and receiving, message construction and parsing, ... + +link:route.html[Routing Family Library] (libnl-route):: +Adresses, links, neighbours, routing, traffic control, neighbour tables, ... + +Netfilter Library (libnl-nf):: +Connection tracking, logging, queueing + +Generic Netlink Library (libnl-genl):: +Controller API, family and command registration + + +=== How To Read This Documentation + +The libraries provide a broad set of APIs of which most applications only +require a small subset of it. Depending on the type of application, some +users may only be interested in the low level netlink messaging API while +others wish to make heavy use of the high level API. + +In any case it is recommended to get familiar with the netlink protocol +first. + +- <> + +The low level APIs are described in: + +- <> +- <> + + +=== Linking to this Library + +.Checking the presence of the library using autoconf + +Projects using autoconf may use +PKG_CHECK_MODULES()+ to check if +a specific version of libnl is available on the system. The example +below also shows how to retrieve the +CFLAGS+ and linking dependencies +required to link against the library. + +The following example shows how to check for a specific version of libnl. If +found, it extends the `CFLAGS` and `LIBS` variable appropriately: + +[source] +---- +PKG_CHECK_MODULES(LIBNL3, libnl-3.0 >= 3.1, [have_libnl3=yes], [have_libnl3=no]) +if (test "${have_libnl3}" = "yes"); then + CFLAGS+="$LIBNL3_CFLAGS" + LIBS+="$LIBNL3_LIBS" +fi +---- + +NOTE: The pkgconfig file is named +libnl-3.0.pc+ for historic reasons, it also + covers library versions >= 3.1. + +.Header Files + +The main header file is ``. Additional headers may need to +be included in your sources depending on the subsystems and components your +program makes use of. + +[source,c] +----- +#include +#include +#include +----- + +.Version Dependent Code + +If your code wishes to be capable to link against multiple versions of libnl +you may have direct the compiler to only include portions on the code depending +on the version of libnl that it is compiled against. + +[source,c] +----- +#include + +#if LIBNL_VER_NUM >= LIBNL_VER(3,1) + /* include code if compiled with libnl version >= 3.1 */ +#endif +----- + +.Linking +----- +$ gcc myprogram.c -o myprogram $(pkgconfig --cflags --libs libnl-3.0) +----- + +=== Debugging + +The library has been compiled with debugging statements enabled it will +print debug information to +stderr+ if the environment variable +NLDBG+ +is set to > 0. + +----- +$ NLDBG=2 ./myprogram +----- + +.Debugging Levels +[options="header", width="80%", cols="1,5", align="center"] +|=============================================================== +| Level | Description +| 0 | Debugging disabled (default) +| 1 | Warnings, important events and notifications +| 2 | More or less important debugging messages +| 3 | Repetitive events causing a flood of debugging messages +| 4 | Even less important messages +|=============================================================== + +.Debugging the Netlink Protocol + +It is often useful to peek into the stream of netlink messages exchanged +with other sockets. Setting the environment variable +NLCB=debug+ will +cause the debugging message handlers to be used which in turn print the +netlink messages exchanged in a human readable format to to +stderr+: + +----- +$ NLCB=debug ./myprogram +-- Debug: Sent Message: +-------------------------- BEGIN NETLINK MESSAGE --------------------------- + [HEADER] 16 octets + .nlmsg_len = 20 + .nlmsg_type = 18 + .nlmsg_flags = 773 + .nlmsg_seq = 1301410712 + .nlmsg_pid = 20014 + [PAYLOAD] 16 octets + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ +--------------------------- END NETLINK MESSAGE --------------------------- +-- Debug: Received Message: +-------------------------- BEGIN NETLINK MESSAGE --------------------------- + [HEADER] 16 octets + .nlmsg_len = 996 + .nlmsg_type = 16 + .nlmsg_flags = 2 + .nlmsg_seq = 1301410712 + .nlmsg_pid = 20014 + [PAYLOAD] 16 octets + 00 00 04 03 01 00 00 00 49 00 01 00 00 00 00 00 ........I....... + [ATTR 03] 3 octets + 6c 6f 00 lo. + [PADDING] 1 octets + 00 . + [ATTR 13] 4 octets + 00 00 00 00 .... + [ATTR 16] 1 octets + 00 . + [PADDING] 3 octets + 00 00 00 ... + [ATTR 17] 1 octets + 00 . + [...] +--------------------------- END NETLINK MESSAGE --------------------------- + +----- + +[[core_netlink_fundamentals]] +== Netlink Protocol Fundamentals + +The netlink protocol is a socket based IPC mechanism used for +communication between userspace processes and the kernel or between +userspace processes themselves. The netlink protocol is based on BSD +sockets and uses the +AF_NETLINK+ address family. Every netlink +protocol uses its own protocol number (e.g. +NETLINK_ROUTE+, ++NETLINK_NETFILTER+, etc). Its addressing schema is based on a 32 bit +port number, formerly referred to as PID, which uniquely identifies +each peer. + +[[core_addressing]] +=== Addressing + +The netlink address (port) consists of a 32bit integer. Port 0 (zero) +is reserved for the kernel and refers to the kernel side socket of each +netlink protocol family. Other port numbers usually refer to user space +owned sockets, although this is not enforced. + +NOTE: In the beginning, it was common practice to use the process + identifier (PID) as the local port number. This became unpractical + with the introduction of threaded netlink applications and + applications requiring multiple sockets. Therefore libnl generates + unique port numbers based on the process identifier and adds an + offset to it allowing for multiple sockets to be used. The initial + socket will still equal to the process identifier for backwards + compatibility reasons. + +image:addressing.png["Addressing Example"] + +The above figure illustrates three applications and the kernel side +exposing two kernel side sockets. It shows the common netlink use +cases: + + * User space to kernel + * User space to user space + * Listening to kernel multicast notifications + +.User Space to Kernel + +The most common form of netlink usage is for a user space application +to send requests to the kernel and process the reply which is either +an error message or a success notification. + +["mscgen"] +-------- +msc { + App1,App2,Kernel; + App1=>Kernel [label="request (src=11, dst=0)"]; + Kernel<=App1 [label="reply (src=0, dst=11)"]; + ...; + App2=>Kernel [label="request (src=21, dst=0)"]; + Kernel<=App2 [label="reply (src=0, dst=21)"]; +} +-------- + +.User Space to User Space + +Netlink may also be used as an IPC mechanism to communicate between user +space applications directly. Communication is not limited to two peers, +any number of peers may communicate with each other and multicasting +capabilities allow to reach multiple peers with a single message. + +In order for the sockets to be visible to each other, both sockets must +be created for the same netlink protocol family. + +["mscgen"] +-------- +msc { + App2,App3; + App2=>App3 [label="request (src=22, dst=31)"]; + App2<=App3 [label="reply (src=31, dst=22)"]; + ...; +} +-------- + +.User space listening to kernel notifications + +This form of netlink communication is typically found in user space +daemons that need to act on certain kernel events. Such daemons will +typically maintain a netlink socket subscribed to a multicast group that +is used by the kernel to notify interested user space parties about +specific events. + +["mscgen"] +-------- +msc { + Kernel,App3; + Kernel=>App3 [label="notification (src=0, group=foo)"]; + ...; +} +-------- + +Use of multicasting is preferred over direct addressing due to the +flexibility in exchanging the user space component at any time without +the kernel noticing. + +[[core_msg_format]] +=== Message Format + +A netlink protocol is typically based on messages and consists of the +netlink message header (+struct nlmsghdr+) plus the payload attached +to it. The payload can consist of arbitrary data but usually contains +a fixed size protocol specific header followed by a stream of +attributes. + +.Netlink message header (struct nlmsghdr) + +image:nlmsghdr.png[align="center", alt="Netlink Message Header"] + +Total Length (32bit):: +Total length of the message in bytes including the netlink message header. + +Message Type (16bit):: +The message type specifies the type of payload the message is carrying. +Several standard message types are defined by the netlink protocol. +Additional message types may be defined by each protocol family. See +<> for additional information. + +Message Flags (16bit):: +The message flags may be used to modify the behaviour of a message type. +See section <> for a list of standard message flags. + +Sequence Number (32bit):: +The sequence number is optional and may be used to allow referring to +a previous message, e.g. an error message can refer to the original +request causing the error. + +Port Number (32bit):: +The port number specifies the peer to which the message should be delivered +to. If not specified, the message will be delivered to the first matching +kernel side socket of the same protocol family. + +[[core_msg_types]] +=== Message Types + +Netlink differs between requests, notifications, and replies. Requests +are messages which have the +NLM_F_REQUEST+ flag set and are meant to +request an action from the receiver. A request is typically sent from +a userspace process to the kernel. While not strictly enforced, requests +should carry a sequence number incremented for each request sent. + +Depending on the nature of the request, the receiver may reply to the +request with another netlink message. The sequence number of a reply +must match the sequence number of the request it relates to. + +Notifications are of informal nature and no reply is expected, therefore +the sequence number is typically set to 0. + +["mscgen"] +-------- +msc { + A,B; + A=>B [label="GET (seq=1, NLM_F_REQUEST)"]; + A<=B [label="PUT (seq=1)"]; + ...; + A<=B [label="NOTIFY (seq=0)"]; +} +-------- + + +The type of message is primarly identified by its 16 bit message type set +in the message header. The following standard message types are defined: + +- +NLMSG_NOOP+ - No operation, message must be discarded +- +NLMSG_ERROR+ - Error message or ACK, see <> + respectively <> +- +NLMSG_DONE+ - End of multipart sequence, see <> +- +NLMSG_OVERRUN+ - Overrun notification (Error) + +Every netlink protocol is free to define own message types. Note that +message type values +< NLMSG_MIN_TYPE (0x10)+ are reserved and may +not be used. + +It is common practice to use own message types to implement RPC schemas. +Suppose the goal of the netlink protocol you are implementing is allow +configuration of a particular network device, therefore you want to +provide read/write access to various configuration options. The typical +"netlink way" of doing this would be to define two message types ++MSG_SETCFG+, +MSG_GETCFG+: + +[source,c] +-------- +#define MSG_SETCFG 0x11 +#define MSG_GETCFG 0x12 +-------- + +Sending a +MSG_GETCFG+ request message will typically trigger a reply +with the message type +MSG_SETCFG+ containing the current configuration. +In object oriented terms one would describe this as "the kernel sets +the local copy of the configuration in userspace". + +["mscgen"] +-------- +msc { + A,B; + A=>B [label="MSG_GETCFG (seq=1, NLM_F_REQUEST)"]; + A<=B [label="MSG_SETCFG (seq=1)"]; +} +-------- + +The configuration may be changed by sending a +MSG_SETCFG+ which will +be responded to with either a ACK (see <>) +or a error message (see <>). + +["mscgen"] +-------- +msc { + A,B; + A=>B [label="MSG_SETCFG (seq=1, NLM_F_REQUEST, NLM_F_ACK)"]; + A<=B [label="ACK (seq=1)"]; +} +-------- + +Optionally, the kernel may send out notifications for configuration +changes allowing userspace to listen for changes instead of polling +frequently. Notifications typically reuse an existing message type +and rely on the application using a separate socket to differ between +requests and notifications but you may also specify a separate message +type. + +["mscgen"] +-------- +msc { + A,B; + A<=B [label="MSG_SETCFG (seq=0)"]; +} +-------- + +[[core_multipart]] +==== Multipart Messages + +Although in theory a netlink message can be up to 4GiB in size. The socket +buffers are very likely not large enough to hold message of such sizes. +Therefore it is common to limit messages to one page size (PAGE_SIZE) and +use the multipart mechanism to split large pieces of data into several +messages. A multipart message has the flag +NLM_F_MULTI+ set and the +receiver is expected to continue receiving and parsing until the special +message type +NLMSG_DONE+ is received. + +Multipart messages unlike fragmented ip packets must not be reassmbled +even though it is perfectly legal to do so if the protocols wishes to +work this way. Often multipart message are used to send lists or trees +of objects were each multipart message simply carries multiple objects +allow for each message to be parsed independently. + +["mscgen"] +-------- +msc { + A,B; + A=>B [label="GET (seq=1, NLM_F_REQUEST)"]; + A<=B [label="PUT (seq=1, NLM_F_MULTI)"]; + ...; + A<=B [label="PUT (seq=1, NLM_F_MULTI)"]; + A<=B [label="NLMSG_DONE (seq=1)"]; +} +-------- + +[[core_errmsg]] +==== Error Message + +Error messages can be sent in response to a request. Error messages must +use the standard message type +NLMSG_ERROR+. The payload consists of a +error code and the original netlink mesage header of the request. + +image:nlmsgerr.png["Netlink Errror Message header"] + +Error messages should set the sequence number to the sequence number +of the request which caused the error. + +["mscgen"] +-------- +msc { + A,B; + A=>B [label="GET (seq=1, NLM_F_REQUEST)"]; + A<=B [label="NLMSG_ERROR code=EINVAL (seq=1)"]; +} +-------- + +[[core_msg_ack]] +==== ACKs + +A sender can request an ACK message to be sent back for each request +processed by setting the +NLM_F_ACK+ flag in the request. This is typically +used to allow the sender to synchronize further processing until the +request has been processed by the receiver. + +["mscgen"] +-------- +msc { + A,B; + A=>B [label="GET (seq=1, NLM_F_REQUEST | NLM_F_ACK)"]; + A<=B [label="ACK (seq=1)"]; +} +-------- + +ACK messages also use the message type +NLMSG_ERROR+ and payload +format but the error code is set to 0. + +[[core_msg_flags]] +==== Message Flags + +The following standard flags are defined + +[source,c] +-------- +#define NLM_F_REQUEST 1 +#define NLM_F_MULTI 2 +#define NLM_F_ACK 4 +#define NLM_F_ECHO 8 +-------- + +- `NLM_F_REQUEST` - Message is a request, see <>. +- `NLM_F_MULTI` - Multipart message, see <> +- `NLM_F_ACK` - ACK message requested, see <>. +- `NLM_F_ECHO` - Request to echo the request. + +The flag +NLM_F_ECHO+ is similar to the `NLM_F_ACK` flag. It can be +used in combination with `NLM_F_REQUEST` and causes a notification +which is sent as a result of a request to also be sent to the sender +regardless of whether the sender has subscribed to the corresponding +multicast group or not. See <> + +Additional universal message flags are defined which only apply for ++GET+ requests: + +[source,c] +-------- +#define NLM_F_ROOT 0x100 +#define NLM_F_MATCH 0x200 +#define NLM_F_ATOMIC 0x400 +#define NLM_F_DUMP (NLM_F_ROOT|NLM_F_MATCH) +-------- + +- `NLM_F_ROOT` - Return based on root of tree. +- `NLM_F_MATCH` - Return all matching entries. +- `NLM_F_ATOMIC` - Obsoleted, once used to request an atomic operation. +- `NLM_F_DUMP` - Return a list of all objects + (`NLM_F_ROOT`|`NLM_F_MATCH`). + +Use of these flags is completely optional and many netlink protocols only +make use of the `NLM_F_DUMP` flag which typically requests the receiver +to send a list of all objects in the context of the message type as a +sequence of multipart messages (see <>). + +Another set of flags exist related to `NEW` or `SET` requests. These +flags are mutually exclusive to the `GET` flags: + +[source,c] +-------- +#define NLM_F_REPLACE 0x100 +#define NLM_F_EXCL 0x200 +#define NLM_F_CREATE 0x400 +#define NLM_F_APPEND 0x800 +-------- + +- `NLM_F_REPLACE` - Replace an existing object if it exists. +- `NLM_F_EXCL` - Do not update object if it exists already. +- `NLM_F_CREATE` - Create object if it does not exist yet. +- `NLM_F_APPEND` - Add object at end of list. + +Behaviour of these flags may differ slightly between different netlink +protocols. + +[[core_seq_num]] +=== Sequence Numbers + +Netlink allows the use of sequence numbers to help relate replies to +requests. It should be noted that unlike in protocols such as TCP +there is no strict enforcment of the sequence number. The sole purpose +of sequence numbers is to assist a sender in relating replies to the +corresponding requests. See <> for more information. + +Sequence numbers are managed on a per socket basis, see +<> for more information on how to use sequence numbers. + +[[core_multicast]] +=== Multicast Groups + +TODO + +See <> + +[[core_sockets]] +== Netlink Sockets + +In order to use the netlink protocol, a netlink socket is required. +Each socket defines an independent context for sending and receiving of +messages. An application may make use multiple sockets, e.g. a socket to +send requests and receive the replies and another socket subscribed to a +multicast group to receive notifications. + +=== Socket structure (struct nl_sock) + +The netlink socket and all related attributes including the actual file +descriptor are represented by +struct nl_sock+. + +[source,c] +-------- +#include + +struct nl_sock *nl_socket_alloc(void) +void nl_socket_free(struct nl_sock *sk) +-------- + +The application must allocate an instance of +struct nl_sock+ for each +netlink socket it wishes to use. + +[[core_sk_seq_num]] +=== Sequence Numbers + +The library will automatically take care of sequence number handling +for the application. A sequence number counter is stored in the +socket structure which is used and incremented automatically when a +message needs to be sent which is expected to generate a reply such as +an error or any other message type that needs to be related to the +original message. + +Alternatively, the counter can be used directly via the function +nl_socket_use_seq(). It will return the current value of the counter +and increment it by one afterwards. + +[source,c] +-------- +#include + +unsigned int nl_socket_use_seq(struct nl_sock *sk); +-------- + +Most applications will not want to deal with sequence number handling +themselves though. When using nl_send_auto() the sequence number is +filled in automatically and matched again when a reply is received. See +section <> for more information. + +This behaviour can and must be disabled if the netlink protocol +implemented does not use a request/reply model, e.g. when a socket is +used to receive notification messages. + +[source,c] +-------- +#include + +void nl_socket_disable_seq_check(struct nl_sock *sk); +-------- + +For more information on the theory behind netlink sequence numbers, +see section <>. + +[[core_sk_multicast]] +=== Multicast Group Subscriptions + +Each socket can subscribe to any number of multicast groups of the +netlink protocol it is connected to. The socket will then receive a +copy of each message sent to any of the groups. Multicast groups are +commonly used to implement event notifications. + +Prior to kernel 2.6.14 the group subscription was performed using a +bitmask which limited the number of groups per protocol family to 32. +This outdated interface can still be accessed via the function +nl_join_groups() even though it is not recommended for new code. + +[source,c] +-------- +#include + +void nl_join_groups(struct nl_sock *sk, int bitmask); +-------- + +Starting with 2.6.14 a new method was introduced which supports subscribing +to an almost infinite number of multicast groups. + +[source,c] +-------- +#include + +int nl_socket_add_memberships(struct nl_sock *sk, int group, ...); +int nl_socket_drop_memberships(struct nl_sock *sk, int group, ...); +-------- + +==== Multicast Example + +[source,c] +-------- +#include +#include +#include + +/* + * This function will be called for each valid netlink message received + * in nl_recvmsgs_default() + */ +static int my_func(struct nl_msg *msg, void *arg) +{ + return 0; +} + +struct nl_sock *sk; + +/* Allocate a new socket */ +sk = nl_socket_alloc(); + +/* + * Notifications do not use sequence numbers, disable sequence number + * checking. + */ +nl_socket_disable_seq_check(sk); + +/* + * Define a callback function, which will be called for each notification + * received + */ +nl_socket_modify_cb(sk, NL_CB_VALID, NL_CB_CUSTOM, my_func, NULL); + +/* Connect to routing netlink protocol */ +nl_connect(sk, NETLINK_ROUTE); + +/* Subscribe to link notifications group */ +nl_socket_add_memberships(sk, RTNLGRP_LINK, 0); + +/* + * Start receiving messages. The function nl_recvmsgs_default() will block + * until one or more netlink messages (notification) are received which + * will be passed on to my_func(). + */ +while (1) + nl_recvmsgs_default(sock); +-------- + +[[core_sk_cb]] +=== Modifiying Socket Callback Configuration + +See <> for more information on +callback hooks and overwriting capabilities. + +Each socket is assigned a callback configuration which controls the +behaviour of the socket. This is f.e. required to have a separate +message receive function per socket. It is perfectly legal to share +callback configurations between sockets though. + +The following functions can be used to access and set the callback +configuration of a socket: + +[source,c] +-------- +#include + +struct nl_cb *nl_socket_get_cb(const struct nl_sock *sk); +void nl_socket_set_cb(struct nl_sock *sk, struct nl_cb *cb); +-------- + +Additionaly a shortcut exists to modify the callback configuration +assigned to a socket directly: + +[source,c] +-------- +#include + +int nl_socket_modify_cb(struct nl_sock *sk, enum nl_cb_type type, enum nl_cb_kind kind, + nl_recvmsg_msg_cb_t func, void *arg); +-------- + +.Example: +[source,c] +-------- +#include + +// Call my_input() for all valid messages received in socket sk +nl_socket_modify_cb(sk, NL_CB_VALID, NL_CB_CUSTOM, my_input, NULL); +-------- + +=== Socket Attributes + +.Local Port + +The local port number uniquely identifies the socket and is used to +address it. A unique local port is generated automatically when the +socket is allocated. It will consist of the Process ID (22 bits) and a +random number (10 bits) thus allowing up to 1024 sockets per process. + +[source,c] +-------- +#include + +uint32_t nl_socket_get_local_port(const struct nl_sock *sk); +void nl_socket_set_local_port(struct nl_sock *sk, uint32_t port); +-------- + +See section <> for more information on port numbers. + +CAUTION: Overwriting the local port is possible but you have to ensure +that the provided value is unique and no other socket in any other +application is using the same value. + +.Peer Port + +A peer port can be assigned to the socket which will result in all +unicast messages sent over the socket to be addresses to the peer. If +no peer is specified, the message is sent to the kernel which will try +to automatically bind the socket to a kernel side socket of the same +netlink protocol family. It is common practice not to bind the socket +to a peer port as typically only one kernel side socket exists per +netlink protocol family. + +[source,c] +-------- +#include + +uint32_t nl_socket_get_peer_port(const struct nl_sock *sk); +void nl_socket_set_peer_port(struct nl_sock *sk, uint32_t port); +-------- + +See section <> for more information on port numbers. + +.File Descriptor + +Netlink uses the BSD socket interface, therefore a file descriptor is +behind each socket and you may use it directly. + +[source,c] +-------- +#include + +int nl_socket_get_fd(const struct nl_sock *sk); +-------- + +If a socket is used to only receive notifications it usually is best +to put the socket in non-blocking mode and periodically poll for new +notifications. + +[source,c] +-------- +#include + +int nl_socket_set_nonblocking(const struct nl_sock *sk); +-------- + +.Send/Receive Buffer Size + +The socket buffer is used to queue netlink messages between sender and +receiver. The size of these buffers specifies the maximum size you +will be able to write() to a netlink socket, i.e. it will indirectly +define the maximum message size. The default is 32KiB. + +[source,c] +-------- +#include + +int nl_socket_set_buffer_size(struct nl_sock *sk, int rx, int tx); +-------- + +[[core_sk_cred]] +.Enable/Disable Credentials + +TODO + +[source,c] +-------- +#include + +int nl_socket_set_passcred(struct nl_sock *sk, int state); +-------- + +.Enable/Disable Auto-ACK Mode + +The following functions allow to enable/disable Auto-ACK mode on a socket. +See <> for more information on what implications that has. +Auto-ACK mode is enabled by default. + +[source,c] +-------- +#include + +void nl_socket_enable_auto_ack(struct nl_sock *sk); +void nl_socket_disable_auto_ack(struct nl_sock *sk); +-------- + +.Enable/Disable Message Peeking + +If enabled, message peeking causes nl_recv() to try and use MSG_PEEK +to retrieve the size of the next message received and allocate a +buffer of that size. Message peeking is enabled by default but can be +disabled using the following function: + +[source,c] +-------- +#include + +void nl_socket_enable_msg_peek(struct nl_sock *sk); +void nl_socket_disable_msg_peek(struct nl_sock *sk); +-------- + +.Enable/Disable Receival of Packet Information + +If enabled, each received netlink message from the kernel will include +an additional struct nl_pktinfo in the control message. The following +function can be used to enable/disable receival of packet information. + +[source,c] +-------- +#include + +int nl_socket_recv_pktinfo(struct nl_sock *sk, int state); +-------- + +CAUTION: Processing of NETLINK_PKTINFO has not been implemented yet. + +[[core_send_recv]] +== Sending and Receiving of Messages / Data + +[[core_send]] +=== Sending Messages + +The standard method of sending a netlink message over a netlink socket +is to use the function nl_send_auto(). It will automatically complete +the netlink message by filling the missing bits and pieces in the +netlink message header and will deal with addressing based on the +options and address set in the netlink socket. The message is then +passed on to nl_send(). + +If the default sending semantics implemented by nl_send() do not suit +the application, it may overwrite the sending function nl_send() by +specifying an own implementation using the function +nl_cb_overwrite_send(). + +[source,c] +-------- + nl_send_auto(sk, msg) + | + |-----> nl_complete_msg(sk, msg) + | + | + | Own send function specified via nl_cb_overwrite_send() + |- - - - - - - - - - - - - - - - - - - - + v v + nl_send(sk, msg) send_func() +-------- + +.Using nl_send() + +If you do not require any of the automatic message completion +functionality you may use nl_send() directly but beware that any +internal calls to nl_send_auto() by the library to send netlink +messages will still use nl_send(). Therefore if you wish to use any +higher level interfaces and the behaviour of nl_send() is to your +dislike then you must overwrite the nl_send() function via +nl_cb_overwrite_send() + +The purpose of nl_send() is to embed the netlink message into a iovec +structure and pass it on to nl_send_iovec(). + +[source,c] +-------- + nl_send(sk, msg) + | + v + nl_send_iovec(sk, msg, iov, iovlen) +-------- + +.Using nl_send_iovec() + +nl_send_iovec() expects a finalized netlink message and fills out the +struct msghdr used for addressing. It will first check if the struct +nl_msg is addressed to a specific peer (see nlmsg_set_dst()). If not, +it will try to fall back to the peer address specified in the socket +(see nl_socket_set_peer_port(). Otherwise the message will be sent +unaddressed and it is left to the kernel to find the correct peer. + +nl_send_iovec() also adds credentials if present and enabled +(see <>). + +The message is then passed on to nl_sendmsg(). + +[source,c] +-------- + nl_send_iovec(sk, msg, iov, iovlen) + | + v + nl_sendmsg(sk, msg, msghdr) +-------- + +.Using nl_sendmsg() + +nl_sendmsg() expects a finalized netlink message and an optional +struct msghdr containing the peer address. It will copy the local +address as defined in the socket (see nl_socket_set_local_port()) into +the netlink message header. + +At this point, construction of the message finished and it is ready to +be sent. + +[source,c] +-------- + nl_sendmsg(sk, msg, msghdr) + |- - - - - - - - - - - - - - - - - - - - v + | NL_CB_MSG_OUT() + |<- - - - - - - - - - - - - - - - - - - -+ + v + sendmsg() +-------- + +Before sending the application has one last chance to modify the +message. It is passed to the NL_CB_MSG_OUT callback function which +may inspect or modify the message and return an error code. If this +error code is NL_OK the message is sent using sendmsg() resulting in +the number of bytes written being returned. Otherwise the message +sending process is aborted and the error code specified by the +callback function is returned. See <> for more information +on how to set callbacks. + +.Sending Raw Data with nl_sendto() + +If you wish to send raw data over a netlink socket, the following +function will pass on any buffer provided to it directly to sendto(): + +[source,c] +-------- +#include + +int nl_sendto(struct nl_sock *sk, void *buf, size_t size); +-------- + +.Sending of Simple Messages + +A special interface exists for sending of trivial messages. The function +expects the netlink message type, optional netlink message flags, and an +optional data buffer and data length. +[source,c] +-------- +#include + +int nl_send_simple(struct nl_sock *sk, int type, int flags, + void *buf, size_t size); +-------- + +The function will construct a netlink message header based on the message +type and flags provided and append the data buffer as message payload. The +newly constructed message is sent with nl_send_auto(). + +The following example will send a netlink request message causing the +kernel to dump a list of all network links to userspace: + +[source,c] +-------- +#include + +struct nl_sock *sk; +struct rtgenmsg rt_hdr = { + .rtgen_family = AF_UNSPEC, +}; + +sk = nl_socket_alloc(); +nl_connect(sk, NETLINK_ROUTE); + +nl_send_simple(sock, RTM_GETLINK, NLM_F_DUMP, &rt_hdr, sizeof(rt_hdr)); +-------- + +[[core_recv]] +=== Receiving Messages + +The easiest method to receive netlink messages is to call nl_recvmsgs_default(). +It will receive messages based on the semantics defined in the socket. The +application may customize these in detail although the default behaviour will +probably suit most applications. + +nl_recvmsgs_default() will also be called internally by the library whenever +it needs to receive and parse a netlink message. + +The function will fetch the callback configuration stored in the socket and +call nl_recvmsgs(): + +[source,c] +-------- + nl_recvmsgs_default(sk) + | + | cb = nl_socket_get_cb(sk) + v + nl_recvmsgs(sk, cb) +-------- + +.Using nl_recvmsgs() + +nl_recvmsgs() implements the actual receiving loop, it blocks until a +netlink message has been received unless the socket has been put into +non-blocking mode. + +For the unlikely scenario that certain required receive characteristics +can not be achieved by fine tuning the internal recvmsgs function using +the callback configuration (see <>) the application may provide +a complete own implementation of it and overwrite all calls to nl_recvmsgs() +with the function nl_cb_overwrite_recvmsgs(). + +[source,c] +-------- + nl_recvmsgs(sk, cb) + | + | Own recvmsgs function specified via nl_cb_overwrite_recvmsgs() + |- - - - - - - - - - - - - - - - - - - - + v v + internal_recvmsgs() my_recvmsgs() +-------- + +[[core_recv_character]] +.Receive Characteristics + +If the application does not provide its own recvmsgs() implementation +with the function nl_cb_overwrite_recvmsgs() the following characteristics +apply while receiving data from a netlink socket: + +[source,c] +-------- + internal_recvmsgs() + | ++-------------->| Own recv function specified with nl_cb_overwrite_recv() +| |- - - - - - - - - - - - - - - - +| v v +| nl_recv() my_recv() +| |<- - - - - - - - - - - - - - -+ +| |<-------------+ +| v | More data to parse? (nlmsg_next()) +| Parse Message | +| |--------------+ +| v ++------- NLM_F_MULTI set? + | + v + (SUCCESS) +-------- + +The function nl_recv() is invoked first to receive data from the +netlink socket. This function may be overwritten by the application +by an own implementation using the function nl_cb_overwrite_recv(). +This may be useful if the netlink byte stream is in fact not received +from a socket directly but is read from a file or another source. + +If data has been read, it will be attemped to parse the data. This +will be done repeately until the parser returns NL_STOP, an error was +returned or all data has been parsed. + +In case the last message parsed successfully was a multipart message +(see <>) and the parser did not +quit due to either an error or NL_STOP nl_recv() respectively the +applications own implementation will be called again and the parser +starts all over. + +See <> for information on how to extract valid +netlink messages from the parser and on how to control the behaviour +of it. + +[[core_parse_character]] +.Parsing Characteristics + +The internal parser is invoked for each netlink message received from +a netlink socket. It is typically fed by nl_recv() (see +<>). + +The parser will first ensure that the length of the data stream +provided is sufficient to contain a netlink message header and that +the message length as specified in the message header does not exceed +it. + +If this criteria is met, a new struct nl_msg is allocated and the +message is passed on to the the callback function NL_CB_MSG_IN if one +is set. Like any other callback function, it may return NL_SKIP to +skip the current message but continue parsing the next message or +NL_STOP to stop parsing completely. + +The next step is to check the sequence number of the message against +the currently expected sequence number. The application may provide +its own sequence number checking algorithm by setting the callback +function NL_CB_SEQ_CHECK to its own implementation. In fact, calling +nl_socket_disable_seq_check() to disable sequence number checking will +do nothing more than set the NL_CB_SEQ_CHECK hook to a function which +always returns NL_OK. + +Another callback hook NL_CB_SEND_ACK exists which is called if the +message has the NLM_F_ACK flag set. Although I am not aware of any +userspace netlink socket doing this, the application may want to send +an ACK message back to the sender (see <>). + +[source,c] +-------- + parse() + | + v + nlmsg_ok() --> Ignore + | + |- - - - - - - - - - - - - - - v + | NL_CB_MSG_IN() + |<- - - - - - - - - - - - - - -+ + | + |- - - - - - - - - - - - - - - v + Sequence Check NL_CB_SEQ_CHECK() + |<- - - - - - - - - - - - - - -+ + | + | Message has NLM_F_ACK set + |- - - - - - - - - - - - - - - v + | NL_CB_SEND_ACK() + |<- - - - - - - - - - - - - - -+ + | + Handle Message Type +-------- + +[[core_auto_ack]] +=== Auto-ACK Mode + +TODO + +== Message Parsing & Construction + +=== Message Format + +See <> for an introduction to the netlink +protocol and its message format. + +.Alignment + +Most netlink protocols enforce a strict alignment policy for all +boundries. The alignment value is defined by NLMSG_ALIGNTO and is +fixed to 4 bytes. Therefore all netlink message headers, begin of +payload sections, protocol specific headers, and attribute sections +must start at an offset which is a multiple of NLMSG_ALIGNTO. + +[source,c] +-------- +#include + +int nlmsg_size(int payloadlen); +int nlmsg_total_size(int payloadlen); +-------- + +The library provides a set of function to handle alignment +requirements automatically. The function nlmsg_total_size() returns +the total size of a netlink message including the padding to ensure +the next message header is aligned correctly. + +[source,c] +-------- + <----------- nlmsg_total_size(len) ------------> + <----------- nlmsg_size(len) ------------> + +-------------------+- - -+- - - - - - - - +- - -+-------------------+- - - + | struct nlmsghdr | Pad | Payload | Pad | struct nlsmghdr | + +-------------------+- - -+- - - - - - - - +- - -+-------------------+- - - + <---- NLMSG_HDRLEN -----> <- NLMSG_ALIGN(len) -> <---- NLMSG_HDRLEN --- +-------- + +If you need to know if padding needs to be added at the end of a +message, nlmsg_padlen() returns the number of padding bytes that need +to be added for a specific payload length. + +[source,c] +-------- +#include +int nlmsg_padlen(int payloadlen); +-------- + +=== Parsing a Message + +The library offers two different methods of parsing netlink messages. +It offers a low level interface for applications which want to do all +the parsing manually. This method is described below. Alternatively +the library also offers an interface to implement a parser as part of +a cache operations set which is especially useful when your protocol +deals with objects of any sort such as network links, routes, etc. +This high level interface is described in <>. + +.Splitting a byte stream into separate messages + +What you receive from a netlink socket is typically a stream of +messages. You will be given a buffer and its length, the buffer may +contain any number of netlink messages. + +The first message header starts at the beginning of message stream. +Any subsequent message headers are access by calling nlmsg_next() on +the previous header. + +[source,c] +-------- +#include + +struct nlmsghdr *nlmsg_next(struct nlmsghdr *hdr, int *remaining); +-------- + +The function nlmsg_next() will automatically substract the size of the +previous message from the remaining number of bytes. + +Please note, there is no indication in the previous message whether +another message follows or not. You must assume that more messages +follow until all bytes of the message stream have been processed. + +To simplify this, the function nlmsg_ok() exists which returns true if +another message fits into the remaining number of bytes in the message +stream. nlmsg_valid_hdr() is similar, it checks whether a specific +netlink message contains at least a minimum of payload. + +[source,c] +-------- +#include + +int nlmsg_valid_hdr(const struct nlmsghdr *hdr, int payloadlen); +int nlmsg_ok(const struct nlmsghdr *hdr, int remaining); +-------- + +A typical use of these functions looks like this: + +[source,c] +-------- +#include + +void my_parse(void *stream, int length) +{ + struct nlmsghdr *hdr = stream; + + while (nlmsg_ok(hdr, length)) { + // Parse message here + hdr = nlmsg_next(hdr, &length); + } +} +-------- + +CAUTION: nlmsg_ok() only returns true if the *complete* message including + the message payload fits into the remaining buffer length. It will + return false if only a part of it fits. + +The above can also be written using the iterator nlmsg_for_each(): + +[source,c] +-------- +#include + +struct nlmsghdr *hdr; + +nlmsg_for_each(hdr, stream, length) { + /* do something with message */ +} +-------- + +.Message Payload + +The message payload is appended to the message header and is guranteed +to start at a multiple of +NLMSG_ALIGNTO+. Padding at the end of the +message header is added if necessary to ensure this. The function +nlmsg_data() will calculate the necessary offset based on the message +and returns a pointer to the start of the message payload. + +[source,c] +-------- +#include + +void *nlmsg_data(const struct nlmsghdr *nlh); +void *nlmsg_tail(const struct nlmsghdr *nlh); +int nlmsg_datalen(const struct nlmsghdr *nlh); +-------- + +The length of the message payload is returned by nlmsg_datalen(). + +[source,c] +-------- + <--- nlmsg_datalen(nlh) ---> + +-------------------+- - -+----------------------------+- - -+ + | struct nlmsghdr | Pad | Payload | Pad | + +-------------------+- - -+----------------------------+- - -+ +nlmsg_data(nlh) ---------------^ ^ +nlmsg_tail(nlh) --------------------------------------------------^ +-------- + +The payload may consist of arbitary data but may have strict alignment +and formatting rules depening on the actual netlink protocol. + +[[core_msg_attr]] +.Message Attributes + +Most netlink protocols use netlink attributes. It not only makes the +protocol self documenting but also gives flexibility in expanding the +protocol at a later point. New attributes can be added at any time and +older attributes can be obsoleted by newer ones without breaking +binary compatibility of the protocol. + +[source,c] +-------- + <---------------------- payload -------------------------> + <----- hdrlen ----> <- nlmsg_attrlen(nlh, hdrlen) -> + +-------------------+- - -+----- ------------+- - -+--------------------------------+- - -+ + | struct nlmsghdr | Pad | Protocol Header | Pad | Attributes | Pad | + +-------------------+- - -+-------------------+- - -+--------------------------------+- - -+ +nlmsg_attrdata(nlh, hdrlen) -----------------------------^ +-------- + +The function nlmsg_attrdata() returns a pointer to the begin of the +attributes section. The length of the attributes section is returned +by the function nlmsg_attrlen(). + +[source,c] +-------- +#include + +struct nlattr *nlmsg_attrdata(const struct nlmsghdr *hdr, int hdrlen); +int nlmsg_attrlen(const struct nlmsghdr *hdr, int hdrlen); +-------- + +See <> for more information on how to use netlink attributes. + +.Parsing a Message the Easy Way + +The function nlmsg_parse() validate a complete netlink message in one +step. If +hdrlen > 0+ it will first call nlmsg_valid_hdr() to check +if the protocol header fits into the message. If there is more payload +to parse, it will assume it to be attributes and parse the payload +accordingly. The function behaves exactly like nla_parse() when +parsing attributes, see <>. + +[source,c] +-------- +int nlmsg_parse(struct nlmsghdr *hdr, int hdrlen, struct nlattr **attrs, + int maxtype, struct nla_policy *policy); +-------- + +The function nlmsg_validate() is based on nla_validate() and behaves +exactly the same as nlmsg_parse() except that it only validates and +will not fill a array with pointers to each attribute. + +[source,c] +-------- +int nlmsg_validate(struct nlmsghdr *hdr, int hdrlen, intmaxtype, + struct nla_policy *policy); +-------- + +See <> for an example and more information on +attribute parsing. + +=== Construction of a Message + +See <> for information on the netlink message format +and alignment requirements. + +Message construction is based on struct nl_msg which uses an internal +buffer to store the actual netlink message. struct nl_msg +does not+ +point to the netlink message header. Use nlmsg_hdr() to retrieve a +pointer to the netlink message header. + +At allocation time, a maximum message size is specified. It defaults +to a page (PAGE_SIZE). The application constructing the message will +reserve space out of this maximum message size repeatedly for each +header or attribute added. This allows construction of messages across +various layers of code where lower layers do not need to know about +the space requirements of upper layers. + ++Why is setting the maximum message size necessary?+ This +question is often raised in combination with the proposed solution of +reallocating the message payload buffer on the fly using realloc(). +While it is possible to reallocate the buffer during construction +using nlmsg_expand() it will make all pointers into the message buffer +become stale. This breaks usage of nlmsg_hdr(), nla_nest_start(), and +nla_nest_end() and is therefore not acceptable as default behaviour. + +.Allocating struct nl_msg + +The first step in constructing a new netlink message it to allocate a +`struct nl_msg` to hold the message header and payload. Several +functions exist to simplify various tasks. + +[source,c] +-------- +#include + +struct nl_msg *nlmsg_alloc(void); +void nlmsg_free(struct nl_msg *msg); +-------- + +The function nlmsg_alloc() is the default message allocation function. +It allocates a new message using the default maximum message size which +equals to one page (PAGE_SIZE). The application can change the default +size for messages by calling nlmsg_set_default_size(): + +[source,c] +-------- +void nlmsg_set_default_size(size_t); +-------- + +CAUTION: Calling nlmsg_set_default_size() does not change the maximum + message size of already allocated messages. + +[source,c] +-------- +struct nl_msg *nlmsg_alloc_size(size_t max); +-------- + +Instead of changing the default message size, the function +nlmsg_alloc_size() can be used to allocate a message with a individual +maximum message size. + + +If the netlink message header is already known at allocation time, the +application may sue nlmsg_inherit(). It will allocate a message using +the default maximum message size and copy the header into the message. +Calling nlmsg_inherit with +set+ to NULL is equivalent to calling +nlmsg_alloc(). + +[source,c] +-------- +struct nl_msg *nlmsg_inherit(struct nlmsghdr *hdr); +-------- + +Alternatively nlmsg_alloc_simple() takes a netlink message type and +netlink message flags. It is equivalent to nlmsg_inherit() except that it +takes the two common header fields as arguments instead of a complete +header. + +[source,c] +-------- +#include + +struct nl_msg *nlmsg_alloc_simple(int nlmsg_type, int flags); +-------- + +.Appending the netlink message header + +After allocating struct nl_msg, the netlink message header needs to be +added unless one of the function nlmsg_alloc_simple() or nlmsg_inherit() +have been used for allocation in which case this step will replace the +netlink message header already in place. + +[source,c] +-------- +#include + +struct nlmsghdr *nlmsg_put(struct nl_msg *msg, uint32_t port, uint32_t seqnr, + int nlmsg_type, int payload, int nlmsg_flags); +-------- + +The function nlmsg_put() will build a netlink message header out of ++nlmsg_type+, +nlmsg_flags+, +seqnr+, and +port+ and copy it into the +netlink message. +seqnr+ can be set to +NL_AUTO_SEQ+ to indiciate +that the next possible sequence number should be used automatically. +To use this feature, the message must be sent using the function +nl_send_auto(). Like +port+, the argument +seqnr+ can be set to ++NL_AUTO_PORT+ indicating that the local port assigned to the socket +should be used as source port. This is generally a good idea unless +you are replying to a request. See <> +for more information on how to fill the header. + +NOTE: The argument +payload+ can be used by the application to reserve + room for additional data after the header. A value of > 0 is + equivalent to calling +nlmsg_reserve(msg, payload, NLMSG_ALIGNTO)+. + See <> for more information on reserving room for + data. + +.Example +[source,c] +-------- +#include + +struct nlmsghdr *hdr; +struct nl_msg *msg; +struct myhdr { + uint32_t foo1, foo2; +} hdr = { 10, 20 }; + +/* Allocate a message with the default maximum message size */ +msg = nlmsg_alloc(); + +/* + * Add header with message type MY_MSGTYPE, the flag NLM_F_CREATE, + * let library fill port and sequence number, and reserve room for + * struct myhdr + */ +hdr = nlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, MY_MSGTYPE, sizeof(hdr), NLM_F_CREATE); + +/* Copy own header into newly reserved payload section */ +memcpy(nlmsg_data(hdr), &hdr, sizeof(hdr)); + +/* + * The message will now look like this: + * +-------------------+- - -+----------------+- - -+ + * | struct nlmsghdr | Pad | struct myhdr | Pad | + * +-------------------+-----+----------------+- - -+ + * nlh -^ / \ + * +--------+---------+ + * | foo1 | foo2 | + * +--------+---------+ + */ +-------- + +[[core_msg_reserve]] +.Reserving room at the end of the message + +Most functions described later on will automatically take care of +reserving room for the data that is added to the end of the netlink +message. In some situations it may be requried for the application +to reserve room directly though. + +[source,c] +-------- +#include + +void *nlmsg_reserve(struct nl_msg *msg, size_t len, int pad); +-------- + +The function nlmsg_reserve() reserves +len+ bytes at the end of the +netlink message and returns a pointer to the start of the reserved area. +The +pad+ argument can be used to request +len+ to be aligned to any +number of bytes prior to reservation. + +The following example requests to reserve a 17 bytes area at the end of +message aligned to 4 bytes. Therefore a total of 20 bytes will be +reserved. + +[source,c] +-------- +#include + +void *buf = nlmsg_reserve(msg, 17, 4); +-------- + +NOTE: `nlmsg_reserve()` will *not* align the start of the buffer. Any + alignment requirements must be provided by the owner of the + previous message section. + +.Appending data at the end of the message + +The function `nlmsg_append()` appends `len` bytes at the end of the +message, padding it if requested and necessary. + +[source,c] +-------- +#include + +int nlmsg_append(struct nl_msg *msg, void *data, size_t len, int pad); +-------- + +It is equivalent to calling `nlmsg_reserve()` and `memcpy()`ing the +data into the freshly reserved data section. + +NOTE: `nlmsg_append()` will *not* align the start of the data. Any + alignment requirements must be provided by the owner of the + previous message section. + +.Adding attribtues to a message + +Construction of attributes and addition of attribtues to the message is +covereted in section <>. + +[[core_attr]] +== Attributes + +Any form of payload should be encoded as netlink attributes whenever +possible. Use of attributes allows to extend any netlink protocol in +the future without breaking binary compatibility. F.e. Suppose your +device may currently be using 32 bit counters for statistics but years +later the device switches to maintaining 64 bit counters to account +for faster network hardware. If your protocol is using attributes the +move to 64 bit counters is trivial and only involves in sending an +additional attribute containing the 64 bit variants while still +providing the old legacy 32 bit counters. If your protocol is not using +attributes you will not be able to switch data types without breaking +all existing users of the protocol. + +The concept of nested attributes also allows for subsystems of your +protocol to implement and maintain their own attribute schemas. Suppose +a new generation of network device is introduced which requires a +completely new set of configuration settings which was unthinkable when +the netlink protocol was initially designed. Using attributes the new +generation of devices may define a new attribute and fill it with its +own new structure of attributes which extend or even obsolete the old +attributes. + +Therefore, _always_ use attributes even if you are almost certain that +the message format will never ever change in the future. + +[[core_attr_format]] +=== Attribute Format + +Netlink attributes allow for any number of data chunks of arbitary +length to be attached to a netlink message. See <> +for more information on where attributes are stored in the message. + +The format of the attributes data returned by nlmsg_attrdata() is as +follows: + +[source,c] +-------- + <----------- nla_total_size(payload) -----------> + <---------- nla_size(payload) -----------> + +-----------------+- - -+- - - - - - - - - +- - -+-----------------+- - - + | struct nlattr | Pad | Payload | Pad | struct nlattr | + +-----------------+- - -+- - - - - - - - - +- - -+-----------------+- - - + <---- NLA_HDRLEN -----> <--- NLA_ALIGN(len) ---> <---- NLA_HDRLEN --- +-------- + +Every attribute must start at an offset which is a multiple of ++NLA_ALIGNTO+ (4 bytes). If you need to know whether an attribute needs +to be padded at the end, the function nla_padlen() returns the number +of padding bytes that will or need to be added. + +image:attribute_hdr.png["Netlink Attribute Header"] + +Every attribute is encoded with a type and length field, both 16 bits, +stored in the attribute header (struct nlattr) preceding the attribute +payload. The length of an attribute is used to calculate the offset to +the next attribute. + +[[core_attr_parse]] +=== Parsing Attributes + +[[core_attr_parse_split]] +.Splitting an Attributes Stream into Attributes + +Although most applications will use one of the functions from the +nlmsg_parse() family (See <>) an interface exists +to split the attributes stream manually. + +As described in <> the attributes section contains a +infinite sequence or stream of attributes. The pointer returned by +nlmsg_attrdata() (See <>) points to the first attribute +header. Any subsequent attribute is accessed with the function nla_next() +based on the previous header. + +[source,c] +-------- +#include + +struct nlattr *nla_next(const struct nlattr *attr, int *remaining); +-------- + +The semantics are equivalent to nlmsg_next() and thus nla_next() will also +subtract the size of the previous attribute from the remaining number of +bytes in the attributes stream. + +Like messages, attributes do not contain an indicator whether another +attribute follows or not. The only indication is the number of bytes left +in the attribute stream. The function nla_ok() exists to determine whether +another attribute fits into the remaining number of bytes or not. + +[source,c] +-------- +#include + +int nla_ok(const struct nlattr *attr, int remaining); +-------- + +A typical use of nla_ok() and nla_next() looks like this: + +.nla_ok()/nla_next() usage +[source,c] +-------- +#include +#include + +struct nlattr *hdr = nlmsg_attrdata(msg, 0); +int remaining = nlmsg_attrlen(msg, 0); + +while (nla_ok(hdr, remaining)) { + /* parse attribute here */ + hdr = nla_next(hdr, &remaining); +}; +-------- + +NOTE: `nla_ok()` only returns true if the *complete* attributes + including the attribute payload fits into the remaining number + of bytes. + +.Accessing Attribute Header and Payload + +Once the individual attributes have been sorted out by either splitting +the attributes stream or using another interface the attribute header +and payload can be accessed. + +[source,c] +-------- + <- nla_len(hdr) -> + +-----------------+- - -+- - - - - - - - - +- - -+ + | struct nlattr | Pad | Payload | Pad | + +-----------------+- - -+- - - - - - - - - +- - -+ +nla_data(hdr) ---------------^ +-------- + +The functions nla_len() and nla_type() can be used to access the attribute +header. nla_len() will return the length of the payload not including +eventual padding bytes. nla_type returns the attribute type. + +[source,c] +-------- +#include + +int nla_len(const struct nlattr *hdr); +int nla_type(const struct nlattr *hdr); +-------- + +The function nla_data() will return a pointer to the attribute +payload. Please note that due to +NLA_ALIGNTO+ being 4 bytes it may +not be safe to cast and dereference the pointer for any datatype +larger than 32 bit depending on the architecture the application is +run on. + +[source,c] +-------- +#include + +void *nla_data(const struct nlattr *hdr); +-------- + +[NOTE] +Never rely on the size of a payload being what you expect it to be. +_Always_ verify the payload size and make sure that it matches your +expectations. See <> + +[[core_attr_validation]] +.Attribute Validation + +When receiving netlink attributes, the receiver has certain expections +on how the attributes should look like. These expectations must be +defined to make sure the sending side meets our expecations. For this +purpose, a attribute validation interface exists which must be used +prior to accessing any payload. + +All functions providing attribute validation functionality are based +on struct nla_policy: + +[source,c] +-------- +struct nla_policy { + uint16_t type; + uint16_t minlen; + uint16_t maxlen; +}; +-------- + +The +type+ member specifies the datatype of the attribute, e.g. ++NLA_U32+, +NLA_STRING+, +NLA_FLAG+. The default is +NLA_UNSPEC+. The ++minlen+ member defines the minmum payload length of an attribute to +be considered a valid attribute. The value for +minlen+ is implicit +for most basic datatypes such as integers or flags. The +maxlen+ +member can be used to define a maximum payload length for an +attribute to still be considered valid. + +NOTE: Specyfing a maximum payload length is not recommended when + encoding structures in an attribute as it will prevent any + extension of the structure in the future. Something that is + frequently done in netlink protocols and does not break + backwards compatibility. + +One of the functions which use struct nla_policy is nla_validate(). +The function expects an array of struct nla_policy and will access the +array using the attribute type as index. If an attribute type is out +of bounds the attribute is assumed to be valid. This is intentional +behaviour to allow older applications not yet aware of recently +introduced attributes to continue functioning. + +[source,c] +-------- +#include + +int nla_validate(struct nlattr *head, int len, int maxtype, struct nla_policy *policy); +-------- + +The function nla_validate() returns 0 if all attributes are valid, +otherwise a validation failure specific error code is returned. + +Most applications will rarely use nla_validate() directly but use +nla_parse() instead which takes care of validation in the same way but +also parses the the attributes in the same step. See +<> for an example and more information. + +The validation process in detail: + +. If attribute type is 0 or exceeds +maxtype+ attribute is + considered valid, 0 is returned. +. If payload length is < +minlen+, +-NLE_ERANGE+ is returned. +. If +maxlen+ is defined and payload exceeds it, +-NLE_ERANGE+ + is returned. +. Datatype specific requirements rules, see <> +. If all is ok, 0 is returned. + +[[core_attr_parse_easy]] +.Parsing Attributes the Easy Way + +Most applications will not want to deal with splitting attribute +streams themselves as described in <> +A much easier method is to use nla_parse(). + +[source,c] +-------- +#include + +int nla_parse(struct nlattr **attrs, int maxtype, struct nlattr *head, + int len, struct nla_policy *policy); +-------- + +The function nla_parse() will iterate over a stream of attributes, +validate each attribute as described in <> +If the validation of all attributes succeeds, a pointer to each attribute +is stored in the +attrs+ array at `attrs[nla_type(attr)]`. + +As an alernative to nla_parse() the function nlmsg_parse() can be used +to parse the message and its attributes in one step. See +<> for information on how to use these functions. + +.Example: + +The following example demonstrates how to parse a netlink message sent +over a netlink protocol which does not use protocol headers. The example +does enforce a attribute policy however, the attribute MY_ATTR_FOO must +be a 32 bit integer, and the attribute MY_ATTR_BAR must be a string with +a maximum length of 16 characters. + +[source,c] +--------- +#include +#include + +enum { + MY_ATTR_FOO = 1, + MY_ATTR_BAR, + __MY_ATTR_MAX, +}; + +#define MY_ATTR_MAX (__MY_ATTR_MAX - 1) + +static struct nla_policy my_policy[MY_ATTR_MAX+1] = { + [MY_ATTR_FOO] = { .type = NLA_U32 }, + [MY_ATTR_BAR] = { .type = NLA_STRING, + .maxlen = 16 }, +}; + +void parse_msg(struct nlmsghdr *nlh) +{ + struct nlattr *attrs[MY_ATTR_MAX+1]; + + if (nlmsg_parse(nlh, 0, attrs, MY_ATTR_MAX, my_policy) < 0) + /* error */ + + if (attrs[MY_ATTR_FOO]) { + /* MY_ATTR_FOO is present in message */ + printf("value: %u\n", nla_get_u32(attrs[MY_ATTR_FOO])); + } +} +--------- + +.Locating a Single Attribute + +An application only interested in a single attribute can use one of the +functions nla_find() or nlmsg_find_attr(). These function will iterate +over all attributes, search for a matching attribute and return a pointer +to the corresponding attribute header. + +[source,c] +-------- +#include + +struct nlattr *nla_find(struct nlattr *head, int len, int attrtype); +-------- + +[source,c] +-------- +#include + +struct nlattr *nlmsg_find_attr(struct nlmsghdr *hdr, int hdrlen, int attrtype); +-------- + +NOTE: `nla_find()` and `nlmsg_find_attr()` will *not* search in nested + attributes recursively, see <>. + +==== Iterating over a Stream of Attributes + +In some situations it does not make sense to assign a unique attribute +type to each attribute in the attribute stream. For example a list may +be transferd using a stream of attributes and even if the attribute type +is incremented for each attribute it may not make sense to use the +nlmsg_parse() or nla_parse() function to fill an array. + +Therefore methods exist to iterate over a stream of attributes: + +[source,c] +-------- +#include + +nla_for_each_attr(attr, head, len, remaining) +-------- + +nla_for_each_attr() is a macro which can be used in front of a code +block: + +[source,c] +-------- +#include + +struct nalttr *nla; +int rem; + +nla_for_each_attr(nla, attrstream, streamlen, rem) { + /* validate & parse attribute */ +} + +if (rem > 0) + /* unparsed attribute data */ +-------- + +[[core_attr_constr]] +=== Attribute Construction + +The interface to add attributes to a netlink message is based on the +regular message construction interface. It assumes that the message +header and an eventual protocol header has been added to the message +already. + +[source,c] +-------- +struct nlattr *nla_reserve(struct nl_msg *msg, int attrtype, int len); +-------- + +The function nla_reserve() adds an attribute header at the end of the +message and reserves room for +len+ bytes of payload. The function +returns a pointer to the attribute payload section inside the message. +Padding is added at the end of the attribute to ensure the next +attribute is properly aligned. + +[source,c] +-------- +int nla_put(struct nl_msg *msg, int attrtype, int attrlen, const void *data); +-------- + +The function nla_put() is base don nla_reserve() but takes an additional +pointer +data+ pointing to a buffer containing the attribute payload. +It will copy the buffer into the message automatically. + +.Example: + +[source,c] +-------- +struct my_attr_struct { + uint32_t a; + uint32_t b; +}; + +int my_put(struct nl_msg *msg) +{ + struct my_attr_struct obj = { + .a = 10, + .b = 20, + }; + + return nla_put(msg, ATTR_MY_STRUCT, sizeof(obj), &obj); +} +-------- + +See <> for datatype specific attribute construction +functions. + +.Exception Based Attribute Construction + +Like in the kernel API an exception based construction interface is +provided. The behaviour of the macros is identical to their regular +function counterparts except that in case of an error, the target +`nla_put_failure` is jumped. + +.Example: +[source,c] +-------- +#include +#include + +void construct_attrs(struct nl_msg *msg) +{ + NLA_PUT_STRING(msg, MY_ATTR_FOO1, "some text"); + NLA_PUT_U32(msg, MY_ATTR_FOO1, 0x1010); + NLA_PUT_FLAG(msg, MY_ATTR_FOO3, 1); + + return 0; + +nla_put_failure: + /* NLA_PUT* macros jump here in case of an error */ + return -EMSGSIZE; +} +-------- + +See <> for more information on the datatype specific +exception based variants. + +[[core_attr_types]] +=== Attribute Data Types + +A number of basic data types have been defined to simplify access and +validation of attributes. The datatype is not encoded in the +attribute, therefore bthe sender and receiver are required to use the +same definition on what attribute is of what type. + +[options="header", cols="1m,5"] +|================================================ +| Type | Description +| NLA_UNSPEC | Unspecified attribute +| NLA_U{8\|16\|32} | Integers +| NLA_STRING | String +| NLA_FLAG | Flag +| NLA_NESTED | Nested attribute +|================================================ + +Besides simplified access to the payload of such datatypes, the major +advantage is the automatic validation of each attribute based on a +policy. The validation ensures safe access to the payload by checking +for minimal payload size and can also be used to enforce maximum +payload size for some datatypes. + +==== Integer Attributes + +The most frequently used datatypes are integers. Integers come in four +different sizes: +[horizontal] +NLA_U8:: 8bit integer +NLA_U16:: 16bit integer +NLA_U32:: 32bit integer +NLA_U64:: 64bit integer + +Note that due to the alignment requirements of attributes the integer +attribtue +NLA_u8+ and +NLA_U16+ will not result in space savings in +the netlink message. Their use is intended to limit the range of +values. + +.Parsing Integer Attributes + +[source,c] +-------- +#include + +uint8_t nla_get_u8(struct nlattr *hdr); +uint16_t nla_get_u16(struct nlattr *hdr); +uint32_t nla_get_u32(struct nlattr *hdr); +uint64_t nla_get_u64(struct nlattr *hdr); +-------- + +Example: + +[source,c] +-------- +if (attrs[MY_ATTR_FOO]) + uint32_t val = nla_get_u32(attrs[MY_ATTR_FOO]); +-------- + +.Constructing Integer Attributes + +[source,c] +-------- +#include + +int nla_put_u8(struct nl_msg *msg, int attrtype, uint8_t value); +int nla_put_u16(struct nl_msg *msg, int attrtype, uint16_t value); +int nla_put_u32(struct nl_msg *msg, int attrtype, uint32_t value); +int nla_put_u64(struct nl_msg *msg, int attrtype, uint64_t value); +-------- + +Exception based: + +[source,c] +-------- +NLA_PUT_U8(msg, attrtype, value) +NLA_PUT_U16(msg, attrtype, value) +NLA_PUT_U32(msg, attrtype, value) +NLA_PUT_U64(msg, attrtype, value) +-------- + +.Validation + +Use +NLA_U8+, +NLA_U16+, +NLA_U32+, or +NLA_U64+ to define the type of +integer when filling out a struct nla_policy array. It will +automatically enforce the correct minimum payload length policy. + +Validation does not differ between signed and unsigned integers, only +the size matters. If the appliaction wishes to enforce particular value +ranges it must do so itself. + +[source,c] +-------- +static struct nla_policy my_policy[ATTR_MAX+1] = { + [ATTR_FOO] = { .type = NLA_U32 }, + [ATTR_BAR] = { .type = NLA_U8 }, +}; +-------- + +The above is equivalent to: +[source,c] +-------- +static struct nla_policy my_policy[ATTR_MAX+1] = { + [ATTR_FOO] = { .minlen = sizeof(uint32_t) }, + [ATTR_BAR] = { .minlen = sizeof(uint8_t) }, +}; +-------- + +==== String Attributes + +The string datatype represents a NUL termianted character string of +variable length. It is not intended for binary data streams. + +The payload of string attributes can be accessed with the function +nla_get_string(). nla_strdup() calls strdup() on the payload and returns +the newly allocated string. + +[source,c] +-------- +#include + +char *nla_get_string(struct nlattr *hdr); +char *nla_strdup(struct nlattr *hdr); +-------- + +String attributes are constructed with the function +nla_put_string()+ +respectively +NLA_PUT_STRING()+. The length of the payload will be +strlen()+1, the trailing NUL byte is included. + +[source,c] +-------- +int nla_put_string(struct nl_msg *msg, int attrtype, const char *data); + +NLA_PUT_STRING(msg, attrtype, data) +-------- + +For validation purposes the type +NLA_STRING+ can be used in ++struct nla_policy+ definitions. It implies a minimum payload length +of 1 byte and checks for a trailing NUL byte. Optionally the +maxlen+ +member defines the maximum length of a character string (including the +trailing NUL byte). + +[source,c] +-------- +static struct nla_policy my_policy[] = { + [ATTR_FOO] = { .type = NLA_STRING, + .maxlen = IFNAMSIZ }, +}; +-------- + +==== Flag Attributes + +The flag attribute represents a boolean datatype. The presence of the +attribute implies a value of +true+, the absence of the attribute +implies the value +false+. Therefore the payload length of flag +attributes is always 0. + +[source,c] +-------- +int nla_get_flag(struct nlattr *hdr); +int nla_put_flag(struct nl_msg *msg, int attrtype); +-------- + +The type +NLA_FLAG+ is used for validation purposes. It implies a ++maxlen+ value of 0 and thus enforces a maximum payload length of 0. + +.Example: +[source,c] +-------- +/* nla_put_flag() appends a zero sized attribute to the message. */ +nla_put_flag(msg, ATTR_FLAG); + +/* There is no need for a receival function, the presence is the value. */ +if (attrs[ATTR_FLAG]) + /* flag is present */ +-------- + +[[core_attr_nested]] +==== Nested Attributes + +As described in <>, attributes can be nested allowing for +complex tree structures of attributes. It is commonly used to delegate +the responsibility of a subsection of the message to a subsystem. +Nested attributes are also commonly used for transmitting list of objects. + +When nesting attributes, the nested attributes are included as payload +of a container attribute. + +NOTE: When validating the attributes using nlmsg_validate(), + nlmsg_parse(), nla_validate(), or nla_parse() only the + attributes on the first level are being validated. None of these + functions will validate attributes recursively. Therefore you + must explicitely call nla_validate() or use nla_parse_nested() + for each level of nested attributes. + +The type +NLA_NESTED+ should be used when defining nested attributes +in a struct nla_policy definition. It will not enforce any minimum +payload length unless +minlen+ is specified explicitely. This is +because some netlink protocols implicitely allow empty container +attributes. + +[source,c] +-------- +static struct nla_policy my_policy[] = { + [ATTR_OPTS] = { .type = NLA_NESTED }, +}; +-------- + +.Parsing of Nested Attributes + +The function nla_parse_nested() is used to parse nested attributes. +Its behaviour is identical to nla_parse() except that it takes a +struct nlattr as argument and will use the payload as stream of +attributes. + +[source,c] +-------- +if (attrs[ATTR_OPTS]) { + struct nlattr *nested[NESTED_MAX+1]; + struct nla_policy nested_policy[] = { + [NESTED_FOO] = { .type = NLA_U32 }, + }; + + if (nla_parse_nested(nested, NESTED_MAX, attrs[ATTR_OPTS], nested_policy) < 0) + /* error */ + + if (nested[NESTED_FOO]) + uint32_t val = nla_get_u32(nested[NESTED_FOO]); +} +-------- + +.Construction of Nested Attributes + +Attributes are nested by surrounding them with calls to nla_nest_start() +and nla_nest_end(). nla_nest_start() will add a attribute header to +the message but no actual payload. All data added to the message from +this point on will be part of the container attribute until nla_nest_end() +is called which "closes" the attribute, correcting its payload length to +include all data length. + +[source,c] +-------- +int put_opts(struct nl_msg *msg) +{ + struct nlattr *opts; + + if (!(opts = nla_nest_start(msg, ATTR_OPTS))) + goto nla_put_failure; + + NLA_PUT_U32(msg, NESTED_FOO, 123); + NLA_PUT_STRING(msg, NESTED_BAR, "some text"); + + nla_nest_end(msg, opts); + return 0; + +nla_put_failure: + nla_nest_cancel(msg, opts); + return -EMSGSIZE; +} +-------- + +==== Unspecified Attribute + +This is the default attribute type and used when none of the basic +datatypes is suitable. It represents data of arbitary type and length. + +See <> for a more information on +a special interface allowing the allocation of abstract address object +based on netlink attributes which carry some form of network address. + +See <> for more information +on how to allocate abstract data objects based on netlink attributes. + +Use the function nla_get() and nla_put() to access the payload and +construct attributes. See <> +for an example. + +=== Examples + +==== Constructing a Netlink Message with Attributes + +[source,c] +-------- +struct nl_msg *build_msg(int ifindex, struct nl_addr *lladdr, int mtu) +{ + struct nl_msg *msg; + struct nlattr *info, *vlan; + struct ifinfomsg ifi = { + .ifi_family = AF_INET, + .ifi_index = ifindex, + }; + + /* Allocate a default sized netlink message */ + if (!(msg = nlmsg_alloc_simple(RTM_SETLINK, 0))) + return NULL; + + /* Append the protocol specific header (struct ifinfomsg)*/ + if (nlmsg_append(msg, &ifi, sizeof(ifi), NLMSG_ALIGNTO) < 0) + goto nla_put_failure + + /* Append a 32 bit integer attribute to carry the MTU */ + NLA_PUT_U32(msg, IFLA_MTU, mtu); + + /* Append a unspecific attribute to carry the link layer address */ + NLA_PUT_ADDR(msg, IFLA_ADDRESS, lladdr); + + /* Append a container for nested attributes to carry link information */ + if (!(info = nla_nest_start(msg, IFLA_LINKINFO))) + goto nla_put_failure; + + /* Put a string attribute into the container */ + NLA_PUT_STRING(msg, IFLA_INFO_KIND, "vlan"); + + /* + * Append another container inside the open container to carry + * vlan specific attributes + */ + if (!(vlan = nla_nest_start(msg, IFLA_INFO_DATA))) + goto nla_put_failure; + + /* add vlan specific info attributes here... */ + + /* Finish nesting the vlan attributes and close the second container. */ + nla_nest_end(msg, vlan); + + /* Finish nesting the link info attribute and close the first container. */ + nla_nest_end(msg, info); + + return msg; + +nla_put_failure: + nlmsg_free(msg); + return NULL; +} +------ + +==== Parsing a Netlink Message with Attributes + +[source,c] +-------- +int parse_message(struct nlmsghdr *hdr) +{ + /* + * The policy defines two attributes: a 32 bit integer and a container + * for nested attributes. + */ + struct nla_policy attr_policy[] = { + [ATTR_FOO] = { .type = NLA_U32 }, + [ATTR_BAR] = { .type = NLA_NESTED }, + }; + struct nlattr *attrs[ATTR_MAX+1]; + int err; + + /* + * The nlmsg_parse() function will make sure that the message contains + * enough payload to hold the header (struct my_hdr), validates any + * attributes attached to the messages and stores a pointer to each + * attribute in the attrs[] array accessable by attribute type. + */ + if ((err = nlmsg_parse(hdr, sizeof(struct my_hdr), attrs, ATTR_MAX, + attr_policy)) < 0) + goto errout; + + if (attrs[ATTR_FOO]) { + /* + * It is safe to directly access the attribute payload without + * any further checks since nlmsg_parse() enforced the policy. + */ + uint32_t foo = nla_get_u32(attrs[ATTR_FOO]); + } + + if (attrs[ATTR_BAR]) { + struct *nested[NESTED_MAX+1]; + + /* + * Attributes nested in a container can be parsed the same way + * as top level attributes. + */ + err = nla_parse_nested(nested, NESTED_MAX, attrs[ATTR_BAR], + nested_policy); + if (err < 0) + goto errout; + + // Process nested attributes here. + } + + err = 0; +errout: + return err; +} +-------- + +[[core_cb]] +== Callback Configurations + +Callback hooks and overwriting capabilities are provided in various places +inside library to control the behaviour of several functions. All the +callback and overwrite functions are packed together in struct nl_cb which +is attached to a netlink socket or passed on to functions directly. + +=== Callback Hooks + +Callback hooks are spread across the library to provide entry points for +message processing and to take action upon certain events. + +Callback functions may return the following return codes: +[options="header", cols="1m,4"] +|======================================================================== +| Return Code | Description +| NL_OK | Proceed. +| NL_SKIP | Skip message currently being processed and continue + parsing the receive buffer. +| NL_STOP | Stop parsing and discard all remaining data in the + receive buffer. +|======================================================================== + +.Default Callback Implementations + +The library provides three sets of default callback implementations: +* +NL_CB_DEFAULT+ This is the default set. It implets the default behaviour. + See the table below for more information on the return codes of each + function. +* +NL_CB_VERBOSE+ This set is based on the default set but will cause an + error message to be printed to stderr for error messages, invalid + messages, message overruns and unhandled valid messages. The + +arg+ pointer in nl_cb_set() and nl_cb_err() can be used to + provide a FILE * which overwrites stderr. +* +NL_CB_DEBUG+ This set is intended for debugging purposes. It is + based on the verbose set but will decode and dump each message sent + or received to the console. + +.Message Processing Callbacks + +.nl_sendmsg() callback hooks: +[cols="2m,4e,1m", options="header"] +|============================================================================ +| Callback ID | Description | Default Return Value +| NL_CB_MSG_OUT | Each message sent | NL_OK +|============================================================================ + +Any function called by NL_CB_MSG_OUT may return a negative error code to +prevent the message from being sent and the error code being returned. + +nl_recvmsgs() callback hooks (ordered by priority): +[cols="2m,4e,1m", options="header"] +|============================================================================ +| Callback ID | Description | Default Return Value +| NL_CB_MSG_IN | Each message received | NL_OK +| NL_CB_SEQ_CHECK | May overwrite sequence check algo | NL_OK +| NL_CB_INVALID | Invalid messages | NL_STOP +| NL_CB_SEND_ACK | Messages with NLM_F_ACK flag set | NL_OK +| NL_CB_FINISH | Messages of type NLMSG_DONE | NL_STOP +| NL_CB_SKIPPED | Messages of type NLMSG_NOOP | NL_SKIP +| NL_CB_OVERRUN | Messages of type NLMSG_OVERRUN | NL_STOP +| NL_CB_ACK | ACK Messages | NL_STOP +| NL_CB_VALID | Each valid message | NL_OK +|============================================================================ + +Any of these functions may return NL_OK, NL_SKIP, or NL_STOP. + +Message processing callback functions are set with nl_cb_set(): +[source,c] +-------- +#include + +int nl_cb_set(struct nl_cb *cb, enum nl_cb_type type, enum nl_cb_kind kind, + nl_recvmsg_msg_cb_t func, void *cb); + +typedef int (*nl_recvmsg_msg_cb_t)(struct nl_msg *msg, void *arg); +-------- + +.Callback for Error Messages + +A special function prototype is used for the error message callback hook: + +[source,c] +-------- +#include + +int nl_cb_err(struct nl_cb *cb, enum nl_cb_kind kind, nl_recvmsg_err_cb_t func, void * arg); + +typedef int(* nl_recvmsg_err_cb_t)(struct sockaddr_nl *nla, struct nlmsgerr *nlerr, void *arg); +-------- + +.Example: Setting up a callback set +[source,c] +-------- +#include + +/* Allocate a callback set and initialize it to the verbose default set */ +struct nl_cb *cb = nl_cb_alloc(NL_CB_VERBOSE); + +/* Modify the set to call my_func() for all valid messages */ +nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, my_func, NULL); + +/* + * Set the error message handler to the verbose default implementation + * and direct it to print all errors to the given file descriptor. + */ +FILE *file = fopen(...); +nl_cb_err(cb, NL_CB_VERBOSE, NULL, file); +-------- + +=== Overwriting of Internal Functions + +When the library needs to send or receive netlink messages in high level +interfaces it does so by calling its own low level API. In the case the +default characteristics are not sufficient for the application, it may +overwrite several internal function calls with own implementations. + +.Overwriting recvmsgs() + +See <> for more information on +how and when recvmsgs() is called internally. + +[source,c] +-------- +#include + +void nl_cb_overwrite_recvmsgs(struct nl_cb *cb, + int (*func)(struct nl_sock *sk, struct nl_cb *cb)); +-------- + +The following criteras must be met if a recvmsgs() implementation is +supposed to work with high level interfaces: + +- MUST respect the callback configuration +cb+, therefore: + - MUST call +NL_CB_VALID+ for all valid messages, passing on + - MUST call +NL_CB_ACK+ for all ACK messages + - MUST correctly handle multipart messages, calling NL_CB_VALID for + each message until a NLMSG_DONE message is received. +- MUST report error code if a NLMSG_ERROR or NLMSG_OVERRUN mesasge is + received. + +.Overwriting nl_recv() + +Often it is sufficient to overwrite `nl_recv()` which is responsible +from receiving the actual data from the socket instead of replacing +the complete `recvmsgs()` logic. + +See <> for more +information on how and when `nl_recv()` is called internally. + +[source,c] +-------- +#include + +void nl_cb_overwrite_recv(struct nl_cb *cb, + int (*func)(struct nl_sock * sk, + struct sockaddr_nl *addr, + unsigned char **buf, + struct ucred **cred)); +-------- + +The following criteras must be met for an own `nl_recv()` +implementation: + + - *MUST* return the number of bytes read or a negative error code if + an error occured. The function may also return 0 to indicate that + no data has been read. + - *MUST* set `*buf` to a buffer containing the data read. It must be + safe for the caller to access the number of bytes read returned as + return code. + - *MAY* fill out `*addr` with the netlink address of the peer the + data has been received from. + - *MAY* set `*cred` to a newly allocated struct ucred containg + credentials. + +.Overwriting nl_send() + +See <> for more information on +how and when nl_send() is called internally. + +[source,c] +-------- +#include + +void nl_cb_overwrite_send(struct nl_cb *cb, int (*func)(struct nl_sock *sk, + struct nl_msg *msg)); +-------- + +Own implementations must send the netlink message and return 0 on success +or a negative error code. + +[[core_cache]] +== Cache System + +=== Allocation of Caches + +Almost all subsystem provide a function to allocate a new cache +of some form. The function usually looks like this: +[source,c] +-------- +struct nl_cache *_alloc_cache(struct nl_sock *sk); +-------- + +These functions allocate a new cache for the own object type, +initializes it properly and updates it to represent the current +state of their master, e.g. a link cache would include all +links currently configured in the kernel. + +Some of the allocation functions may take additional arguments +to further specify what will be part of the cache. + +All such functions return a newly allocated cache or NULL +in case of an error. + +=== Cache Manager + +The purpose of a cache manager is to keep track of caches and +automatically receive event notifications to keep the caches +up to date with the kernel state. Each manager has exactly one +netlink socket assigned which limits the scope of each manager +to exactly one netlink family. Therefore all caches committed +to a manager must be part of the same netlink family. Due to the +nature of a manager, it is not possible to have a cache maintain +two instances of the same cache type. The socket is subscribed +to the event notification group of each cache and also put into +non-blocking mode. Functions exist to poll() on the socket to +wait for new events to be received. + + +---- + App libnl Kernel + | | + +-----------------+ [ notification, link change ] + | | Cache Manager | | [ (IFF_UP | IFF_RUNNING) ] + | | | + | | +------------+| | | [ notification, new addr ] + <-------|---| route/link |<-------(async)--+ [ 10.0.1.1/32 dev eth1 ] + | | +------------+| | | + | +------------+| | + <---|---|---| route/addr |<------|-(async)--------------+ + | +------------+| + | | +------------+| | + <-------|---| ... || + | | +------------+| | + +-----------------+ + | | +---- + +.Creating a new cache manager + +[source,c] +---- +struct nl_cache_mngr *mngr; + +// Allocate a new cache manager for RTNETLINK and automatically +// provide the caches added to the manager. +mngr = nl_cache_mngr_alloc(NETLINK_ROUTE, NL_AUTO_PROVIDE); +---- + +.Keep track of a cache + +[source,c] +---- +struct nl_cache *cache; + +// Create a new cache for links/interfaces and ask the manager to +// keep it up to date for us. This will trigger a full dump request +// to initially fill the cache. +cache = nl_cache_mngr_add(mngr, "route/link"); +----- + +.Make the manager receive updates + +[source,c] +---- +// Give the manager the ability to receive updates, will call poll() +// with a timeout of 5 seconds. +if (nl_cache_mngr_poll(mngr, 5000) > 0) { + // Manager received at least one update, dump cache? + nl_cache_dump(cache, ...); +} +---- + +.Release cache manager + +[source,c] +---- +nl_cache_mngr_free(mngr); +---- + +== Abstract Data Types + +A few high level abstract data types which are used by a majority netlink +protocols are implemented in the core library. More may be added in the +future if the need arises. + +=== Abstract Address + +Most netlink protocols deal with networking related topics and thus +dealing with network addresses is a common task. + +Currently the following address families are supported: + +[options="compact"] + * `AF_INET` + * `AF_INET6` + * `AF_LLC` + * `AF_DECnet` + * `AF_UNSPEC` + +[[core_addr_alloc]] +.Address Allocation + +The function nl_addr_alloc() allocates a new empty address. The ++maxsize+ argument defines the maximum length of an address in bytes. +The size of an address is address family specific. If the address +family and address data are known at allocation time the function +nl_addr_build() can be used alternatively. You may also clone +an address by calling nl_addr_clone() + +[source,c] +-------- +#include + +struct nl_addr *nl_addr_alloc(size_t maxsize); +struct nl_addr *nl_addr_clone(struct nl_addr *addr); +struct nl_addr *nl_addr_build(int family, void *addr, size_t size); +-------- + +If the address is transported in a netlink attribute, the function +nl_addr_alloc_attr() allocates a new address based on the payload +of the attribute provided. The +family+ argument is used to specify +the address family of the address, set to +AF_UNSPEC+ if unknown. + +[source,c] +-------- +#include + +struct nl_addr *nl_addr_alloc_attr(struct nlattr *attr, int family); +-------- + +If the address is provided by a user, it is usually stored in a human +readable format. The function nl_addr_parse() parses a character +string representing an address and allocates a new address based on +it. + +[source,c] +-------- +#include + +int nl_addr_parse(const char *addr, int hint, struct nl_addr **result); +-------- + +If parsing succeeds the function returns 0 and the allocated address +is stored in +*result+. + +NOTE: Make sure to return the reference to an address using + `nl_addr_put()` after usage to allow memory being freed. + +.Example: Transform character string to abstract address +[source,c] +----- +struct nl_addr *a = nl_addr_parse("::1", AF_UNSPEC); +printf("Address family: %s\n", nl_af2str(nl_addr_get_family(a))); +nl_addr_put(a); +a = nl_addr_parse("11:22:33:44:55:66", AF_UNSPEC); +printf("Address family: %s\n", nl_af2str(nl_addr_get_family(a))); +nl_addr_put(a); +----- + +.Address References + +Abstract addresses use reference counting to account for all users of +a particular address. After the last user has returned the reference +the address is freed. + +If you pass on a address object to another function and you are not +sure how long it will be used, make sure to call nl_addr_get() to +acquire an additional reference and have that function or code path +call nl_addr_put() as soon as it has finished using the address. + +[source,c] +-------- +#include + +struct nl_addr *nl_addr_get(struct nl_addr *addr); +void nl_addr_put(struct nl_addr *addr); +int nl_addr_shared(struct nl_addr *addr); +-------- + +You may call nl_addr_shared() at any time to check if you are the only +user of an address. + +.Address Attributes + +The address is usually set at allocation time. If it was unknown at that +time it can be specified later by calling nl_addr_set_family() and is +accessed with the function nl_addr_get_family(). + +[source,c] +-------- +#include + +void nl_addr_set_family(struct nl_addr *addr, int family); +int nl_addr_get_family(struct nl_addr *addr); +-------- + +The same is true for the actual address data. It is typically present +at allocation time. For exceptions it can be specified later or +overwritten with the function `nl_addr_set_binary_addr()`. Beware that +the length of the address may not exceed `maxlen` specified at +allocation time. The address data is returned by the function +`nl_addr_get_binary_addr()` and its length by the function +`nl_addr_get_len()`. + +[source,c] +-------- +#include + +int nl_addr_set_binary_addr(struct nl_addr *addr, void *data, size_t size); +void *nl_addr_get_binary_addr(struct nl_addr *addr); +unsigned int nl_addr_get_len(struct nl_addr *addr); +-------- + +If you only want to check if the address data consists of all zeros +the function `nl_addr_iszero()` is a shortcut to that. + +[source,c] +-------- +#include + +int nl_addr_iszero(struct nl_addr *addr); +-------- + +==== Address Prefix Length + +Although this functionality is somewhat specific to routing it has +been implemented here. Addresses can have a prefix length assigned +which implies that only the first n bits are of importance. This is +f.e. used to implement subnets. + +Use set functions `nl_addr_set_prefixlen()` and +`nl_addr_get_prefixlen()` to work with prefix lengths. + +[source,c] +-------- +#include + +void nl_addr_set_prefixlen(struct nl_addr *addr, int n); +unsigned int nl_addr_get_prefixlen(struct nl_addr *addr); +-------- + +NOTE: The default prefix length is set to (address length * 8) + +.Address Helpers + +Several functions exist to help when dealing with addresses. The +function `nl_addr_cmp()` compares two addresses and returns an integer +less than, equal to or greater than zero without considering the +prefix length at all. If you want to consider the prefix length, use +the function `nl_addr_cmp_prefix()`. + +[source,c] +-------- +#include + +int nl_addr_cmp(struct nl_addr *addr, struct nl_addr *addr); +int nl_addr_cmp_prefix(struct nl_addr *addr, struct nl_addr *addr); +-------- + +If an abstract address needs to presented to the user it should be +done in a human readable format which differs depending on the address +family. The function `nl_addr2str()` takes care of this by calling the +appropriate conversion functions internaly. It expects a `buf` of +length `size` to write the character string into and returns a pointer +to `buf` for easy `printf()` usage. + +[source,c] +-------- +#include + +char *nl_addr2str(struct nl_addr *addr, char *buf, size_t size); +-------- + +If the address family is unknown, the address data will be printed in +hexadecimal format `AA:BB:CC:DD:...` + +Often the only way to figure out the address family is by looking at +the length of the address. The function `nl_addr_guess_family()` does +just this and returns the address family guessed based on the address +size. + +[source,c] +-------- +#include + +int nl_addr_guess_family(struct nl_addr *addr); +-------- + +Before allocating an address you may want to check if the character +string actually represents a valid address of the address family you +are expecting. The function `nl_addr_valid()` can be used for that, it +returns 1 if the supplised `addr` is a valid address in the context of +`family`. See `inet_pton(3)`, `dnet_pton(3)` for more information on +valid adddress formats. + +[source,c] +-------- +#include + +int nl_addr_valid(char *addr, int family); +-------- + +=== Abstract Data + +The abstract data type is a trivial datatype with the primary purpose +to simplify usage of netlink attributes of arbitary length. + +[[core_data_alloc]] +.Allocation of a Data Object +The function `nl_data_alloc()` alloctes a new abstract data object and +fill it with the provided data. `nl_data_alloc_attr()` does the same +but bases the data on the payload of a netlink attribute. New data +objects can also be allocated by cloning existing ones by using +`nl_data_clone()`. + +[source,c] +-------- +struct nl_data *nl_data_alloc(void *buf, size_t size); +struct nl_data *nl_data_alloc_attr(struct nlattr *attr); +struct nl_data *nl_data_clone(struct nl_data *data); +void nl_data_free(struct nl_data *data); +-------- + +.Access to Data + +The function `nl_data_get()` returns a pointer to the data, the size +of data is returned by `nl_data_get_size()`. + +[source,c] +-------- +void *nl_data_get(struct nl_data *data); +size_t nl_data_get_size(struct nl_data *data); +-------- + +.Data Helpers + +The function nl_data_append() reallocates the internal data buffers +and appends the specified `buf` to the existing data. + +[source,c] +-------- +int nl_data_append(struct nl_data *data, void *buf, size_t size); +-------- + +CAUTION: Any call to `nl_data_append()` invalidates all pointers + returned by `nl_data_get()` of the same data object. + +[source,c] +-------- +int nl_data_cmp(struct nl_data *data, struct nl_data *data); +-------- diff --git a/doc/doxygen-link.py b/doc/doxygen-link.py new file mode 100755 index 0000000..fda193c --- /dev/null +++ b/doc/doxygen-link.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python + +import fileinput +import re +import sys + +links = {} + +for line in open(sys.argv[1], 'r'): + m = re.match('^([^=]+)=([^\n]+)$', line); + if m: + link = "" + m.group(1) + "" + links[m.group(1)] = link + +def translate(match): + return links[match.group(0)] + +rc = re.compile('|'.join(map(re.escape, sorted(links, reverse=True)))) +for line in open(sys.argv[2], 'r'): + print rc.sub(translate, line), diff --git a/doc/gen-tags.sh b/doc/gen-tags.sh new file mode 100755 index 0000000..862ec09 --- /dev/null +++ b/doc/gen-tags.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +# +# Based on a script found on the englinemtn-devel mailinglist +# written by Carsten Haitzler +# + +for f in api/group__*.html +do + bf=$(basename $f) + + grep -oE "href=\"$bf#[a-z0-9]+\">[^<]+" $f | + sed 's/href="\([^"]*\)">\([^<]*\)<\/a>/\2=api\/\1/' +done diff --git a/doc/html/.gitignore b/doc/html/.gitignore deleted file mode 100644 index 72e8ffc..0000000 --- a/doc/html/.gitignore +++ /dev/null @@ -1 +0,0 @@ -* diff --git a/doc/images/.gitignore b/doc/images/.gitignore new file mode 100644 index 0000000..efcc7e2 --- /dev/null +++ b/doc/images/.gitignore @@ -0,0 +1,3 @@ +core__* +asciidoc__*.png +*.odg diff --git a/doc/images/addressing.png b/doc/images/addressing.png new file mode 100644 index 0000000..9dcaaff Binary files /dev/null and b/doc/images/addressing.png differ diff --git a/doc/images/attribute_hdr.png b/doc/images/attribute_hdr.png new file mode 100644 index 0000000..0e6cfda Binary files /dev/null and b/doc/images/attribute_hdr.png differ diff --git a/doc/images/classful_qdisc.png b/doc/images/classful_qdisc.png new file mode 100644 index 0000000..7e77350 Binary files /dev/null and b/doc/images/classful_qdisc.png differ diff --git a/doc/images/classless_qdisc.png b/doc/images/classless_qdisc.png new file mode 100644 index 0000000..bcf2c1c Binary files /dev/null and b/doc/images/classless_qdisc.png differ diff --git a/doc/images/classless_qdisc_nbands.png b/doc/images/classless_qdisc_nbands.png new file mode 100644 index 0000000..14cb026 Binary files /dev/null and b/doc/images/classless_qdisc_nbands.png differ diff --git a/doc/images/icons/README b/doc/images/icons/README new file mode 100644 index 0000000..f12b2a7 --- /dev/null +++ b/doc/images/icons/README @@ -0,0 +1,5 @@ +Replaced the plain DocBook XSL admonition icons with Jimmac's DocBook +icons (http://jimmac.musichall.cz/ikony.php3). I dropped transparency +from the Jimmac icons to get round MS IE and FOP PNG incompatibilies. + +Stuart Rackham diff --git a/doc/images/icons/callouts/1.png b/doc/images/icons/callouts/1.png new file mode 100644 index 0000000..7d47343 Binary files /dev/null and b/doc/images/icons/callouts/1.png differ diff --git a/doc/images/icons/callouts/10.png b/doc/images/icons/callouts/10.png new file mode 100644 index 0000000..997bbc8 Binary files /dev/null and b/doc/images/icons/callouts/10.png differ diff --git a/doc/images/icons/callouts/11.png b/doc/images/icons/callouts/11.png new file mode 100644 index 0000000..ce47dac Binary files /dev/null and b/doc/images/icons/callouts/11.png differ diff --git a/doc/images/icons/callouts/12.png b/doc/images/icons/callouts/12.png new file mode 100644 index 0000000..31daf4e Binary files /dev/null and b/doc/images/icons/callouts/12.png differ diff --git a/doc/images/icons/callouts/13.png b/doc/images/icons/callouts/13.png new file mode 100644 index 0000000..14021a8 Binary files /dev/null and b/doc/images/icons/callouts/13.png differ diff --git a/doc/images/icons/callouts/14.png b/doc/images/icons/callouts/14.png new file mode 100644 index 0000000..64014b7 Binary files /dev/null and b/doc/images/icons/callouts/14.png differ diff --git a/doc/images/icons/callouts/15.png b/doc/images/icons/callouts/15.png new file mode 100644 index 0000000..0d65765 Binary files /dev/null and b/doc/images/icons/callouts/15.png differ diff --git a/doc/images/icons/callouts/2.png b/doc/images/icons/callouts/2.png new file mode 100644 index 0000000..5d09341 Binary files /dev/null and b/doc/images/icons/callouts/2.png differ diff --git a/doc/images/icons/callouts/3.png b/doc/images/icons/callouts/3.png new file mode 100644 index 0000000..ef7b700 Binary files /dev/null and b/doc/images/icons/callouts/3.png differ diff --git a/doc/images/icons/callouts/4.png b/doc/images/icons/callouts/4.png new file mode 100644 index 0000000..adb8364 Binary files /dev/null and b/doc/images/icons/callouts/4.png differ diff --git a/doc/images/icons/callouts/5.png b/doc/images/icons/callouts/5.png new file mode 100644 index 0000000..4d7eb46 Binary files /dev/null and b/doc/images/icons/callouts/5.png differ diff --git a/doc/images/icons/callouts/6.png b/doc/images/icons/callouts/6.png new file mode 100644 index 0000000..0ba694a Binary files /dev/null and b/doc/images/icons/callouts/6.png differ diff --git a/doc/images/icons/callouts/7.png b/doc/images/icons/callouts/7.png new file mode 100644 index 0000000..472e96f Binary files /dev/null and b/doc/images/icons/callouts/7.png differ diff --git a/doc/images/icons/callouts/8.png b/doc/images/icons/callouts/8.png new file mode 100644 index 0000000..5e60973 Binary files /dev/null and b/doc/images/icons/callouts/8.png differ diff --git a/doc/images/icons/callouts/9.png b/doc/images/icons/callouts/9.png new file mode 100644 index 0000000..a0676d2 Binary files /dev/null and b/doc/images/icons/callouts/9.png differ diff --git a/doc/images/icons/caution.png b/doc/images/icons/caution.png new file mode 100644 index 0000000..9a8c515 Binary files /dev/null and b/doc/images/icons/caution.png differ diff --git a/doc/images/icons/example.png b/doc/images/icons/example.png new file mode 100644 index 0000000..1199e86 Binary files /dev/null and b/doc/images/icons/example.png differ diff --git a/doc/images/icons/home.png b/doc/images/icons/home.png new file mode 100644 index 0000000..37a5231 Binary files /dev/null and b/doc/images/icons/home.png differ diff --git a/doc/images/icons/important.png b/doc/images/icons/important.png new file mode 100644 index 0000000..be685cc Binary files /dev/null and b/doc/images/icons/important.png differ diff --git a/doc/images/icons/next.png b/doc/images/icons/next.png new file mode 100644 index 0000000..64e126b Binary files /dev/null and b/doc/images/icons/next.png differ diff --git a/doc/images/icons/note.png b/doc/images/icons/note.png new file mode 100644 index 0000000..7c1f3e2 Binary files /dev/null and b/doc/images/icons/note.png differ diff --git a/doc/images/icons/prev.png b/doc/images/icons/prev.png new file mode 100644 index 0000000..3e8f12f Binary files /dev/null and b/doc/images/icons/prev.png differ diff --git a/doc/images/icons/tip.png b/doc/images/icons/tip.png new file mode 100644 index 0000000..f087c73 Binary files /dev/null and b/doc/images/icons/tip.png differ diff --git a/doc/images/icons/up.png b/doc/images/icons/up.png new file mode 100644 index 0000000..2db1ce6 Binary files /dev/null and b/doc/images/icons/up.png differ diff --git a/doc/images/icons/warning.png b/doc/images/icons/warning.png new file mode 100644 index 0000000..d41edb9 Binary files /dev/null and b/doc/images/icons/warning.png differ diff --git a/doc/images/ifinfomsg.png b/doc/images/ifinfomsg.png new file mode 100644 index 0000000..fb94cf7 Binary files /dev/null and b/doc/images/ifinfomsg.png differ diff --git a/doc/images/library_overview.png b/doc/images/library_overview.png new file mode 100644 index 0000000..dd9d5fe Binary files /dev/null and b/doc/images/library_overview.png differ diff --git a/doc/images/nlmsgerr.png b/doc/images/nlmsgerr.png new file mode 100644 index 0000000..58e53d5 Binary files /dev/null and b/doc/images/nlmsgerr.png differ diff --git a/doc/images/nlmsghdr.png b/doc/images/nlmsghdr.png new file mode 100644 index 0000000..dd39b9c Binary files /dev/null and b/doc/images/nlmsghdr.png differ diff --git a/doc/images/qdisc_default.png b/doc/images/qdisc_default.png new file mode 100644 index 0000000..a7ba167 Binary files /dev/null and b/doc/images/qdisc_default.png differ diff --git a/doc/images/qdisc_mq.png b/doc/images/qdisc_mq.png new file mode 100644 index 0000000..c6318b2 Binary files /dev/null and b/doc/images/qdisc_mq.png differ diff --git a/doc/images/tc_obj.png b/doc/images/tc_obj.png new file mode 100644 index 0000000..bfc8145 Binary files /dev/null and b/doc/images/tc_obj.png differ diff --git a/doc/images/tc_overview.png b/doc/images/tc_overview.png new file mode 100644 index 0000000..ce23e67 Binary files /dev/null and b/doc/images/tc_overview.png differ diff --git a/doc/index.txt b/doc/index.txt new file mode 100644 index 0000000..c207b44 --- /dev/null +++ b/doc/index.txt @@ -0,0 +1,22 @@ +Documentation Overview - libnl Suite +==================================== + +== Libraries + +image:library_overview.png["Library Hierarchy"] + +link:core.html[Netlink Library] (libnl):: +Socket handling, sending and receiving, message construction and parsing, ... + +link:route.html[Routing Family Library] (libnl-route):: +Adresses, links, neighbours, routing, traffic control, neighbour tables, ... + +Netfilter Library (libnl-nf):: +Connection tracking, logging, queueing + +Generic Netlink Library (libnl-genl):: +Controller API, family and command registration + +== Python Packages + - netlink.core + - netlink.route.link diff --git a/doc/libnl.css b/doc/libnl.css index b92bafc..8589450 100644 --- a/doc/libnl.css +++ b/doc/libnl.css @@ -2,21 +2,41 @@ body, table, div, p, dl { font-family: Lucida Grande, Verdana, Geneva, Arial, sans-serif; - font-size: 12px; + font-size: 13px; + line-height: 1.3; } /* @group Heading Levels */ h1 { - font-size: 180%; + font-size: 150%; +} + +.title { + font-size: 150%; + font-weight: bold; + margin: 10px 2px; } h2 { - font-size: 140%; + font-size: 120%; } h3 { - font-size: 120%; + font-size: 100%; +} + +h1, h2, h3, h4, h5, h6 { + -webkit-transition: text-shadow 0.5s linear; + -moz-transition: text-shadow 0.5s linear; + -ms-transition: text-shadow 0.5s linear; + -o-transition: text-shadow 0.5s linear; + transition: text-shadow 0.5s linear; + margin-right: 15px; +} + +h1.glow, h2.glow, h3.glow, h4.glow, h5.glow, h6.glow { + text-shadow: 0 0 15px cyan; } dt { @@ -66,8 +86,6 @@ div.qindex, div.navtab{ background-color: #EBEFF6; border: 1px solid #A3B4D7; text-align: center; - margin: 2px; - padding: 2px; } div.qindex, div.navpath { @@ -117,12 +135,12 @@ a.el { a.elRef { } -a.code { - color: #4665A2; +a.code, a.code:visited { + color: #4665A2; } -a.codeRef { - color: #4665A2; +a.codeRef, a.codeRef:visited { + color: #4665A2; } /* @end */ @@ -131,20 +149,72 @@ dl.el { margin-left: -1cm; } -.fragment { - font-family: monospace, fixed; - font-size: 105%; +pre.fragment { + border: 1px solid #C4CFE5; + background-color: #FBFCFD; + padding: 4px 6px; + margin: 4px 8px 4px 2px; + overflow: auto; + word-wrap: break-word; + font-size: 9pt; + line-height: 125%; + font-family: monospace, fixed; + font-size: 105%; } -pre.fragment { - border: 1px solid #C4CFE5; +div.fragment { + padding: 4px; + margin: 4px; background-color: #FBFCFD; - padding: 4px 6px; - margin: 4px 8px 4px 2px; - overflow: auto; - word-wrap: break-word; - font-size: 9pt; - line-height: 125%; + border: 1px solid #C4CFE5; +} + +div.line { + font-family: monospace, fixed; + font-size: 13px; + min-height: 13px; + line-height: 1.0; + text-wrap: unrestricted; + white-space: -moz-pre-wrap; /* Moz */ + white-space: -pre-wrap; /* Opera 4-6 */ + white-space: -o-pre-wrap; /* Opera 7 */ + white-space: pre-wrap; /* CSS3 */ + word-wrap: break-word; /* IE 5.5+ */ + text-indent: -53px; + padding-left: 53px; + padding-bottom: 0px; + margin: 0px; + -webkit-transition-property: background-color, box-shadow; + -webkit-transition-duration: 0.5s; + -moz-transition-property: background-color, box-shadow; + -moz-transition-duration: 0.5s; + -ms-transition-property: background-color, box-shadow; + -ms-transition-duration: 0.5s; + -o-transition-property: background-color, box-shadow; + -o-transition-duration: 0.5s; + transition-property: background-color, box-shadow; + transition-duration: 0.5s; +} + +div.line.glow { + background-color: cyan; + box-shadow: 0 0 10px cyan; +} + + +span.lineno { + padding-right: 4px; + text-align: right; + border-right: 2px solid #0F0; + background-color: #E8E8E8; + white-space: pre; +} +span.lineno a { + background-color: #D8D8D8; +} + +span.lineno a:hover { + background-color: #C8C8C8; } div.ah { @@ -158,6 +228,7 @@ div.ah { border-radius: 0.5em; -webkit-border-radius: .5em; -moz-border-radius: .5em; + box-shadow: 2px 2px 3px #999; -webkit-box-shadow: 2px 2px 3px #999; -moz-box-shadow: rgba(0, 0, 0, 0.15) 2px 2px 2px; background-image: -webkit-gradient(linear, left top, left bottom, from(#eee), to(#000),color-stop(0.3, #444)); @@ -167,7 +238,6 @@ div.ah { div.groupHeader { margin-left: 16px; margin-top: 12px; - margin-bottom: 6px; font-weight: bold; } @@ -177,24 +247,15 @@ div.groupText { } body { - background: white; + background-color: white; color: black; margin: 0; } div.contents { margin-top: 10px; - margin-left: 10px; - margin-right: 10px; -} - -div.contents p { - margin-left: 30px; -} - -div.contents .fragment { - margin-left: 30px; - margin-right: 20px; + margin-left: 12px; + margin-right: 8px; } td.indexkey { @@ -203,6 +264,8 @@ td.indexkey { border: 1px solid #C4CFE5; margin: 2px 0px 2px 0; padding: 2px 10px; + white-space: nowrap; + vertical-align: top; } td.indexvalue { @@ -295,6 +358,13 @@ span.vhdllogic { color: #ff0000 } +blockquote { + background-color: #F7F8FB; + border-left: 2px solid #9CAFD4; + margin: 0 24px 0 4px; + padding: 0 12px 0 16px; +} + /* @end */ /* @@ -348,6 +418,24 @@ table.memberdecls { padding: 0px; } +.memberdecls td { + -webkit-transition-property: background-color, box-shadow; + -webkit-transition-duration: 0.5s; + -moz-transition-property: background-color, box-shadow; + -moz-transition-duration: 0.5s; + -ms-transition-property: background-color, box-shadow; + -ms-transition-duration: 0.5s; + -o-transition-property: background-color, box-shadow; + -o-transition-duration: 0.5s; + transition-property: background-color, box-shadow; + transition-duration: 0.5s; +} + +.memberdecls td.glow { + background-color: cyan; + box-shadow: 0 0 15px cyan; +} + .mdescLeft, .mdescRight, .memItemLeft, .memItemRight, .memTemplItemLeft, .memTemplItemRight, .memTemplParams { @@ -370,6 +458,10 @@ table.memberdecls { white-space: nowrap; } +.memItemRight { + width: 100%; +} + .memTemplParams { color: #4665A2; white-space: nowrap; @@ -382,10 +474,10 @@ table.memberdecls { /* Styles for detailed member documentation */ .memtemplate { - font-size: 100%; + font-size: 80%; color: #4665A2; font-weight: normal; - margin-left: 3px; + margin-left: 9px; } .memnav { @@ -397,30 +489,93 @@ table.memberdecls { padding: 2px; } +.mempage { + width: 100%; +} + .memitem { padding: 0; - border: 1px solid #C4CFE5; - margin-bottom: 30px; + margin-bottom: 10px; + margin-right: 5px; + -webkit-transition: box-shadow 0.5s linear; + -moz-transition: box-shadow 0.5s linear; + -ms-transition: box-shadow 0.5s linear; + -o-transition: box-shadow 0.5s linear; + transition: box-shadow 0.5s linear; + display: table !important; + width: 100%; +} + +.memitem.glow { + box-shadow: 0 0 15px cyan; } .memname { - white-space: nowrap; font-weight: bold; margin-left: 6px; } -.memproto { - background-color: #F9FAFC; - border-bottom: 1px solid #A3B4D7; +.memname td { + vertical-align: bottom; +} + +.memproto, dl.reflist dt { + border-top: 1px solid #A8B8D9; + border-left: 1px solid #A8B8D9; + border-right: 1px solid #A8B8D9; padding: 6px 0px 6px 0px; - color: #000000; + color: #253555; font-weight: bold; + text-shadow: 0px 1px 1px rgba(255, 255, 255, 0.9); + background-image:url('nav_f.png'); + background-repeat:repeat-x; + background-color: #E2E8F2; + /* opera specific markup */ + box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); + border-top-right-radius: 4px; + border-top-left-radius: 4px; + /* firefox specific markup */ + -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px; + -moz-border-radius-topright: 4px; + -moz-border-radius-topleft: 4px; + /* webkit specific markup */ + -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); + -webkit-border-top-right-radius: 4px; + -webkit-border-top-left-radius: 4px; } -.memdoc { - padding: 2px 5px; - margin-left: 30px; +.memdoc, dl.reflist dd { + border-bottom: 1px solid #A8B8D9; + border-left: 1px solid #A8B8D9; + border-right: 1px solid #A8B8D9; + padding: 6px 10px 2px 10px; + background-color: #FBFCFD; + border-top-width: 0; + background-image:url('nav_g.png'); + background-repeat:repeat-x; + background-color: #FFFFFF; + /* opera specific markup */ + border-bottom-left-radius: 4px; + border-bottom-right-radius: 4px; + box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); + /* firefox specific markup */ + -moz-border-radius-bottomleft: 4px; + -moz-border-radius-bottomright: 4px; + -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px; + /* webkit specific markup */ + -webkit-border-bottom-left-radius: 4px; + -webkit-border-bottom-right-radius: 4px; + -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); +} + +dl.reflist dt { + padding: 5px; +} + +dl.reflist dd { + margin: 0px 0px 10px 0px; + padding: 5px; } .paramkey { @@ -438,103 +593,138 @@ table.memberdecls { .paramname em { font-style: normal; } - -/* @end */ - -/* @group Directory (tree) */ - -/* for the tree view */ - -.ftvtree { - font-family: sans-serif; - margin: 0px; +.paramname code { + line-height: 14px; } -/* these are for tree view when used as main index */ +.params, .retval, .exception, .tparams { + margin-left: 0px; + padding-left: 0px; +} -.directory { - font-size: 9pt; - font-weight: bold; - margin: 5px; +.params .paramname, .retval .paramname { + font-weight: bold; + vertical-align: top; +} + +.params .paramtype { + font-style: italic; + vertical-align: top; +} + +.params .paramdir { + font-family: "courier new",courier,monospace; + vertical-align: top; } -.directory h3 { - margin: 0px; - margin-top: 1em; - font-size: 11pt; +table.mlabels { + border-spacing: 0px; } -/* -The following two styles can be used to replace the root node title -with an image of your choice. Simply uncomment the next two styles, -specify the name of your image and be sure to set 'height' to the -proper pixel height of your image. -*/ - -/* -.directory h3.swap { - height: 61px; - background-repeat: no-repeat; - background-image: url("yourimage.gif"); -} -.directory h3.swap span { - display: none; -} -*/ - -.directory > h3 { - margin-top: 0; +td.mlabels-left { + width: 100%; + padding: 0px; } -.directory p { - margin: 0px; +td.mlabels-right { + vertical-align: bottom; + padding: 0px; white-space: nowrap; } -.directory div { - display: none; - margin: 0px; +span.mlabels { + margin-left: 8px; +} + +span.mlabel { + background-color: #728DC1; + border-top:1px solid #5373B4; + border-left:1px solid #5373B4; + border-right:1px solid #C4CFE5; + border-bottom:1px solid #C4CFE5; + text-shadow: none; + color: white; + margin-right: 4px; + padding: 2px 3px; + border-radius: 3px; + font-size: 7pt; + white-space: nowrap; +} + + + +/* @end */ + +/* these are for tree view when not used as main index */ + +div.directory { + margin: 10px 0px; + border-top: 1px solid #A8B8D9; + border-bottom: 1px solid #A8B8D9; + width: 100%; +} + +.directory table { + border-collapse:collapse; +} + +.directory td { + margin: 0px; + padding: 0px; + vertical-align: top; +} + +.directory td.entry { + white-space: nowrap; + padding-right: 6px; +} + +.directory td.entry a { + outline:none; +} + +.directory td.entry a img { + border: none; +} + +.directory td.desc { + width: 100%; + padding-left: 6px; + padding-right: 6px; + border-left: 1px solid rgba(0,0,0,0.05); +} + +.directory tr.even { + padding-left: 6px; + background-color: #F7F8FB; } .directory img { vertical-align: -30%; } -/* these are for tree view when not used as main index */ - -.directory-alt { - font-size: 100%; - font-weight: bold; +.directory .levels { + white-space: nowrap; + width: 100%; + text-align: right; + font-size: 9pt; } -.directory-alt h3 { - margin: 0px; - margin-top: 1em; - font-size: 11pt; +.directory .levels span { + cursor: pointer; + padding-left: 2px; + padding-right: 2px; + color: #3D578C; } -.directory-alt > h3 { - margin-top: 0; -} - -.directory-alt p { - margin: 0px; - white-space: nowrap; -} - -.directory-alt div { - display: none; - margin: 0px; -} - -.directory-alt img { - vertical-align: -30%; -} - -/* @end */ - div.dynheader { margin-top: 8px; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; } address { @@ -544,6 +734,8 @@ address { table.doxtable { border-collapse:collapse; + margin-top: 4px; + margin-bottom: 4px; } table.doxtable td, table.doxtable th { @@ -557,9 +749,60 @@ table.doxtable th { font-size: 110%; padding-bottom: 4px; padding-top: 5px; - text-align:left; } +table.fieldtable { + width: 100%; + margin-bottom: 10px; + border: 1px solid #A8B8D9; + border-spacing: 0px; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; + -moz-box-shadow: rgba(0, 0, 0, 0.15) 2px 2px 2px; + -webkit-box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.15); + box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.15); +} + +.fieldtable td, .fieldtable th { + padding: 3px 7px 2px; +} + +.fieldtable td.fieldtype, .fieldtable td.fieldname { + white-space: nowrap; + border-right: 1px solid #A8B8D9; + border-bottom: 1px solid #A8B8D9; + vertical-align: top; +} + +.fieldtable td.fielddoc { + border-bottom: 1px solid #A8B8D9; + width: 100%; +} + +.fieldtable tr:last-child td { + border-bottom: none; +} + +.fieldtable th { + background-image:url('nav_f.png'); + background-repeat:repeat-x; + background-color: #E2E8F2; + font-size: 90%; + color: #253555; + padding-bottom: 4px; + padding-top: 5px; + text-align:left; + -moz-border-radius-topleft: 4px; + -moz-border-radius-topright: 4px; + -webkit-border-top-left-radius: 4px; + -webkit-border-top-right-radius: 4px; + border-top-left-radius: 4px; + border-top-right-radius: 4px; + border-bottom: 1px solid #A8B8D9; +} + + .tabsearch { top: 0px; left: 10px; @@ -589,14 +832,14 @@ table.doxtable th { list-style-type:none; float:left; padding-left:10px; - padding-right: 15px; + padding-right:15px; background-image:url('bc_s.png'); background-repeat:no-repeat; background-position:right; color:#364D7C; } -.navpath a +.navpath li.navelem a { height:32px; display:block; @@ -604,11 +847,25 @@ table.doxtable th { outline: none; } -.navpath a:hover +.navpath li.navelem a:hover { color:#6884BD; } +.navpath li.footer +{ + list-style-type:none; + float:right; + padding-left:10px; + padding-right:15px; + background-image:none; + background-repeat:no-repeat; + background-position:right; + color:#364D7C; + font-size: 8pt; +} + + div.summary { float: right; @@ -623,6 +880,18 @@ div.summary a white-space: nowrap; } +div.ingroups +{ + font-size: 8pt; + width: 50%; + text-align: left; +} + +div.ingroups a +{ + white-space: nowrap; +} + div.header { background-image:url('nav_h.png'); @@ -634,6 +903,254 @@ div.header div.headertitle { - padding: 5px 5px 5px 10px; + padding: 5px 5px 5px 7px; +} + +dl +{ + padding: 0 0 0 10px; +} + +/* dl.note, dl.warning, dl.attention, dl.pre, dl.post, dl.invariant, dl.deprecated, dl.todo, dl.test, dl.bug */ +dl.section +{ + margin-left: 0px; + padding-left: 0px; +} + +dl.note +{ + margin-left:-7px; + padding-left: 3px; + border-left:4px solid; + border-color: #D0C000; +} + +dl.warning, dl.attention +{ + margin-left:-7px; + padding-left: 3px; + border-left:4px solid; + border-color: #FF0000; +} + +dl.pre, dl.post, dl.invariant +{ + margin-left:-7px; + padding-left: 3px; + border-left:4px solid; + border-color: #00D000; +} + +dl.deprecated +{ + margin-left:-7px; + padding-left: 3px; + border-left:4px solid; + border-color: #505050; +} + +dl.todo +{ + margin-left:-7px; + padding-left: 3px; + border-left:4px solid; + border-color: #00C0E0; +} + +dl.test +{ + margin-left:-7px; + padding-left: 3px; + border-left:4px solid; + border-color: #3030E0; +} + +dl.bug +{ + margin-left:-7px; + padding-left: 3px; + border-left:4px solid; + border-color: #C08050; +} + +dl.section dd { + margin-bottom: 6px; +} + + +#projectlogo +{ + text-align: center; + vertical-align: bottom; + border-collapse: separate; +} + +#projectlogo img +{ + border: 0px none; +} + +#projectname +{ + font: 300% Tahoma, Arial,sans-serif; + margin: 0px; + padding: 2px 0px; +} + +#projectbrief +{ + font: 120% Tahoma, Arial,sans-serif; + margin: 0px; + padding: 0px; +} + +#projectnumber +{ + font: 50% Tahoma, Arial,sans-serif; + margin: 0px; + padding: 0px; +} + +#titlearea +{ + padding: 0px; + margin: 0px; + width: 100%; + border-bottom: 1px solid #5373B4; +} + +.image +{ + text-align: center; +} + +.dotgraph +{ + text-align: center; +} + +.mscgraph +{ + text-align: center; +} + +.caption +{ + font-weight: bold; +} + +div.zoom +{ + border: 1px solid #90A5CE; +} + +dl.citelist { + margin-bottom:50px; +} + +dl.citelist dt { + color:#334975; + float:left; + font-weight:bold; + margin-right:10px; + padding:5px; +} + +dl.citelist dd { + margin:2px 0; + padding:5px 0; +} + +div.toc { + padding: 14px 25px; + background-color: #F4F6FA; + border: 1px solid #D8DFEE; + border-radius: 7px 7px 7px 7px; + float: right; + height: auto; + margin: 0 20px 10px 10px; + width: 200px; +} + +div.toc li { + background: url("bdwn.png") no-repeat scroll 0 5px transparent; + font: 10px/1.2 Verdana,DejaVu Sans,Geneva,sans-serif; + margin-top: 5px; + padding-left: 10px; + padding-top: 2px; +} + +div.toc h3 { + font: bold 12px/1.2 Arial,FreeSans,sans-serif; + color: #4665A2; + border-bottom: 0 none; + margin: 0; +} + +div.toc ul { + list-style: none outside none; + border: medium none; + padding: 0px; +} + +div.toc li.level1 { + margin-left: 0px; +} + +div.toc li.level2 { + margin-left: 15px; +} + +div.toc li.level3 { + margin-left: 30px; +} + +div.toc li.level4 { + margin-left: 45px; +} + +.inherit_header { + font-weight: bold; + color: gray; + cursor: pointer; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.inherit_header td { + padding: 6px 0px 2px 5px; +} + +.inherit { + display: none; +} + +tr.heading h2 { + margin-top: 12px; + margin-bottom: 4px; +} + +@media print +{ + #top { display: none; } + #side-nav { display: none; } + #nav-path { display: none; } + body { overflow:visible; } + h1, h2, h3, h4, h5, h6 { page-break-after: avoid; } + .summary { display: none; } + .memitem { page-break-inside: avoid; } + #doc-content + { + margin-left:0 !important; + height:auto !important; + width:auto !important; + overflow:inherit; + display:inline; + } } diff --git a/doc/m4/ax_python.m4 b/doc/m4/ax_python.m4 new file mode 100644 index 0000000..f9a5135 --- /dev/null +++ b/doc/m4/ax_python.m4 @@ -0,0 +1,97 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_python.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_PYTHON +# +# DESCRIPTION +# +# This macro does a complete Python development environment check. +# +# It recurses through several python versions (from 2.1 to 2.6 in this +# version), looking for an executable. When it finds an executable, it +# looks to find the header files and library. +# +# It sets PYTHON_BIN to the name of the python executable, +# PYTHON_INCLUDE_DIR to the directory holding the header files, and +# PYTHON_LIB to the name of the Python library. +# +# This macro calls AC_SUBST on PYTHON_BIN (via AC_CHECK_PROG), +# PYTHON_INCLUDE_DIR and PYTHON_LIB. +# +# LICENSE +# +# Copyright (c) 2008 Michael Tindal +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 9 + +AC_DEFUN([AX_PYTHON], +[AC_MSG_CHECKING(for python build information) +AC_MSG_RESULT([]) +for python in python3.3 python3.2 python3.1 python3.0 python2.7 python2.6 python2.5 python2.4 python2.3 python2.2 python2.1 python; do +AC_CHECK_PROGS(PYTHON_BIN, [$python]) +ax_python_bin=$PYTHON_BIN +if test x$ax_python_bin != x; then + AC_CHECK_LIB($ax_python_bin, main, ax_python_lib=$ax_python_bin, ax_python_lib=no) + AC_CHECK_HEADER([$ax_python_bin/Python.h], + [[ax_python_header=`locate $ax_python_bin/Python.h | sed -e s,/Python.h,,`]], + ax_python_header=no) + if test x$ax_python_lib != xno; then + if test x$ax_python_header != xno; then + break; + fi + fi +fi +done +if test x$ax_python_bin = x; then + ax_python_bin=no +fi +if test x$ax_python_header = x; then + ax_python_header=no +fi +if test x$ax_python_lib = x; then + ax_python_lib=no +fi + +AC_MSG_RESULT([ results of the Python check:]) +AC_MSG_RESULT([ Binary: $ax_python_bin]) +AC_MSG_RESULT([ Library: $ax_python_lib]) +AC_MSG_RESULT([ Include Dir: $ax_python_header]) + +if test x$ax_python_header != xno; then + PYTHON_INCLUDE_DIR=$ax_python_header + AC_SUBST(PYTHON_INCLUDE_DIR) +fi +if test x$ax_python_lib != xno; then + PYTHON_LIB=$ax_python_lib + AC_SUBST(PYTHON_LIB) +fi +])dnl diff --git a/doc/resolve-asciidoc-refs.py b/doc/resolve-asciidoc-refs.py new file mode 100755 index 0000000..5418747 --- /dev/null +++ b/doc/resolve-asciidoc-refs.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python + +import fileinput +import re +import sys + +refs = {} +complete_file = "" + +for line in open(sys.argv[1], 'r'): + complete_file += line + +for m in re.findall('\[\[(.+)\]\]\n=+ ([^\n]+)', complete_file): + ref, title = m + refs["<<" + ref + ">>"] = "<<" + ref + ", " + title + ">>" + +def translate(match): + try: + return refs[match.group(0)] + except KeyError: + return "" + +rc = re.compile('|'.join(map(re.escape, sorted(refs, reverse=True)))) +for line in open(sys.argv[1], 'r'): + print rc.sub(translate, line), diff --git a/doc/route.txt b/doc/route.txt new file mode 100644 index 0000000..c8f1735 --- /dev/null +++ b/doc/route.txt @@ -0,0 +1,1395 @@ +//// + vim.syntax: asciidoc + + Copyright (c) 2011 Thomas Graf +//// + +Routing Family Netlink Library (libnl-route) +============================================ +Thomas Graf +3.1, Aug 11 2011: + +== Introduction + +This library provides APIs to the kernel interfaces of the routing family. + + +NOTE: Work in progress. + +== Addresses + +[[route_link]] +== Links (Network Devices) + +The link configuration interface is part of the +NETLINK_ROUTE+ protocol +family and implements the following netlink message types: + +- View and modify the configuration of physical and virtual network devices. +- Create and delete virtual network devices (e.g. dummy devices, VLAN devices, + tun devices, bridging devices, ...) +- View and modify per link network configuration settings (e.g. + +net.ipv6.conf.eth0.accept_ra+, +net.ipv4.conf.eth1.forwarding+, ...) + +.Naming Convention (network device, link, interface) + +In networking several terms are commonly used to refer to network devices. +While they have distinct meanings they have been used interchangeably in +the past. Within the Linux kernel, the term _network device_ or _netdev_ is +commonly used In user space the term _network interface_ is very common. +The routing netlink protocol uses the term _link_ and so does the _iproute2_ +utility and most routing daemons. + +=== Netlink Protocol + +This section describes the protocol semantics of the netlink based link +configuration interface. The following messages are defined: + +[options="header", cols="1,2,2"] +|============================================================================== +| Message Type | User -> Kernel | Kernel -> User +| +RTM_NEWLINK+ | Create or update virtual network device +| Reply to +RTM_GETLINK+ request or notification of link added or updated +| +RTM_DELLINK+ | Delete virtual network device +| Notification of link deleted or disappeared +| +RTM_GETLINK+ | Retrieve link configuration and statistics | +| +RTM_SETLINK+ | Modify link configuration | +|============================================================================== + +See link:core.html#core_msg_types[Netlink Library - Message Types] for more +information on common semantics of these message types. + +==== Link Message Format + +All netlink link messages share a common header (+struct ifinfomsg+) which +is appended after the netlink header (+struct nlmsghdr+). + +image:ifinfomsg.png["Link Message Header"] + +The meaning of each field may differ depending on the message type. A ++struct ifinfomsg+ is defined in ++ to represent the +header. + +Address Family (8bit):: +The address family is usually set to +AF_UNSPEC+ but may be specified in ++RTM_GETLINK+ requests to limit the returned links to a specific address +family. + +Link Layer Type (16bit):: +Currently only used in kernel->user messages to report the link layer type +of a link. The value corresponds to the +ARPHRD_*+ defines found in +++. Translation from/to strings can be done using the +functions nl_llproto2str()/nl_str2llproto(). + +Link Index (32bit):: +Carries the interface index and is used to identify existing links. + +Flags (32bit):: +In kernel->user messages the value of this field represents the current +state of the link flags. In user->kernel messages this field is used to +change flags or set the initial flag state of new links. Note that in order +to change a flag, the flag must also be set in the _Flags Change Mask_ field. + +Flags Change Mask (32bit):: +The primary use of this field is to specify a mask of flags that should be +changed based on the value of the _Flags_ field. A special meaning is given +to this field when present in link notifications, see TODO. + +Attributes (variable):: +All link message types may carry netlink attributes. They are defined in the +header file and share the prefix +IFLA_+. + +==== Link Message Types + +.RTM_GETLINK (user->kernel) + +Lookup link by 1. interface index or 2. link name (+IFLA_IFNAME+) and return +a single +RTM_NEWLINK+ message containing the link configuration and statistics +or a netlink error message if no such link was found. + +*Parameters:* + +* *Address family* +** If the address family is set to +PF_BRIDGE+, only bridging devices will be + returned. +** If the address family is set to +PF_INET6+, only ipv6 enabled devices will + be returned. + +*Flags:* + +* +NLM_F_DUMP+ If set, all links will be returned in form of a multipart + message. + +*Returns:* + +* +EINVAL+ if neither interface nor link name are set +* +ENODEV+ if no link was found +* +ENOBUFS+ if allocation failed + +.RTM_NEWLINK (user->kernel) + +Creates a new or updates an existing link. Only virtual links may be created +but all links may be updated. + +*Flags:* + +- +NLM_F_CREATE+ Create link if it does not exist +- +NLM_F_EXCL+ Return +EEXIST+ if link already exists + +*Returns:* + +- +EINVAL+ malformed message or invalid configuration parameters +- +EAFNOSUPPORT+ if a address family specific configuration (+IFLA_AF_SPEC+) + is not supported. +- +EOPNOTSUPP+ if the link does not support modification of parameters +- +EEXIST+ if +NLM_F_EXCL+ was set and the link exists alraedy +- +ENODEV+ if the link does not exist and +NLM_F_CREATE+ is not set + +.RTM_NEWLINK (kernel->user) + +This message type is used in reply to a +RTM_GETLINK+ request and carries +the configuration and statistics of a link. If multiple links need to +be sent, the messages will be sent in form of a multipart message. + +The message type is also used for notifications sent by the kernel to the +multicast group +RTNLGRP_LINK+ to inform about various link events. It is +therefore recommended to always use a separate link socket for link +notifications in order to separate between the two message types. + +TODO: document how to detect different notifications + +.RTM_DELLINK (user->kernel) + +Lookup link by 1. interface index or 2. link name (+IFLA_IFNAME+) and delete +the virtual link. + +*Returns:* + +* +EINVAL+ if neither interface nor link name are set +* +ENODEV+ if no link was found +* +ENOTSUPP+ if the operation is not supported (not a virtual link) + +.RTM_DELLINK (kernel->user) + +Notification sent by the kernel to the multicast group +RTNLGRP_LINK+ when + +a. a network device was unregistered (change == ~0) +b. a bridging device was deleted (address family will be +PF_BRIDGE+) + +=== Get / List + +[[link_list]] +==== Get list of links + +To retrieve the list of links in the kernel, allocate a new link cache +using +rtnl_link_alloc_cache()+ to hold the links. It will automatically +construct and send a +RTM_GETLINK+ message requesting a dump of all links +from the kernel and feed the returned +RTM_NEWLINK+ to the internal link +message parser which adds the returned links to the cache. + +[source,c] +----- +#include + +int rtnl_link_alloc_cache(struct nl_sock *sk, int family, struct nl_cache **result) +----- + +The cache will contain link objects (+struct rtnl_link+, see <>) +and can be accessed using the standard cache functions. By setting the ++family+ parameter to an address familly other than +AF_UNSPEC+, the resulting +cache will only contain links supporting the specified address family. + +The following direct search functions are provided to search by interface +index and by link name: + +[source,c] +----- +#include + +struct rtnl_link *rtnl_link_get(struct nl_cache *cache, int ifindex); +struct rtnl_link *rtnl_link_get_by_name(struct nl_cache *cache, const char *name); +----- + +.Example: Link Cache + +[source,c] +----- +struct nl_cache *cache; +struct rtnl_link *link; + +if (rtnl_link_alloc_cache(sock, AF_UNSPEC, &cache)) < 0) + /* error */ + +if (!(link = rtnl_link_get_by_name(cache, "eth1"))) + /* link does not exist */ + +/* do something with link */ + +rtnl_link_put(link); +nl_cache_put(cache); +----- + +[[link_direct_lookup]] +==== Lookup Single Link (Direct Lookup) + +If only a single link is of interest, the link can be looked up directly +without the use of a link cache using the function +rtnl_link_get_kernel()+. + +[source,c] +----- +#include + +int rtnl_link_get_kernel(struct nl_sock *sk, int ifindex, const char *name, struct rtnl_link **result); +----- + +It will construct and send a +RTM_GETLINK+ request using the parameters +provided and wait for a +RTM_NEWLINK+ or netlink error message sent in +return. If the link exists, the link is returned as link object +(see <>). + +.Example: Direct link lookup +[source,c] +----- +struct rtnl_link *link; + +if (rtnl_link_get_kernel(sock, 0, "eth1", &link) < 0) + /* error */ + +/* do something with link */ + +rtnl_link_put(link); +----- + +NOTE: While using this function can save a substantial amount of bandwidth + on the netlink socket, the result will not be cached, subsequent calls + to rtnl_link_get_kernel() will always trigger sending a +RTM_GETLINK+ + request. + +[[link_translate_ifindex]] +==== Translating interface index to link name + +Applications which require to translate interface index to a link name or +vice verase may use the following functions to do so. Both functions require +a filled link cache to work with. + +[source,c] +----- +char *rtnl_link_i2name (struct nl_cache *cache, int ifindex, char *dst, size_t len); +int rtnl_link_name2i (struct nl_cache *cache, const char *name); +----- + +=== Add / Modify + +Several types of virtual link can be added on the fly using the function ++rtnl_link_add()+. + +[source,c] +----- +#include + +int rtnl_link_add(struct nl_sock *sk, struct rtnl_link *link, int flags); +----- + +=== Delete + +The deletion of virtual links such as VLAN devices or dummy devices is done +using the function +rtnl_link_delete()+. The link passed on to the function +can be a link from a link cache or it can be construct with the minimal +attributes needed to identify the link. + +[source,c] +----- +#include + +int rtnl_link_delete(struct nl_sock *sk, const struct rtnl_link *link); +----- + +The function will construct and send a +RTM_DELLINK+ request message and +returns any errors returned by the kernel. + +.Example: Delete link by name +[source,c] +----- +struct rtnl_link *link; + +if (!(link = rtnl_link_alloc())) + /* error */ + +rtnl_link_set_name(link, "my_vlan"); + +if (rtnl_link_delete(sock, link) < 0) + /* error */ + +rtnl_link_put(link); +----- + +[[link_object]] +=== Link Object + +A link is represented by the structure +struct rtnl_link+. Instances may be +created with the function +rtnl_link_alloc()+ or via a link cache (see +<>) and are freed again using the function +rtnl_link_put()+. + +[source,c] +----- +#include + +struct rtnl_link *rtnl_link_alloc(void); +void rtnl_link_put(struct rtnl_link *link); +----- + +[[link_attr_name]] +==== Name +The name serves as unique, human readable description of the link. By +default, links are named based on their type and then enumerated, e.g. +eth0, eth1, ethn but they may be renamed at any time. + +Kernels >= 2.6.11 support identification by link name. + +[source,c] +----- +#include + +void rtnl_link_set_name(struct rtnl_link *link, const char *name); +char *rtnl_link_get_name(struct rtnl_link *link); +----- + +*Accepted link name format:* +[^ /]*+ (maximum length: 15 characters) + +[[link_attr_ifindex]] +==== Interface Index (Identifier) +The interface index is an integer uniquely identifying a link. If present +in any link message, it will be used to identify an existing link. + +[source,c] +----- +#include + +void rtnl_link_set_ifindex(struct rtnl_link *link, int ifindex); +int rtnl_link_get_ifindex(struct rtnl_link *link); +----- + +[[link_attr_group]] +==== Group +Each link can be assigned a numeric group identifier to group a bunch of links +together and apply a set of changes to a group instead of just a single link. + + +[source,c] +----- +#include + +void rtnl_link_set_group(struct rtnl_link *link, uint32_t group); +uint32_t rtnl_link_get_group(struct rtnl_link *link); +----- + +[[link_attr_address]] +==== Link Layer Address +The link layer address (e.g. MAC address). + +[source,c] +----- +#include + +void rtnl_link_set_addr(struct rtnl_link *link, struct nl_addr *addr); +struct nl_addr *rtnl_link_get_addr(struct rtnl_link *link); +----- + +[[link_attr_broadcast]] +==== Broadcast Address +The link layer broadcast address + +[source,c] +----- +#include + +void rtnl_link_set_broadcast(struct rtnl_link *link, struct nl_addr *addr); +struct nl_addr *rtnl_link_get_broadcast(struct rtnl_link *link); +----- + +[[link_attr_mtu]] +==== MTU (Maximum Transmission Unit) +The maximum transmission unit specifies the maximum packet size a network +device can transmit or receive. This value may be lower than the capability +of the physical network device. + +[source,c] +----- +#include + +void rtnl_link_set_mtu(struct rtnl_link *link, unsigned int mtu); +unsigned int rtnl_link_get_mtu(struct rtnl_link *link); +----- + +[[link_attr_flags]] +==== Flags +The flags of a link enable or disable various link features or inform about +the state of the link. + +[source,c] +----- +#include + +void rtnl_link_set_flags(struct rtnl_link *link, unsigned int flags); +void rtnl_link_unset_flags(struct rtnl_link *link, unsigned int flags); +unsigned int rtnl_link_get_flags(struct rtnl_link *link); +----- + +[options="compact"] +[horizontal] +IFF_UP:: Link is up (administratively) +IFF_RUNNING:: Link is up and carrier is OK (RFC2863 OPER_UP) +IFF_LOWER_UP:: Link layer is operational +IFF_DORMANT:: Driver signals dormant +IFF_BROADCAST:: Link supports broadcasting +IFF_MULTICAST:: Link supports multicasting +IFF_ALLMULTI:: Link supports multicast routing +IFF_DEBUG:: Tell driver to do debugging (currently unused) +IFF_LOOPBACK:: Link loopback network +IFF_POINTOPOINT:: Point-to-point link +IFF_NOARP:: ARP is not supported +IFF_PROMISC:: Status of promiscious mode +IFF_MASTER:: Master of a load balancer (bonding) +IFF_SLAVE:: Slave to a master link +IFF_PORTSEL:: Driver supports setting media type (only used by ARM ethernet) +IFF_AUTOMEDIA:: Link selects port automatically (only used by ARM ethernet) +IFF_ECHO:: Echo sent packets (testing feature, CAN only) +IFF_DYNAMIC:: Unused (BSD compatibility) +IFF_NOTRAILERS:: Unused (BSD compatibility) + +To translate a link flag to a link flag name or vice versa: + +[source,c] +----- +#include + +char *rtnl_link_flags2str(int flags, char *buf, size_t size); +int rtnl_link_str2flags(const char *flag_name); +----- + +[[link_attr_txqlen]] +==== Transmission Queue Length + +The transmission queue holds packets before packets are delivered to +the driver for transmission. It is usually specified in number of +packets but the unit may be specific to the link type. + +[source,c] +----- +#include + +void rtnl_link_set_txqlen(struct rtnl_link *link, unsigned int txqlen); +unsigned int rtnl_link_get_txqlen(struct rtnl_link *link); +----- + +[[link_attr_operstate]] +==== Operational Status +The operational status has been introduced to provide extended information +on the link status. Traditionally the link state has been described using +the link flags +IFF_UP, IFF_RUNNING, IFF_LOWER_UP+, and +IFF_DORMANT+ which +was no longer sufficient for some link types. + +[source,c] +----- +#include + +void rtnl_link_set_operstate(struct rtnl_link *link, uint8_t state); +uint8_t rtnl_link_get_operstate(struct rtnl_link *link); +----- + +[options="compact"] +[horizontal] +IF_OPER_UNKNOWN:: Unknown state +IF_OPER_NOTPRESENT:: Link not present +IF_OPER_DOWN:: Link down +IF_OPER_LOWERLAYERDOWN:: L1 down +IF_OPER_TESTING:: Testing +IF_OPER_DORMANT:: Dormant +IF_OPER_UP:: Link up + +Translation of operational status code to string and vice versa: + +[source,c] +----- +#include + +char *rtnl_link_operstate2str(uint8_t state, char *buf, size_t size); +int rtnl_link_str2operstate(const char *name); +----- + +[[link_attr_mode]] +==== Mode +Currently known link modes are: + +[options="compact"] +[horizontal] +IF_LINK_MODE_DEFAULT:: Default link mode +IF_LINK_MODE_DORMANT:: Limit upward transition to dormant + +[source,c] +----- +#include + +void rtnl_link_set_linkmode(struct rtnl_link *link, uint8_t mode); +uint8_t rtnl_link_get_linkmode(struct rtnl_link *link); +----- + +Translation of link mode to string and vice versa: + +[source,c] +----- +char *rtnl_link_mode2str(uint8_t mode, char *buf, size_t len); +uint8_t rtnl_link_str2mode(const char *name); +----- + +[[link_attr_alias]] +==== IfAlias +Alternative name for the link, primarly used for SNMP IfAlias. + +[source,c] +----- +#include + +const char *rtnl_link_get_ifalias(struct rtnl_link *link); +void rtnl_link_set_ifalias(struct rtnl_link *link, const char *alias); +----- + +*Length limit:* 256 + +[[link_attr_arptype]] +==== Hardware Type + +[source,c] +----- +#include +#include + +void rtnl_link_set_arptype(struct rtnl_link *link, unsigned int arptype); +unsigned int rtnl_link_get_arptype(struct rtnl_link *link); +---- + +Translation of hardware type to character string and vice versa: + +[source,c] +----- +#include + +char *nl_llproto2str(int arptype, char *buf, size_t len); +int nl_str2llproto(const char *name); +----- + +[[link_attr_qdisc]] +==== Qdisc +The name of the queueing discipline used by the link is of informational +nature only. It is a read-only attribute provided by the kernel and cannot +be modified. The set function is provided solely for the purpose of creating +link objects to be used for comparison. + +For more information on how to modify the qdisc of a link, see section +<>. + +[source,c] +----- +#include + +void rtnl_link_set_qdisc(struct rtnl_link *link, const char *name); +char *rtnl_link_get_qdisc(struct rtnl_link *link); +----- + +[[link_attr_promiscuity]] +==== Promiscuity +The number of subsystem currently depending on the link being promiscuous mode. +A value of 0 indicates that the link is not in promiscuous mode. It is a +read-only attribute provided by the kernel and cannot be modified. The set +function is provided solely for the purpose of creating link objects to be +used for comparison. + +[source,c] +----- +#include + +void rtnl_link_set_promiscuity(struct rtnl_link *link, uint32_t count); +uint32_t rtnl_link_get_promiscuity(struct rtnl_link *link); +----- + +[[link_num_rxtx_queues]] +==== RX/TX Queues +The number of RX/TX queues the link provides. The attribute is writable but +will only be considered when creating a new network device via netlink. + +[source,c] +----- +#include + +void rtnl_link_set_num_tx_queues(struct rtnl_link *link, uint32_t nqueues); +uint32_t rtnl_link_get_num_tx_queues(struct rtnl_link *link); + +void rtnl_link_set_num_rx_queues(struct rtnl_link *link, uint32_t nqueues); +uint32_t rtnl_link_get_num_rx_queues(struct rtnl_link *link); +----- + +[[link_attr_weight]] +==== Weight +This attribute is unused and obsoleted in all recent kernels. + + +[[link_modules]] +=== Modules + +[[link_bonding]] +==== Bonding + +.Example: Add bonding link +[source,c] +----- +#include + +struct rtnl_link *link; + +link = rtnl_link_bond_alloc(); +rtnl_link_set_name(link, "my_bond"); + +/* requires admin privileges */ +if (rtnl_link_add(sk, link, NLM_F_CREATE) < 0) + /* error */ + +rtnl_link_put(link); +----- + +[[link_vlan]] +==== VLAN + +[source,c] +----- +extern char * rtnl_link_vlan_flags2str(int, char *, size_t); +extern int rtnl_link_vlan_str2flags(const char *); + +extern int rtnl_link_vlan_set_id(struct rtnl_link *, int); +extern int rtnl_link_vlan_get_id(struct rtnl_link *); + +extern int rtnl_link_vlan_set_flags(struct rtnl_link *, + unsigned int); +extern int rtnl_link_vlan_unset_flags(struct rtnl_link *, + unsigned int); +extern unsigned int rtnl_link_vlan_get_flags(struct rtnl_link *); + +extern int rtnl_link_vlan_set_ingress_map(struct rtnl_link *, + int, uint32_t); +extern uint32_t * rtnl_link_vlan_get_ingress_map(struct rtnl_link *); + +extern int rtnl_link_vlan_set_egress_map(struct rtnl_link *, + uint32_t, int); +extern struct vlan_map *rtnl_link_vlan_get_egress_map(struct rtnl_link *, + int *); +----- + +.Example: Add a VLAN device +[source,c] +----- +struct rtnl_link *link; +int master_index; + +/* lookup interface index of eth0 */ +if (!(master_index = rtnl_link_name2i(link_cache, "eth0"))) + /* error */ + +/* allocate new link object of type vlan */ +link = rtnl_link_vlan_alloc(); + +/* set eth0 to be our master device */ +rtnl_link_set_link(link, master_index); + +rtnl_link_vlan_set_id(link, 10); + +if ((err = rtnl_link_add(sk, link, NLM_F_CREATE)) < 0) + /* error */ + +rtnl_link_put(link); +----- + +== Neighbouring + +== Routing + +[[route_tc]] +== Traffic Control + +The traffic control architecture allows the queueing and +prioritization of packets before they are enqueued to the network +driver. To a limited degree it is also possible to take control of +network traffic as it enters the network stack. + +The architecture consists of three different types of modules: + +- *Queueing disciplines (qdisc)* provide a mechanism to enqueue packets + in different forms. They may be used to implement fair queueing, + prioritization of differentiated services, enforce bandwidth + limitations, or even to simulate network behaviour such as packet + loss and packet delay. Qdiscs can be classful in which case they + allow traffic classes described in the next paragraph to be attached + to them. + +- *Traffic classes (class)* are supported by several qdiscs to build + a tree structure for different types of traffic. Each class may be + assigned its own set of attributes such as bandwidth limits or + queueing priorities. Some qdiscs even allow borrowing of bandwidth + between classes. + +- *Classifiers (cls)* are used to decide which qdisc/class the packet + should be enqueued to. Different types of classifiers exists, + ranging from classification based on protocol header values to + classification based on packet priority or firewall marks. + Additionally most classifiers support *extended matches (ematch)* + which allow extending classifiers by a set of matcher modules, and + *actions* which allow classifiers to take actions such as mangling, + mirroring, or even rerouting of packets. + +.Default Qdisc + +The default qdisc used on all network devices is `pfifo_fast`. +Network devices which do not require a transmit queue such as the +loopback device do not have a default qdisc attached. The `pfifo_fast` +qdisc provides three bands to prioritize interactive traffic over bulk +traffic. Classification is based on the packet priority (diffserv). + +image:qdisc_default.png["Default Qdisc"] + +.Multiqueue Default Qdisc + +If the network device provides multiple transmit queues the `mq` +qdisc is used by default. It will automatically create a separate +class for each transmit queue available and will also replace +the single per device tx lock with a per queue lock. + +image:qdisc_mq.png["Multiqueue default Qdisc"] + +.Example of a customized classful qdisc setup + +The following figure illustrates a possible combination of different +queueing and classification modules to implement quality of service +needs. + +image:tc_overview.png["Classful Qdisc diagram"] + +=== Traffic Control Object + +Each type traffic control module (qdisc, class, classifier) is +represented by its own structure. All of them are based on the traffic +control object represented by `struct rtnl_tc` which itself is based +on the generic object `struct nl_object` to make it cacheable. The +traffic control object contains all attributes, implementation details +and statistics that are shared by all of the traffic control object +types. + +image:tc_obj.png["struct rtnl_tc hierarchy"] + +It is not possible to allocate a `struct rtnl_tc` object, instead the +actual tc object types must be allocated directly using +`rtnl_qdisc_alloc()`, `rtnl_class_alloc()`, `rtnl_cls_alloc()` and +then casted to `struct rtnl_tc` using the `TC_CAST()` macro. + +.Usage Example: Allocation, Casting, Freeing +[source,c] +----- +#include +#include + +struct rtnl_qdisc *qdisc; + +/* Allocation of a qdisc object */ +qdisc = rtnl_qdisc_alloc(); + +/* Cast the qdisc to a tc object using TC_CAST() to use rtnl_tc_ functions. */ +rtnl_tc_set_mpu(TC_CAST(qdisc), 64); + +/* Free the qdisc object */ +rtnl_qdisc_put(qdisc); +----- + +[[tc_attr]] +==== Attributes + +Handle:: +The handle uniquely identifies a tc object and is used to refer +to other tc objects when constructing tc trees. ++ +[source,c] +----- +void rtnl_tc_set_handle(struct rtnl_tc *tc, uint32_t handle); +uint32_t rtnl_tc_get_handle(struct rtnl_tc *tc); +----- + +Interface Index:: +The interface index specifies the network device the traffic object +is attached to. The function `rtnl_tc_set_link()` should be preferred +when setting the interface index. It stores the reference to the link +object in the tc object and allows retrieving the `mtu` and `linktype` +automatically. ++ +[source,c] +----- +void rtnl_tc_set_ifindex(struct rtnl_tc *tc, int ifindex); +void rtnl_tc_set_link(struct rtnl_tc *tc, struct rtnl_link *link); +int rtnl_tc_get_ifindex(struct rtnl_tc *tc); +----- + +Link Type:: +The link type specifies the kind of link that is used by the network +device (e.g. ethernet, ATM, ...). It is derived automatically when +the network device is specified with `rtnl_tc_set_link()`. +The default fallback is `ARPHRD_ETHER` (ethernet). ++ +[source,c] +----- +void rtnl_tc_set_linktype(struct rtnl_tc *tc, uint32_t type); +uint32_t rtnl_tc_get_linktype(struct rtnl_tc *tc); +----- + +Kind:: +The kind character string specifies the type of qdisc, class, +classifier. Setting the kind results in the module specific +structure being allocated. Therefore it is imperative to call +`rtnl_tc_set_kind()` before using any type specific API functions +such as `rtnl_htb_set_rate()`. ++ +[source,c] +----- +int rtnl_tc_set_kind(struct rtnl_tc *tc, const char *kind); +char *rtnl_tc_get_kind(struct rtnl_tc *tc); +----- + +MPU:: +The Minimum Packet Unit specifies the minimum packet size which will +be transmitted +ever be seen by this traffic control object. This value is used for +rate calculations. Not all object implementations will make use of +this value. The default value is 0. ++ +[source,c] +----- +void rtnl_tc_set_mpu(struct rtnl_tc *tc, uint32_t mpu); +uint32_t rtnl_tc_get_mpu(struct rtnl_tc *tc); +----- + +MTU:: +The Maximum Transmission Unit specifies the maximum packet size which +will be transmitted. The value is derived from the link specified +with `rtnl_tc_set_link()` if not overwritten with `rtnl_tc_set_mtu()`. +If no link and MTU is specified, the value defaults to 1500 +(ethernet). ++ +[source,c] +----- +void rtnl_tc_set_mtu(struct rtnl_tc *tc, uint32_t mtu); +uint32_t rtnl_tc_get_mtu(struct rtnl_tc *tc); +----- + +Overhead:: +The overhead specifies the additional overhead per packet caused by +the network layer. This value can be used to correct packet size +calculations if the packet size on the wire does not match the packet +size seen by the kernel. The default value is 0. ++ +[source,c] +----- +void rtnl_tc_set_overhead(struct rtnl_tc *tc, uint32_t overhead); +uint32_t rtnl_tc_get_overhead(struct rtnl_tc *tc); +----- + +Parent:: +Specifies the parent traffic control object. The parent is identifier +by its handle. Special values are: +- `TC_H_ROOT`: attach tc object directly to network device (root + qdisc, root classifier) +- `TC_H_INGRESS`: same as `TC_H_ROOT` but on the ingress side of the + network stack. ++ +[source,c] +----- +void rtnl_tc_set_parent(struct rtnl_tc *tc, uint32_t parent); +uint32_t rtnl_tc_get_parent(struct rtnl_tc *tc); +----- + +Statistics:: +Generic statistics, see <> for additional information. ++ +[source,c] +----- +uint64_t rtnl_tc_get_stat(struct rtnl_tc *tc, enum rtnl_tc_stat id); +----- + +[[tc_stats]] +==== Accessing Statistics + +The traffic control object holds a set of generic statistics. Not all +traffic control modules will make use of all of these statistics. Some +modules may provide additional statistics via their own APIs. + +.Statistic identifiers `(enum rtnl_tc_stat)` +[cols="m,,", options="header", frame="topbot"] +|==================================================================== +| ID | Type | Description +| RTNL_TC_PACKETS | Counter | Total # of packets transmitted +| RTNL_TC_BYTES | Counter | Total # of bytes transmitted +| RTNL_TC_RATE_BPS | Rate | Current bytes/s rate +| RTNL_TC_RATE_PPS | Rate | Current packets/s rate +| RTNL_TC_QLEN | Rate | Current length of the queue +| RTNL_TC_BACKLOG | Rate | # of packets currently backloged +| RTNL_TC_DROPS | Counter | # of packets dropped +| RTNL_TC_REQUEUES | Counter | # of packets requeued +| RTNL_TC_OVERLIMITS | Counter | # of packets that exceeded the limit +|==================================================================== + +NOTE: `RTNL_TC_RATE_BPS` and `RTNL_TC_RATE_PPS` only return meaningful + values if a rate estimator has been configured. + +.Usage Example: Retrieving tc statistics +[source,c] +------- +#include + +uint64_t drops, qlen; + +drops = rtnl_tc_get_stat(TC_CAST(qdisc), RTNL_TC_DROPS); +qlen = rtnl_tc_get_stat(TC_CAST(qdisc), RTNL_TC_QLEN); +------- + +==== Rate Table Calculations + +[[tc_qdisc]] +=== Queueing Discipline (qdisc) + +.Classless Qdisc + +The queueing discipline (qdisc) is used to implement fair queueing, +priorization or rate control. It provides a _enqueue()_ and +_dequeue()_ operation. Whenever a network packet leaves the networking +stack over a network device, be it a physical or virtual device, it +will be enqueued to a qdisc unless the device is queueless. The +_enqueue()_ operation is followed by an immediate call to _dequeue()_ +for the same qdisc to eventually retrieve a packet which can be +scheduled for transmission by the driver. Additionally, the networking +stack runs a watchdog which polls the qdisc regularly to dequeue and +send packets even if no new packets are being enqueued. + +This additional watchdog is required due to the fact that qdiscs may +hold on to packets and not return any packets upon _dequeue()_ in +order to enforce bandwidth restrictions. + +image:classless_qdisc_nbands.png[alt="Multiband Qdisc", float="right"] + +The figure illustrates a trivial example of a classless qdisc +consisting of three bands (queues). Use of multiple bands is a common +technique in qdiscs to implement fair queueing between flows or +prioritize differentiated services. + +Classless qdiscs can be regarded as a blackbox, their inner workings +can only be steered using the configuration parameters provided by the +qdisc. There is no way of taking influence on the structure of its +internal queues itself. + +.Classful Qdisc + +Classful qdiscs allow for the queueing structure and classification +process to be created by the user. + +image:classful_qdisc.png["Classful Qdisc"] + +The figure above shows a classful qdisc with a classifier attached to +it which will make the decision whether to enqueue a packet to traffic +class +1:1+ or +1:2+. Unlike with classless qdiscs, classful qdiscs +allow the classification process and the structure of the queues to be +defined by the user. This allows for complex traffic class rules to +be applied. + +.List of Qdisc Implementations +[options="header", frame="topbot", cols="2,1^,8"] +|====================================================================== +| Qdisc | Classful | Description +| ATM | Yes | FIXME +| Blackhole | No | This qdisc will drop all packets passed to it. +| CBQ | Yes | +The CBQ (Class Based Queueing) is a classful qdisc which allows +creating traffic classes and enforce bandwidth limitations for each +class. +| DRR | Yes | +The DRR (Deficit Round Robin) scheduler is a classful qdisc +impelemting fair queueing. Each class is assigned a quantum specyfing +the maximum number of bytes that can be served per round. Unused +quantum at the end of the round is carried over to the next round. +| DSMARK | Yes | FIXME +| FIFO | No | FIXME +| GRED | No | FIXME +| HFSC | Yes | FIXME +| HTB | Yes | FIXME +| mq | Yes | FIXME +| multiq | Yes | FIXME +| netem | No | FIXME +| Prio | Yes | FIXME +| RED | Yes | FIXME +| SFQ | Yes | FIXME +| TBF | Yes | FIXME +| teql | No | FIXME +|====================================================================== + + +.QDisc API Overview +[cols="a,a", options="header", frame="topbot"] +|==================================================================== +| Attribute | C Interface +| +Allocation / Freeing:: +| +[source,c] +----- +struct rtnl_qdisc *rtnl_qdisc_alloc(void); +void rtnl_qdisc_put(struct rtnl_qdisc *qdisc); +----- +| +Addition:: +| +[source,c] +----- +int rtnl_qdisc_build_add_request(struct rtnl_qdisc *qdisc, int flags, + struct nl_msg **result); +int rtnl_qdisc_add(struct nl_sock *sock, struct rtnl_qdisc *qdisc, + int flags); +----- +| +Modification:: +| +[source,c] +----- +int rtnl_qdisc_build_change_request(struct rtnl_qdisc *old, + struct rtnl_qdisc *new, + struct nl_msg **result); +int rtnl_qdisc_change(struct nl_sock *sock, struct rtnl_qdisc *old, + struct rtnl_qdisc *new); +----- +| +Deletion:: +| +[source,c] +----- +int rtnl_qdisc_build_delete_request(struct rtnl_qdisc *qdisc, + struct nl_msg **result); +int rtnl_qdisc_delete(struct nl_sock *sock, struct rtnl_qdisc *qdisc); +----- +| +Cache:: +| +[source,c] +----- +int rtnl_qdisc_alloc_cache(struct nl_sock *sock, + struct nl_cache **cache); +struct rtnl_qdisc *rtnl_qdisc_get(struct nl_cache *cache, int, uint32_t); + +struct rtnl_qdisc *rtnl_qdisc_get_by_parent(struct nl_cache *, int, uint32_t); +----- +|==================================================================== + +[[qdisc_get]] +==== Retrieving Qdisc Configuration + +The function rtnl_qdisc_alloc_cache() is used to retrieve the current +qdisc configuration in the kernel. It will construct a +RTM_GETQDISC+ +netlink message, requesting the complete list of qdiscs configured in +the kernel. + +[source,c] +------- +#include + +struct nl_cache *all_qdiscs; + +if (rtnl_link_alloc_cache(sock, &all_qdiscs) < 0) + /* error while retrieving qdisc cfg */ +------- + +The cache can be accessed using the following functions: + +- Search qdisc with matching ifindex and handle: ++ +[source,c] +-------- +struct rtnl_qdisc *rtnl_qdisc_get(struct nl_cache *cache, int ifindex, uint32_t handle); +-------- +- Search qdisc with matching ifindex and parent: ++ +[source,c] +-------- +struct rtnl_qdisc *rtnl_qdisc_get_by_parent(struct nl_cache *cache, int ifindex , uint32_t parent); +-------- +- Or any of the generic cache functions (e.g. nl_cache_search(), nl_cache_dump(), etc.) + +.Example: Search and print qdisc +[source,c] +------- +struct rtnl_qdisc *qdisc; +int ifindex; + +ifindex = rtnl_link_get_ifindex(eth0_obj); + +/* search for qdisc on eth0 with handle 1:0 */ +if (!(qdisc = rtnl_qdisc_get(all_qdiscs, ifindex, TC_HANDLE(1, 0)))) + /* no such qdisc found */ + +nl_object_dump(OBJ_CAST(qdisc), NULL); + +rtnl_qdisc_put(qdisc); +------- + +[[qdisc_add]] +==== Adding a Qdisc + +In order to add a new qdisc to the kernel, a qdisc object needs to be +allocated. It will hold all attributes of the new qdisc. + +[source,c] +----- +#include + +struct rtnl_qdisc *qdisc; + +if (!(qdisc = rtnl_qdisc_alloc())) + /* OOM error */ +----- + +The next step is to specify all generic qdisc attributes using the tc +object interface described in the section <>. + +The following attributes must be specified: +- IfIndex +- Parent +- Kind + +[source,c] +----- +/* Attach qdisc to device eth0 */ +rtnl_tc_set_link(TC_CAST(qdisc), eth0_obj); + +/* Make this the root qdisc */ +rtnl_tc_set_parent(TC_CAST(qdisc), TC_H_ROOT); + +/* Set qdisc identifier to 1:0, if left unspecified, a handle will be generated by the kernel. */ +rtnl_tc_set_handle(TC_CAST(qdisc), TC_HANDLE(1, 0)); + +/* Make this a HTB qdisc */ +rtnl_tc_set_kind(TC_CAST(qdisc), "htb"); +----- + +After specyfing the qdisc kind (rtnl_tc_set_kind()) the qdisc type +specific interface can be used to set attributes which are specific +to the respective qdisc implementations: + +[source,c] +------ +/* HTB feature: Make unclassified packets go to traffic class 1:5 */ +rtnl_htb_set_defcls(qdisc, TC_HANDLE(1, 5)); +------ + +Finally, the qdisc is ready to be added and can be passed on to the +function rntl_qdisc_add() which takes care of constructing a netlink +message requesting the addition of the new qdisc, sends the message to +the kernel and waits for the response by the kernel. The function +returns 0 if the qdisc has been added or updated successfully or a +negative error code if an error occured. + +CAUTION: The kernel operation for updating and adding a qdisc is the + same. Therefore when calling rtnl_qdisc_add() any existing + qdisc with matching handle will be updated unless the flag + NLM_F_EXCL is specified. + +The following flags may be specified: +[horizontal] +NLM_F_CREATE:: Create qdisc if it does not exist, otherwise + -NLE_OBJ_NOTFOUND is returned. +NLM_F_REPLACE:: If another qdisc is already attached to the same + parent and their handles mismatch, replace the qdisc + instead of returning -EEXIST. +NLM_F_EXCL:: Return -NLE_EXISTS if a qdisc with matching handles + exists already. + +WARNING: The function rtnl_qdisc_add() requires administrator + privileges. + +[source,c] +------ +/* Submit request to kernel and wait for response */ +err = rtnl_qdisc_add(sock, qdisc, NLM_F_CREATE); + +/* Return the qdisc object to free memory resources */ +rtnl_qdisc_put(qdisc); + +if (err < 0) { + fprintf(stderr, "Unable to add qdisc: %s\n", nl_geterror(err)); + return err; +} +------ + +==== Deleting a qdisc + +[source,c] +------ +#include + +struct rtnl_qdisc *qdisc; + +qdisc = rtnl_qdisc_alloc(); + +rtnl_tc_set_link(TC_CAST(qdisc), eth0_obj); +rtnl_tc_set_parent(TC_CAST(qdisc), TC_H_ROOT); + +rtnl_qdisc_delete(sock, qdisc) + +rtnl_qdisc_put(qdisc); +------ + +WARNING: The function rtnl_qdisc_delete() requires administrator + privileges. + + +[[qdisc_htb]] +==== HTB - Hierarchical Token Bucket + +.HTB Qdisc Attributes + +Default Class:: +The default class is the fallback class to which all traffic which +remained unclassified is directed to. If no default class or an +invalid default class is specified, packets are transmitted directly +to the next layer (direct transmissions). ++ +[source,c] +----- +uint32_t rtnl_htb_get_defcls(struct rtnl_qdisc *qdisc); +int rtnl_htb_set_defcls(struct rtnl_qdisc *qdisc, uint32_t defcls); +----- + +Rate to Quantum (r2q):: +TODO ++ +[source,c] +----- +uint32_t rtnl_htb_get_rate2quantum(struct rtnl_qdisc *qdisc); +int rtnl_htb_set_rate2quantum(struct rtnl_qdisc *qdisc, uint32_t rate2quantum); +----- + + +.HTB Class Attributes + +Priority:: ++ +[source,c] +----- +uint32_t rtnl_htb_get_prio(struct rtnl_class *class); +int rtnl_htb_set_prio(struct rtnl_class *class, uint32_t prio); +----- + +Rate:: +The rate (bytes/s) specifies the maximum bandwidth an invidivual class +can use without borrowing. The rate of a class should always be greater +or erqual than the rate of its children. ++ +[source,c] +----- +uint32_t rtnl_htb_get_rate(struct rtnl_class *class); +int rtnl_htb_set_rate(struct rtnl_class *class, uint32_t ceil); +----- + +Ceil Rate:: +The ceil rate specifies the maximum bandwidth an invidivual class +can use. This includes bandwidth that is being borrowed from other +classes. Ceil defaults to the class rate implying that by default +the class will not borrow. The ceil rate of a class should always +be greater or erqual than the ceil rate of its children. ++ +[source,c] +----- +uint32_t rtnl_htb_get_ceil(struct rtnl_class *class); +int rtnl_htb_set_ceil(struct rtnl_class *class, uint32_t ceil); +----- + +Burst:: +TODO ++ +[source,c] +----- +uint32_t rtnl_htb_get_rbuffer(struct rtnl_class *class); +int rtnl_htb_set_rbuffer(struct rtnl_class *class, uint32_t burst); +----- + +Ceil Burst:: +TODO ++ +[source,c] +----- +uint32_t rtnl_htb_get_bbuffer(struct rtnl_class *class); +int rtnl_htb_set_bbuffer(struct rtnl_class *class, uint32_t burst); +----- + +Quantum:: +TODO ++ +[source,c] +----- +int rtnl_htb_set_quantum(struct rtnl_class *class, uint32_t quantum); +----- + +extern int rtnl_htb_set_cbuffer(struct rtnl_class *, uint32_t); + + + + +[[tc_class]] +=== Class + +[options="header", cols="s,a,a,a,a"] +|======================================================================= +| | UNSPEC | TC_H_ROOT | 0:pY | pX:pY +| UNSPEC 3+^| +[horizontal] +qdisc =:: root-qdisc +class =:: root-qdisc:0 +| +[horizontal] +qdisc =:: pX:0 +class =:: pX:0 +| 0:hY 3+^| +[horizontal] +qdisc =:: root-qdisc +class =:: root-qdisc:hY +| +[horizontal] +qdisc =:: pX:0 +class =:: pX:hY +| hX:hY 3+^| +[horizontal] +qdisc =:: hX: +class =:: hX:hY +| +if pX != hX + return -EINVAL +[horizontal] +qdisc =:: hX: +class =:: hX:hY +|======================================================================= + +[[tc_cls]] +=== Classifier (cls) + +TODO + +[[tc_classid_mngt]] +=== ClassID Management + +TODO + +[[tc_pktloc]] +=== Packet Location Aliasing (pktloc) + +TODO + +[[tc_api]] +=== Traffic Control Module API + +TODO diff --git a/doc/src/core.c b/doc/src/core.c deleted file mode 100644 index a173a43..0000000 --- a/doc/src/core.c +++ /dev/null @@ -1,2303 +0,0 @@ -/** - * \cond skip - * vim:syntax=c.doxygen - * \endcond - -\page core_doc Netlink Core Library (-lnl) - -\section core_intro Introduction - -The core library contains the fundamentals required to communicate over -netlink sockets. It deals with connecting and disconnectng of sockets, -sending and receiving of data, construction and parsing of messages, -provides a customizeable receiving state machine, and provides a abstract -data type framework which eases the implementation of object based netlink -protocols where objects are added, removed, or modified with the help of -netlink messages. - -\section core_toc Table of Contents - -- \ref core_lib - - \ref core_lib_howto - - \ref core_lib_link -- \ref proto_fund - - \ref core_format - - \ref core_msg_type - - \ref core_multipart - - \ref core_errmsg - - \ref core_ack - - \ref core_msg_flags - - \ref core_seq - - \ref core_multicast -- \ref sk_doc - - \ref core_sk_alloc - - \ref core_sk_seq_num - - \ref core_sk_groups - - \ref core_sk_cb - - \ref core_sk_attrs - - \ref core_sk_local_port - - \ref core_sk_peer_port - - \ref core_sk_fd - - \ref core_sk_buffer_size - - \ref core_sk_cred - - \ref core_sk_auto_ack - - \ref core_sk_msg_peek - - \ref core_sk_pktinfo -- \ref core_send_recv - - \ref core_send - - \ref core_nl_send - - \ref core_nl_send_iovec - - \ref core_nl_sendmsg - - \ref core_send_raw - - \ref core_send_simple - - \ref core_recv - - \ref core_nl_recvmsgs - - \ref core_recvmsgs - - \ref core_recv_parse - - \ref core_auto_ack -- \ref core_msg - - \ref core_msg_format - - \ref core_msg_fmt_align - - \ref core_msg_parse - - \ref core_msg_split - - \ref core_msg_payload - - \ref core_msg_parse_attr - - \ref core_nlmsg_parse - - \ref core_msg_constr - - \ref core_msg_alloc - - \ref core_msg_nlmsg_put - - \ref core_msg_reserve - - \ref core_msg_append -- \ref core_attr - - \ref core_attr_format - - \ref core_attr_parse - - \ref core_attr_parse_split - - \ref core_attr_payload - - \ref core_attr_validation - - \ref core_attr_nla_parse - - \ref core_attr_find - - \ref core_attr_iterate - - \ref core_attr_constr - - \ref core_attr_exception - - \ref core_attr_data_type - - \ref core_attr_int - - \ref core_attr_string - - \ref core_attr_flag - - \ref core_attr_nested - - \ref core_attr_unspec - - \ref core_attr_examples - - \ref core_attr_example_constr - - \ref core_attr_example_parse -- \ref core_cb - - \ref core_cb_hooks - - \ref core_cb_default - - \ref core_cb_msg_proc - - \ref core_cb_errmsg - - \ref core_cb_example - - \ref core_cb_overwrite - - \ref core_cb_ow_recvmsgs - - \ref core_cb_ow_recv - - \ref core_cb_ow_send -- \ref core_cache -- \ref core_abstract_types - - \ref core_abstract_addr - - \ref core_abstract_addr_alloc - - \ref core_abstract_addr_ref - - \ref core_abstract_addr_attr - - \ref core_abstract_addr_prefix - - \ref core_abstract_addr_helpers - - \ref core_abstract_data - - \ref core_abstract_data_alloc - - \ref core_abstract_data_access - - \ref core_abstract_data_helpers - -\section core_lib 1. Introduction to the Library - -\subsection core_lib_howto 1.1 How To Read This Documentation - -The documentation consists of this manual and the API reference pages. -Both contain references to each other and as many examples as possible. - -Even though the library tries to be as consistent and as intuitive as -possible it may be difficult to understand where to start looking for -information. - - - -\subsection core_lib_link 1.2 Linking to this Library - - -\subsection flags Flags to Character StringTranslations - -All functions converting a set of flags to a character string follow -the same principles, therefore, the following information applies -to all functions convertings flags to a character string and vice versa. - -\subsubsection flags2str Flags to Character String -\code -char *_flags2str(int flags, char *buf, size_t len) -\endcode -\arg flags Flags. -\arg buf Destination buffer. -\arg len Buffer length. - -Converts the specified flags to a character string separated by -commas and stores it in the specified destination buffer. - -\return The destination buffer - -\subsubsection str2flags Character String to Flags -\code -int _str2flags(const char *name) -\endcode -\arg name Name of flag. - -Converts the provided character string specifying a flag -to the corresponding numeric value. - -\return Link flag or a negative value if none was found. - -\subsubsection type2str Type to Character String -\code -char *_2str(int type, char *buf, size_t len) -\endcode -\arg type Type as numeric value -\arg buf Destination buffer. -\arg len Buffer length. - -Converts an identifier (type) to a character string and stores -it in the specified destination buffer. - -\return The destination buffer or the type encoded in hexidecimal - form if the identifier is unknown. - -\subsubsection str2type Character String to Type -\code -int _str2(const char *name) -\endcode -\arg name Name of identifier (type). - -Converts the provided character string specifying a identifier -to the corresponding numeric value. - -\return Identifier as numeric value or a negative value if none was found. - - -\section proto_fund 1. Netlink Protocol Fundamentals - -The netlink protocol is a socket based IPC mechanism used for communication -between userspace processes and the kernel or between userspace processes -themselves. The netlink protocol is based on BSD sockets and uses the -\c AF_NETLINK address family. Every netlink protocol uses its own protocol -number (e.g. NETLINK_ROUTE, NETLINK_NETFILTER, etc). Its addressing schema -is based on a 32 bit port number, formerly referred to as PID, which uniquely -identifies each peer. - -\subsection core_format 1.1 Message Format - -A netlink protocol is typically based on messages and consists of the -netlink message header (struct nlmsghdr) plus the payload attached to it. -The payload can consist of arbitary data but usually contains a fixed -size protocol specific header followed by a stream of attributes. - -The netlink message header (struct nlmsghdr) has the following format: - -\code -0 1 2 3 -0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 -+-------------------------------------------------------------+ -| Length | -+------------------------------+------------------------------+ -| Type | Flags | -+------------------------------+------------------------------+ -| Sequence Number | -+-------------------------------------------------------------+ -| Port (Address) | -+-------------------------------------------------------------+ -\endcode - -\subsection core_msg_type 1.2 Message Types - -Netlink differs between requests, notifications, and replies. Requests -are messages which have the \c NLM_F_REQUEST flag set and are meant to -request an action from the receiver. A request is typically sent from -a userspace process to the kernel. While not strictly enforced, requests -should carry a sequence number incremented for each request sent. - -Depending on the nature of the request, the receiver may reply to the -request with another netlink message. The sequence number of a reply -must match the sequence number of the request it relates to. - -Notifications are of informal nature and no reply is expected, therefore -the sequence number is typically set to 0. - -\msc -A,B; -A=>B [label="GET (seq=1, NLM_F_REQUEST)"]; -A<=B [label="PUT (seq=1)"]; -...; -A<=B [label="NOTIFY (seq=0)"]; -\endmsc - -The type of message is primarly identified by its 16 bit message type set -in the message header. The following standard message types are defined: - -- \c NLMSG_NOOP - No operation, message must be discarded -- \c NLMSG_ERROR - Error message or ACK, see \ref core_errmsg, - respectively \ref core_ack -- \c NLMSG_DONE - End of multipart sequence, see \ref core_multipart. -- \c NLMSG_OVERRUN - Overrun notification (Error) - -Every netlink protocol is free to define own message types. Note that -message type values < \c NLMSG_MIN_TYPE (0x10) are reserved and may not -be used. - -It is common practice to use own message types to implement RPC schemas. -Suppose the goal of the netlink protocol you are implementing is allow -configuration of a particular network device, therefore you want to -provide read/write access to various configuration options. The typical -"netlink way" of doing this would be to define two message types -\c MSG_SETCFG, \c MSG_GETCFG: - -\code -#define MSG_SETCFG 0x11 -#define MSG_GETCFG 0x12 -\endcode - -Sending a \c MSG_GETCFG request message will typically trigger a reply -with the message type \c MSG_SETCFG containing the current configuration. -In object oriented terms one would describe this as "the kernel sets -the local copy of the configuration in userspace". - -\msc -A,B; -A=>B [label="MSG_GETCFG (seq=1, NLM_F_REQUEST)"]; -A<=B [label="MSG_SETCFG (seq=1)"]; -\endmsc - -The configuration may be changed by sending a \c MSG_SETCFG which will -be responded to with either a ACK (see \ref core_ack) or a error -message (see \ref core_errmsg). - -\msc -A,B; -A=>B [label="MSG_SETCFG (seq=1, NLM_F_REQUEST, NLM_F_ACK)"]; -A<=B [label="ACK (seq=1)"]; -\endmsc - -Optionally, the kernel may send out notifications for configuration -changes allowing userspace to listen for changes instead of polling -frequently. Notifications typically reuse an existing message type -and rely on the application using a separate socket to differ between -requests and notifications but you may also specify a separate message -type. - -\msc -A,B; -A<=B [label="MSG_SETCFG (seq=0)"]; -\endmsc - -\subsubsection core_multipart 1.2.1 Multipart Messages - -Although in theory a netlink message can be up to 4GiB in size. The socket -buffers are very likely not large enough to hold message of such sizes. -Therefore it is common to limit messages to one page size (PAGE_SIZE) and -use the multipart mechanism to split large pieces of data into several -messages. A multipart message has the \c flag NLM_F_MULTI set and the -receiver is expected to continue receiving and parsing until the special -message type \c NLMSG_DONE is received. - -Multipart messages unlike fragmented ip packets must not be reassmbled -even though it is perfectly legal to do so if the protocols wishes to -work this way. Often multipart message are used to send lists or trees -of objects were each multipart message simply carries multiple objects -allow for each message to be parsed independently. - -\msc -A,B; -A=>B [label="GET (seq=1, NLM_F_REQUEST)"]; -A<=B [label="PUT (seq=1, NLM_F_MULTI)"]; -...; -A<=B [label="PUT (seq=1, NLM_F_MULTI)"]; -A<=B [label="NLMSG_DONE (seq=1)"]; -\endmsc - -\subsubsection core_errmsg 1.2.2 Error Message - -Error messages can be sent in response to a request. Error messages must -use the standard message type \c NLMSG_ERROR. The payload consists of a -error code and the original netlink mesage header of the request. - -\code -0 1 2 3 -0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 -+-------------------------------------------------------------+ -| Length | -+------------------------------+------------------------------+ -| .nlmsg_type = NLMSG_ERROR | .nlmsg_flags = 0 | -+------------------------------+------------------------------+ -| Sequence number of the orig request | -+-------------------------------------------------------------+ -| Port number of the orig request | -+-------------------------------------------------------------+ -| Error Code (e.g. EINVAL) | -+-------------------------------------------------------------+ -| Netlink Message Header of orig. request | -. . -. . -+-------------------------------------------------------------+ -\endcode - -Error messages should set the sequence number to the sequence number -of the request which caused the error. - -\msc -A,B; -A=>B [label="GET (seq=1, NLM_F_REQUEST)"]; -A<=B [label="NLMSG_ERROR code=EINVAL (seq=1)"]; -\endmsc - -\subsubsection core_ack 1.2.3 ACKs - -A sender can request an ACK message to be sent back for each request -processed by setting the \c NLM_F_ACK flag in the request. This is typically -used to allow the sender to synchronize further processing until the -request has been processed by the receiver. - -\msc -A,B; -A=>B [label="GET (seq=1, NLM_F_REQUEST | NLM_F_ACK)"]; -A<=B [label="ACK (seq=1)"]; -\endmsc - -ACK messages also use the message type \c NLMSG_ERROR and payload format -but the error code is set to 0. - -\subsubsection core_msg_flags 1.3 Message Flags - -The following standard flags are defined - -\code -#define NLM_F_REQUEST 1 -#define NLM_F_MULTI 2 -#define NLM_F_ACK 4 -#define NLM_F_ECHO 8 -\endcode - -- \c NLM_F_REQUEST - Message is a request, see \ref core_msg_type. -- \c NLM_F_MULTI - Multipart message, see \ref core_multipart. -- \c NLM_F_ACK - ACK message requested, see \ref core_ack. -- \c NLM_F_ECHO - Request to echo the request. - -The flag \c NLM_F_ECHO is similar to the \c NLM_F_ACK flag. It can be -used in combination with \c NLM_F_REQUEST and causes a notification -which is sent as a result of a request to also be sent to the sender -regardless of whether the sender has subscribed to the corresponding -multicast group or not. See \ref core_multicast. - -Additional universal message flags are defined which only apply for -\c GET requests: - -\code -#define NLM_F_ROOT 0x100 -#define NLM_F_MATCH 0x200 -#define NLM_F_ATOMIC 0x400 -#define NLM_F_DUMP (NLM_F_ROOT|NLM_F_MATCH) -\endcode - -- \c NLM_F_ROOT - Return based on root of tree. -- \c NLM_F_MATCH - Return all matching entries. -- \c NLM_F_ATOMIC - Obsoleted, once used to request an atomic operation. -- \c NLM_F_DUMP - Return a list of all objects \c (NLM_F_ROOT|NLM_F_MATCH). - -Use of these flags is completely optional and many netlink protocols only -make use of the \c NLM_F_DUMP flag which typically requests the receiver -to send a list of all objects in the context of the message type as a -sequence of multipart messages (see \ref core_multipart). - -Another set of flags exist related to \c NEW or \c SET requests. These -flags are mutually exclusive to the \c GET flags: - -\code -#define NLM_F_REPLACE 0x100 -#define NLM_F_EXCL 0x200 -#define NLM_F_CREATE 0x400 -#define NLM_F_APPEND 0x800 -\endcode - -- \c NLM_F_REPLACE - Replace an existing object if it exists. -- \c NLM_F_EXCL - Do not update object if it exists already. -- \c NLM_F_CREATE - Create object if it does not exist yet. -- \c NLM_F_APPEND - Add object at end of list. - -Behaviour of these flags may differ slightly between different netlink -protocols. - -\subsection core_seq 1.4 Sequence Numbers - -Netlink allows the use of sequence numbers to help relate replies to -requests. It should be noted that unlike in protocols such as TCP there -is no strict enforcment of the sequence number. The sole purpose of -sequence numbers is to assist a sender in relating replies to the -corresponding requests. See \ref core_msg_type for more information. - -Sequence numbers are managed on a per socket basis, see -\ref core_sk_seq_num for more information on how to use sequence numbers. - -\subsection core_multicast 1.5 Multicast Groups - -TODO - -See \ref core_sk_groups. - -\section sk_doc 2. Netlink Sockets - -In order to use the netlink protocol, a netlink socket is required. Each -socket defines a completely independent context for sending and receiving -of messages. An application may use multiple sockets for the same netlink -protocol, e.g. one socket to send requests and receive replies and another -socket subscribed to a multicast group to receive notifications. - -\subsection core_sk_alloc 2.1 Socket Allocation & Freeing - -The netlink socket and all its related attributes are represented by -struct nl_sock. - -\code -#include - -struct nl_sock *nl_socket_alloc(void) -void nl_socket_free(struct nl_sock *sk) -\endcode - -\subsection core_sk_seq_num 2.2 Sequence Numbers - -The library will automatically take care of sequence number handling for -the application. A sequence number counter is stored in struct nl_sock which -is meant to be used when sending messages which will produce a reply, error -or any other message which needs to be related to the original message. - -The counter can be used directly with the function nl_socket_use_seq() -which will return the current value of the counter and increment it by -one afterwards. - -\code -#include - -unsigned int nl_socket_use_seq(struct nl_sock *sk); -\endcode - -Most applications will not want to deal with sequence number handling -themselves though. When using nl_send_auto() the sequence number is -filled out automatically and matched again on the receiving side. See -\ref core_send_recv for more information. - -This behaviour can and must be disabled if the netlink protocol -implemented does not use a request/reply model, e.g. when a socket is -used to receive notification messages. - -\code -#include - -void nl_socket_disable_seq_check(struct nl_sock *sk); -\endcode - -\subsection core_sk_groups 2.3 Multicast Group Subscriptions - -Each socket can subscribe to any number of multicast groups of the -netlink protocol it is connected to. The socket will then receive a copy -of each message sent to any of the groups. Multicast groups are commonly -used to implement event notifications. - -Prior to kernel 2.6.14 the group subscription was performed using a bitmask -which limited the number of groups per protocol family to 32. This outdated -interface can still be accessed via the function nl_join_groups even though -it is not recommended for new code. - -\code -#include - -void nl_join_groups(struct nl_sock *sk, int bitmask); -\endcode - -Starting with 2.6.14 a new method was introduced which supports subscribing -to an almost infinite number of multicast groups. - -\code -#include - -int nl_socket_add_memberships(struct nl_sock *sk, int group, ...); -int nl_socket_drop_memberships(struct nl_sock *sk, int group, ...); -\endcode - -\subsubsection core_sk_group_example 2.3.1 Multicast Example - -\include sk_group_example.c - -\subsection core_sk_cb 2.4 Modifiying Socket Callback Configuration - -See \ref core_cb for more information on callback hooks and overwriting -capabilities - -Each socket is assigned a callback configuration which controls the -behaviour of the socket. This is f.e. required to have a separate message -receive function per socket. It is perfectly legal to share callback -configurations between sockets though. - -The following functions can be used to access and set the callback -configuration of a socket: - -\code -#include - -struct nl_cb *nl_socket_get_cb(const struct nl_sock *sk); -void nl_socket_set_cb(struct nl_sock *sk, struct nl_cb *cb); -\endcode - -Additionaly a shortcut exists to modify the callback configuration assigned -to a socket directly: - -\code -#include - -int nl_socket_modify_cb(struct nl_sock *sk, enum nl_cb_type type, enum nl_cb_kind kind, - nl_recvmsg_msg_cb_t func, void *arg); -\endcode - -Example: -\code -#include - -// Call my_input() for all valid messages received in socket sk -nl_socket_modify_cb(sk, NL_CB_VALID, NL_CB_CUSTOM, my_input, NULL); -\endcode - -\subsection core_sk_attrs 2.5 Socket Attributes - -\subsubsection core_sk_local_port 2.5.1 Local Port - -The local port number uniquely identifies the socket and is used to -address it. A unique local port is generated automatically when the socket -is allocated. It will consist of the Process ID (22 bits) and a random -number (10 bits) thus allowing up to 1024 sockets per process. - -\code -#include - -uint32_t nl_socket_get_local_port(const struct nl_sock *sk); -void nl_socket_set_local_port(struct nl_sock *sk, uint32_t port); -\endcode - -\b Note: Overwriting the local port is possible but you have to ensure -that the provided value is unique and no other socket in any other -application is using the same value. - -\subsubsection core_sk_peer_port 2.5.2 Peer Port - -A peer port can be assigned to the socket which will result in all unicast -messages sent over the socket to be addresses to the peer. If no peer is -specified, the message is sent to the kernel which will try to automatically -bind the socket to a kernel side socket of the same netlink protocol family. -It is common practice not to bind the socket to a peer port as typically -only one kernel side socket exists per netlink protocol family. - -\code -#include - -uint32_t nl_socket_get_peer_port(const struct nl_sock *sk); -void nl_socket_set_peer_port(struct nl_sock *sk, uint32_t port); -\endcode - -\subsubsection core_sk_fd 2.5.3 File Descriptor - -Netlink uses the BSD socket interface, therefore a file descriptor -is behind each socket and you may use it directly. - -\code -#include - -int nl_socket_get_fd(const struct nl_sock *sk); -\endcode - -If a socket is used to only receive notifications it usually is best -to put the socket in non-blocking mode and periodically poll for new -notifications. - -\code -#include - -int nl_socket_set_nonblocking(const struct nl_sock *sk); -\endcode - -\subsubsection core_sk_buffer_size 2.5.4 Send/Receive Buffer Size - -The socket buffer is used to queue netlink messages between sender -and receiver. The size of these buffers specifies the maximum size -you will be able to write() to a netlink socket, i.e. it will indirectly -define the maximum message size. The default is 32KiB. - -\code -#include - -int nl_socket_set_buffer_size(struct nl_sock *sk, int rx, int tx); -\endcode - - -\subsubsection core_sk_cred 2.5.5 Enable/Disable Credentials - -TODO - -\code -#include - -int nl_socket_set_passcred(struct nl_sock *sk, int state); -\endcode - -\subsubsection core_sk_auto_ack 2.5.6 Enable/Disable Auto-ACK Mode - -The following functions allow to enable/disable Auto-ACK mode on a -socket. See \ref core_auto_ack for more information on what implications -that has. Auto-ACK mode is enabled by default. - -\code -#include - -void nl_socket_enable_auto_ack(struct nl_sock *sk); -void nl_socket_disable_auto_ack(struct nl_sock *sk); -\endcode - -\subsubsection core_sk_msg_peek 2.5.7 Enable/Disable Message Peeking - -If enabled, message peeking causes nl_recv() to try and use MSG_PEEK to -retrieve the size of the next message received and allocate a buffer -of that size. Message peeking is enabled by default but can be disabled -using the following function: - -\code -#include - -void nl_socket_enable_msg_peek(struct nl_sock *sk); -void nl_socket_disable_msg_peek(struct nl_sock *sk); -\endcode - -\subsubsection core_sk_pktinfo 2.5.8 Enable/Disable Receival of Packet Information - -If enabled, each received netlink message from the kernel will include an -additional struct nl_pktinfo in the control message. The following function -can be used to enable/disable receival of packet information. - -\code -#include - -int nl_socket_recv_pktinfo(struct nl_sock *sk, int state); -\endcode - -\b Note: Processing of NETLINK_PKTINFO has not been implemented yet. - -\section core_send_recv 3. Sending and Receiving of Messages / Data - -\subsection core_send 3.1 Sending Netlink Messages - -The standard method of sending a netlink message over a netlink socket -is to use the function nl_send_auto(). It will automatically complete -the netlink message by filling the missing bits and pieces in the -netlink message header and will deal with addressing based on the -options and address set in the netlink socket. The message is then passed -on to nl_send(). - -If the default sending semantics implemented by nl_send() do not suit the -application, it may overwrite the sending function nl_send() by -specifying an own implementation using the function nl_cb_overwrite_send(). - -\code - nl_send_auto(sk, msg) - | - |-----> nl_complete_msg(sk, msg) - | - | - | Own send function specified via nl_cb_overwrite_send() - |- - - - - - - - - - - - - - - - - - - - - v v - nl_send(sk, msg) send_func() -\endcode - -\subsubsection core_nl_send 3.1.1 Using nl_send() - -If you do not require any of the automatic message completion functionality -you may use nl_send() directly but beware that any internal calls to -nl_send_auto() by the library to send netlink messages will still use -nl_send(). Therefore if you wish to use any higher level interfaces and the -behaviour of nl_send() is to your dislike then you must overwrite the -nl_send() function via nl_cb_overwrite_send() - -The purpose of nl_send() is to embed the netlink message into a iovec -structure and pass it on to nl_send_iovec(). - -\code - nl_send(sk, msg) - | - v - nl_send_iovec(sk, msg, iov, iovlen) -\endcode - -\subsubsection core_nl_send_iovec 3.1.2 Using nl_send_iovec() - -nl_send_iovec() expects a finalized netlink message and fills out the -struct msghdr used for addressing. It will first check if the struct nl_msg -is addressed to a specific peer (see nlmsg_set_dst()). If not, it will try -to fall back to the peer address specified in the socket (see -nl_socket_set_peer_port(). Otherwise the message will be sent unaddressed -and it is left to the kernel to find the correct peer. - -nl_send_iovec() also adds credentials if present and enabled -(see \ref core_sk_cred). - -The message is then passed on to nl_sendmsg(). - -\code - nl_send_iovec(sk, msg, iov, iovlen) - | - v - nl_sendmsg(sk, msg, msghdr) -\endcode - -\subsubsection core_nl_sendmsg 3.1.3 Using nl_sendmsg() - -nl_sendmsg() expects a finalized netlink message and an optional struct -msghdr containing the peer address. It will copy the local address as -defined in the socket (see nl_socket_set_local_port()) into the netlink -message header. - -At this point, construction of the message finished and it is ready to -be sent. - -\code - nl_sendmsg(sk, msg, msghdr) - |- - - - - - - - - - - - - - - - - - - - v - | NL_CB_MSG_OUT() - |<- - - - - - - - - - - - - - - - - - - -+ - v - sendmsg() -\endcode - -Before sending the application has one last chance to modify the message. -It is passed to the NL_CB_MSG_OUT callback function which may inspect or -modify the message and return an error code. If this error code is NL_OK -the message is sent using sendmsg() resulting in the number of bytes -written being returned. Otherwise the message sending process is aborted -and the error code specified by the callback function is returned. See -\ref core_sk_cb for more information on how to set callbacks. - -\subsubsection core_send_raw 3.1.4 Sending Raw Data with nl_sendto() - -If you wish to send raw data over a netlink socket, the following -function will pass on any buffer provided to it directly to sendto(): - -\code -#include - -int nl_sendto(struct nl_sock *sk, void *buf, size_t size); -\endcode - -\subsubsection core_send_simple 3.1.5 Sending of Simple Messages - -A special interface exists for sending of trivial messages. The function -expects the netlink message type, optional netlink message flags, and an -optional data buffer and data length. -\code -#include - -int nl_send_simple(struct nl_sock *sk, int type, int flags, - void *buf, size_t size); -\endcode - -The function will construct a netlink message header based on the message -type and flags provided and append the data buffer as message payload. The -newly constructed message is sent with nl_send_auto(). - -The following example will send a netlink request message causing the -kernel to dump a list of all network links to userspace: -\include nl_send_simple.c - -\subsection core_recv 3.2 Receiving Netlink Messages - -The easiest method to receive netlink messages is to call nl_recvmsgs_default(). -It will receive messages based on the semantics defined in the socket. The -application may customize these in detail although the default behaviour will -probably suit most applications. - -nl_recvmsgs_default() will also be called internally by the library whenever -it needs to receive and parse a netlink message. - -The function will fetch the callback configuration stored in the socket and -call nl_recvmsgs(): - -\code - nl_recvmsgs_default(sk) - | - | cb = nl_socket_get_cb(sk) - v - nl_recvmsgs(sk, cb) -\endcode - -\subsubsection core_nl_recvmsgs 3.2.1 Using nl_recvmsgs() - -nl_recvmsgs() implements the actual receiving loop, it blocks until a -netlink message has been received unless the socket has been put into -non-blocking mode. - -See \ref core_recvmsgs for more information on the behaviour of -nl_recvmsgs(). - -For the unlikely scenario that certain required receive characteristics -can not be achieved by fine tuning the internal recvmsgs function using -the callback configuration (see \ref core_sk_cb) the application may -provide a complete own implementation of it and overwrite all calls to -nl_recvmsgs() with the function nl_cb_overwrite_recvmsgs(). - -\code - nl_recvmsgs(sk, cb) - | - | Own recvmsgs function specified via nl_cb_overwrite_recvmsgs() - |- - - - - - - - - - - - - - - - - - - - - v v - internal_recvmsgs() my_recvmsgs() -\endcode - -\subsubsection core_recvmsgs 3.2.2 Receive Characteristics - -If the application does not provide its own recvmsgs() implementation -with the function nl_cb_overwrite_recvmsgs() the following characteristics -apply while receiving data from a netlink socket: - -\code - internal_recvmsgs() - | -+-------------->| Own recv function specified with nl_cb_overwrite_recv() -| |- - - - - - - - - - - - - - - - -| v v -| nl_recv() my_recv() -| |<- - - - - - - - - - - - - - -+ -| |<-------------+ -| v | More data to parse? (nlmsg_next()) -| Parse Message | -| |--------------+ -| v -+------- NLM_F_MULTI set? - | - v - (SUCCESS) -\endcode - -The function nl_recv() is invoked first to receive data from the netlink -socket. This function may be overwritten by the application by an own -implementation using the function nl_cb_overwrite_recv(). This may be -useful if the netlink byte stream is in fact not received from a socket -directly but is read from a file or another source. - -If data has been read, it will be attemped to parse the data -(see \ref core_recv_parse). This will be done repeately until the parser -returns NL_STOP, an error was returned or all data has been parsed. - -In case the last message parsed successfully was a multipart message -(see \ref core_multipart) and the parser did not quit due to either an -error or NL_STOP nl_recv() respectively the applications own implementation -will be called again and the parser starts all over. - -See \ref core_recv_parse for information on how to extract valid netlink -messages from the parser and on how to control the behaviour of it. - -\subsubsection core_recv_parse 3.2.3 Parsing Characteristics - -The internal parser is invoked for each netlink message received from a -netlink socket. It is typically fed by nl_recv() (see \ref core_recvmsgs). - -The parser will first ensure that the length of the data stream provided -is sufficient to contain a netlink message header and that the message -length as specified in the message header does not exceed it. - -If this criteria is met, a new struct nl_msg is allocated and the message -is passed on to the the callback function NL_CB_MSG_IN if one is set. Like -any other callback function, it may return NL_SKIP to skip the current -message but continue parsing the next message or NL_STOP to stop parsing -completely. - -The next step is to check the sequence number of the message against the -currently expected sequence number. The application may provide its own -sequence number checking algorithm by setting the callback function -NL_CB_SEQ_CHECK to its own implementation. In fact, calling -nl_socket_disable_seq_check() to disable sequence number checking will -do nothing more than set the NL_CB_SEQ_CHECK hook to a function which -always returns NL_OK. - -Another callback hook NL_CB_SEND_ACK exists which is called if the -message has the NLM_F_ACK flag set. Although I am not aware of any -userspace netlink socket doing this, the application may want to send -an ACK message back to the sender (see \ref core_ack). - -\code - parse() - | - v - nlmsg_ok() --> Ignore - | - |- - - - - - - - - - - - - - - v - | NL_CB_MSG_IN() - |<- - - - - - - - - - - - - - -+ - | - |- - - - - - - - - - - - - - - v - Sequence Check NL_CB_SEQ_CHECK() - |<- - - - - - - - - - - - - - -+ - | - | Message has NLM_F_ACK set - |- - - - - - - - - - - - - - - v - | NL_CB_SEND_ACK() - |<- - - - - - - - - - - - - - -+ - | - Handle Message Type -\endcode - -\subsection core_auto_ack 3.3 Auto-ACK Mode - -TODO - -\section core_msg 4. Netlink Message Parsing & Construction - -\subsection core_msg_format 4.1 Message Format - -See \ref proto_fund for an introduction to the netlink protocol and its -message format. - -\subsubsection core_msg_fmt_align 4.1.1 Alignment - -Most netlink protocols enforce a strict alignment policy for all boundries. -The alignment value is defined by NLMSG_ALIGNTO and is fixed to 4 bytes. -Therefore all netlink message headers, begin of payload sections, protocol -specific headers, and attribute sections must start at an offset which is -a multiple of NLMSG_ALIGNTO. - -\code -#include - -int nlmsg_size(int payloadlen); -int nlmsg_total_size(int payloadlen); -\endcode - -The library provides a set of function to handle alignment requirements -automatically. The function nlmsg_total_size() returns the total size -of a netlink message including the padding to ensure the next message -header is aligned correctly. - -\code - <----------- nlmsg_total_size(len) ------------> - <----------- nlmsg_size(len) ------------> - +-------------------+- - -+- - - - - - - - +- - -+-------------------+- - - - | struct nlmsghdr | Pad | Payload | Pad | struct nlsmghdr | - +-------------------+- - -+- - - - - - - - +- - -+-------------------+- - - - <---- NLMSG_HDRLEN -----> <- NLMSG_ALIGN(len) -> <---- NLMSG_HDRLEN --- -\endcode - -If you need to know if padding needs to be added at the end of a message, -nlmsg_padlen() returns the number of padding bytes that need to be added -for a specific payload length. - -\code -#include -int nlmsg_padlen(int payloadlen); -\endcode - -\subsection core_msg_parse 4.2 Parsing a Message - -The library offers two different methods of parsing netlink messages. -It offers a low level interface for applications which want to do all -the parsing manually. This method is described below. Alternatively -the library also offers an interface to implement a parser as part of -a cache operations set which is especially useful when your protocol -deals with objects of any sort such as network links, routes, etc. -This high level interface is described in \ref core_cache. - -\subsubsection core_msg_split 4.2.1 Splitting a byte stream into separate messages - -What you receive from a netlink socket is typically a stream of -messages. You will be given a buffer and its length, the buffer may -contain any number of netlink messages. - -The first message header starts at the beginning of message stream. Any -subsequent message headers are access by calling nlmsg_next() on the -previous header. - -\code -#include - -struct nlmsghdr *nlmsg_next(struct nlmsghdr *hdr, int *remaining); -\endcode - -The function nlmsg_next() will automatically substract the size of -the previous message from the remaining number of bytes. - -Please note, there is no indication in the previous message whether -another message follows or not. You must assume that more messages -follow until all bytes of the message stream have been processed. - -To simplify this, the function nlmsg_ok() exists which returns true if -another message fits into the remaining number of bytes in the message -stream. nlmsg_valid_hdr() is similar, it checks whether a specific -netlink message contains at least a minimum of payload. - -\code -#include - -int nlmsg_valid_hdr(const struct nlmsghdr *hdr, int payloadlen); -int nlmsg_ok(const struct nlmsghdr *hdr, int remaining); -\endcode - -A typical use of these functions looks like this: - -\include my_parse.c - -\b Note: nlmsg_ok() only returns true if the \b complete message including - the message payload fits into the remaining buffer length. It will - return false if only a part of it fits. - -The above can also be written using the iterator nlmsg_for_each(): - -\include nlmsg_for_each.c - - -\subsubsection core_msg_payload 4.2.2 Message Payload - -The message payload is appended to the message header and is guranteed -to start at a multiple of NLMSG_ALIGNTO. Padding at the end of the -message header is added if necessary to ensure this. The function -nlmsg_data() will calculate the necessary offset based on the message -and returns a pointer to the start of the message payload. - -\code -#include - -void *nlmsg_data(const struct nlmsghdr *nlh); -void *nlmsg_tail(const struct nlmsghdr *nlh); -int nlmsg_datalen(const struct nlmsghdr *nlh); -\endcode - -The length of the message payload is returned by nlmsg_datalen(). - -\code - <--- nlmsg_datalen(nlh) ---> - +-------------------+- - -+----------------------------+- - -+ - | struct nlmsghdr | Pad | Payload | Pad | - +-------------------+- - -+----------------------------+- - -+ -nlmsg_data(nlh) ---------------^ ^ -nlmsg_tail(nlh) --------------------------------------------------^ -\endcode - -The payload may consist of arbitary data but may have strict alignment -and formatting rules depening on the actual netlink protocol. - -\subsubsection core_msg_parse_attr 4.2.3 Message Attributes - -Most netlink protocols use netlink attributes. It not only makes the -protocol self documenting but also gives flexibility in expanding -the protocol at a later point. New attributes can be added at any time -and older attributes can be obsoleted by newer ones without breaking -binary compatibility of the protocol. - -\code - <---------------------- payload -------------------------> - <----- hdrlen ----> <- nlmsg_attrlen(nlh, hdrlen) -> - +-------------------+- - -+----- ------------+- - -+--------------------------------+- - -+ - | struct nlmsghdr | Pad | Protocol Header | Pad | Attributes | Pad | - +-------------------+- - -+-------------------+- - -+--------------------------------+- - -+ -nlmsg_attrdata(nlh, hdrlen) -----------------------------^ -\endcode - -The function nlmsg_attrdata() returns a pointer to the begin of the -attributes section. The length of the attributes section is returned -by the function nlmsg_attrlen(). - -\code -#include - -struct nlattr *nlmsg_attrdata(const struct nlmsghdr *hdr, int hdrlen); -int nlmsg_attrlen(const struct nlmsghdr *hdr, int hdrlen); -\endcode - -See \ref core_attr for more information on how to use netlink attributes. - -\subsubsection core_nlmsg_parse 4.2.4 Parsing a Message the Easy Way - -The function nlmsg_parse() validate a complete netlink message in -one step. If \p hdrlen > 0 it will first call nlmsg_valid_hdr() to -check if the protocol header fits into the message. If there is -more payload to parse, it will assume it to be attributes and parse -the payload accordingly. The function behaves exactly like nla_parse() -when parsing attributes, see \ref core_attr_nla_parse. - -\code -int nlmsg_parse(struct nlmsghdr *hdr, int hdrlen, struct nlattr **attrs, - int maxtype, struct nla_policy *policy); -\endcode - -The function nlmsg_validate() is based on nla_validate() and behaves -exactly the same as nlmsg_parse() except that it only validates and will -not fill a array with pointers to each attribute. - -\code -int nlmsg_validate(struct nlmsghdr *hdr, int hdrlen, intmaxtype, - struct nla_policy *policy); -\endcode - -See \ref core_attr_nla_parse for an example and more information on -attribute parsing. - -\subsection core_msg_constr 4.3 Construction of a Message - -See \ref core_msg_format for information on the netlink message format -and alignment requirements. - -Message construction is based on struct nl_msg which uses an internal -buffer to store the actual netlink message. struct nl_msg \b does \b not -point to the netlink message header. Use nlmsg_hdr() to retrieve a -pointer to the netlink message header. - -At allocation time, a maximum message size is specified. It defaults to -a page (PAGE_SIZE). The application constructing the message will reserve -space out of this maximum message size repeatedly for each header or -attribute added. This allows construction of messages across various -layers of code where lower layers do not need to know about the space -requirements of upper layers. - -Why is setting the maximum message size necessary? This question -is often raised in combination with the proposed solution of reallocating -the message payload buffer on the fly using realloc(). While it is -possible to reallocate the buffer during construction using nlmsg_expand() -it will make all pointers into the message buffer become stale. This -breaks usage of nlmsg_hdr(), nla_nest_start(), and nla_nest_end() and is -therefore not acceptable as default behaviour. - -\subsubsection core_msg_alloc 4.3.1 Allocating struct nl_msg - -The first step in constructing a new netlink message it to allocate a -\c struct \c nl_msg to hold the message header and payload. Several -functions exist to simplify various tasks. - -\code -#include - -struct nl_msg *nlmsg_alloc(void); -void nlmsg_free(struct nl_msg *msg); -\endcode - -The function nlmsg_alloc() is the default message allocation function. -It allocates a new message using the default maximum message size which -equals to one page (PAGE_SIZE). The application can change the default -size for messages by calling nlmsg_set_default_size(): - -\code -void nlmsg_set_default_size(size_t); -\endcode - -\b Note: Calling nlmsg_set_default_size() does not change the maximum - message size of already allocated messages. - -\code -struct nl_msg *nlmsg_alloc_size(size_t max); -\endcode - -Instead of changing the default message size, the function -nlmsg_alloc_size() can be used to allocate a message with a individual -maximum message size. - - -If the netlink message header is already known at allocation time, the -application may sue nlmsg_inherit(). It will allocate a message using -the default maximum message size and copy the header into the message. -Calling nlmsg_inherit with \p set to NULL is equivalent to calling -nlmsg_alloc(). - -\code -struct nl_msg *nlmsg_inherit(struct nlmsghdr *hdr); -\endcode - -Alternatively nlmsg_alloc_simple() takes a netlink message type and -netlink message flags. It is equivalent to nlmsg_inherit() except that it -takes the two common header fields as arguments instead of a complete -header. - -\code -#include - -struct nl_msg *nlmsg_alloc_simple(int nlmsg_type, int flags); -\endcode - -\subsubsection core_msg_nlmsg_put 4.3.2 Appending the netlink message header - -After allocating struct nl_msg, the netlink message header needs to be -added unless one of the function nlmsg_alloc_simple() or nlmsg_inherit() -have been used for allocation in which case this step will replace the -netlink message header already in place. - -\code -#include - -struct nlmsghdr *nlmsg_put(struct nl_msg *msg, uint32_t port, uint32_t seqnr, - int nlmsg_type, int payload, int nlmsg_flags); -\endcode - -The function nlmsg_put() will build a netlink message header out of -\p nlmsg_type, \p nlmsg_flags, \p seqnr, and \p port and copy it into -the netlink message. \p seqnr can be set to \p NL_AUTO_SEQ to indiciate -that the next possible sequence number should be used automatically. To -use this feature, the message must be sent using the function -nl_send_auto(). Like \p port, the argument \p seqnr can be set to -\c NL_AUTO_PORT indicating that the local port assigned to the socket -should be used as source port. This is generally a good idea unless you -are replying to a request. See \ref proto_fund for more information on -how to fill the header. - -The argument \p payload can be used by the application to reserve room -for additional data after the header. A value of > 0 is equivalent to -calling nlmsg_reserve(msg, payload, NLMSG_ALIGNTO). See -\ref core_msg_reserve for more information on reserving room for data. - -\b Example: -\include nlmsg_put.c - -\subsubsection core_msg_reserve 4.3.3 Reserving room at the end of the message - -Most functions described later on will automatically take care of -reserving room for the data that is added to the end of the netlink -message. In some situations it may be requried for the application -to reserve room directly though. - -\code -#include - -void *nlmsg_reserve(struct nl_msg *msg, size_t len, int pad); -\endcode - -The function nlmsg_reserve() reserves \p len bytes at the end of the -netlink message and returns a pointer to the start of the reserved area. -The \p pad argument can be used to request \p len to be aligned to any -number of bytes prior to reservation. - -The following example requests to reserve a 17 bytes area at the end of -message aligned to 4 bytes. Therefore a total of 20 bytes will be -reserved. - -\code -#include - -void *buf = nlmsg_reserve(msg, 17, 4); -\endcode - -\b Note: nlmsg_reserve() will \b not align the start of the buffer. Any - alignment requirements must be provided by the owner of the - previous message section. - -\subsubsection core_msg_append 4.3.4 Appending data at the end of the message - -The function nlmsg_append() appends \p len bytes at the end of the message, -padding it if requested and necessary. - -\code -#include - -int nlmsg_append(struct nl_msg *msg, void *data, size_t len, int pad); -\endcode - -It is equivalent to calling nlmsg_reserve() and memcpy()ing the data into -the freshly reserved data section. - -\b Note: nlmsg_append() will \b not align the start of the data. Any - alignment requirements must be provided by the owner of the - previous message section. - -\subsubsection core_msg_put_attr 4.3.5 Adding attribtues to a message - -Construction of attributes and addition of attribtues to the message is -covereted in section \ref core_attr. - -\section core_attr 5. Netlink Attributes - -Any form of payload should be encoded as netlink attributes whenever -possible. Use of attributes allows to extend any netlink protocol in -the future without breaking binary compatibility. F.e. Suppose your -device may currently be using 32 bit counters for statistics but years -later the device switches to maintaining 64 bit counters to account -for faster network hardware. If your protocol is using attributes the -move to 64 bit counters is trivial and only involves in sending an -additional attribute containing the 64 bit variants while still -providing the old legacy 32 bit counters. If your protocol is not using -attributes you will not be able to switch data types without breaking -all existing users of the protocol. - -The concept of nested attributes also allows for subsystems of your -protocol to implement and maintain their own attribute schemas. Suppose -a new generation of network device is introduced which requires a -completely new set of configuration settings which was unthinkable when -the netlink protocol was initially designed. Using attributes the new -generation of devices may define a new attribute and fill it with its -own new structure of attributes which extend or even obsolete the old -attributes. - -Therefore, \e always use attributes even if you are almost certain that -the message format will never ever change in the future. - -\subsection core_attr_format 5.1 Attribute Format - -Netlink attributes allow for any number of data chunks of arbitary -length to be attached to a netlink message. See \ref core_msg_parse_attr -for more information on where attributes are stored in the message. - -The format of the attributes data returned by nlmsg_attrdata() is as -follows: - -\code - <----------- nla_total_size(payload) -----------> - <---------- nla_size(payload) -----------> - +-----------------+- - -+- - - - - - - - - +- - -+-----------------+- - - - | struct nlattr | Pad | Payload | Pad | struct nlattr | - +-----------------+- - -+- - - - - - - - - +- - -+-----------------+- - - - <---- NLA_HDRLEN -----> <--- NLA_ALIGN(len) ---> <---- NLA_HDRLEN --- -\endcode - -Every attribute must start at an offset which is a multiple of -\c NLA_ALIGNTO (4 bytes). If you need to know whether an attribute needs -to be padded at the end, the function nla_padlen() returns the number -of padding bytes that will or need to be added. - -\code -0 1 2 3 -0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 -+-------------------------------------------------------------+ -| Length | Type | -+------------------------------+------------------------------+ -| Attribute Payload | -. . -. . -+-------------------------------------------------------------+ -\endcode - -Every attribute is encoded with a type and length field, both 16 bits, -stored in the attribute header (struct nlattr) preceding the attribute -payload. The length of an attribute is used to calculate the offset to -the next attribute. - -\subsection core_attr_parse 5.2 Parsing Attributes - -\subsubsection core_attr_parse_split 5.2.1 Splitting an Attributes Stream into Attributes - -Although most applications will use one of the functions from the -nlmsg_parse() family (See \ref core_attr_nla_parse) an interface -exists to split the attributes stream manually. - -As described in \ref core_attr_format the attributes section contains a -infinite sequence or stream of attributes. The pointer returned by -nlmsg_attrdata() (See \ref core_msg_parse_attr) points to the first -attribute header. Any subsequent attribute is accessed with the function -nla_next() based on the previous header. - -\code -#include - -struct nlattr *nla_next(const struct nlattr *attr, int *remaining); -\endcode - -The semantics are equivalent to nlmsg_next() and thus nla_next() will also -subtract the size of the previous attribute from the remaining number of -bytes in the attributes stream. - -Like messages, attributes do not contain an indicator whether another -attribute follows or not. The only indication is the number of bytes left -in the attribute stream. The function nla_ok() exists to determine whether -another attribute fits into the remaining number of bytes or not. - -\code -#include - -int nla_ok(const struct nlattr *attr, int remaining); -\endcode - -A typical use of nla_ok() and nla_next() looks like this: - -\include nla_ok.c - -\b Note: nla_ok() only returns true if the \b complete attributes - including the attribute payload fits into the remaining number - of bytes. - -\subsubsection core_attr_payload 5.2.2 Accessing Attribute Header and Payload - -Once the individual attributes have been sorted out by either splitting -the attributes stream or using another interface the attribute header -and payload can be accessed. - -\code - <- nla_len(hdr) -> - +-----------------+- - -+- - - - - - - - - +- - -+ - | struct nlattr | Pad | Payload | Pad | - +-----------------+- - -+- - - - - - - - - +- - -+ -nla_data(hdr) ---------------^ -\endcode - -The functions nla_len() and nla_type() can be used to access the attribute -header. nla_len() will return the length of the payload not including -eventual padding bytes. nla_type returns the attribute type. - -\code -#include - -int nla_len(const struct nlattr *hdr); -int nla_type(const struct nlattr *hdr); -\endcode - -The function nla_data() will return a pointer to the attribute payload. -Please note that due to NLA_ALIGNTO being 4 bytes it may not be safe to -cast and dereference the pointer for any datatype larger than 32 bit -depending on the architecture the application is run on. - -\code -#include - -void *nla_data(const struct nlattr *hdr); -\endcode - -\b Note: Never rely on the size of a payload being what you expect it to - be. \e Always verify the payload size and make sure that it - matches your expectations. See \ref core_attr_validation. - -\subsubsection core_attr_validation 5.2.3 Attribute Validation - -When receiving netlink attributes, the receiver has certain expections -on how the attributes should look like. These expectations must be -defined to make sure the sending side meets our expecations. For this -purpose, a attribute validation interface exists which must be used -prior to accessing any payload. - -All functions providing attribute validation functionality are based -on struct nla_policy: - -\code -struct nla_policy { - uint16_t type; - uint16_t minlen; - uint16_t maxlen; -}; -\endcode - -The \p type member specifies the datatype of the attribute, e.g. -NLA_U32, NLA_STRING, NLA_FLAG. The default is NLA_UNSPEC. The \p minlen -member defines the minmum payload length of an attribute to be -considered a valid attribute. The value for \p minlen is implicit for -most basic datatypes such as integers or flags. The \p maxlen member -can be used to define a maximum payload length for an attribute to -still be considered valid. - -\b Note: Specyfing a maximum payload length is not recommended when -encoding structures in an attribute as it will prevent any extension of -the structure in the future. Something that is frequently done in -netlink protocols and does not break backwards compatibility. - -One of the functions which use struct nla_policy is nla_validate(). -The function expects an array of struct nla_policy and will access the -array using the attribute type as index. If an attribute type is out -of bounds the attribute is assumed to be valid. This is intentional -behaviour to allow older applications not yet aware of recently -introduced attributes to continue functioning. - -\code -#include - -int nla_validate(struct nlattr *head, int len, int maxtype, - struct nla_policy *policy); -\endcode - -The function nla_validate() returns 0 if all attributes are valid, -otherwise a validation failure specific error code is returned. - -Most applications will rarely use nla_validate() directly but use -nla_parse() instead which takes care of validation in the same way but -also parses the the attributes in the same step. See -\ref core_attr_nla_parse for an example and more information. - -The validation process in detail: --# If attribute type is 0 or exceeds \p maxtype attribute is - considered valid, 0 is returned. --# If payload length is < \p minlen, -NLE_ERANGE is returned. --# If \p maxlen is defined and payload exceeds it, NLE_ERANGE - is returned. --# Datatype specific requirements rules, see \ref core_attr_data_type. --# If all is ok, 0 is returned. - -\subsubsection core_attr_nla_parse 5.2.4 Parsing Attributes the Easy Way - -Most applications will not want to deal with splitting attribute streams -themselves as described in \ref core_attr_parse_split. A much easier -method is to use nla_parse(). - -\code -#include - -int nla_parse(struct nlattr **attrs, int maxtype, struct nlattr *head, - int len, struct nla_policy *policy); -\endcode - -The function nla_parse() will iterate over a stream of attributes, -validate each attribute as described in \ref core_attr_validation. If -the validation of all attributes succeeds, a pointer to each attribute -is stored in the \p attrs array at \c attrs[nla_type(attr)]. - -As an alernative to nla_parse() the function nlmsg_parse() can be used -to parse the message and its attributes in one step. See -\ref core_nlmsg_parse for information on how to use these functions. - -\b Example: - -The following example demonstrates how to parse a netlink message sent -over a netlink protocol which does not use protocol headers. The example -does enforce a attribute policy however, the attribute MY_ATTR_FOO must -be a 32 bit integer, and the attribute MY_ATTR_BAR must be a string with -a maximum length of 16 characters. - -\include nlmsg_parse.c - -\subsubsection core_attr_find 5.2.5 Locating a Single Attribute - -An application only interested in a single attribute can use one of the -functions nla_find() or nlmsg_find_attr(). These function will iterate -over all attributes, search for a matching attribute and return a pointer -to the corresponding attribute header. - -\code -#include - -struct nlattr *nla_find(struct nlattr *head, int len, int attrtype); -\endcode - -\code -#include - -struct nlattr *nlmsg_find_attr(struct nlmsghdr *hdr, int hdrlen, int attrtype); -\endcode - -\b Note: nla_find() and nlmsg_find_attr() will \b not search in nested - attributes recursively, see \ref core_attr_nested. - -\subsubsection core_attr_iterate 5.2.6 Iterating over a Stream of Attributes - -In some situations it does not make sense to assign a unique attribute -type to each attribute in the attribute stream. For example a list may -be transferd using a stream of attributes and even if the attribute type -is incremented for each attribute it may not make sense to use the -nlmsg_parse() or nla_parse() function to fill an array. - -Therefore methods exist to iterate over a stream of attributes: - -\code -#include - -nla_for_each_attr(attr, head, len, remaining) -\endcode - -nla_for_each_attr() is a macro which can be used in front of a code -block: - -\include nla_for_each_attr.c - -\subsection core_attr_constr 5.3 Attribute Construction - -The interface to add attributes to a netlink message is based on the -regular message construction interface. It assumes that the message -header and an eventual protocol header has been added to the message -already. - -\code -struct nlattr *nla_reserve(struct nl_msg *msg, int attrtype, int len); -\endcode - -The function nla_reserve() adds an attribute header at the end of the -message and reserves room for \p len bytes of payload. The function -returns a pointer to the attribute payload section inside the message. -Padding is added at the end of the attribute to ensure the next -attribute is properly aligned. - -\code -int nla_put(struct nl_msg *msg, int attrtype, int attrlen, const void *data); -\endcode - -The function nla_put() is base don nla_reserve() but takes an additional -pointer \p data pointing to a buffer containing the attribute payload. -It will copy the buffer into the message automatically. - -\b Example: - -\include nla_put.c - -See \ref core_attr_data_type for datatype specific attribute construction -functions. - -\subsubsection core_attr_exception 5.3.1 Exception Based Attribute Construction - -Like in the kernel API an exception based construction interface is -provided. The behaviour of the macros is identical to their regular -function counterparts except that in case of an error, the target -\c nla_put_failure is jumped. - -\b Example: - -\include NLA_PUT.c - -See \ref core_attr_data_type for more information on the datatype -specific exception based variants. - -\subsection core_attr_data_type 5.4 Attribute Data Types - -A number of basic data types have been defined to simplify access and -validation of attributes. The datatype is not encoded in the attribute, -therefore bthe sender and receiver are required to use the same -definition on what attribute is of what type. - -Besides simplified access to the payload of such datatypes, the major -advantage is the automatic validation of each attribute based on a -policy. The validation ensures safe access to the payload by checking -for minimal payload size and can also be used to enforce maximum -payload size for some datatypes. - -\subsubsection core_attr_int 5.4.1 Integer Attributes - -The most frequently used datatypes are integers. Integers come in four -different sizes: -- \c NLA_U8 - 8bit integer -- \c NLA_U16 - 16bit integer -- \c NLA_U32 - 32bit integer -- \c NLA_U64 - 64bit integer - -Note that due to the alignment requirements of attributes the integer -attribtue \c NLA_u8 and \c NLA_U16 will not result in space savings in -the netlink message. Their use is intended to limit the range of values. - -Parsing Integer Attributes - -\code -#include - -uint8_t nla_get_u8(struct nlattr *hdr); -uint16_t nla_get_u16(struct nlattr *hdr); -uint32_t nla_get_u32(struct nlattr *hdr); -uint64_t nla_get_u64(struct nlattr *hdr); -\endcode - -Example: - -\code -if (attrs[MY_ATTR_FOO]) - uint32_t val = nla_get_u32(attrs[MY_ATTR_FOO]); -\endcode - -Constructing Integer Attributes - -\code -#include - -int nla_put_u8(struct nl_msg *msg, int attrtype, uint8_t value); -int nla_put_u16(struct nl_msg *msg, int attrtype, uint16_t value); -int nla_put_u32(struct nl_msg *msg, int attrtype, uint32_t value); -int nla_put_u64(struct nl_msg *msg, int attrtype, uint64_t value); -\endcode - -Exception based: - -\code -NLA_PUT_U8(msg, attrtype, value) -NLA_PUT_U16(msg, attrtype, value) -NLA_PUT_U32(msg, attrtype, value) -NLA_PUT_U64(msg, attrtype, value) -\endcode - -Validation - -Use \p NLA_U8, \p NLA_U16, \p NLA_U32, or \p NLA_U64 to define the type -of integer when filling out a struct nla_policy array. It will -automatically enforce the correct minimum payload length policy. - -Validation does not differ between signed and unsigned integers, only -the size matters. If the appliaction wishes to enforce particular value -ranges it must do so itself. - -\code -static struct nla_policy my_policy[ATTR_MAX+1] = { - [ATTR_FOO] = { .type = NLA_U32 }, - [ATTR_BAR] = { .type = NLA_U8 }, -}; -\endcode - -The above is equivalent to: -\code -static struct nla_policy my_policy[ATTR_MAX+1] = { - [ATTR_FOO] = { .minlen = sizeof(uint32_t) }, - [ATTR_BAR] = { .minlen = sizeof(uint8_t) }, -}; -\endcode - -\subsubsection core_attr_string 5.4.2 String Attributes - -The string datatype represents a NUL termianted character string of -variable length. It is not intended for binary data streams. - -The payload of string attributes can be accessed with the function -nla_get_string(). nla_strdup() calls strdup() on the payload and returns -the newly allocated string. - -\code -#include - -char *nla_get_string(struct nlattr *hdr); -char *nla_strdup(struct nlattr *hdr); -\endcode - -String attributes are constructed with the function nla_put_string() -respectively NLA_PUT_STRING(). The length of the payload will be strlen()+1, -the trailing NUL byte is included. - -\code -int nla_put_string(struct nl_msg *msg, int attrtype, const char *data); - -NLA_PUT_STRING(msg, attrtype, data) -\endcode - -For validation purposes the type \p NLA_STRING can be used in -struct nla_policy definitions. It implies a minimum payload length of 1 -byte and checks for a trailing NUL byte. Optionally the \p maxlen member -defines the maximum length of a character string (including the trailing -NUL byte). - -\code -static struct nla_policy my_policy[] = { - [ATTR_FOO] = { .type = NLA_STRING, - .maxlen = IFNAMSIZ }, -}; -\endcode - -\subsubsection core_attr_flag 5.4.3 Flag Attributes - -The flag attribute represents a boolean datatype. The presence of the -attribute implies a value of \p true, the absence of the attribute -implies the value \p false. Therefore the payload length of flag -attributes is always 0. - -\code -int nla_get_flag(struct nlattr *hdr); -int nla_put_flag(struct nl_msg *msg, int attrtype); -\endcode - -The type \p NLA_FLAG is used for validation purposes. It implies a -\p maxlen value of 0 and thus enforces a maximum payload length of 0. - -\b Example: - -\include nla_flag.c - -\subsubsection core_attr_nested 5.4.4 Nested Attributes - -As described in \ref core_attr, attributes can be nested allowing for -complex tree structures of attributes. It is commonly used to delegate -the responsibility of a subsection of the message to a subsystem. -Nested attributes are also commonly used for transmitting list of -objects. - -When nesting attributes, the nested attributes are included as payload -of a container attribute. - -IMPORTANT NOTICE: When validating the attributes using -nlmsg_validate(), nlmsg_parse(), nla_validate(), or nla_parse() only -the attributes on the first level are being validated. None of these -functions will validate attributes recursively. Therefore you must -explicitely call nla_validate() or use nla_parse_nested() for each -level of nested attributes. - -The type \p NLA_NESTED should be used when defining nested attributes -in a struct nla_policy definition. It will not enforce any minimum -payload length unless \p minlen is specified explicitely. This is -because some netlink protocols implicitely allow empty container -attributes. - -\code -static struct nla_policy my_policy[] = { - [ATTR_OPTS] = { .type = NLA_NESTED }, -}; -\endcode - -Parsing of Nested Attributes - -The function nla_parse_nested() is used to parse nested attributes. -Its behaviour is identical to nla_parse() except that it takes a -struct nlattr as argument and will use the payload as stream of -attributes. - -\include nla_parse_nested.c - -Construction of Nested Attributes - -Attributes are nested by surrounding them with calls to nla_nest_start() -and nla_nest_end(). nla_nest_start() will add a attribute header to -the message but no actual payload. All data added to the message from -this point on will be part of the container attribute until nla_nest_end() -is called which "closes" the attribute, correcting its payload length to -include all data length. - -\include nla_nest_start.c - -\subsubsection core_attr_unspec 5.4.5 Unspecified Attribute - -This is the default attribute type and used when none of the basic -datatypes is suitable. It represents data of arbitary type and length. - -See \ref core_abstract_addr_alloc for a more information on a special -interface allowing the allocation of abstract address object based on -netlink attributes which carry some form of network address. - -See \ref core_abstract_data_alloc for more information on how to -allocate abstract data objects based on netlink attributes. - -Use the function nla_get() and nla_put() to access the payload and -construct attributes. See \ref core_attr_constr for an example. - -\subsection core_attr_examples 5.5 Examples - -\subsubsection core_attr_example_constr 5.5.1 Constructing a Netlink Message with Attributes - -\include msg_constr_attr.c - -\subsubsection core_attr_example_parse 5.5.2 Parsing a Netlink Message with Attributes - -\include msg_parse_attr.c - -\section core_cb 6. Callback Configurations - -Callback hooks and overwriting capabilities are provided in various places -inside library to control the behaviour of several functions. All the -callback and overwrite functions are packed together in struct nl_cb which -is attached to a netlink socket or passed on to functions directly. - -\subsection core_cb_hooks 6.1 Callback Hooks - -Callback hooks are spread across the library to provide entry points for -message processing and to take action upon certain events. - -Callback functions may return the following return codes: -\code -Return Code | Description -------------------------------------------------------------------------- -NL_OK | Proceed. -NL_SKIP | Skip message currently being processed and continue - | parsing the receive buffer. -NL_STOP | Stop parsing and discard all remaining data in the - | receive buffer. -\endcode - -\subsubsection core_cb_default 6.1.1 Default Callback Implementations - -The library provides three sets of default callback implementations: -- \b NL_CB_DEFAULT This is the default set. It implets the default behaviour. - See the table below for more information on the return codes of each - function. -- \b NL_CB_VERBOSE This set is based on the default set but will cause an - error message to be printed to stderr for error messages, invalid - messages, message overruns and unhandled valid messages. The \p arg - pointer in nl_cb_set() and nl_cb_err() can be used to provide a FILE * - which overwrites stderr. -- \b NL_CB_DEBUG This set is intended for debugging purposes. It is based - on the verbose set but will decode and dump each message sent or - received to the console. - -\subsubsection core_cb_msg_proc 6.1.2 Message Processing Callbacks - -nl_sendmsg() callback hooks: -\code -Callback ID | Description | Default Return Value ------------------------------------------------------------------------------ -NL_CB_MSG_OUT | Each message sent | NL_OK -\endcode - -Any function called by NL_CB_MSG_OUT may return a negative error code to -prevent the message from being sent and the error code being returned. - -nl_recvmsgs() callback hooks (ordered by priority): -\code -Callback ID | Description | Default Return Value ------------------------------------------------------------------------------ -NL_CB_MSG_IN | Each message received | NL_OK -NL_CB_SEQ_CHECK | May overwrite sequence check algo | NL_OK -NL_CB_INVALID | Invalid messages | NL_STOP -NL_CB_SEND_ACK | Messages with NLM_F_ACK flag set | NL_OK -NL_CB_FINISH | Messages of type NLMSG_DONE | NL_STOP -NL_CB_SKIPPED | Messages of type NLMSG_NOOP | NL_SKIP -NL_CB_OVERRUN | Messages of type NLMSG_OVERRUN | NL_STOP -NL_CB_ACK | ACK Messages | NL_STOP -NL_CB_VALID | Each valid message | NL_OK -\endcode - -Any of these functions may return NL_OK, NL_SKIP, or NL_STOP. - -Message processing callback functions are set with nl_cb_set(): -\code -#include - -int nl_cb_set(struct nl_cb *cb, enum nl_cb_type type, enum nl_cb_kind kind, - nl_recvmsg_msg_cb_t func, void *cb); - -typedef int (*nl_recvmsg_msg_cb_t)(struct nl_msg *msg, void *arg); -\endcode - -\subsubsection core_cb_errmsg 6.1.4 Callback for Error Messages - -A special function prototype is used for the error message callback hook: - -\code -#include - -int nl_cb_err(struct nl_cb *cb, enum nl_cb_kind kind, nl_recvmsg_err_cb_t func, void * arg); - -typedef int(* nl_recvmsg_err_cb_t)(struct sockaddr_nl *nla, struct nlmsgerr *nlerr, void *arg); -\endcode - -\subsubsection core_cb_example 6.1.4 Example: Setting up a callback set -\include nl_cb_set.c - -\subsection core_cb_overwrite 6.2 Overwriting of Internal Functions - -When the library needs to send or receive netlink messages in high level -interfaces it does so by calling its own low level API. In the case the -default characteristics are not sufficient for the application, it may -overwrite several internal function calls with own implementations. - -\subsubsection core_cb_ow_recvmsgs 6.2.1 Overwriting recvmsgs() - -See \ref core_recv for more information on how and when recvmsgs() is -called internally. - -\code -#include - -void nl_cb_overwrite_recvmsgs(struct nl_cb *cb, - int (*func)(struct nl_sock *sk, struct nl_cb *cb)); -\endcode - -The following criteras must be met if a recvmsgs() implementation is -supposed to work with high level interfaces: -- MUST respect the callback configuration in \c cb, therefore: - - MUST call NL_CB_VALID for all valid messages, passing on - - MUST call NL_CB_ACK for all ACK messages - - MUST correctly handle multipart messages, calling NL_CB_VALID for - each message until a NLMSG_DONE message is received. -- MUST report error code if a NLMSG_ERROR or NLMSG_OVERRUN mesasge is - received. - -\subsubsection core_cb_ow_recv 6.2.2 Overwriting nl_recv() - -Often it is sufficient to overwrite nl_recv() which is responsible from -receiving the actual data from the socket instead of replacing the complete -recvmsgs() logic. - -See \ref core_recvmsgs for more information on how and when nl_recv() -is called internally. - -\code -#include - -void nl_cb_overwrite_recv(struct nl_cb *cb, - int (*func)(struct nl_sock * sk, - struct sockaddr_nl *addr, - unsigned char **buf, - struct ucred **cred)); -\endcode - -The following criteras must be met for an own nl_recv() implementation: -- MUST return the number of bytes read or a negative error code if an - error occured. The function may also return 0 to indicate that no - data has been read. -- MUST set \c *buf to a buffer containing the data read. It must be safe - for the caller to access the number of bytes read returned as return code. -- MAY fill out \c *addr with the netlink address of the peer the data - has been received from. -- MAY set \c *cred to a newly allocated struct ucred containg credentials. - -\subsubsection core_cb_ow_send 6.2.3 Overwriting nl_send() - -See \ref core_send for more information on how and when nl_send() is -called internally. - -\code -#include - -void nl_cb_overwrite_send(struct nl_cb *cb, int (*func)(struct nl_sock *sk, - struct nl_msg *msg)); -\endcode - -Own implementations must send the netlink message and return 0 on success -or a negative error code. - -\section core_cache 7. Cache System - -\subsection cache_alloc 7.1 Allocation of Caches - -Almost all subsystem provide a function to allocate a new cache -of some form. The function usually looks like this: -\code -struct nl_cache *_alloc_cache(struct nl_sock *sk); -\endcode - -These functions allocate a new cache for the own object type, -initializes it properly and updates it to represent the current -state of their master, e.g. a link cache would include all -links currently configured in the kernel. - -Some of the allocation functions may take additional arguments -to further specify what will be part of the cache. - -All such functions return a newly allocated cache or NULL -in case of an error. - -\section core_abstract_types 8. Abstract Data Types - -A few high level abstract data types which are used by a majority netlink -protocols are implemented in the core library. More may be added in the -future if the need arises. - -\subsection core_abstract_addr 8.1 Abstract Address - -Most netlink protocols deal with networking related topics and thus -dealing with network addresses is a common task. - -Currently the following address families are supported: -- AF_INET -- AF_INET6 -- AF_LLC -- AF_DECnet -- AF_UNSPEC - -\subsubsection core_abstract_addr_alloc 8.1.1 Address Allocation - -The function nl_addr_alloc() allocates a new empty address. The -\p maxsize argument defines the maximum length of an address in bytes. -The size of an address is address family specific. If the address -family and address data are known at allocation time the function -nl_addr_build() can be used alternatively. You may also clone -an address by calling nl_addr_clone() - -\code -#include - -struct nl_addr *nl_addr_alloc(size_t maxsize); -struct nl_addr *nl_addr_clone(struct nl_addr *addr); -struct nl_addr *nl_addr_build(int family, void *addr, size_t size); -\endcode - -If the address is transported in a netlink attribute, the function -nl_addr_alloc_attr() allocates a new address based on the payload -of the attribute provided. The \p family argument is used to specify -the address family of the address, set to \p AF_UNSPEC if unknown. - -\code -#include - -struct nl_addr *nl_addr_alloc_attr(struct nlattr *attr, int family); -\endcode - -If the address is provided by a user, it is usually stored in a human -readable format. The function nl_addr_parse() parses a character -string representing an address and allocates a new address based on -it. - -\code -#include - -int nl_addr_parse(const char *addr, int hint, struct nl_addr **result); -\endcode - -If parsing succeeds the function returns 0 and the allocated address -is stored in \p *result. - -\b Note: Make sure to return the reference to an address using - nl_addr_put() after usage to allow memory being freed. - -\subsubsection core_abstract_addr_ref 8.1.2 Address References - -Abstract addresses use reference counting to account for all users of -a particular address. After the last user has returned the reference -the address is freed. - -If you pass on a address object to another function and you are not -sure how long it will be used, make sure to call nl_addr_get() to -acquire an additional reference and have that function or code path -call nl_addr_put() as soon as it has finished using the address. - -\code -#include - -struct nl_addr *nl_addr_get(struct nl_addr *addr); -void nl_addr_put(struct nl_addr *addr); -int nl_addr_shared(struct nl_addr *addr); -\endcode - -You may call nl_addr_shared() at any time to check if you are the only -user of an address. - -\subsubsection core_abstract_addr_attr 8.1.3 Address Attributes - -The address is usually set at allocation time. If it was unknown at that -time it can be specified later by calling nl_addr_set_family() and is -accessed with the function nl_addr_get_family(). - -\code -#include - -void nl_addr_set_family(struct nl_addr *addr, int family); -int nl_addr_get_family(struct nl_addr *addr); -\endcode - -The same is true for the actual address data. It is typically present -at allocation time. For exceptions it can be specified later or -overwritten with the function nl_addr_set_binary_addr(). Beware that -the length of the address may not exceed \p maxlen specified at -allocation time. The address data is returned by the function -nl_addr_get_binary_addr() and its length by the function -nl_addr_get_len(). - -\code -#include - -int nl_addr_set_binary_addr(struct nl_addr *addr, void *data, size_t size); -void *nl_addr_get_binary_addr(struct nl_addr *addr); -unsigned int nl_addr_get_len(struct nl_addr *addr); -\endcode - -If you only want to check if the address data consists of all zeros -the function nl_addr_iszero() is a shortcut to that. - -\code -#include - -int nl_addr_iszero(struct nl_addr *addr); -\endcode - -\subsubsection core_abstract_addr_prefix 8.1.4 Address Prefix Length - -Although this functionality is somewhat specific to routing it has -been implemented here. Addresses can have a prefix length assigned -which implies that only the first n bits are of importance. This -is f.e. used to implement subnets. - -Use set functions nl_addr_set_prefixlen() and nl_addr_get_prefixlen() -to work with prefix lengths. - -\code -#include - -void nl_addr_set_prefixlen(struct nl_addr *addr, int n); -unsigned int nl_addr_get_prefixlen(struct nl_addr *addr); -\endcode - -\b Note: The default prefix length is set to (address length * 8) - -\subsubsection core_abstract_addr_helpers 8.1.5 Address Helpers - -Several functions exist to help when dealing with addresses. The -function nl_addr_cmp() compares two addresses and returns an integer -less than, equal to or greater than zero without considering the prefix -length at all. If you want to consider the prefix length, use the -function nl_addr_cmp_prefix(). - -\code -#include - -int nl_addr_cmp(struct nl_addr *addr, struct nl_addr *addr); -int nl_addr_cmp_prefix(struct nl_addr *addr, struct nl_addr *addr); -\endcode - -If an abstract address needs to presented to the user it should be done -in a human readable format which differs depending on the address -family. The function nl_addr2str() takes care of this by calling the -appropriate conversion functions internaly. It expects a \p buf of -length \p size to write the character string into and returns a pointer -to \p buf for easy printf() usage. - -\code -#include - -char *nl_addr2str(struct nl_addr *addr, char *buf, size_t size); -\endcode - -If the address family is unknown, the address data will be printed in -hexadecimal format AA:BB:CC:DD:... - -Often the only way to figure out the address family is by looking at -the length of the address. The function nl_addr_guess_family() does just -this and returns the address family guessed based on the address size. - -\code -#include - -int nl_addr_guess_family(struct nl_addr *addr); -\endcode - -Before allocating an address you may want to check if the character -string actually represents a valid address of the address family you are -expecting. The function nl_addr_valid() can be used for that, it returns -1 if the supplised \p addr is a valid address in the context of \p family. -See inet_pton(3), dnet_pton(3) for more information on valid adddress -formats. - -\code -#include - -int nl_addr_valid(char *addr, int family); -\endcode - -\subsection core_abstract_data 8.2 Abstract Data - -The abstract data type is a trivial datatype with the primary purpose -to simplify usage of netlink attributes of arbitary length. - -\subsubsection core_abstract_data_alloc 8.2.1 Allocation of a Data Object - -The function nl_data_alloc() alloctes a new abstract data object and -fill it with the provided data. nl_data_alloc_attr() does the same but -bases the data on the payload of a netlink attribute. New data objects -can also be allocated by cloning existing ones by using nl_data_clone(). - -\code -struct nl_data *nl_data_alloc(void *buf, size_t size); -struct nl_data *nl_data_alloc_attr(struct nlattr *attr); -struct nl_data *nl_data_clone(struct nl_data *data); -void nl_data_free(struct nl_data *data); -\endcode - -\subsubsection core_abstract_data_access 8.2.2 Access to Data - -The function nl_data_get() returns a pointer to the data, the size of -data is returned by nl_data_get_size(). - -\code -void *nl_data_get(struct nl_data *data); -size_t nl_data_get_size(struct nl_data *data); -\endcode - -\subsubsection core_abstract_data_helpers 8.2.3 Data Helpers - -The function nl_data_append() reallocates the internal data buffers and -appends the specified \p buf to the existing data. - -\code -int nl_data_append(struct nl_data *data, void *buf, size_t size); -\endcode - -\b Note: Call nl_data_append() invalidates all pointers returned by - nl_data_get(). - -\code -int nl_data_cmp(struct nl_data *data, struct nl_data *data); -\endcode - -*/ diff --git a/doc/src/examples/NLA_PUT.c b/doc/src/examples/NLA_PUT.c deleted file mode 100644 index c3afb47..0000000 --- a/doc/src/examples/NLA_PUT.c +++ /dev/null @@ -1,15 +0,0 @@ -#include -#include - -void construct_attrs(struct nl_msg *msg) -{ - NLA_PUT_STRING(msg, MY_ATTR_FOO1, "some text"); - NLA_PUT_U32(msg, MY_ATTR_FOO1, 0x1010); - NLA_PUT_FLAG(msg, MY_ATTR_FOO3, 1); - - return 0; - -nla_put_failure: - /* NLA_PUT* macros jump here in case of an error */ - return -EMSGSIZE; -} diff --git a/doc/src/examples/msg_constr_attr.c b/doc/src/examples/msg_constr_attr.c deleted file mode 100644 index bfa00fe..0000000 --- a/doc/src/examples/msg_constr_attr.c +++ /dev/null @@ -1,51 +0,0 @@ -struct nl_msg *build_msg(int ifindex, struct nl_addr *lladdr, int mtu) -{ - struct nl_msg *msg; - struct nlattr *info, *vlan; - struct ifinfomsg ifi = { - .ifi_family = AF_INET, - .ifi_index = ifindex, - }; - - /* Allocate a default sized netlink message */ - if (!(msg = nlmsg_alloc_simple(RTM_SETLINK, 0))) - return NULL; - - /* Append the protocol specific header (struct ifinfomsg)*/ - if (nlmsg_append(msg, &ifi, sizeof(ifi), NLMSG_ALIGNTO) < 0) - goto nla_put_failure - - /* Append a 32 bit integer attribute to carry the MTU */ - NLA_PUT_U32(msg, IFLA_MTU, mtu); - - /* Append a unspecific attribute to carry the link layer address */ - NLA_PUT_ADDR(msg, IFLA_ADDRESS, lladdr); - - /* Append a container for nested attributes to carry link information */ - if (!(info = nla_nest_start(msg, IFLA_LINKINFO))) - goto nla_put_failure; - - /* Put a string attribute into the container */ - NLA_PUT_STRING(msg, IFLA_INFO_KIND, "vlan"); - - /* - * Append another container inside the open container to carry - * vlan specific attributes - */ - if (!(vlan = nla_nest_start(msg, IFLA_INFO_DATA))) - goto nla_put_failure; - - /* add vlan specific info attributes here... */ - - /* Finish nesting the vlan attributes and close the second container. */ - nla_nest_end(msg, vlan); - - /* Finish nesting the link info attribute and close the first container. */ - nla_nest_end(msg, info); - - return msg; - -nla_put_failure: - nlmsg_free(msg); - return NULL; -} diff --git a/doc/src/examples/msg_parse_attr.c b/doc/src/examples/msg_parse_attr.c deleted file mode 100644 index 6e275e9..0000000 --- a/doc/src/examples/msg_parse_attr.c +++ /dev/null @@ -1,50 +0,0 @@ -int parse_message(struct nlmsghdr *hdr) -{ - /* - * The policy defines two attributes: a 32 bit integer and a container - * for nested attributes. - */ - struct nla_policy attr_policy[] = { - [ATTR_FOO] = { .type = NLA_U32 }, - [ATTR_BAR] = { .type = NLA_NESTED }, - }; - struct nlattr *attrs[ATTR_MAX+1]; - int err; - - /* - * The nlmsg_parse() function will make sure that the message contains - * enough payload to hold the header (struct my_hdr), validates any - * attributes attached to the messages and stores a pointer to each - * attribute in the attrs[] array accessable by attribute type. - */ - if ((err = nlmsg_parse(hdr, sizeof(struct my_hdr), attrs, ATTR_MAX, - attr_policy)) < 0) - goto errout; - - if (attrs[ATTR_FOO]) { - /* - * It is safe to directly access the attribute payload without - * any further checks since nlmsg_parse() enforced the policy. - */ - uint32_t foo = nla_get_u32(attrs[ATTR_FOO]); - } - - if (attrs[ATTR_BAR]) { - struct *nested[NESTED_MAX+1]; - - /* - * Attributes nested in a container can be parsed the same way - * as top level attributes. - */ - err = nla_parse_nested(nested, NESTED_MAX, attrs[ATTR_BAR], - nested_policy); - if (err < 0) - goto errout; - - // Process nested attributes here. - } - - err = 0; -errout: - return err; -} diff --git a/doc/src/examples/my_parse.c b/doc/src/examples/my_parse.c deleted file mode 100644 index c1ff637..0000000 --- a/doc/src/examples/my_parse.c +++ /dev/null @@ -1,11 +0,0 @@ -#include - -void my_parse(void *stream, int length) -{ - struct nlmsghdr *hdr = stream; - - while (nlmsg_ok(hdr, length)) { - // Parse message here - hdr = nlmsg_next(hdr, &length); - } -} diff --git a/doc/src/examples/nl_cb_set.c b/doc/src/examples/nl_cb_set.c deleted file mode 100644 index 4fbaefc..0000000 --- a/doc/src/examples/nl_cb_set.c +++ /dev/null @@ -1,14 +0,0 @@ -#include - -/* Allocate a callback set and initialize it to the verbose default set */ -struct nl_cb *cb = nl_cb_alloc(NL_CB_VERBOSE); - -/* Modify the set to call my_func() for all valid messages */ -nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, my_func, NULL); - -/* - * Set the error message handler to the verbose default implementation - * and direct it to print all errors to the given file descriptor. - */ -FILE *file = fopen(...); -nl_cb_err(cb, NL_CB_VERBOSE, NULL, file); diff --git a/doc/src/examples/nl_send_simple.c b/doc/src/examples/nl_send_simple.c deleted file mode 100644 index afbed7a..0000000 --- a/doc/src/examples/nl_send_simple.c +++ /dev/null @@ -1,11 +0,0 @@ -#include - -struct nl_sock *sk; -struct rtgenmsg rt_hdr = { - .rtgen_family = AF_UNSPEC, -}; - -sk = nl_socket_alloc(); -nl_connect(sk, NETLINK_ROUTE); - -nl_send_simple(sock, RTM_GETLINK, NLM_F_DUMP, &rt_hdr, sizeof(rt_hdr)); diff --git a/doc/src/examples/nla_flag.c b/doc/src/examples/nla_flag.c deleted file mode 100644 index dc3d0f9..0000000 --- a/doc/src/examples/nla_flag.c +++ /dev/null @@ -1,7 +0,0 @@ -/* nla_put_flag() appends a zero sized attribute to the message. */ -nla_put_flag(msg, ATTR_FLAG); - - -/* There is no need for a receival function, the presence is the value. */ -if (attrs[ATTR_FLAG]) - /* flag is present */ diff --git a/doc/src/examples/nla_for_each_attr.c b/doc/src/examples/nla_for_each_attr.c deleted file mode 100644 index 9d81835..0000000 --- a/doc/src/examples/nla_for_each_attr.c +++ /dev/null @@ -1,11 +0,0 @@ -#include - -struct nalttr *nla; -int rem; - -nla_for_each_attr(nla, attrstream, streamlen, rem) { - /* validate & parse attribute */ -} - -if (rem > 0) - /* unparsed attribute data */ diff --git a/doc/src/examples/nla_nest_start.c b/doc/src/examples/nla_nest_start.c deleted file mode 100644 index 51cd616..0000000 --- a/doc/src/examples/nla_nest_start.c +++ /dev/null @@ -1,16 +0,0 @@ -int put_opts(struct nl_msg *msg) -{ - struct nlattr *opts; - - if (!(opts = nla_nest_start(msg, ATTR_OPTS))) - goto nla_put_failure; - - NLA_PUT_U32(msg, NESTED_FOO, 123); - NLA_PUT_STRING(msg, NESTED_BAR, "some text"); - - nla_nest_end(msg, opts); - return 0; - -nla_put_failure: - return -EMSGSIZE; -} diff --git a/doc/src/examples/nla_ok.c b/doc/src/examples/nla_ok.c deleted file mode 100644 index 4485a96..0000000 --- a/doc/src/examples/nla_ok.c +++ /dev/null @@ -1,10 +0,0 @@ -#include -#include - -struct nlattr *hdr = nlmsg_attrdata(msg, 0); -int remaining = nlmsg_attrlen(msg, 0); - -while (nla_ok(hdr, remaining)) { - /* parse attribute here */ - hdr = nla_next(hdr, &remaining); -}; diff --git a/doc/src/examples/nla_parse_nested.c b/doc/src/examples/nla_parse_nested.c deleted file mode 100644 index 563bfc8..0000000 --- a/doc/src/examples/nla_parse_nested.c +++ /dev/null @@ -1,12 +0,0 @@ -if (attrs[ATTR_OPTS]) { - struct nlattr *nested[NESTED_MAX+1]; - struct nla_policy nested_policy[] = { - [NESTED_FOO] = { .type = NLA_U32 }, - }; - - if (nla_parse_nested(nested, NESTED_MAX, attrs[ATTR_OPTS], nested_policy) < 0) - /* error */ - - if (nested[NESTED_FOO]) - uint32_t val = nla_get_u32(nested[NESTED_FOO]); -} diff --git a/doc/src/examples/nla_put.c b/doc/src/examples/nla_put.c deleted file mode 100644 index 0683fa5..0000000 --- a/doc/src/examples/nla_put.c +++ /dev/null @@ -1,14 +0,0 @@ -struct my_attr_struct { - uint32_t a; - uint32_t b; -}; - -int my_put(struct nl_msg *msg) -{ - struct my_attr_struct obj = { - .a = 10, - .b = 20, - }; - - return nla_put(msg, ATTR_MY_STRUCT, sizeof(obj), &obj); -} diff --git a/doc/src/examples/nlmsg_for_each.c b/doc/src/examples/nlmsg_for_each.c deleted file mode 100644 index ae2ee66..0000000 --- a/doc/src/examples/nlmsg_for_each.c +++ /dev/null @@ -1,7 +0,0 @@ -#include - -struct nlmsghdr *hdr; - -nlmsg_for_each(hdr, stream, length) { - /* do something with message */ -} diff --git a/doc/src/examples/nlmsg_parse.c b/doc/src/examples/nlmsg_parse.c deleted file mode 100644 index ac6acb3..0000000 --- a/doc/src/examples/nlmsg_parse.c +++ /dev/null @@ -1,29 +0,0 @@ -#include -#include - -enum { - MY_ATTR_FOO = 1, - MY_ATTR_BAR, - __MY_ATTR_MAX, -}; - -#define MY_ATTR_MAX (__MY_ATTR_MAX - 1) - -static struct nla_policy my_policy[MY_ATTR_MAX+1] = { - [MY_ATTR_FOO] = { .type = NLA_U32 }, - [MY_ATTR_BAR] = { .type = NLA_STRING, - .maxlen = 16 }, -}; - -void parse_msg(struct nlmsghdr *nlh) -{ - struct nlattr *attrs[MY_ATTR_MAX+1]; - - if (nlmsg_parse(nlh, 0, attrs, MY_ATTR_MAX, my_policy) < 0) - /* error */ - - if (attrs[MY_ATTR_FOO]) { - /* MY_ATTR_FOO is present in message */ - printf("value: %u\n", nla_get_u32(attrs[MY_ATTR_FOO])); - } -} diff --git a/doc/src/examples/nlmsg_put.c b/doc/src/examples/nlmsg_put.c deleted file mode 100644 index 5e609c6..0000000 --- a/doc/src/examples/nlmsg_put.c +++ /dev/null @@ -1,31 +0,0 @@ -#include - -struct nlmsghdr *hdr; -struct nl_msg *msg; -struct myhdr { - uint32_t foo1, foo2; -} hdr = { 10, 20 }; - -/* Allocate a message with the default maximum message size */ -msg = nlmsg_alloc(); - -/* - * Add header with message type MY_MSGTYPE, the flag NLM_F_CREATE, - * let library fill port and sequence number, and reserve room for - * struct myhdr - */ -hdr = nlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, MY_MSGTYPE, sizeof(hdr), NLM_F_CREATE); - -/* Copy own header into newly reserved payload section */ -memcpy(nlmsg_data(hdr), &hdr, sizeof(hdr)); - -/* - * The message will now look like this: - * +-------------------+- - -+----------------+- - -+ - * | struct nlmsghdr | Pad | struct myhdr | Pad | - * +-------------------+-----+----------------+- - -+ - * nlh -^ / \ - * +--------+---------+ - * | foo1 | foo2 | - * +--------+---------+ - */ diff --git a/doc/src/examples/sk_group_example.c b/doc/src/examples/sk_group_example.c deleted file mode 100644 index f948e18..0000000 --- a/doc/src/examples/sk_group_example.c +++ /dev/null @@ -1,43 +0,0 @@ -#include -#include -#include - -/* - * This function will be called for each valid netlink message received - * in nl_recvmsgs_default() - */ -static int my_func(struct nl_msg *msg, void *arg) -{ - return 0; -} - -struct nl_sock *sk; - -/* Allocate a new socket */ -sk = nl_socket_alloc(); - -/* - * Notifications do not use sequence numbers, disable sequence number - * checking. - */ -nl_socket_disable_seq_check(sk); - -/* - * Define a callback function, which will be called for each notification - * received - */ -nl_socket_modify_cb(sk, NL_CB_VALID, NL_CB_CUSTOM, my_func, NULL); - -/* Connect to routing netlink protocol */ -nl_connect(sk, NETLINK_ROUTE); - -/* Subscribe to link notifications group */ -nl_socket_add_memberships(sk, RTNLGRP_LINK); - -/* - * Start receiving messages. The function nl_recvmsgs_default() will block - * until one or more netlink messages (notification) are received which - * will be passed on to my_func(). - */ -while (1) - nl_recvmsgs_default(sock); diff --git a/doc/src/genl.c b/doc/src/genl.c deleted file mode 100644 index 8585c62..0000000 --- a/doc/src/genl.c +++ /dev/null @@ -1,10 +0,0 @@ -/** - * \cond skip - * vim:syntax=doxygen - * \endcond - -\page genl_doc Generic Netlink Library (-lnl-genl) - -\section genl_intro Introduction - -*/ diff --git a/doc/src/hidden.c b/doc/src/hidden.c new file mode 100644 index 0000000..a63621d --- /dev/null +++ b/doc/src/hidden.c @@ -0,0 +1,42 @@ +/** + * \cond skip + * vim:syntax=doxygen + * \endcond + +\page auto_ack_warning Disabling Auto-ACK + +\attention Disabling Auto-ACK (nl_socket_disable_auto_ack()) will cause this + function to return immediately after sending the netlink message. + The function will not wait for an eventual error message. It is + the responsibility of the caller to handle any error messages or + ACKs returned. + +\page pointer_lifetime_warning Pointer Lifetime + +\attention The reference counter of the returned object is not incremented. + Therefore, the returned pointer is only valid during the lifetime + of the parent object. Increment the reference counter if the object + is supposed to stay around after the parent object was freed. + +\page private_struct Private Structure + +\note The definition of this structure is private to allow modification + without breaking API. Use the designated accessor functions to + access individual object attributes. + +\page read_only_attribute Read-Only Attribute + +\note The attribute this accessor is modifying is a read-only attribute + which can not be modified in the kernel. Any changes to the + attribute only have an effect on the local copy of the object. The + accessor function is provided solely for the purpose of creating + objects for comparison and filtering. + +\page low_level_api Low Level API + +\note This is a low level API function. A high level function implementing + the same functionality with a simplified usage pattern exists. This + function is available as an alternative if the default library + behaviour is not desirable. + +*/ diff --git a/doc/src/nf.c b/doc/src/nf.c deleted file mode 100644 index 006500d..0000000 --- a/doc/src/nf.c +++ /dev/null @@ -1,10 +0,0 @@ -/** - * \cond skip - * vim:syntax=doxygen - * \endcond - -\page nf_doc Netfilter Netlink Library (-lnl-nf) - -\section nf_intro Introduction - -*/ diff --git a/doc/src/route.c b/doc/src/route.c deleted file mode 100644 index 2c042db..0000000 --- a/doc/src/route.c +++ /dev/null @@ -1,10 +0,0 @@ -/** - * \cond skip - * vim:syntax=doxygen - * \endcond - -\page route_doc Routing Netlink Library (-lnl-route) - -\section route_intro Introduction - -*/ diff --git a/doc/src/toc.c b/doc/src/toc.c index c33727c..570c1df 100644 --- a/doc/src/toc.c +++ b/doc/src/toc.c @@ -9,11 +9,11 @@ libnl is a set of libraries to deal with the netlink protocol and some of the high level protocols implemented on top of it. The goal is to -provide APIs on different levels of abstraction. The core library libnl.so +provide APIs on different levels of abstraction. The core library libnl provides a fundamental set of functions to deal with sockets, construct messages, and send/receive those messages. Additional high level interfaces for several individual netlink protocols are provided in separate -libraries (e.g. \ref route_doc "nl-route.so", \ref genl_doc "nl-genl.so", ...). +libraries (e.g. "nl-route", "nl-genl", ...). The library is designed to ensure that all components are optional, i.e. even though the core library provides a caching system which allows to @@ -28,26 +28,14 @@ version is used with a considerably older kernel. \section main_toc Table of Contents -- \subpage core_doc "1. Netlink Core Library (-lnl)" -- \subpage route_doc "2. Routing Netlink Library (-lnl-route)" -- \subpage genl_doc "3. Generic Netlink Library (-lnl-genl)" -- \subpage nf_doc "4. Netfilter Netlink Library (-lnl-nf)" - \section main_trees GIT Trees \subsection tree_dev Development Tree @code -git://git.kernel.org/pub/scm/libs/netlink/libnl.git +git://git.infradead.org/users/tgr/libnl.git @endcode -- Web: http://www.kernel.org/pub/scm/libs/netlink/libnl.git - -\subsection tree_stable Stable Tree - -@code -git://git.kernel.org/pub/scm/libs/netlink/libnl-stable.git -@endcode -- Web: http://www.kernel.org/pub/scm/libs/netlink/libnl-stable.git +- Web: http://git.infradead.org/users/tgr/libnl.git \section main_website Website @@ -55,7 +43,7 @@ git://git.kernel.org/pub/scm/libs/netlink/libnl-stable.git \section main_mailinglist Mailinglist -Please post question and patches to the libnl mailinglist: +Please post questions and patches to the libnl mailinglist: @code libnl@lists.infradead.org diff --git a/doc/stylesheets/asciidoc-manpage.css b/doc/stylesheets/asciidoc-manpage.css new file mode 100644 index 0000000..45eba23 --- /dev/null +++ b/doc/stylesheets/asciidoc-manpage.css @@ -0,0 +1,18 @@ +/* Overrides for manpage documents */ +h1 { + padding-top: 0.5em; + padding-bottom: 0.5em; + border-top: 2px solid silver; + border-bottom: 2px solid silver; +} +h2 { + border-style: none; +} +div.sectionbody { + margin-left: 3em; +} + +@media print { + div#toc { display: none; } +} + diff --git a/doc/stylesheets/asciidoc.css b/doc/stylesheets/asciidoc.css new file mode 100644 index 0000000..2852168 --- /dev/null +++ b/doc/stylesheets/asciidoc.css @@ -0,0 +1,526 @@ +/* Shared CSS for AsciiDoc xhtml11 and html5 backends */ + +/* Default font. */ +body { + font-family: Georgia,serif; +} + +/* Title font. */ +h1, h2, h3, h4, h5, h6, +div.title, caption.title, +thead, p.table.header, +#toctitle, +#author, #revnumber, #revdate, #revremark, +#footer { + /* OLD: font-family: Arial,Helvetica,sans-serif; */ + font-family: Lucida Grande, Verdana, Geneva, Arial, sans-serif; +} + +body { + margin: 1em 5% 1em 5%; +} + +a { + /* color: blue; */ + color: #990000; + text-decoration: none; +} +a:visited { + /* color: fuchsia; */ +} + +a:hover { + text-decoration: underline; +} + +em { + font-style: italic; + /* color: navy; */ +} + +strong { + font-weight: bold; + color: black; + /* color: #083194; */ +} + +h1, h2, h3, h4, h5, h6 { + /* color: #527bbd; */ + color: #990000; + margin-top: 1.2em; + margin-bottom: 0.5em; + line-height: 1.3; +} + +h1, h2, h3 { + border-bottom: 2px solid silver; +} +h2 { + padding-top: 0.5em; +} +h3 { + float: left; +} +h3 + * { + clear: left; +} +h5 { + font-size: 1.0em; +} + +div.sectionbody { + margin-left: 0; +} + +hr { + border: 1px solid silver; +} + +p { + margin-top: 0.5em; + margin-bottom: 0.5em; +} + +ul, ol, li > p { + margin-top: 0; +} +ul > li { color: #aaa; } +ul > li > * { color: black; } + +pre { + padding: 0; + margin: 0; +} + +#author { + /* color: #527bbd; */ + font-weight: bold; + font-size: 1.1em; +} +#email { +} +#revnumber, #revdate, #revremark { +} + +#footer { + font-size: small; + border-top: 2px solid silver; + padding-top: 0.5em; + margin-top: 4.0em; +} +#footer-text { + float: left; + padding-bottom: 0.5em; +} +#footer-badges { + float: right; + padding-bottom: 0.5em; +} + +#preamble { + margin-top: 1.5em; + margin-bottom: 1.5em; +} +div.imageblock, div.exampleblock, div.verseblock, +div.quoteblock, div.literalblock, div.listingblock, div.sidebarblock, +div.admonitionblock { + margin-top: 1.0em; + margin-bottom: 1.5em; +} +div.admonitionblock { + margin-top: 2.0em; + margin-bottom: 2.0em; + margin-right: 10%; + /* color: #606060; */ +} + +div.content { /* Block element content. */ + padding: 0; +} + +/* Block element titles. */ +div.title, caption.title { + /* OLD: color: #527bbd; */ + color: #990000; + font-weight: bold; + text-align: left; + margin-top: 1.0em; + margin-bottom: 0.5em; +} +div.title + * { + margin-top: 0; +} + +td div.title:first-child { + margin-top: 0.0em; +} +div.content div.title:first-child { + margin-top: 0.0em; +} +div.content + div.title { + margin-top: 0.0em; +} + +div.sidebarblock > div.content { + background: #ffffee; + border: 1px solid #dddddd; + border-left: 4px solid #f0f0f0; + padding: 0.5em; +} + +div.listingblock > div.content { + border: 1px solid #dddddd; + /* border-left: 5px solid #f0f0f0; */ + background: #f8f8f8; + padding: 0.5em; +} + +div.quoteblock, div.verseblock { + padding-left: 1.0em; + margin-left: 1.0em; + margin-right: 10%; + border-left: 5px solid #f0f0f0; + color: #777777; +} + +div.quoteblock > div.attribution { + padding-top: 0.5em; + text-align: right; +} + +div.verseblock > pre.content { + font-family: inherit; + font-size: inherit; +} +div.verseblock > div.attribution { + padding-top: 0.75em; + text-align: left; +} +/* DEPRECATED: Pre version 8.2.7 verse style literal block. */ +div.verseblock + div.attribution { + text-align: left; +} + +div.admonitionblock .icon { + vertical-align: top; + font-size: 1.1em; + font-weight: bold; + text-decoration: underline; + /* OLD: color: #527bbd; */ + color: #990000; + padding-right: 0.5em; +} +div.admonitionblock td.content { + padding-left: 0.5em; + border-left: 3px solid #dddddd; +} + +div.exampleblock > div.content { + border-left: 3px solid #dddddd; + padding-left: 0.5em; +} + +div.imageblock div.content { padding-left: 0; } +span.image img { border-style: none; } +a.image:visited { color: white; } + +dl { + margin-top: 0.8em; + margin-bottom: 0.8em; +} +dt { + margin-top: 0.5em; + margin-bottom: 0; + font-style: normal; + color: navy; +} +dd > *:first-child { + margin-top: 0.1em; +} + +ul, ol { + list-style-position: outside; +} +ol.arabic { + list-style-type: decimal; +} +ol.loweralpha { + list-style-type: lower-alpha; +} +ol.upperalpha { + list-style-type: upper-alpha; +} +ol.lowerroman { + list-style-type: lower-roman; +} +ol.upperroman { + list-style-type: upper-roman; +} + +div.compact ul, div.compact ol, +div.compact p, div.compact p, +div.compact div, div.compact div { + margin-top: 0.1em; + margin-bottom: 0.1em; +} + +tfoot { + font-weight: bold; +} +td > div.verse { + white-space: pre; +} + +div.hdlist { + margin-top: 0.8em; + margin-bottom: 0.8em; +} +div.hdlist tr { + padding-bottom: 15px; +} +dt.hdlist1.strong, td.hdlist1.strong { + font-weight: bold; +} +td.hdlist1 { + vertical-align: top; + font-style: normal; + padding-right: 0.8em; + /* color: navy; */ + color: #990000; +} +td.hdlist2 { + vertical-align: top; +} +div.hdlist.compact tr { + margin: 0; + padding-bottom: 0; +} + +.comment { + background: yellow; +} + +.footnote, .footnoteref { + font-size: 0.8em; +} + +span.footnote, span.footnoteref { + vertical-align: super; +} + +#footnotes { + margin: 20px 0 20px 0; + padding: 7px 0 0 0; +} + +#footnotes div.footnote { + margin: 0 0 5px 0; +} + +#footnotes hr { + border: none; + border-top: 1px solid silver; + height: 1px; + text-align: left; + margin-left: 0; + width: 20%; + min-width: 100px; +} + +div.colist td { + padding-right: 0.5em; + padding-bottom: 0.3em; + vertical-align: top; +} +div.colist td img { + margin-top: 0.3em; +} + +@media print { + #footer-badges { display: none; } +} + +#toc { + margin-bottom: 2.5em; +} + +#toctitle { + /* color: #527bbd; */ + color: #990000; + font-size: 1.1em; + font-weight: bold; + margin-top: 1.0em; + margin-bottom: 0.1em; +} + +div.toclevel1, div.toclevel2, div.toclevel3, div.toclevel4 { + margin-top: 0; + margin-bottom: 0; +} +div.toclevel2 { + margin-left: 2em; + font-size: 0.9em; +} +div.toclevel3 { + margin-left: 4em; + font-size: 0.9em; +} +div.toclevel4 { + margin-left: 6em; + font-size: 0.9em; +} + +span.aqua { color: aqua; } +span.black { color: black; } +span.blue { color: blue; } +span.fuchsia { color: fuchsia; } +span.gray { color: gray; } +span.green { color: green; } +span.lime { color: lime; } +span.maroon { color: maroon; } +span.navy { color: navy; } +span.olive { color: olive; } +span.purple { color: purple; } +span.red { color: red; } +span.silver { color: silver; } +span.teal { color: teal; } +span.white { color: white; } +span.yellow { color: yellow; } + +span.aqua-background { background: aqua; } +span.black-background { background: black; } +span.blue-background { background: blue; } +span.fuchsia-background { background: fuchsia; } +span.gray-background { background: gray; } +span.green-background { background: green; } +span.lime-background { background: lime; } +span.maroon-background { background: maroon; } +span.navy-background { background: navy; } +span.olive-background { background: olive; } +span.purple-background { background: purple; } +span.red-background { background: red; } +span.silver-background { background: silver; } +span.teal-background { background: teal; } +span.white-background { background: white; } +span.yellow-background { background: yellow; } + +span.big { font-size: 2em; } +span.small { font-size: 0.6em; } + +span.underline { text-decoration: underline; } +span.overline { text-decoration: overline; } +span.line-through { text-decoration: line-through; } + + +/* + * xhtml11 specific + * + * */ + +tt { + font-family: monospace; + font-size: inherit; + /* color: navy; */ + color: black; +} + +div.tableblock { + margin-top: 1.0em; + margin-bottom: 1.5em; +} +div.tableblock > table { + /* border: 3px solid #527bbd; */ + border: 2px solid #990000; +} +thead, p.table.header { + font-weight: bold; + /* color: #527bbd; */ + color: #990000; +} +p.table { + margin-top: 0; +} +/* Because the table frame attribute is overriden by CSS in most browsers. */ +div.tableblock > table[frame="void"] { + border-style: none; +} +div.tableblock > table[frame="hsides"] { + border-left-style: none; + border-right-style: none; +} +div.tableblock > table[frame="vsides"] { + border-top-style: none; + border-bottom-style: none; +} + + +/* + * html5 specific + * + * */ + +.monospaced { + font-family: monospace; + font-size: inherit; + color: navy; +} + +table.tableblock { + margin-top: 1.0em; + margin-bottom: 1.5em; +} +thead, p.tableblock.header { + font-weight: bold; + /* color: #527bbd; */ + color: #990000; +} +p.tableblock { + margin-top: 0; +} +table.tableblock { + border-width: 3px; + border-spacing: 0px; + border-style: solid; + /* border-color: #527bbd; */ + border-color: #990000; + border-collapse: collapse; +} +th.tableblock, td.tableblock { + border-width: 1px; + padding: 4px; + border-style: solid; + /* border-color: #527bbd; */ + border-color: #990000; +} + +table.tableblock.frame-topbot { + border-left-style: hidden; + border-right-style: hidden; +} +table.tableblock.frame-sides { + border-top-style: hidden; + border-bottom-style: hidden; +} +table.tableblock.frame-none { + border-style: hidden; +} + +th.tableblock.halign-left, td.tableblock.halign-left { + text-align: left; +} +th.tableblock.halign-center, td.tableblock.halign-center { + text-align: center; +} +th.tableblock.halign-right, td.tableblock.halign-right { + text-align: right; +} + +th.tableblock.valign-top, td.tableblock.valign-top { + vertical-align: top; +} +th.tableblock.valign-middle, td.tableblock.valign-middle { + vertical-align: middle; +} +th.tableblock.valign-bottom, td.tableblock.valign-bottom { + vertical-align: bottom; +} diff --git a/doc/stylesheets/docbook-xsl.css b/doc/stylesheets/docbook-xsl.css new file mode 100644 index 0000000..6df2944 --- /dev/null +++ b/doc/stylesheets/docbook-xsl.css @@ -0,0 +1,322 @@ +/* + CSS stylesheet for XHTML produced by DocBook XSL stylesheets. + Tested with XSL stylesheets 1.61.2, 1.67.2 +*/ + +span.strong { + font-weight: bold; +} + +body blockquote { + margin-top: .75em; + line-height: 1.5; + margin-bottom: .75em; +} + +html body { + margin: 1em 5% 1em 5%; + line-height: 1.2; +} + +body div { + margin: 0; +} + +h1, h2, h3, h4, h5, h6 +{ + color: #527bbd; + font-family: tahoma, verdana, sans-serif; +} + +div.toc p:first-child, +div.list-of-figures p:first-child, +div.list-of-tables p:first-child, +div.list-of-examples p:first-child, +div.example p.title, +div.sidebar p.title +{ + font-weight: bold; + color: #527bbd; + font-family: tahoma, verdana, sans-serif; + margin-bottom: 0.2em; +} + +body h1 { + margin: .0em 0 0 -4%; + line-height: 1.3; + border-bottom: 2px solid silver; +} + +body h2 { + margin: 0.5em 0 0 -4%; + line-height: 1.3; + border-bottom: 2px solid silver; +} + +body h3 { + margin: .8em 0 0 -3%; + line-height: 1.3; +} + +body h4 { + margin: .8em 0 0 -3%; + line-height: 1.3; +} + +body h5 { + margin: .8em 0 0 -2%; + line-height: 1.3; +} + +body h6 { + margin: .8em 0 0 -1%; + line-height: 1.3; +} + +body hr { + border: none; /* Broken on IE6 */ +} +div.footnotes hr { + border: 1px solid silver; +} + +div.navheader th, div.navheader td, div.navfooter td { + font-family: sans-serif; + font-size: 0.9em; + font-weight: bold; + color: #527bbd; +} +div.navheader img, div.navfooter img { + border-style: none; +} +div.navheader a, div.navfooter a { + font-weight: normal; +} +div.navfooter hr { + border: 1px solid silver; +} + +body td { + line-height: 1.2 +} + +body th { + line-height: 1.2; +} + +ol { + line-height: 1.2; +} + +ul, body dir, body menu { + line-height: 1.2; +} + +html { + margin: 0; + padding: 0; +} + +body h1, body h2, body h3, body h4, body h5, body h6 { + margin-left: 0 +} + +body pre { + margin: 0.5em 10% 0.5em 1em; + line-height: 1.0; + color: navy; +} + +tt.literal, code.literal { + color: navy; +} + +.programlisting, .screen { + border: 1px solid silver; + background: #f4f4f4; + margin: 0.5em 10% 0.5em 0; + padding: 0.5em 1em; +} + +div.sidebar { + background: #ffffee; + margin: 1.0em 10% 0.5em 0; + padding: 0.5em 1em; + border: 1px solid silver; +} +div.sidebar * { padding: 0; } +div.sidebar div { margin: 0; } +div.sidebar p.title { + margin-top: 0.5em; + margin-bottom: 0.2em; +} + +div.bibliomixed { + margin: 0.5em 5% 0.5em 1em; +} + +div.glossary dt { + font-weight: bold; +} +div.glossary dd p { + margin-top: 0.2em; +} + +dl { + margin: .8em 0; + line-height: 1.2; +} + +dt { + margin-top: 0.5em; +} + +dt span.term { + font-style: normal; + color: navy; +} + +div.variablelist dd p { + margin-top: 0; +} + +div.itemizedlist li, div.orderedlist li { + margin-left: -0.8em; + margin-top: 0.5em; +} + +ul, ol { + list-style-position: outside; +} + +div.sidebar ul, div.sidebar ol { + margin-left: 2.8em; +} + +div.itemizedlist p.title, +div.orderedlist p.title, +div.variablelist p.title +{ + margin-bottom: -0.8em; +} + +div.revhistory table { + border-collapse: collapse; + border: none; +} +div.revhistory th { + border: none; + color: #527bbd; + font-family: tahoma, verdana, sans-serif; +} +div.revhistory td { + border: 1px solid silver; +} + +/* Keep TOC and index lines close together. */ +div.toc dl, div.toc dt, +div.list-of-figures dl, div.list-of-figures dt, +div.list-of-tables dl, div.list-of-tables dt, +div.indexdiv dl, div.indexdiv dt +{ + line-height: normal; + margin-top: 0; + margin-bottom: 0; +} + +/* + Table styling does not work because of overriding attributes in + generated HTML. +*/ +div.table table, +div.informaltable table +{ + margin-left: 0; + margin-right: 5%; + margin-bottom: 0.8em; +} +div.informaltable table +{ + margin-top: 0.4em +} +div.table thead, +div.table tfoot, +div.table tbody, +div.informaltable thead, +div.informaltable tfoot, +div.informaltable tbody +{ + /* No effect in IE6. */ + border-top: 3px solid #527bbd; + border-bottom: 3px solid #527bbd; +} +div.table thead, div.table tfoot, +div.informaltable thead, div.informaltable tfoot +{ + font-weight: bold; +} + +div.mediaobject img { + margin-bottom: 0.8em; +} +div.figure p.title, +div.table p.title +{ + margin-top: 1em; + margin-bottom: 0.4em; +} + +div.calloutlist p +{ + margin-top: 0em; + margin-bottom: 0.4em; +} + +a img { + border-style: none; +} + +@media print { + div.navheader, div.navfooter { display: none; } +} + +span.aqua { color: aqua; } +span.black { color: black; } +span.blue { color: blue; } +span.fuchsia { color: fuchsia; } +span.gray { color: gray; } +span.green { color: green; } +span.lime { color: lime; } +span.maroon { color: maroon; } +span.navy { color: navy; } +span.olive { color: olive; } +span.purple { color: purple; } +span.red { color: red; } +span.silver { color: silver; } +span.teal { color: teal; } +span.white { color: white; } +span.yellow { color: yellow; } + +span.aqua-background { background: aqua; } +span.black-background { background: black; } +span.blue-background { background: blue; } +span.fuchsia-background { background: fuchsia; } +span.gray-background { background: gray; } +span.green-background { background: green; } +span.lime-background { background: lime; } +span.maroon-background { background: maroon; } +span.navy-background { background: navy; } +span.olive-background { background: olive; } +span.purple-background { background: purple; } +span.red-background { background: red; } +span.silver-background { background: silver; } +span.teal-background { background: teal; } +span.white-background { background: white; } +span.yellow-background { background: yellow; } + +span.big { font-size: 2em; } +span.small { font-size: 0.6em; } + +span.underline { text-decoration: underline; } +span.overline { text-decoration: overline; } +span.line-through { text-decoration: line-through; } diff --git a/doc/stylesheets/flask-manpage.css b/doc/stylesheets/flask-manpage.css new file mode 100644 index 0000000..75a2dda --- /dev/null +++ b/doc/stylesheets/flask-manpage.css @@ -0,0 +1 @@ +/* Empty placeholder file */ diff --git a/doc/stylesheets/flask.css b/doc/stylesheets/flask.css new file mode 100644 index 0000000..8d33bc4 --- /dev/null +++ b/doc/stylesheets/flask.css @@ -0,0 +1,584 @@ +/* + * AsciiDoc 'flask' theme for xhtml11 and html5 backends. A shameless knock-off + * of the Flask website styling (http://flask.pocoo.org/docs/). + * + * The implementation is straight-forward, consisting of the asciidoc.css file + * followed by theme specific overrides. + * + * */ + + +/* Shared CSS for AsciiDoc xhtml11 and html5 backends */ + +/* Default font. */ +body { + font-family: Georgia,serif; +} + +/* Title font. */ +h1, h2, h3, h4, h5, h6, +div.title, caption.title, +thead, p.table.header, +#toctitle, +#author, #revnumber, #revdate, #revremark, +#footer { + font-family: Arial,Helvetica,sans-serif; +} + +body { + margin: 1em 5% 1em 5%; +} + +a { + color: blue; + text-decoration: underline; +} +a:visited { + color: fuchsia; +} + +em { + font-style: italic; + color: navy; +} + +strong { + font-weight: bold; + color: #083194; +} + +h1, h2, h3, h4, h5, h6 { + color: #527bbd; + margin-top: 1.2em; + margin-bottom: 0.5em; + line-height: 1.3; +} + +h1, h2, h3 { + border-bottom: 2px solid silver; +} +h2 { + padding-top: 0.5em; +} +h3 { + float: left; +} +h3 + * { + clear: left; +} +h5 { + font-size: 1.0em; +} + +div.sectionbody { + margin-left: 0; +} + +hr { + border: 1px solid silver; +} + +p { + margin-top: 0.5em; + margin-bottom: 0.5em; +} + +ul, ol, li > p { + margin-top: 0; +} +ul > li { color: #aaa; } +ul > li > * { color: black; } + +pre { + padding: 0; + margin: 0; +} + +#author { + color: #527bbd; + font-weight: bold; + font-size: 1.1em; +} +#email { +} +#revnumber, #revdate, #revremark { +} + +#footer { + font-size: small; + border-top: 2px solid silver; + padding-top: 0.5em; + margin-top: 4.0em; +} +#footer-text { + float: left; + padding-bottom: 0.5em; +} +#footer-badges { + float: right; + padding-bottom: 0.5em; +} + +#preamble { + margin-top: 1.5em; + margin-bottom: 1.5em; +} +div.imageblock, div.exampleblock, div.verseblock, +div.quoteblock, div.literalblock, div.listingblock, div.sidebarblock, +div.admonitionblock { + margin-top: 1.0em; + margin-bottom: 1.5em; +} +div.admonitionblock { + margin-top: 2.0em; + margin-bottom: 2.0em; + margin-right: 10%; + color: #606060; +} + +div.content { /* Block element content. */ + padding: 0; +} + +/* Block element titles. */ +div.title, caption.title { + color: #527bbd; + font-weight: bold; + text-align: left; + margin-top: 1.0em; + margin-bottom: 0.5em; +} +div.title + * { + margin-top: 0; +} + +td div.title:first-child { + margin-top: 0.0em; +} +div.content div.title:first-child { + margin-top: 0.0em; +} +div.content + div.title { + margin-top: 0.0em; +} + +div.sidebarblock > div.content { + background: #ffffee; + border: 1px solid #dddddd; + border-left: 4px solid #f0f0f0; + padding: 0.5em; +} + +div.listingblock > div.content { + border: 1px solid #dddddd; + border-left: 5px solid #f0f0f0; + background: #f8f8f8; + padding: 0.5em; +} + +div.quoteblock, div.verseblock { + padding-left: 1.0em; + margin-left: 1.0em; + margin-right: 10%; + border-left: 5px solid #f0f0f0; + color: #777777; +} + +div.quoteblock > div.attribution { + padding-top: 0.5em; + text-align: right; +} + +div.verseblock > pre.content { + font-family: inherit; + font-size: inherit; +} +div.verseblock > div.attribution { + padding-top: 0.75em; + text-align: left; +} +/* DEPRECATED: Pre version 8.2.7 verse style literal block. */ +div.verseblock + div.attribution { + text-align: left; +} + +div.admonitionblock .icon { + vertical-align: top; + font-size: 1.1em; + font-weight: bold; + text-decoration: underline; + color: #527bbd; + padding-right: 0.5em; +} +div.admonitionblock td.content { + padding-left: 0.5em; + border-left: 3px solid #dddddd; +} + +div.exampleblock > div.content { + border-left: 3px solid #dddddd; + padding-left: 0.5em; +} + +div.imageblock div.content { padding-left: 0; } +span.image img { border-style: none; } +a.image:visited { color: white; } + +dl { + margin-top: 0.8em; + margin-bottom: 0.8em; +} +dt { + margin-top: 0.5em; + margin-bottom: 0; + font-style: normal; + color: navy; +} +dd > *:first-child { + margin-top: 0.1em; +} + +ul, ol { + list-style-position: outside; +} +ol.arabic { + list-style-type: decimal; +} +ol.loweralpha { + list-style-type: lower-alpha; +} +ol.upperalpha { + list-style-type: upper-alpha; +} +ol.lowerroman { + list-style-type: lower-roman; +} +ol.upperroman { + list-style-type: upper-roman; +} + +div.compact ul, div.compact ol, +div.compact p, div.compact p, +div.compact div, div.compact div { + margin-top: 0.1em; + margin-bottom: 0.1em; +} + +tfoot { + font-weight: bold; +} +td > div.verse { + white-space: pre; +} + +div.hdlist { + margin-top: 0.8em; + margin-bottom: 0.8em; +} +div.hdlist tr { + padding-bottom: 15px; +} +dt.hdlist1.strong, td.hdlist1.strong { + font-weight: bold; +} +td.hdlist1 { + vertical-align: top; + font-style: normal; + padding-right: 0.8em; + color: navy; +} +td.hdlist2 { + vertical-align: top; +} +div.hdlist.compact tr { + margin: 0; + padding-bottom: 0; +} + +.comment { + background: yellow; +} + +.footnote, .footnoteref { + font-size: 0.8em; +} + +span.footnote, span.footnoteref { + vertical-align: super; +} + +#footnotes { + margin: 20px 0 20px 0; + padding: 7px 0 0 0; +} + +#footnotes div.footnote { + margin: 0 0 5px 0; +} + +#footnotes hr { + border: none; + border-top: 1px solid silver; + height: 1px; + text-align: left; + margin-left: 0; + width: 20%; + min-width: 100px; +} + +div.colist td { + padding-right: 0.5em; + padding-bottom: 0.3em; + vertical-align: top; +} +div.colist td img { + margin-top: 0.3em; +} + +@media print { + #footer-badges { display: none; } +} + +#toc { + margin-bottom: 2.5em; +} + +#toctitle { + color: #527bbd; + font-size: 1.1em; + font-weight: bold; + margin-top: 1.0em; + margin-bottom: 0.1em; +} + +div.toclevel1, div.toclevel2, div.toclevel3, div.toclevel4 { + margin-top: 0; + margin-bottom: 0; +} +div.toclevel2 { + margin-left: 2em; + font-size: 0.9em; +} +div.toclevel3 { + margin-left: 4em; + font-size: 0.9em; +} +div.toclevel4 { + margin-left: 6em; + font-size: 0.9em; +} + +span.aqua { color: aqua; } +span.black { color: black; } +span.blue { color: blue; } +span.fuchsia { color: fuchsia; } +span.gray { color: gray; } +span.green { color: green; } +span.lime { color: lime; } +span.maroon { color: maroon; } +span.navy { color: navy; } +span.olive { color: olive; } +span.purple { color: purple; } +span.red { color: red; } +span.silver { color: silver; } +span.teal { color: teal; } +span.white { color: white; } +span.yellow { color: yellow; } + +span.aqua-background { background: aqua; } +span.black-background { background: black; } +span.blue-background { background: blue; } +span.fuchsia-background { background: fuchsia; } +span.gray-background { background: gray; } +span.green-background { background: green; } +span.lime-background { background: lime; } +span.maroon-background { background: maroon; } +span.navy-background { background: navy; } +span.olive-background { background: olive; } +span.purple-background { background: purple; } +span.red-background { background: red; } +span.silver-background { background: silver; } +span.teal-background { background: teal; } +span.white-background { background: white; } +span.yellow-background { background: yellow; } + +span.big { font-size: 2em; } +span.small { font-size: 0.6em; } + +span.underline { text-decoration: underline; } +span.overline { text-decoration: overline; } +span.line-through { text-decoration: line-through; } + + +/* + * xhtml11 specific + * + * */ + +tt { + font-family: monospace; + font-size: inherit; + color: navy; +} + +div.tableblock { + margin-top: 1.0em; + margin-bottom: 1.5em; +} +div.tableblock > table { + border: 3px solid #527bbd; +} +thead, p.table.header { + font-weight: bold; + color: #527bbd; +} +p.table { + margin-top: 0; +} +/* Because the table frame attribute is overriden by CSS in most browsers. */ +div.tableblock > table[frame="void"] { + border-style: none; +} +div.tableblock > table[frame="hsides"] { + border-left-style: none; + border-right-style: none; +} +div.tableblock > table[frame="vsides"] { + border-top-style: none; + border-bottom-style: none; +} + + +/* + * html5 specific + * + * */ + +.monospaced { + font-family: monospace; + font-size: inherit; + color: navy; +} + +table.tableblock { + margin-top: 1.0em; + margin-bottom: 1.5em; +} +thead, p.tableblock.header { + font-weight: bold; + color: #527bbd; +} +p.tableblock { + margin-top: 0; +} +table.tableblock { + border-width: 3px; + border-spacing: 0px; + border-style: solid; + border-color: #527bbd; + border-collapse: collapse; +} +th.tableblock, td.tableblock { + border-width: 1px; + padding: 4px; + border-style: solid; + border-color: #527bbd; +} + +table.tableblock.frame-topbot { + border-left-style: hidden; + border-right-style: hidden; +} +table.tableblock.frame-sides { + border-top-style: hidden; + border-bottom-style: hidden; +} +table.tableblock.frame-none { + border-style: hidden; +} + +th.tableblock.halign-left, td.tableblock.halign-left { + text-align: left; +} +th.tableblock.halign-center, td.tableblock.halign-center { + text-align: center; +} +th.tableblock.halign-right, td.tableblock.halign-right { + text-align: right; +} + +th.tableblock.valign-top, td.tableblock.valign-top { + vertical-align: top; +} +th.tableblock.valign-middle, td.tableblock.valign-middle { + vertical-align: middle; +} +th.tableblock.valign-bottom, td.tableblock.valign-bottom { + vertical-align: bottom; +} + + +/* + * Theme specific overrides of the preceding (asciidoc.css) CSS. + * + */ +body { + font-family: Garamond, Georgia, serif; + font-size: 17px; + color: #3E4349; + line-height: 1.3em; +} +h1, h2, h3, h4, h5, h6, +div.title, caption.title, +thead, p.table.header, +#toctitle, +#author, #revnumber, #revdate, #revremark, +#footer { + font-family: Garmond, Georgia, serif; + font-weight: normal; + border-bottom-width: 0; + color: #3E4349; +} +div.title, caption.title { color: #596673; font-weight: bold; } +h1 { font-size: 240%; } +h2 { font-size: 180%; } +h3 { font-size: 150%; } +h4 { font-size: 130%; } +h5 { font-size: 100%; } +h6 { font-size: 100%; } +#header h1 { margin-top: 0; } +#toc { + color: #444444; + line-height: 1.5; + padding-top: 1.5em; +} +#toctitle { + font-size: 20px; +} +#toc a { + border-bottom: 1px dotted #999999; + color: #444444 !important; + text-decoration: none !important; +} +#toc a:hover { + border-bottom: 1px solid #6D4100; + color: #6D4100 !important; + text-decoration: none !important; +} +div.toclevel1 { margin-top: 0.2em; font-size: 16px; } +div.toclevel2 { margin-top: 0.15em; font-size: 14px; } +em, dt, td.hdlist1 { color: black; } +strong { color: #3E4349; } +a { color: #004B6B; text-decoration: none; border-bottom: 1px dotted #004B6B; } +a:visited { color: #615FA0; border-bottom: 1px dotted #615FA0; } +a:hover { color: #6D4100; border-bottom: 1px solid #6D4100; } +div.tableblock > table, table.tableblock { border: 3px solid #E8E8E8; } +th.tableblock, td.tableblock { border: 1px solid #E8E8E8; } +ul > li > * { color: #3E4349; } +pre, tt, .monospaced { font-family: Consolas,Menlo,'Deja Vu Sans Mono','Bitstream Vera Sans Mono',monospace; } +tt, .monospaced { font-size: 0.9em; color: black; +} +div.exampleblock > div.content, div.sidebarblock > div.content, div.listingblock > div.content { border-width: 0 0 0 3px; border-color: #E8E8E8; } +div.verseblock { border-left-width: 0; margin-left: 3em; } +div.quoteblock { border-left-width: 3px; margin-left: 0; margin-right: 0;} +div.admonitionblock td.content { border-left: 3px solid #E8E8E8; } diff --git a/doc/stylesheets/pygments.css b/doc/stylesheets/pygments.css new file mode 100644 index 0000000..9ca3659 --- /dev/null +++ b/doc/stylesheets/pygments.css @@ -0,0 +1,66 @@ +/* + pygmentize filter +*/ +.highlight .hll { background-color: #ffffcc } +.highlight { background: #f4f4f4; } +.highlight .c { color: #008800; font-style: italic } /* Comment */ +.highlight .err { border: 1px solid #FF0000 } /* Error */ +.highlight .k { color: #AA22FF; font-weight: bold } /* Keyword */ +.highlight .o { color: #666666 } /* Operator */ +.highlight .cm { color: #008800; font-style: italic } /* Comment.Multiline */ +.highlight .cp { color: #008800 } /* Comment.Preproc */ +.highlight .c1 { color: #008800; font-style: italic } /* Comment.Single */ +.highlight .cs { color: #008800; font-weight: bold } /* Comment.Special */ +.highlight .gd { color: #A00000 } /* Generic.Deleted */ +.highlight .ge { font-style: italic } /* Generic.Emph */ +.highlight .gr { color: #FF0000 } /* Generic.Error */ +.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ +.highlight .gi { color: #00A000 } /* Generic.Inserted */ +.highlight .go { color: #808080 } /* Generic.Output */ +.highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ +.highlight .gs { font-weight: bold } /* Generic.Strong */ +.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ +.highlight .gt { color: #0040D0 } /* Generic.Traceback */ +.highlight .kc { color: #AA22FF; font-weight: bold } /* Keyword.Constant */ +.highlight .kd { color: #AA22FF; font-weight: bold } /* Keyword.Declaration */ +.highlight .kn { color: #AA22FF; font-weight: bold } /* Keyword.Namespace */ +.highlight .kp { color: #AA22FF } /* Keyword.Pseudo */ +.highlight .kr { color: #AA22FF; font-weight: bold } /* Keyword.Reserved */ +.highlight .kt { color: #00BB00; font-weight: bold } /* Keyword.Type */ +.highlight .m { color: #666666 } /* Literal.Number */ +.highlight .s { color: #BB4444 } /* Literal.String */ +.highlight .na { color: #BB4444 } /* Name.Attribute */ +.highlight .nb { color: #AA22FF } /* Name.Builtin */ +.highlight .nc { color: #0000FF } /* Name.Class */ +.highlight .no { color: #880000 } /* Name.Constant */ +.highlight .nd { color: #AA22FF } /* Name.Decorator */ +.highlight .ni { color: #999999; font-weight: bold } /* Name.Entity */ +.highlight .ne { color: #D2413A; font-weight: bold } /* Name.Exception */ +.highlight .nf { color: #00A000 } /* Name.Function */ +.highlight .nl { color: #A0A000 } /* Name.Label */ +.highlight .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ +.highlight .nt { color: #008000; font-weight: bold } /* Name.Tag */ +.highlight .nv { color: #B8860B } /* Name.Variable */ +.highlight .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ +.highlight .w { color: #bbbbbb } /* Text.Whitespace */ +.highlight .mf { color: #666666 } /* Literal.Number.Float */ +.highlight .mh { color: #666666 } /* Literal.Number.Hex */ +.highlight .mi { color: #666666 } /* Literal.Number.Integer */ +.highlight .mo { color: #666666 } /* Literal.Number.Oct */ +.highlight .sb { color: #BB4444 } /* Literal.String.Backtick */ +.highlight .sc { color: #BB4444 } /* Literal.String.Char */ +.highlight .sd { color: #BB4444; font-style: italic } /* Literal.String.Doc */ +.highlight .s2 { color: #BB4444 } /* Literal.String.Double */ +.highlight .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */ +.highlight .sh { color: #BB4444 } /* Literal.String.Heredoc */ +.highlight .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */ +.highlight .sx { color: #008000 } /* Literal.String.Other */ +.highlight .sr { color: #BB6688 } /* Literal.String.Regex */ +.highlight .s1 { color: #BB4444 } /* Literal.String.Single */ +.highlight .ss { color: #B8860B } /* Literal.String.Symbol */ +.highlight .bp { color: #AA22FF } /* Name.Builtin.Pseudo */ +.highlight .vc { color: #B8860B } /* Name.Variable.Class */ +.highlight .vg { color: #B8860B } /* Name.Variable.Global */ +.highlight .vi { color: #B8860B } /* Name.Variable.Instance */ +.highlight .il { color: #666666 } /* Literal.Number.Integer.Long */ + diff --git a/doc/stylesheets/slidy.css b/doc/stylesheets/slidy.css new file mode 100644 index 0000000..bbb790e --- /dev/null +++ b/doc/stylesheets/slidy.css @@ -0,0 +1,445 @@ +/* slidy.css + + Copyright (c) 2005-2010 W3C (MIT, ERCIM, Keio), All Rights Reserved. + W3C liability, trademark, document use and software licensing + rules apply, see: + + http://www.w3.org/Consortium/Legal/copyright-documents + http://www.w3.org/Consortium/Legal/copyright-software +*/ + +/* + SJR: 2010-09-29: Modified for AsciiDoc slidy backend. + Mostly just commented out stuff that is handled by AsciiDoc's CSS files. +*/ + +body +{ + margin: 0 0 0 0; + padding: 0 0 0 0; + width: 100%; + height: 100%; + color: black; + background-color: white; +/* + font-family: "Gill Sans MT", "Gill Sans", GillSans, sans-serif; +*/ + font-size: 14pt; +} + +div.toolbar { + position: fixed; z-index: 200; + top: auto; bottom: 0; left: 0; right: 0; + height: 1.2em; text-align: right; + padding-left: 1em; + padding-right: 1em; + font-size: 60%; + color: red; + background-color: rgb(240,240,240); + border-top: solid 1px rgb(180,180,180); +} + +div.toolbar span.copyright { + color: black; + margin-left: 0.5em; +} + +div.initial_prompt { + position: absolute; + z-index: 1000; + bottom: 1.2em; + width: 90%; + background-color: rgb(200,200,200); + opacity: 0.35; + background-color: rgb(200,200,200, 0.35); + cursor: pointer; +} + +div.initial_prompt p.help { + text-align: center; +} + +div.initial_prompt p.close { + text-align: right; + font-style: italic; +} + +div.slidy_toc { + position: absolute; + z-index: 300; + width: 60%; + max-width: 30em; + height: 30em; + overflow: auto; + top: auto; + right: auto; + left: 4em; + bottom: 4em; + padding: 1em; + background: rgb(240,240,240); + border-style: solid; + border-width: 2px; + font-size: 60%; +} + +div.slidy_toc .toc_heading { + text-align: center; + width: 100%; + margin: 0; + margin-bottom: 1em; + border-bottom-style: solid; + border-bottom-color: rgb(180,180,180); + border-bottom-width: 1px; +} + +div.slide { + z-index: 20; + margin: 0 0 0 0; + padding-top: 0; + padding-bottom: 0; + padding-left: 20px; + padding-right: 20px; + border-width: 0; + clear: both; + top: 0; + bottom: 0; + left: 0; + right: 0; + line-height: 120%; + background-color: transparent; +} + +div.background { + display: none; +} + +div.handout { + margin-left: 20px; + margin-right: 20px; +} + +div.slide.titlepage { + text-align: center; +} + +div.slide.titlepage.h1 { + padding-top: 10%; +} + +div.slide h1 { + padding-left: 0; + padding-right: 20pt; + padding-top: 4pt; + padding-bottom: 4pt; + margin-top: 0; + margin-left: 0; + margin-right: 60pt; + margin-bottom: 0.5em; + display: block; + font-size: 160%; + line-height: 1.2em; + background: transparent; +} + +div.toc { + position: absolute; + top: auto; + bottom: 4em; + left: 4em; + right: auto; + width: 60%; + max-width: 30em; + height: 30em; + border: solid thin black; + padding: 1em; + background: rgb(240,240,240); + color: black; + z-index: 300; + overflow: auto; + display: block; + visibility: visible; +} + +div.toc-heading { + width: 100%; + border-bottom: solid 1px rgb(180,180,180); + margin-bottom: 1em; + text-align: center; +} + +/* +pre { + font-size: 80%; + font-weight: bold; + line-height: 120%; + padding-top: 0.2em; + padding-bottom: 0.2em; + padding-left: 1em; + padding-right: 1em; + border-style: solid; + border-left-width: 1em; + border-top-width: thin; + border-right-width: thin; + border-bottom-width: thin; + border-color: #95ABD0; + color: #00428C; + background-color: #E4E5E7; +} +*/ + +/* +li pre { margin-left: 0; } + +blockquote { font-style: italic } + +img { background-color: transparent } + +p.copyright { font-size: smaller } +*/ + +.center { text-align: center } +.footnote { font-size: smaller; margin-left: 2em; } + +/* +a img { border-width: 0; border-style: none } +*/ + +a:visited { color: navy } +a:link { color: navy } +a:hover { color: red; text-decoration: underline } +a:active { color: red; text-decoration: underline } + +a {text-decoration: none} +.navbar a:link {color: white} +.navbar a:visited {color: yellow} +.navbar a:active {color: red} +.navbar a:hover {color: red} + +/* +ul { list-style-type: square; } +ul ul { list-style-type: disc; } +ul ul ul { list-style-type: circle; } +ul ul ul ul { list-style-type: disc; } +li { margin-left: 0.5em; margin-top: 0.5em; } +li li { font-size: 85%; font-style: italic } +li li li { font-size: 85%; font-style: normal } +*/ + +div dt +{ + margin-left: 0; + margin-top: 1em; + margin-bottom: 0.5em; + font-weight: bold; +} +div dd +{ + margin-left: 2em; + margin-bottom: 0.5em; +} + + +/* +p,pre,ul,ol,blockquote,h2,h3,h4,h5,h6,dl,table { + margin-left: 1em; + margin-right: 1em; +} +*/ + +p.subhead { font-weight: bold; margin-top: 2em; } + +.smaller { font-size: smaller } +.bigger { font-size: 130% } + +/* +td,th { padding: 0.2em } +*/ + +ul { + margin: 0.5em 1.5em 0.5em 1.5em; + padding: 0; +} + +ol { + margin: 0.5em 1.5em 0.5em 1.5em; + padding: 0; +} + +ul { list-style-type: square; } +ul ul { list-style-type: disc; } +ul ul ul { list-style-type: circle; } +ul ul ul ul { list-style-type: disc; } + +/* +ul li { + list-style: square; + margin: 0.1em 0em 0.6em 0; + padding: 0 0 0 0; + line-height: 140%; +} + +ol li { + margin: 0.1em 0em 0.6em 1.5em; + padding: 0 0 0 0px; + line-height: 140%; + list-style-type: decimal; +} + +li ul li { + font-size: 85%; + font-style: italic; + list-style-type: disc; + background: transparent; + padding: 0 0 0 0; +} +li li ul li { + font-size: 85%; + font-style: normal; + list-style-type: circle; + background: transparent; + padding: 0 0 0 0; +} +li li li ul li { + list-style-type: disc; + background: transparent; + padding: 0 0 0 0; +} + +li ol li { + list-style-type: decimal; +} + + +li li ol li { + list-style-type: decimal; +} +*/ + +/* + setting class="outline" on ol or ul makes it behave as an + ouline list where blocklevel content in li elements is + hidden by default and can be expanded or collapsed with + mouse click. Set class="expand" on li to override default +*/ + +ol.outline li:hover { cursor: pointer } +ol.outline li.nofold:hover { cursor: default } + +ul.outline li:hover { cursor: pointer } +ul.outline li.nofold:hover { cursor: default } + +ol.outline { list-style:decimal; } +ol.outline ol { list-style-type:lower-alpha } + +ol.outline li.nofold { + padding: 0 0 0 20px; + background: transparent url(../graphics/nofold-dim.gif) no-repeat 0px 0.5em; +} +ol.outline li.unfolded { + padding: 0 0 0 20px; + background: transparent url(../graphics/fold-dim.gif) no-repeat 0px 0.5em; +} +ol.outline li.folded { + padding: 0 0 0 20px; + background: transparent url(../graphics/unfold-dim.gif) no-repeat 0px 0.5em; +} +ol.outline li.unfolded:hover { + padding: 0 0 0 20px; + background: transparent url(../graphics/fold.gif) no-repeat 0px 0.5em; +} +ol.outline li.folded:hover { + padding: 0 0 0 20px; + background: transparent url(../graphics/unfold.gif) no-repeat 0px 0.5em; +} + +ul.outline li.nofold { + padding: 0 0 0 20px; + background: transparent url(../graphics/nofold-dim.gif) no-repeat 0px 0.5em; +} +ul.outline li.unfolded { + padding: 0 0 0 20px; + background: transparent url(../graphics/fold-dim.gif) no-repeat 0px 0.5em; +} +ul.outline li.folded { + padding: 0 0 0 20px; + background: transparent url(../graphics/unfold-dim.gif) no-repeat 0px 0.5em; +} +ul.outline li.unfolded:hover { + padding: 0 0 0 20px; + background: transparent url(../graphics/fold.gif) no-repeat 0px 0.5em; +} +ul.outline li.folded:hover { + padding: 0 0 0 20px; + background: transparent url(../graphics/unfold.gif) no-repeat 0px 0.5em; +} + +/* for slides with class "title" in table of contents */ +a.titleslide { font-weight: bold; font-style: italic } + +/* + hide images for work around for save as bug + where browsers fail to save images used by CSS +*/ +img.hidden { display: none; visibility: hidden } +div.initial_prompt { display: none; visibility: hidden } + + div.slide { + visibility: visible; + position: inherit; + } + div.handout { + border-top-style: solid; + border-top-width: thin; + border-top-color: black; + } + +@media screen { + .hidden { display: none; visibility: visible } + + div.slide.hidden { display: block; visibility: visible } + div.handout.hidden { display: block; visibility: visible } + div.background { display: none; visibility: hidden } + body.single_slide div.initial_prompt { display: block; visibility: visible } + body.single_slide div.background { display: block; visibility: visible } + body.single_slide div.background.hidden { display: none; visibility: hidden } + body.single_slide .invisible { visibility: hidden } + body.single_slide .hidden { display: none; visibility: hidden } + body.single_slide div.slide { position: absolute } + body.single_slide div.handout { display: none; visibility: hidden } +} + +@media print { + .hidden { display: block; visibility: visible } + +/* + div.slide pre { font-size: 60%; padding-left: 0.5em; } +*/ + div.toolbar { display: none; visibility: hidden; } + div.slidy_toc { display: none; visibility: hidden; } + div.background { display: none; visibility: hidden; } + div.slide { page-break-before: always } + /* :first-child isn't reliable for print media */ + div.slide.first-slide { page-break-before: avoid } +} + + +/* SJR: AsciiDoc slidy backend tweaks */ + +ol, ul { + margin: 0.8em 1.5em 0.8em 1.8em; +} +li > ul, li > ol { + margin-top: 0.5em; +} + +.outline > li.folded, +.outline > li.unfolded { + color: #527bbd; +} +ul > li{ color: #aaa; } +ul > li > *, ol > li > * { color: black; } + +li { + margin-top: 0.5em; + margin-bottom: 0.5em; +} diff --git a/doc/stylesheets/toc2.css b/doc/stylesheets/toc2.css new file mode 100644 index 0000000..a1e368b --- /dev/null +++ b/doc/stylesheets/toc2.css @@ -0,0 +1,34 @@ +@media screen { + body { + max-width: 50em; /* approximately 80 characters wide */ + margin-left: 16em; + } + + #toc { + position: fixed; + top: 0; + left: 0; + bottom: 0; + width: 13em; + padding: 0.5em; + padding-bottom: 1.5em; + margin: 0; + overflow: auto; + border-right: 3px solid #f8f8f8; + } + + #toc .toclevel1 { + margin-top: 0.5em; + } + + #toc .toclevel2 { + margin-top: 0.25em; + display: list-item; + /* OLD color: #aaaaaa; */ + color: #990000; + } + + #toctitle { + margin-top: 0.5em; + } +} diff --git a/doc/stylesheets/volnitsky-manpage.css b/doc/stylesheets/volnitsky-manpage.css new file mode 100644 index 0000000..75a2dda --- /dev/null +++ b/doc/stylesheets/volnitsky-manpage.css @@ -0,0 +1 @@ +/* Empty placeholder file */ diff --git a/doc/stylesheets/volnitsky.css b/doc/stylesheets/volnitsky.css new file mode 100644 index 0000000..b6c4a15 --- /dev/null +++ b/doc/stylesheets/volnitsky.css @@ -0,0 +1,435 @@ +/* + * AsciiDoc 'volnitsky' theme for xhtml11 and html5 backends. + * Based on css from http://volnitsky.com, which was in turn based on default + * theme from AsciiDoc + * + * FIXME: The stlying is still a bit rough in places. + * + */ + +/* Default font. */ +body { + font-family: Georgia,"Times New Roman",Times,serif; +} + +/* Title font. */ +h1, h2, h3, h4, h5, h6, +div.title, caption.title, +thead, p.table.header, +#toctitle, +#author, #revnumber, #revdate, #revremark, +#footer { + font-family: Candara,Arial,sans-serif; +} + + +#toc a { + border-bottom: 1px dotted #999999; + color: #3A3A4D !important; + text-decoration: none !important; +} +#toc a:hover { + border-bottom: 1px solid #6D4100; + color: #6D4100 !important; + text-decoration: none !important; +} +a { color: #666688; text-decoration: none; border-bottom: 1px dotted #666688; } +a:visited { color: #615FA0; border-bottom: 1px dotted #615FA0; } +a:hover { color: #6D4100; border-bottom: 1px solid #6D4100; } + +em { + font-style: italic; + color: #444466; +} + +strong { + font-weight: bold; + color: #444466; +} + +h1, h2, h3, h4, h5, h6 { + color: #666688; + margin-bottom: 0.5em; + line-height: 1.3; + letter-spacing:+0.15em; +} + +h1, h2, h3 { border-bottom: 2px solid #ccd; } +h2 { padding-top: 0.5em; } +h3 { float: left; } +h3 + * { clear: left; } + +div.sectionbody { + margin-left: 0; +} + +hr { + border: 1px solid #444466; +} + +p { + margin-top: 0.5em; + margin-bottom: 0.5em; +} + +ul, ol, li > p { + margin-top: 0; +} + +pre { + padding: 0; + margin: 0; +} + +#author { + color: #444466; + font-weight: bold; + font-size: 1.1em; +} + +#footer { + font-size: small; + border-top: 2px solid silver; + padding-top: 0.5em; + margin-top: 4.0em; +} + +#footer-text { + float: left; + padding-bottom: 0.5em; +} + +#footer-badges { + float: right; + padding-bottom: 0.5em; +} + +#preamble { + margin-top: 1.5em; + margin-bottom: 1.5em; +} + +div.tableblock, div.imageblock, div.exampleblock, div.verseblock, +div.quoteblock, div.literalblock, div.listingblock, div.sidebarblock, +div.admonitionblock { + margin-top: 1.5em; + margin-bottom: 1.5em; +} + +div.admonitionblock { + margin-top: 2.5em; + margin-bottom: 2.5em; +} + +div.content { /* Block element content. */ + padding: 0; +} + +/* Block element titles. */ +div.title, caption.title { + color: #444466; + font-weight: bold; + text-align: left; + margin-top: 1.0em; + margin-bottom: 0.5em; +} +div.title + * { + margin-top: 0; +} + +td div.title:first-child { + margin-top: 0.0em; +} +div.content div.title:first-child { + margin-top: 0.0em; +} +div.content + div.title { + margin-top: 0.0em; +} + +div.sidebarblock > div.content { + background: #ffffee; + border: 1px solid silver; + padding: 0.5em; +} + +div.listingblock > div.content { + border: 1px solid silver; + background: #f4f4f4; + padding: 0.5em; +} + +div.quoteblock { + padding-left: 2.0em; + margin-right: 10%; +} +div.quoteblock > div.attribution { + padding-top: 0.5em; + text-align: right; +} + +div.verseblock { + padding-left: 2.0em; + margin-right: 10%; +} +div.verseblock > pre.content { + font-family: inherit; +} +div.verseblock > div.attribution { + padding-top: 0.75em; + text-align: left; +} +/* DEPRECATED: Pre version 8.2.7 verse style literal block. */ +div.verseblock + div.attribution { + text-align: left; +} + +div.admonitionblock .icon { + vertical-align: top; + font-size: 1.1em; + font-weight: bold; + text-decoration: underline; + color: #444466; + padding-right: 0.5em; +} +div.admonitionblock td.content { + padding-left: 0.5em; + border-left: 2px solid silver; +} + +div.exampleblock > div.content { + border-left: 2px solid silver; + padding: 0.5em; +} + +div.imageblock div.content { padding-left: 0; } +span.image img { border-style: none; } +a.image:visited { color: white; } + +dl { + margin-top: 0.8em; + margin-bottom: 0.8em; +} +dt { + margin-top: 0.5em; + margin-bottom: 0; + font-style: normal; + color: #444466; +} +dd > *:first-child { + margin-top: 0.1em; +} + +ul, ol { + list-style-position: outside; +} +ol.arabic { + list-style-type: decimal; +} +ol.loweralpha { + list-style-type: lower-alpha; +} +ol.upperalpha { + list-style-type: upper-alpha; +} +ol.lowerroman { + list-style-type: lower-roman; +} +ol.upperroman { + list-style-type: upper-roman; +} + +div.compact ul, div.compact ol, +div.compact p, div.compact p, +div.compact div, div.compact div { + margin-top: 0.1em; + margin-bottom: 0.1em; +} + +div.tableblock > table { + border: 3px solid #444466; +} +thead { + font-weight: bold; + color: #444466; +} +tfoot { + font-weight: bold; +} +td > div.verse { + white-space: pre; +} +p.table { + margin-top: 0; +} +/* Because the table frame attribute is overriden by CSS in most browsers. */ +div.tableblock > table[frame="void"] { + border-style: none; +} +div.tableblock > table[frame="hsides"] { + border-left-style: none; + border-right-style: none; +} +div.tableblock > table[frame="vsides"] { + border-top-style: none; + border-bottom-style: none; +} + + +div.hdlist { + margin-top: 0.8em; + margin-bottom: 0.8em; +} +div.hdlist tr { + padding-bottom: 15px; +} +dt.hdlist1.strong, td.hdlist1.strong { + font-weight: bold; +} +td.hdlist1 { + vertical-align: top; + font-style: normal; + padding-right: 0.8em; + color: #444466; +} +td.hdlist2 { + vertical-align: top; +} +div.hdlist.compact tr { + margin: 0; + padding-bottom: 0; +} + +.comment { + background: yellow; +} + +@media print { + #footer-badges { display: none; } +} + +#toctitle { + color: #666688; + font-size: 1.2em; + font-weight: bold; + margin-top: 1.0em; + margin-bottom: 0.1em; +} + +div.toclevel1, div.toclevel2, div.toclevel3, div.toclevel4 { margin-top: 0; margin-bottom: 0; } +div.toclevel1 { margin-top: 0.3em; margin-left: 0; font-size: 1.0em; } +div.toclevel2 { margin-top: 0.25em; margin-left: 2em; font-size: 0.9em; } +div.toclevel3 { margin-left: 4em; font-size: 0.8em; } +div.toclevel4 { margin-left: 6em; font-size: 0.8em; } + +body { + margin: 1em 5%; + max-width: 55em; + padding-left: 0; + +} + +.monospaced, tt, div.listingblock > div.content { + font-family: Consolas, "Andale Mono", "Courier New", monospace; + color: #004400; + background: #f4f4f4; + max-width: 80em; + line-height: 1.2em; +} + +.paragraph p { + line-height: 1.5em; + margin-top: 1em; +} + +.paragraph p, li, dd, .content { max-width: 45em; } +.admonitionblock { max-width: 35em; } + +div.sectionbody div.ulist > ul > li { + list-style-type: square; + color: #aaa; +} + div.sectionbody div.ulist > ul > li > * { + color: black; + /*font-size: 50%;*/ + } + + +div.sectionbody div.ulist > ul > li div.ulist > ul > li { + color: #ccd ; +} + div.sectionbody div.ulist > ul > li div.ulist > ul > li > * { + color: black ; + } + +em { + font-style: normal ! important; + font-weight: bold ! important; + color: #662222 ! important; + letter-spacing:+0.08em ! important; +} + + +/* + * html5 specific + * + * */ + +table.tableblock { + margin-top: 1.0em; + margin-bottom: 1.5em; +} +thead, p.tableblock.header { + font-weight: bold; + color: #666688; +} +p.tableblock { + margin-top: 0; +} +table.tableblock { + border-width: 3px; + border-spacing: 0px; + border-style: solid; + border-color: #444466; + border-collapse: collapse; +} +th.tableblock, td.tableblock { + border-width: 1px; + padding: 4px; + border-style: solid; + border-color: #444466; +} + +table.tableblock.frame-topbot { + border-left-style: hidden; + border-right-style: hidden; +} +table.tableblock.frame-sides { + border-top-style: hidden; + border-bottom-style: hidden; +} +table.tableblock.frame-none { + border-style: hidden; +} + +th.tableblock.halign-left, td.tableblock.halign-left { + text-align: left; +} +th.tableblock.halign-center, td.tableblock.halign-center { + text-align: center; +} +th.tableblock.halign-right, td.tableblock.halign-right { + text-align: right; +} + +th.tableblock.valign-top, td.tableblock.valign-top { + vertical-align: top; +} +th.tableblock.valign-middle, td.tableblock.valign-middle { + vertical-align: middle; +} +th.tableblock.valign-bottom, td.tableblock.valign-bottom { + vertical-align: bottom; +} + + diff --git a/doc/stylesheets/xhtml11-quirks.css b/doc/stylesheets/xhtml11-quirks.css new file mode 100644 index 0000000..b3b46d2 --- /dev/null +++ b/doc/stylesheets/xhtml11-quirks.css @@ -0,0 +1,43 @@ +/* Workarounds for IE6's broken and incomplete CSS2. */ + +div.sidebar-content { + background: #ffffee; + border: 1px solid silver; + padding: 0.5em; +} +div.sidebar-title, div.image-title { + color: #527bbd; + font-family: sans-serif; + font-weight: bold; + margin-top: 0.0em; + margin-bottom: 0.5em; +} + +div.listingblock div.content { + border: 1px solid silver; + background: #f4f4f4; + padding: 0.5em; +} + +div.quoteblock-attribution { + padding-top: 0.5em; + text-align: right; +} + +pre.verseblock-content { + font-family: inherit; +} +div.verseblock-attribution { + padding-top: 0.75em; + text-align: left; +} + +div.exampleblock-content { + border-left: 3px solid #dddddd; + padding-left: 0.5em; +} + +div.imageblock.latex div.image-title { margin-top: 0.5em; } + +/* IE6 sets dynamically generated links as visited. */ +div#toc a:visited { color: blue; } diff --git a/doc/stylesheets/xhtml11.css b/doc/stylesheets/xhtml11.css new file mode 100644 index 0000000..1e6bf5a --- /dev/null +++ b/doc/stylesheets/xhtml11.css @@ -0,0 +1,333 @@ +/* Debug borders */ +p, li, dt, dd, div, pre, h1, h2, h3, h4, h5, h6 { +/* + border: 1px solid red; +*/ +} + +body { + margin: 1em 5% 1em 5%; +} + +a { + color: blue; + text-decoration: underline; +} +a:visited { + color: fuchsia; +} + +em { + font-style: italic; + color: navy; +} + +strong { + font-weight: bold; + color: #083194; +} + +tt { + color: navy; +} + +h1, h2, h3, h4, h5, h6 { + color: #527bbd; + font-family: sans-serif; + margin-top: 1.2em; + margin-bottom: 0.5em; + line-height: 1.3; +} + +h1, h2, h3 { + border-bottom: 2px solid silver; +} +h2 { + padding-top: 0.5em; +} +h3 { + float: left; +} +h3 + * { + clear: left; +} + +div.sectionbody { + font-family: serif; + margin-left: 0; +} + +hr { + border: 1px solid silver; +} + +p { + margin-top: 0.5em; + margin-bottom: 0.5em; +} + +ul, ol, li > p { + margin-top: 0; +} + +pre { + padding: 0; + margin: 0; +} + +span#author { + color: #527bbd; + font-family: sans-serif; + font-weight: bold; + font-size: 1.1em; +} +span#email { +} +span#revnumber, span#revdate, span#revremark { + font-family: sans-serif; +} + +div#footer { + font-family: sans-serif; + font-size: small; + border-top: 2px solid silver; + padding-top: 0.5em; + margin-top: 4.0em; +} +div#footer-text { + float: left; + padding-bottom: 0.5em; +} +div#footer-badges { + float: right; + padding-bottom: 0.5em; +} + +div#preamble { + margin-top: 1.5em; + margin-bottom: 1.5em; +} +div.tableblock, div.imageblock, div.exampleblock, div.verseblock, +div.quoteblock, div.literalblock, div.listingblock, div.sidebarblock, +div.admonitionblock { + margin-top: 1.5em; + margin-bottom: 1.5em; +} +div.admonitionblock { + margin-top: 2.5em; + margin-bottom: 2.5em; +} + +div.content { /* Block element content. */ + padding: 0; +} + +/* Block element titles. */ +div.title, caption.title { + color: #527bbd; + font-family: sans-serif; + font-weight: bold; + text-align: left; + margin-top: 1.0em; + margin-bottom: 0.5em; +} +div.title + * { + margin-top: 0; +} + +td div.title:first-child { + margin-top: 0.0em; +} +div.content div.title:first-child { + margin-top: 0.0em; +} +div.content + div.title { + margin-top: 0.0em; +} + +div.sidebarblock > div.content { + background: #ffffee; + border: 1px solid silver; + padding: 0.5em; +} + +div.listingblock > div.content { + border: 1px solid silver; + background: #f4f4f4; + padding: 0.5em; +} + +div.quoteblock { + padding-left: 2.0em; + margin-right: 10%; +} +div.quoteblock > div.attribution { + padding-top: 0.5em; + text-align: right; +} + +div.verseblock { + padding-left: 2.0em; + margin-right: 10%; +} +div.verseblock > div.content { + white-space: pre; +} +div.verseblock > div.attribution { + padding-top: 0.75em; + text-align: left; +} +/* DEPRECATED: Pre version 8.2.7 verse style literal block. */ +div.verseblock + div.attribution { + text-align: left; +} + +div.admonitionblock .icon { + vertical-align: top; + font-size: 1.1em; + font-weight: bold; + text-decoration: underline; + color: #527bbd; + padding-right: 0.5em; +} +div.admonitionblock td.content { + padding-left: 0.5em; + border-left: 2px solid silver; +} + +div.exampleblock > div.content { + border-left: 2px solid silver; + padding: 0.5em; +} + +div.imageblock div.content { padding-left: 0; } +span.image img { border-style: none; } +a.image:visited { color: white; } + +dl { + margin-top: 0.8em; + margin-bottom: 0.8em; +} +dt { + margin-top: 0.5em; + margin-bottom: 0; + font-style: normal; + color: navy; +} +dd > *:first-child { + margin-top: 0.1em; +} + +ul, ol { + list-style-position: outside; +} +ol.arabic { + list-style-type: decimal; +} +ol.loweralpha { + list-style-type: lower-alpha; +} +ol.upperalpha { + list-style-type: upper-alpha; +} +ol.lowerroman { + list-style-type: lower-roman; +} +ol.upperroman { + list-style-type: upper-roman; +} + +div.compact ul, div.compact ol, +div.compact p, div.compact p, +div.compact div, div.compact div { + margin-top: 0.1em; + margin-bottom: 0.1em; +} + +div.tableblock > table { + border: 3px solid #527bbd; +} +thead { + font-family: sans-serif; + font-weight: bold; +} +tfoot { + font-weight: bold; +} +td > div.verse { + white-space: pre; +} +p.table { + margin-top: 0; +} +/* Because the table frame attribute is overriden by CSS in most browsers. */ +div.tableblock > table[frame="void"] { + border-style: none; +} +div.tableblock > table[frame="hsides"] { + border-left-style: none; + border-right-style: none; +} +div.tableblock > table[frame="vsides"] { + border-top-style: none; + border-bottom-style: none; +} + + +div.hdlist { + margin-top: 0.8em; + margin-bottom: 0.8em; +} +div.hdlist tr { + padding-bottom: 15px; +} +dt.hdlist1.strong, td.hdlist1.strong { + font-weight: bold; +} +td.hdlist1 { + vertical-align: top; + font-style: normal; + padding-right: 0.8em; + color: navy; +} +td.hdlist2 { + vertical-align: top; +} +div.hdlist.compact tr { + margin: 0; + padding-bottom: 0; +} + +.comment { + background: yellow; +} + +@media print { + div#footer-badges { display: none; } +} + +div#toctitle { + color: #527bbd; + font-family: sans-serif; + font-size: 1.1em; + font-weight: bold; + margin-top: 1.0em; + margin-bottom: 0.1em; +} + +div.toclevel1, div.toclevel2, div.toclevel3, div.toclevel4 { + margin-top: 0; + margin-bottom: 0; +} +div.toclevel2 { + margin-left: 2em; + font-size: 0.9em; +} +div.toclevel3 { + margin-left: 4em; + font-size: 0.9em; +} +div.toclevel4 { + margin-left: 6em; + font-size: 0.9em; +} diff --git a/include/Makefile.am b/include/Makefile.am index 16d2e47..e4fe6a4 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -1,6 +1,8 @@ # -*- Makefile -*- -nobase_include_HEADERS = \ +libnlincludedir = $(includedir)/libnl@MAJ_VERSION@ + +nobase_libnlinclude_HEADERS = \ netlink/fib_lookup/lookup.h \ netlink/fib_lookup/request.h \ netlink/genl/ctrl.h \ @@ -8,6 +10,7 @@ nobase_include_HEADERS = \ netlink/genl/genl.h \ netlink/genl/mngt.h \ netlink/netfilter/ct.h \ + netlink/netfilter/exp.h \ netlink/netfilter/log.h \ netlink/netfilter/log_msg.h \ netlink/netfilter/netfilter.h \ @@ -22,22 +25,32 @@ nobase_include_HEADERS = \ netlink/dect/transceiver.h \ netlink/addr.h \ netlink/attr.h \ - netlink/cache-api.h \ netlink/cache.h \ netlink/data.h \ netlink/errno.h \ netlink/handlers.h \ + netlink/hash.h \ + netlink/hashtable.h \ netlink/list.h \ netlink/msg.h \ netlink/netlink-compat.h \ netlink/netlink-kernel.h \ netlink/netlink.h \ - netlink/object-api.h \ netlink/object.h \ + netlink/route/cls/ematch/cmp.h \ + netlink/route/cls/ematch/meta.h \ + netlink/route/cls/ematch/nbyte.h \ + netlink/route/cls/ematch/text.h \ + netlink/route/cls/basic.h \ + netlink/route/cls/cgroup.h \ + netlink/route/cls/ematch.h \ netlink/route/cls/fw.h \ netlink/route/cls/police.h \ netlink/route/cls/u32.h \ - netlink/route/link/info-api.h \ + netlink/route/link/bonding.h \ + netlink/route/link/bridge.h \ + netlink/route/link/can.h \ + netlink/route/link/inet.h \ netlink/route/link/vlan.h \ netlink/route/qdisc/cbq.h \ netlink/route/qdisc/dsmark.h \ @@ -48,6 +61,7 @@ nobase_include_HEADERS = \ netlink/route/qdisc/red.h \ netlink/route/qdisc/sfq.h \ netlink/route/qdisc/tbf.h \ + netlink/route/qdisc/plug.h \ netlink/route/addr.h \ netlink/route/class.h \ netlink/route/classifier.h \ @@ -55,13 +69,73 @@ nobase_include_HEADERS = \ netlink/route/neighbour.h \ netlink/route/neightbl.h \ netlink/route/nexthop.h \ + netlink/route/pktloc.h \ netlink/route/qdisc.h \ netlink/route/route.h \ netlink/route/rtnl.h \ netlink/route/rule.h \ netlink/route/tc.h \ - netlink/route/tc-api.h \ netlink/socket.h \ netlink/types.h \ netlink/utils.h \ - netlink/version.h + netlink/version.h \ + netlink/cache-api.h \ + netlink/object-api.h \ + netlink/route/link/api.h \ + netlink/route/link/info-api.h \ + netlink/route/tc-api.h + + +if ENABLE_CLI +nobase_libnlinclude_HEADERS += \ + netlink/cli/addr.h \ + netlink/cli/class.h \ + netlink/cli/cls.h \ + netlink/cli/ct.h \ + netlink/cli/exp.h \ + netlink/cli/link.h \ + netlink/cli/neigh.h \ + netlink/cli/qdisc.h \ + netlink/cli/route.h \ + netlink/cli/rule.h \ + netlink/cli/tc.h \ + netlink/cli/utils.h +endif + +noinst_HEADERS = \ + linux/fib_rules.h \ + linux/genetlink.h \ + linux/gen_stats.h \ + linux/if_addr.h \ + linux/if_arp.h \ + linux/if_ether.h \ + linux/if.h \ + linux/if_bridge.h \ + linux/if_link.h \ + linux/if_vlan.h \ + linux/inetdevice.h \ + linux/ip_mp_alg.h \ + linux/ipv6.h \ + linux/can/netlink.h \ + linux/neighbour.h \ + linux/netfilter.h \ + linux/netfilter/nf_conntrack_common.h \ + linux/netfilter/nfnetlink_compat.h \ + linux/netfilter/nfnetlink_conntrack.h \ + linux/netfilter/nfnetlink.h \ + linux/netfilter/nfnetlink_log.h \ + linux/netfilter/nfnetlink_queue.h \ + linux/netlink.h \ + linux/pkt_cls.h \ + linux/pkt_sched.h \ + linux/rtnetlink.h \ + linux/snmp.h \ + linux/tc_ematch/tc_em_meta.h \ + netlink-private/genl.h \ + netlink-private/netlink.h \ + netlink-private/tc.h \ + netlink-private/types.h \ + netlink-private/cache-api.h \ + netlink-private/object-api.h \ + netlink-private/route/link/api.h \ + netlink-private/route/tc-api.h diff --git a/include/linux/can/netlink.h b/include/linux/can/netlink.h new file mode 100644 index 0000000..14966dd --- /dev/null +++ b/include/linux/can/netlink.h @@ -0,0 +1,122 @@ +/* + * linux/can/netlink.h + * + * Definitions for the CAN netlink interface + * + * Copyright (c) 2009 Wolfgang Grandegger + * + */ + +#ifndef CAN_NETLINK_H +#define CAN_NETLINK_H + +#include + +/* + * CAN bit-timing parameters + * + * For further information, please read chapter "8 BIT TIMING + * REQUIREMENTS" of the "Bosch CAN Specification version 2.0" + * at http://www.semiconductors.bosch.de/pdf/can2spec.pdf. + */ +struct can_bittiming { + __u32 bitrate; /* Bit-rate in bits/second */ + __u32 sample_point; /* Sample point in one-tenth of a percent */ + __u32 tq; /* Time quanta (TQ) in nanoseconds */ + __u32 prop_seg; /* Propagation segment in TQs */ + __u32 phase_seg1; /* Phase buffer segment 1 in TQs */ + __u32 phase_seg2; /* Phase buffer segment 2 in TQs */ + __u32 sjw; /* Synchronisation jump width in TQs */ + __u32 brp; /* Bit-rate prescaler */ +}; + +/* + * CAN harware-dependent bit-timing constant + * + * Used for calculating and checking bit-timing parameters + */ +struct can_bittiming_const { + char name[16]; /* Name of the CAN controller hardware */ + __u32 tseg1_min; /* Time segement 1 = prop_seg + phase_seg1 */ + __u32 tseg1_max; + __u32 tseg2_min; /* Time segement 2 = phase_seg2 */ + __u32 tseg2_max; + __u32 sjw_max; /* Synchronisation jump width */ + __u32 brp_min; /* Bit-rate prescaler */ + __u32 brp_max; + __u32 brp_inc; +}; + +/* + * CAN clock parameters + */ +struct can_clock { + __u32 freq; /* CAN system clock frequency in Hz */ +}; + +/* + * CAN operational and error states + */ +enum can_state { + CAN_STATE_ERROR_ACTIVE = 0, /* RX/TX error count < 96 */ + CAN_STATE_ERROR_WARNING, /* RX/TX error count < 128 */ + CAN_STATE_ERROR_PASSIVE, /* RX/TX error count < 256 */ + CAN_STATE_BUS_OFF, /* RX/TX error count >= 256 */ + CAN_STATE_STOPPED, /* Device is stopped */ + CAN_STATE_SLEEPING, /* Device is sleeping */ + CAN_STATE_MAX +}; + +/* + * CAN bus error counters + */ +struct can_berr_counter { + __u16 txerr; + __u16 rxerr; +}; + +/* + * CAN controller mode + */ +struct can_ctrlmode { + __u32 mask; + __u32 flags; +}; + +#define CAN_CTRLMODE_LOOPBACK 0x01 /* Loopback mode */ +#define CAN_CTRLMODE_LISTENONLY 0x02 /* Listen-only mode */ +#define CAN_CTRLMODE_3_SAMPLES 0x04 /* Triple sampling mode */ +#define CAN_CTRLMODE_ONE_SHOT 0x08 /* One-Shot mode */ +#define CAN_CTRLMODE_BERR_REPORTING 0x10 /* Bus-error reporting */ + +/* + * CAN device statistics + */ +struct can_device_stats { + __u32 bus_error; /* Bus errors */ + __u32 error_warning; /* Changes to error warning state */ + __u32 error_passive; /* Changes to error passive state */ + __u32 bus_off; /* Changes to bus off state */ + __u32 arbitration_lost; /* Arbitration lost errors */ + __u32 restarts; /* CAN controller re-starts */ +}; + +/* + * CAN netlink interface + */ +enum { + IFLA_CAN_UNSPEC, + IFLA_CAN_BITTIMING, + IFLA_CAN_BITTIMING_CONST, + IFLA_CAN_CLOCK, + IFLA_CAN_STATE, + IFLA_CAN_CTRLMODE, + IFLA_CAN_RESTART_MS, + IFLA_CAN_RESTART, + IFLA_CAN_BERR_COUNTER, + __IFLA_CAN_MAX +}; + +#define IFLA_CAN_MAX (__IFLA_CAN_MAX - 1) + +#endif /* CAN_NETLINK_H */ diff --git a/include/linux/dect.h b/include/linux/dect.h new file mode 100644 index 0000000..e458a9c --- /dev/null +++ b/include/linux/dect.h @@ -0,0 +1,206 @@ +#ifndef _LINUX_DECT_H +#define _LINUX_DECT_H + +#define DECTNAMSIZ 16 + +#include +#include + +/* these have to be macros in order to be usable for module aliases */ +#define DECT_RAW 0 /* raw frames */ +#define DECT_B_SAP 1 /* DLC Broadcast Service */ +#define DECT_S_SAP 2 /* DLC Data Link Service */ +#define DECT_LU1_SAP 3 /* LU1 sockets */ +#define DECT_PROTO_NUM 4 + +/** + * struct sockaddr_dect + * + * @dect_family: address family (AF_DECT) + * @dect_index: cluster index + */ +struct sockaddr_dect { + sa_family_t dect_family; + int dect_index; +}; + +/* raw sockets */ + +#define DECT_RAW_AUXDATA 0 + +/** + * struct dect_raw_auxdata - raw socket auxiliary frame data + * + * @mfn: multi-frame number + * @frame: frame number + * @slot: slot numer + * @rssi: receive signal strength indicator + */ +struct dect_raw_auxdata { + __u32 mfn; + __u8 frame; + __u8 slot; + __u8 rssi; +}; + +#define DECT_BSAP_AUXDATA 0 + +/** + * struct dect_bsap_auxdata + * + * @long_page: message contains a long page + */ +struct dect_bsap_auxdata { + __u8 long_page; +}; + +/** + * enum dect_sapis - S SAP Identifier + * + * @DECT_SAPI_CO_SIGNALLING: connection oriented signalling + * @DECT_SAPI_CL_SIGNALLING: connectionless signalling + * @DECT_SAPI_ANY: wildcard + */ +enum dect_sapis { + DECT_SAPI_CO_SIGNALLING = 0, + DECT_SAPI_CL_SIGNALLING = 3, + DECT_SAPI_ANY = 7, +}; + +/** + * enum dect_llns - Logical Link Numbers + * + * @DECT_LLN_CLASS_U: Class U operation + * @DECT_LLN_CLASS_A: Class A operation + * @DECT_LLN_ASSIGNABLE*: Assignable LLN (class B operation) + * @DECT_LLN_UNASSIGNED: LLN unassigned (class B operation + * @DECT_LLN_ANY: wildcard + */ +enum dect_llns { + DECT_LLN_CLASS_U = 0, + DECT_LLN_CLASS_A = 1, + DECT_LLN_ASSIGNABLE_MIN = 2, + DECT_LLN_ASSIGNABLE_MAX = 6, + DECT_LLN_UNASSIGNED = 7, + DECT_LLN_ANY = 15, +}; + +/** + * struct sockaddr_dect_ssap + * + * @dect_family: family (AF_DECT) + * @dect_lln: logical link number + * @dect_sapi: service access point identifier + * @dect_class: class A/B + * @dect_index: cluster index + * @dect_ari: ARI + * @dect_pmid: PMID + * @dect_lcn: logical connection number + */ +struct sockaddr_dect_ssap { + sa_family_t dect_family; + __u8 dect_lln:4, + dect_sapi:3; + __u8 dect_class; + int dect_index; + __u64 dect_ari:40, + dect_pmid:20, + dect_lcn:3; +}; + +/* S-SAP primitives */ +#define DECT_DL_ENC_KEY 1 +#define DECT_DL_ENCRYPT 2 +#define DECT_DL_MAC_CONN_PARAMS 3 + +enum dect_cipher_states { + DECT_CIPHER_DISABLED, + DECT_CIPHER_ENABLED, +}; + +/** + * enum dect_mac_connection_types - MAC Connection types + * + * @DECT_MAC_CONN_BASIC: Basic connection, always I_N_min_delay service + * @DECT_MAC_CONN_ADVANCED: Advanced connection + * @DECT_MAC_CONN_COMPLEMENT: Complementary connection + */ +enum dect_mac_connection_types { + DECT_MAC_CONN_BASIC, + DECT_MAC_CONN_ADVANCED, + DECT_MAC_CONN_COMPLEMENT, +}; + +enum dect_mac_service_types { + DECT_SERVICE_IN_MIN_DELAY = 0x0, + DECT_SERVICE_IPX_ENCODED_PROTECTED = 0x1, + DECT_SERVICE_IN_NORMAL_DELAY = 0x2, + DECT_SERVICE_UNKNOWN = 0x4, + DECT_SERVICE_C_CHANNEL_ONLY = 0x5, + DECT_SERVICE_IP_ERROR_DETECTION = 0x10, + DECT_SERVICE_IPQ_ERROR_DETECTION = 0x14, + /* Lifetime encoded in low three bits */ + DECT_SERVICE_IP_ERROR_CORRECTION = 0x18, + DECT_SERVICE_IPQ_ERROR_CORRECTION = 0x38, +}; + +/** + * enum dect_slot_types - DECT slot types + * + * @DECT_FULL_SLOT: Full-slot format (480 bits) + * @DECT_HALF_SLOT: Half-slot format (240 bits) + * @DECT_DOUBLE_SLOT: Double-slot format (960 bits) + * @DECT_LONG_SLOT_j640: Long slot format j=640 (800 bits) + * @DECT_LONG_SLOT_j672: Long slot format j=672 (832 bits) + * + * The numeric values must match the MAC-layer attributes-T coding. + */ +enum dect_slot_types { + DECT_FULL_SLOT = 0x0, + DECT_HALF_SLOT = 0x1, + DECT_DOUBLE_SLOT = 0x2, + DECT_LONG_SLOT_640 = 0x3, + DECT_LONG_SLOT_672 = 0x4, +}; + +struct dect_mac_conn_params { + enum dect_mac_connection_types type; + enum dect_mac_service_types service; + enum dect_slot_types slot; +}; + +/** + * struct dect_dl_encrypt - DL_ENCRYPT primitive arguments + * + * @status: desired/achieved encryption status + */ +struct dect_dl_encrypt { + enum dect_cipher_states status; +}; + +/** + * struct sockaddr_dect_lu - DLC U-plane LUx service instance address + * + * @dect_family: address family (AF_DECT) + * @dect_mci: MAC Connection Identifier + */ +struct sockaddr_dect_lu { + sa_family_t dect_family; + int dect_index; + __u64 dect_ari:40, + dect_pmid:20, + dect_lcn:3; +}; + +/* LU1 SAP */ + +#define DECT_LU1_QUEUE_STATS 0 + +struct dect_lu1_queue_stats { + __u32 rx_bytes; + __u32 rx_underflow; + __u32 tx_bytes; + __u32 tx_underflow; +}; + +#endif /* _LINUX_DECT_H */ diff --git a/include/linux/if.h b/include/linux/if.h index 32f910f..238cf43 100644 --- a/include/linux/if.h +++ b/include/linux/if.h @@ -117,6 +117,12 @@ enum { IF_LINK_MODE_DORMANT, /* limit upward transition to dormant */ }; +/* carrier state */ +enum { + IF_CARRIER_DOWN, + IF_CARRIER_UP +}; + /* * Device mapping structure. I'd just gone off and designed a * beautiful scheme using only loadable modules with arguments diff --git a/include/linux/if_addr.h b/include/linux/if_addr.h index 23357ab..c355522 100644 --- a/include/linux/if_addr.h +++ b/include/linux/if_addr.h @@ -52,10 +52,4 @@ struct ifa_cacheinfo { __u32 tstamp; /* updated timestamp, hundredths of seconds */ }; -/* backwards compatibility for userspace */ -#ifndef __KERNEL__ -#define IFA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg)))) -#define IFA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifaddrmsg)) -#endif - #endif diff --git a/include/linux/if_bridge.h b/include/linux/if_bridge.h new file mode 100644 index 0000000..5db2975 --- /dev/null +++ b/include/linux/if_bridge.h @@ -0,0 +1,185 @@ +/* + * Linux ethernet bridge + * + * Authors: + * Lennert Buytenhek + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _UAPI_LINUX_IF_BRIDGE_H +#define _UAPI_LINUX_IF_BRIDGE_H + +#include + +#define SYSFS_BRIDGE_ATTR "bridge" +#define SYSFS_BRIDGE_FDB "brforward" +#define SYSFS_BRIDGE_PORT_SUBDIR "brif" +#define SYSFS_BRIDGE_PORT_ATTR "brport" +#define SYSFS_BRIDGE_PORT_LINK "bridge" + +#define BRCTL_VERSION 1 + +#define BRCTL_GET_VERSION 0 +#define BRCTL_GET_BRIDGES 1 +#define BRCTL_ADD_BRIDGE 2 +#define BRCTL_DEL_BRIDGE 3 +#define BRCTL_ADD_IF 4 +#define BRCTL_DEL_IF 5 +#define BRCTL_GET_BRIDGE_INFO 6 +#define BRCTL_GET_PORT_LIST 7 +#define BRCTL_SET_BRIDGE_FORWARD_DELAY 8 +#define BRCTL_SET_BRIDGE_HELLO_TIME 9 +#define BRCTL_SET_BRIDGE_MAX_AGE 10 +#define BRCTL_SET_AGEING_TIME 11 +#define BRCTL_SET_GC_INTERVAL 12 +#define BRCTL_GET_PORT_INFO 13 +#define BRCTL_SET_BRIDGE_STP_STATE 14 +#define BRCTL_SET_BRIDGE_PRIORITY 15 +#define BRCTL_SET_PORT_PRIORITY 16 +#define BRCTL_SET_PATH_COST 17 +#define BRCTL_GET_FDB_ENTRIES 18 + +#define BR_STATE_DISABLED 0 +#define BR_STATE_LISTENING 1 +#define BR_STATE_LEARNING 2 +#define BR_STATE_FORWARDING 3 +#define BR_STATE_BLOCKING 4 + +struct __bridge_info { + __u64 designated_root; + __u64 bridge_id; + __u32 root_path_cost; + __u32 max_age; + __u32 hello_time; + __u32 forward_delay; + __u32 bridge_max_age; + __u32 bridge_hello_time; + __u32 bridge_forward_delay; + __u8 topology_change; + __u8 topology_change_detected; + __u8 root_port; + __u8 stp_enabled; + __u32 ageing_time; + __u32 gc_interval; + __u32 hello_timer_value; + __u32 tcn_timer_value; + __u32 topology_change_timer_value; + __u32 gc_timer_value; +}; + +struct __port_info { + __u64 designated_root; + __u64 designated_bridge; + __u16 port_id; + __u16 designated_port; + __u32 path_cost; + __u32 designated_cost; + __u8 state; + __u8 top_change_ack; + __u8 config_pending; + __u8 unused0; + __u32 message_age_timer_value; + __u32 forward_delay_timer_value; + __u32 hold_timer_value; +}; + +struct __fdb_entry { + __u8 mac_addr[6]; + __u8 port_no; + __u8 is_local; + __u32 ageing_timer_value; + __u8 port_hi; + __u8 pad0; + __u16 unused; +}; + +/* Bridge Flags */ +#define BRIDGE_FLAGS_MASTER 1 /* Bridge command to/from master */ +#define BRIDGE_FLAGS_SELF 2 /* Bridge command to/from lowerdev */ + +#define BRIDGE_MODE_VEB 0 /* Default loopback mode */ +#define BRIDGE_MODE_VEPA 1 /* 802.1Qbg defined VEPA mode */ + +/* Bridge management nested attributes + * [IFLA_AF_SPEC] = { + * [IFLA_BRIDGE_FLAGS] + * [IFLA_BRIDGE_MODE] + * } + */ +enum { + IFLA_BRIDGE_FLAGS, + IFLA_BRIDGE_MODE, + __IFLA_BRIDGE_MAX, +}; +#define IFLA_BRIDGE_MAX (__IFLA_BRIDGE_MAX - 1) + +/* Bridge multicast database attributes + * [MDBA_MDB] = { + * [MDBA_MDB_ENTRY] = { + * [MDBA_MDB_ENTRY_INFO] + * } + * } + * [MDBA_ROUTER] = { + * [MDBA_ROUTER_PORT] + * } + */ +enum { + MDBA_UNSPEC, + MDBA_MDB, + MDBA_ROUTER, + __MDBA_MAX, +}; +#define MDBA_MAX (__MDBA_MAX - 1) + +enum { + MDBA_MDB_UNSPEC, + MDBA_MDB_ENTRY, + __MDBA_MDB_MAX, +}; +#define MDBA_MDB_MAX (__MDBA_MDB_MAX - 1) + +enum { + MDBA_MDB_ENTRY_UNSPEC, + MDBA_MDB_ENTRY_INFO, + __MDBA_MDB_ENTRY_MAX, +}; +#define MDBA_MDB_ENTRY_MAX (__MDBA_MDB_ENTRY_MAX - 1) + +enum { + MDBA_ROUTER_UNSPEC, + MDBA_ROUTER_PORT, + __MDBA_ROUTER_MAX, +}; +#define MDBA_ROUTER_MAX (__MDBA_ROUTER_MAX - 1) + +struct br_port_msg { + __u8 family; + __u32 ifindex; +}; + +struct br_mdb_entry { + __u32 ifindex; +#define MDB_TEMPORARY 0 +#define MDB_PERMANENT 1 + __u8 state; + struct { + union { + __be32 ip4; + struct in6_addr ip6; + } u; + __be16 proto; + } addr; +}; + +enum { + MDBA_SET_ENTRY_UNSPEC, + MDBA_SET_ENTRY, + __MDBA_SET_ENTRY_MAX, +}; +#define MDBA_SET_ENTRY_MAX (__MDBA_SET_ENTRY_MAX - 1) + +#endif /* _UAPI_LINUX_IF_BRIDGE_H */ diff --git a/include/linux/if_ether.h b/include/linux/if_ether.h index aead93d..a6af32d 100644 --- a/include/linux/if_ether.h +++ b/include/linux/if_ether.h @@ -72,6 +72,7 @@ #define ETH_P_MPLS_UC 0x8847 /* MPLS Unicast traffic */ #define ETH_P_MPLS_MC 0x8848 /* MPLS Multicast traffic */ #define ETH_P_ATMMPOA 0x884c /* MultiProtocol Over ATM */ +#define ETH_P_LINK_CTL 0x886c /* HPNA, wlan link local tunnel */ #define ETH_P_ATMFATE 0x8884 /* Frame-based ATM Transport * over Ethernet */ diff --git a/include/linux/if_link.h b/include/linux/if_link.h index b60ff97..73ee05c 100644 --- a/include/linux/if_link.h +++ b/include/linux/if_link.h @@ -1,5 +1,5 @@ -#ifndef _LINUX_IF_LINK_H -#define _LINUX_IF_LINK_H +#ifndef _UAPI_LINUX_IF_LINK_H +#define _UAPI_LINUX_IF_LINK_H #include #include @@ -88,12 +88,12 @@ struct rtnl_link_ifmap { * * Example: * [IFLA_AF_SPEC] = { + * [AF_INET] = { + * [IFLA_INET_CONF] = ..., + * }, * [AF_INET6] = { * [IFLA_INET6_FLAGS] = ..., * [IFLA_INET6_CONF] = ..., - * }, - * [AF_BRIDGE] = { - * [IFLA_BRIDGE_PORT] = ... * } * } */ @@ -135,18 +135,20 @@ enum { IFLA_VF_PORTS, IFLA_PORT_SELF, IFLA_AF_SPEC, + IFLA_GROUP, /* Group the device belongs to */ + IFLA_NET_NS_FD, + IFLA_EXT_MASK, /* Extended info mask, VFs, etc */ + IFLA_PROMISCUITY, /* Promiscuity count: > 0 means acts PROMISC */ +#define IFLA_PROMISCUITY IFLA_PROMISCUITY + IFLA_NUM_TX_QUEUES, + IFLA_NUM_RX_QUEUES, + IFLA_CARRIER, __IFLA_MAX }; #define IFLA_MAX (__IFLA_MAX - 1) -/* backwards compatibility for userspace */ -#ifndef __KERNEL__ -#define IFLA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifinfomsg)))) -#define IFLA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifinfomsg)) -#endif - enum { IFLA_INET_UNSPEC, IFLA_INET_CONF, @@ -198,6 +200,24 @@ enum { #define IFLA_INET6_MAX (__IFLA_INET6_MAX - 1) +enum { + BRIDGE_MODE_UNSPEC, + BRIDGE_MODE_HAIRPIN, +}; + +enum { + IFLA_BRPORT_UNSPEC, + IFLA_BRPORT_STATE, /* Spanning tree state */ + IFLA_BRPORT_PRIORITY, /* " priority */ + IFLA_BRPORT_COST, /* " cost */ + IFLA_BRPORT_MODE, /* mode (hairpin) */ + IFLA_BRPORT_GUARD, /* bpdu guard */ + IFLA_BRPORT_PROTECT, /* root port protection */ + IFLA_BRPORT_FAST_LEAVE, /* multicast fast leave */ + __IFLA_BRPORT_MAX +}; +#define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1) + struct ifla_cacheinfo { __u32 max_reasm_len; __u32 tstamp; /* ipv6InterfaceTable updated timestamp */ @@ -250,6 +270,7 @@ struct ifla_vlan_qos_mapping { enum { IFLA_MACVLAN_UNSPEC, IFLA_MACVLAN_MODE, + IFLA_MACVLAN_FLAGS, __IFLA_MACVLAN_MAX, }; @@ -259,6 +280,35 @@ enum macvlan_mode { MACVLAN_MODE_PRIVATE = 1, /* don't talk to other macvlans */ MACVLAN_MODE_VEPA = 2, /* talk to other ports through ext bridge */ MACVLAN_MODE_BRIDGE = 4, /* talk to bridge ports directly */ + MACVLAN_MODE_PASSTHRU = 8,/* take over the underlying device */ +}; + +#define MACVLAN_FLAG_NOPROMISC 1 + +/* VXLAN section */ +enum { + IFLA_VXLAN_UNSPEC, + IFLA_VXLAN_ID, + IFLA_VXLAN_GROUP, + IFLA_VXLAN_LINK, + IFLA_VXLAN_LOCAL, + IFLA_VXLAN_TTL, + IFLA_VXLAN_TOS, + IFLA_VXLAN_LEARNING, + IFLA_VXLAN_AGEING, + IFLA_VXLAN_LIMIT, + IFLA_VXLAN_PORT_RANGE, + IFLA_VXLAN_PROXY, + IFLA_VXLAN_RSC, + IFLA_VXLAN_L2MISS, + IFLA_VXLAN_L3MISS, + __IFLA_VXLAN_MAX +}; +#define IFLA_VXLAN_MAX (__IFLA_VXLAN_MAX - 1) + +struct ifla_vxlan_port_range { + __be16 low; + __be16 high; }; /* SR-IOV virtual function management section */ @@ -276,6 +326,7 @@ enum { IFLA_VF_MAC, /* Hardware queue specific attributes */ IFLA_VF_VLAN, IFLA_VF_TX_RATE, /* TX Bandwidth Allocation */ + IFLA_VF_SPOOFCHK, /* Spoof Checking on/off switch */ __IFLA_VF_MAX, }; @@ -297,12 +348,9 @@ struct ifla_vf_tx_rate { __u32 rate; /* Max TX bandwidth in Mbps, 0 disables throttling */ }; -struct ifla_vf_info { +struct ifla_vf_spoofchk { __u32 vf; - __u8 mac[32]; - __u32 vlan; - __u32 qos; - __u32 tx_rate; + __u32 setting; }; /* VF ports management section @@ -377,4 +425,22 @@ struct ifla_port_vsi { __u8 pad[3]; }; -#endif /* _LINUX_IF_LINK_H */ + +/* IPoIB section */ + +enum { + IFLA_IPOIB_UNSPEC, + IFLA_IPOIB_PKEY, + IFLA_IPOIB_MODE, + IFLA_IPOIB_UMCAST, + __IFLA_IPOIB_MAX +}; + +enum { + IPOIB_MODE_DATAGRAM = 0, /* using unreliable datagram QPs */ + IPOIB_MODE_CONNECTED = 1, /* using connected QPs */ +}; + +#define IFLA_IPOIB_MAX (__IFLA_IPOIB_MAX - 1) + +#endif /* _UAPI_LINUX_IF_LINK_H */ diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h index aa2b67f..f16349d 100644 --- a/include/linux/ipv6.h +++ b/include/linux/ipv6.h @@ -1,6 +1,8 @@ #ifndef _IPV6_H #define _IPV6_H +#include + /* The latest drafts declared increase in minimal mtu up to 1280. */ #define IPV6_MIN_MTU 1280 @@ -12,12 +14,6 @@ */ -struct in6_ifreq { - struct in6_addr ifr6_addr; - __u32 ifr6_prefixlen; - int ifr6_ifindex; -}; - #define IPV6_SRCRT_STRICT 0x01 /* Deprecated; will be removed */ #define IPV6_SRCRT_TYPE_0 0 /* Deprecated; will be removed */ #define IPV6_SRCRT_TYPE_2 2 /* IPv6 type 2 Routing Header */ @@ -49,6 +45,7 @@ struct ipv6_opt_hdr { #define ipv6_destopt_hdr ipv6_opt_hdr #define ipv6_hopopt_hdr ipv6_opt_hdr + /* * routing header type 0 (used in cmsghdr struct) */ @@ -83,6 +80,34 @@ struct ipv6_destopt_hao { struct in6_addr addr; } __attribute__((packed)); +/* + * IPv6 fixed header + * + * BEWARE, it is incorrect. The first 4 bits of flow_lbl + * are glued to priority now, forming "class". + */ + +struct ipv6hdr { +#if defined(__LITTLE_ENDIAN_BITFIELD) + __u8 priority:4, + version:4; +#elif defined(__BIG_ENDIAN_BITFIELD) + __u8 version:4, + priority:4; +#else +#error "Please fix " +#endif + __u8 flow_lbl[3]; + + __be16 payload_len; + __u8 nexthdr; + __u8 hop_limit; + + struct in6_addr saddr; + struct in6_addr daddr; +}; + + /* index values for the variables in ipv6_devconf */ enum { DEVCONF_FORWARDING = 0, @@ -117,4 +142,5 @@ enum { DEVCONF_MAX }; + #endif /* _IPV6_H */ diff --git a/include/linux/netfilter/nf_conntrack_common.h b/include/linux/netfilter/nf_conntrack_common.h new file mode 100644 index 0000000..1644cdd --- /dev/null +++ b/include/linux/netfilter/nf_conntrack_common.h @@ -0,0 +1,117 @@ +#ifndef _UAPI_NF_CONNTRACK_COMMON_H +#define _UAPI_NF_CONNTRACK_COMMON_H +/* Connection state tracking for netfilter. This is separated from, + but required by, the NAT layer; it can also be used by an iptables + extension. */ +enum ip_conntrack_info { + /* Part of an established connection (either direction). */ + IP_CT_ESTABLISHED, + + /* Like NEW, but related to an existing connection, or ICMP error + (in either direction). */ + IP_CT_RELATED, + + /* Started a new connection to track (only + IP_CT_DIR_ORIGINAL); may be a retransmission. */ + IP_CT_NEW, + + /* >= this indicates reply direction */ + IP_CT_IS_REPLY, + + IP_CT_ESTABLISHED_REPLY = IP_CT_ESTABLISHED + IP_CT_IS_REPLY, + IP_CT_RELATED_REPLY = IP_CT_RELATED + IP_CT_IS_REPLY, + IP_CT_NEW_REPLY = IP_CT_NEW + IP_CT_IS_REPLY, + /* Number of distinct IP_CT types (no NEW in reply dirn). */ + IP_CT_NUMBER = IP_CT_IS_REPLY * 2 - 1 +}; + +/* Bitset representing status of connection. */ +enum ip_conntrack_status { + /* It's an expected connection: bit 0 set. This bit never changed */ + IPS_EXPECTED_BIT = 0, + IPS_EXPECTED = (1 << IPS_EXPECTED_BIT), + + /* We've seen packets both ways: bit 1 set. Can be set, not unset. */ + IPS_SEEN_REPLY_BIT = 1, + IPS_SEEN_REPLY = (1 << IPS_SEEN_REPLY_BIT), + + /* Conntrack should never be early-expired. */ + IPS_ASSURED_BIT = 2, + IPS_ASSURED = (1 << IPS_ASSURED_BIT), + + /* Connection is confirmed: originating packet has left box */ + IPS_CONFIRMED_BIT = 3, + IPS_CONFIRMED = (1 << IPS_CONFIRMED_BIT), + + /* Connection needs src nat in orig dir. This bit never changed. */ + IPS_SRC_NAT_BIT = 4, + IPS_SRC_NAT = (1 << IPS_SRC_NAT_BIT), + + /* Connection needs dst nat in orig dir. This bit never changed. */ + IPS_DST_NAT_BIT = 5, + IPS_DST_NAT = (1 << IPS_DST_NAT_BIT), + + /* Both together. */ + IPS_NAT_MASK = (IPS_DST_NAT | IPS_SRC_NAT), + + /* Connection needs TCP sequence adjusted. */ + IPS_SEQ_ADJUST_BIT = 6, + IPS_SEQ_ADJUST = (1 << IPS_SEQ_ADJUST_BIT), + + /* NAT initialization bits. */ + IPS_SRC_NAT_DONE_BIT = 7, + IPS_SRC_NAT_DONE = (1 << IPS_SRC_NAT_DONE_BIT), + + IPS_DST_NAT_DONE_BIT = 8, + IPS_DST_NAT_DONE = (1 << IPS_DST_NAT_DONE_BIT), + + /* Both together */ + IPS_NAT_DONE_MASK = (IPS_DST_NAT_DONE | IPS_SRC_NAT_DONE), + + /* Connection is dying (removed from lists), can not be unset. */ + IPS_DYING_BIT = 9, + IPS_DYING = (1 << IPS_DYING_BIT), + + /* Connection has fixed timeout. */ + IPS_FIXED_TIMEOUT_BIT = 10, + IPS_FIXED_TIMEOUT = (1 << IPS_FIXED_TIMEOUT_BIT), + + /* Conntrack is a template */ + IPS_TEMPLATE_BIT = 11, + IPS_TEMPLATE = (1 << IPS_TEMPLATE_BIT), + + /* Conntrack is a fake untracked entry */ + IPS_UNTRACKED_BIT = 12, + IPS_UNTRACKED = (1 << IPS_UNTRACKED_BIT), + + /* Conntrack got a helper explicitly attached via CT target. */ + IPS_HELPER_BIT = 13, + IPS_HELPER = (1 << IPS_HELPER_BIT), +}; + +/* Connection tracking event types */ +enum ip_conntrack_events { + IPCT_NEW, /* new conntrack */ + IPCT_RELATED, /* related conntrack */ + IPCT_DESTROY, /* destroyed conntrack */ + IPCT_REPLY, /* connection has seen two-way traffic */ + IPCT_ASSURED, /* connection status has changed to assured */ + IPCT_PROTOINFO, /* protocol information has changed */ + IPCT_HELPER, /* new helper has been set */ + IPCT_MARK, /* new mark has been set */ + IPCT_NATSEQADJ, /* NAT is doing sequence adjustment */ + IPCT_SECMARK, /* new security mark has been set */ +}; + +enum ip_conntrack_expect_events { + IPEXP_NEW, /* new expectation */ + IPEXP_DESTROY, /* destroyed expectation */ +}; + +/* expectation flags */ +#define NF_CT_EXPECT_PERMANENT 0x1 +#define NF_CT_EXPECT_INACTIVE 0x2 +#define NF_CT_EXPECT_USERSPACE 0x4 + + +#endif /* _UAPI_NF_CONNTRACK_COMMON_H */ diff --git a/include/linux/netfilter/nfnetlink.h b/include/linux/netfilter/nfnetlink.h index 1e59984..4a4efaf 100644 --- a/include/linux/netfilter/nfnetlink.h +++ b/include/linux/netfilter/nfnetlink.h @@ -1,16 +1,7 @@ -#ifndef _NFNETLINK_H -#define _NFNETLINK_H +#ifndef _UAPI_NFNETLINK_H +#define _UAPI_NFNETLINK_H #include - -#ifndef __KERNEL__ -/* nfnetlink groups: Up to 32 maximum - backwards compatibility for userspace */ -#define NF_NETLINK_CONNTRACK_NEW 0x00000001 -#define NF_NETLINK_CONNTRACK_UPDATE 0x00000002 -#define NF_NETLINK_CONNTRACK_DESTROY 0x00000004 -#define NF_NETLINK_CONNTRACK_EXP_NEW 0x00000008 -#define NF_NETLINK_CONNTRACK_EXP_UPDATE 0x00000010 -#define NF_NETLINK_CONNTRACK_EXP_DESTROY 0x00000020 -#endif +#include enum nfnetlink_groups { NFNLGRP_NONE, @@ -34,8 +25,8 @@ enum nfnetlink_groups { /* General form of address family dependent message. */ struct nfgenmsg { - u_int8_t nfgen_family; /* AF_xxx */ - u_int8_t version; /* nfnetlink version */ + __u8 nfgen_family; /* AF_xxx */ + __u8 version; /* nfnetlink version */ __be16 res_id; /* resource id */ }; @@ -55,6 +46,11 @@ struct nfgenmsg { #define NFNL_SUBSYS_CTNETLINK_EXP 2 #define NFNL_SUBSYS_QUEUE 3 #define NFNL_SUBSYS_ULOG 4 -#define NFNL_SUBSYS_COUNT 5 +#define NFNL_SUBSYS_OSF 5 +#define NFNL_SUBSYS_IPSET 6 +#define NFNL_SUBSYS_ACCT 7 +#define NFNL_SUBSYS_CTNETLINK_TIMEOUT 8 +#define NFNL_SUBSYS_CTHELPER 9 +#define NFNL_SUBSYS_COUNT 10 -#endif /* _NFNETLINK_H */ +#endif /* _UAPI_NFNETLINK_H */ diff --git a/include/linux/netfilter/nfnetlink_compat.h b/include/linux/netfilter/nfnetlink_compat.h new file mode 100644 index 0000000..ffb9503 --- /dev/null +++ b/include/linux/netfilter/nfnetlink_compat.h @@ -0,0 +1,63 @@ +#ifndef _NFNETLINK_COMPAT_H +#define _NFNETLINK_COMPAT_H + +#include + +#ifndef __KERNEL__ +/* Old nfnetlink macros for userspace */ + +/* nfnetlink groups: Up to 32 maximum */ +#define NF_NETLINK_CONNTRACK_NEW 0x00000001 +#define NF_NETLINK_CONNTRACK_UPDATE 0x00000002 +#define NF_NETLINK_CONNTRACK_DESTROY 0x00000004 +#define NF_NETLINK_CONNTRACK_EXP_NEW 0x00000008 +#define NF_NETLINK_CONNTRACK_EXP_UPDATE 0x00000010 +#define NF_NETLINK_CONNTRACK_EXP_DESTROY 0x00000020 + +/* Generic structure for encapsulation optional netfilter information. + * It is reminiscent of sockaddr, but with sa_family replaced + * with attribute type. + * ! This should someday be put somewhere generic as now rtnetlink and + * ! nfnetlink use the same attributes methods. - J. Schulist. + */ + +struct nfattr { + __u16 nfa_len; + __u16 nfa_type; /* we use 15 bits for the type, and the highest + * bit to indicate whether the payload is nested */ +}; + +/* FIXME: Apart from NFNL_NFA_NESTED shamelessly copy and pasted from + * rtnetlink.h, it's time to put this in a generic file */ + +#define NFNL_NFA_NEST 0x8000 +#define NFA_TYPE(attr) ((attr)->nfa_type & 0x7fff) + +#define NFA_ALIGNTO 4 +#define NFA_ALIGN(len) (((len) + NFA_ALIGNTO - 1) & ~(NFA_ALIGNTO - 1)) +#define NFA_OK(nfa,len) ((len) > 0 && (nfa)->nfa_len >= sizeof(struct nfattr) \ + && (nfa)->nfa_len <= (len)) +#define NFA_NEXT(nfa,attrlen) ((attrlen) -= NFA_ALIGN((nfa)->nfa_len), \ + (struct nfattr *)(((char *)(nfa)) + NFA_ALIGN((nfa)->nfa_len))) +#define NFA_LENGTH(len) (NFA_ALIGN(sizeof(struct nfattr)) + (len)) +#define NFA_SPACE(len) NFA_ALIGN(NFA_LENGTH(len)) +#define NFA_DATA(nfa) ((void *)(((char *)(nfa)) + NFA_LENGTH(0))) +#define NFA_PAYLOAD(nfa) ((int)((nfa)->nfa_len) - NFA_LENGTH(0)) +#define NFA_NEST(skb, type) \ +({ struct nfattr *__start = (struct nfattr *)skb_tail_pointer(skb); \ + NFA_PUT(skb, (NFNL_NFA_NEST | type), 0, NULL); \ + __start; }) +#define NFA_NEST_END(skb, start) \ +({ (start)->nfa_len = skb_tail_pointer(skb) - (unsigned char *)(start); \ + (skb)->len; }) +#define NFA_NEST_CANCEL(skb, start) \ +({ if (start) \ + skb_trim(skb, (unsigned char *) (start) - (skb)->data); \ + -1; }) + +#define NFM_NFA(n) ((struct nfattr *)(((char *)(n)) \ + + NLMSG_ALIGN(sizeof(struct nfgenmsg)))) +#define NFM_PAYLOAD(n) NLMSG_PAYLOAD(n, sizeof(struct nfgenmsg)) + +#endif /* ! __KERNEL__ */ +#endif /* _NFNETLINK_COMPAT_H */ diff --git a/include/linux/netfilter/nfnetlink_conntrack.h b/include/linux/netfilter/nfnetlink_conntrack.h index d7c3503..43bfe3e 100644 --- a/include/linux/netfilter/nfnetlink_conntrack.h +++ b/include/linux/netfilter/nfnetlink_conntrack.h @@ -7,6 +7,8 @@ enum cntl_msg_types { 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_MAX }; @@ -15,6 +17,7 @@ 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 }; @@ -36,6 +39,14 @@ enum ctattr_type { 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_MAX }; #define CTA_MAX (__CTA_MAX - 1) @@ -76,6 +87,8 @@ enum ctattr_l4proto { 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) @@ -91,21 +104,51 @@ enum ctattr_protoinfo_tcp { }; #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, /* old 64bit counters */ - CTA_COUNTERS_BYTES, /* old 64bit counters */ - CTA_COUNTERS32_PACKETS, - CTA_COUNTERS32_BYTES, + 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_MINIP, - CTA_NAT_MAXIP, + 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) @@ -118,6 +161,15 @@ enum ctattr_protonat { }; #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, @@ -126,15 +178,71 @@ enum ctattr_expect { 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/netfilter/nfnetlink_log.h b/include/linux/netfilter/nfnetlink_log.h index 38fafc1..2cfbf13 100644 --- a/include/linux/netfilter/nfnetlink_log.h +++ b/include/linux/netfilter/nfnetlink_log.h @@ -5,8 +5,8 @@ * and not any kind of function definitions. It is shared between kernel and * userspace. Don't put kernel specific stuff in here */ -#ifndef aligned_be64 -#define aligned_be64 u_int64_t __attribute__((aligned(8))) +#ifndef __aligned_be64 +#define __aligned_be64 u_int64_t __attribute__((aligned(8))) #endif #include @@ -21,30 +21,30 @@ enum nfulnl_msg_types { struct nfulnl_msg_packet_hdr { __be16 hw_protocol; /* hw protocol (network order) */ - u_int8_t hook; /* netfilter hook */ - u_int8_t _pad; + __u8 hook; /* netfilter hook */ + __u8 _pad; }; struct nfulnl_msg_packet_hw { __be16 hw_addrlen; - u_int16_t _pad; - u_int8_t hw_addr[8]; + __u16 _pad; + __u8 hw_addr[8]; }; struct nfulnl_msg_packet_timestamp { - aligned_be64 sec; - aligned_be64 usec; + __aligned_be64 sec; + __aligned_be64 usec; }; enum nfulnl_attr_type { NFULA_UNSPEC, NFULA_PACKET_HDR, - NFULA_MARK, /* u_int32_t nfmark */ + NFULA_MARK, /* __u32 nfmark */ NFULA_TIMESTAMP, /* nfulnl_msg_packet_timestamp */ - NFULA_IFINDEX_INDEV, /* u_int32_t ifindex */ - NFULA_IFINDEX_OUTDEV, /* u_int32_t ifindex */ - NFULA_IFINDEX_PHYSINDEV, /* u_int32_t ifindex */ - NFULA_IFINDEX_PHYSOUTDEV, /* u_int32_t ifindex */ + NFULA_IFINDEX_INDEV, /* __u32 ifindex */ + NFULA_IFINDEX_OUTDEV, /* __u32 ifindex */ + NFULA_IFINDEX_PHYSINDEV, /* __u32 ifindex */ + NFULA_IFINDEX_PHYSOUTDEV, /* __u32 ifindex */ NFULA_HWADDR, /* nfulnl_msg_packet_hw */ NFULA_PAYLOAD, /* opaque data payload */ NFULA_PREFIX, /* string prefix */ @@ -52,6 +52,9 @@ enum nfulnl_attr_type { NFULA_SEQ, /* instance-local sequence number */ NFULA_SEQ_GLOBAL, /* global sequence number */ NFULA_GID, /* group id of socket */ + NFULA_HWTYPE, /* hardware type */ + NFULA_HWHEADER, /* hardware header */ + NFULA_HWLEN, /* hardware header length */ __NFULA_MAX }; @@ -66,23 +69,23 @@ enum nfulnl_msg_config_cmds { }; struct nfulnl_msg_config_cmd { - u_int8_t command; /* nfulnl_msg_config_cmds */ + __u8 command; /* nfulnl_msg_config_cmds */ } __attribute__ ((packed)); struct nfulnl_msg_config_mode { __be32 copy_range; - u_int8_t copy_mode; - u_int8_t _pad; + __u8 copy_mode; + __u8 _pad; } __attribute__ ((packed)); enum nfulnl_attr_config { NFULA_CFG_UNSPEC, NFULA_CFG_CMD, /* nfulnl_msg_config_cmd */ NFULA_CFG_MODE, /* nfulnl_msg_config_mode */ - NFULA_CFG_NLBUFSIZ, /* u_int32_t buffer size */ - NFULA_CFG_TIMEOUT, /* u_int32_t in 1/100 s */ - NFULA_CFG_QTHRESH, /* u_int32_t */ - NFULA_CFG_FLAGS, /* u_int16_t */ + NFULA_CFG_NLBUFSIZ, /* __u32 buffer size */ + NFULA_CFG_TIMEOUT, /* __u32 in 1/100 s */ + NFULA_CFG_QTHRESH, /* __u32 */ + NFULA_CFG_FLAGS, /* __u16 */ __NFULA_CFG_MAX }; #define NFULA_CFG_MAX (__NFULA_CFG_MAX -1) @@ -90,6 +93,7 @@ enum nfulnl_attr_config { #define NFULNL_COPY_NONE 0x00 #define NFULNL_COPY_META 0x01 #define NFULNL_COPY_PACKET 0x02 +/* 0xff is reserved, don't use it for new copy modes. */ #define NFULNL_CFG_F_SEQ 0x0001 #define NFULNL_CFG_F_SEQ_GLOBAL 0x0002 diff --git a/include/linux/netfilter/nfnetlink_queue.h b/include/linux/netfilter/nfnetlink_queue.h index bf7cfb6..95af967 100644 --- a/include/linux/netfilter/nfnetlink_queue.h +++ b/include/linux/netfilter/nfnetlink_queue.h @@ -4,14 +4,15 @@ #include #include -#ifndef aligned_be64 -#define aligned_be64 u_int64_t __attribute__((aligned(8))) +#ifndef __aligned_be64 +#define __aligned_be64 u_int64_t __attribute__((aligned(8))) #endif enum nfqnl_msg_types { NFQNL_MSG_PACKET, /* packet from kernel to userspace */ NFQNL_MSG_VERDICT, /* verdict from userspace to kernel */ NFQNL_MSG_CONFIG, /* connect to a particular queue */ + NFQNL_MSG_VERDICT_BATCH, /* batchv from userspace to kernel */ NFQNL_MSG_MAX }; @@ -19,32 +20,35 @@ enum nfqnl_msg_types { struct nfqnl_msg_packet_hdr { __be32 packet_id; /* unique ID of packet in queue */ __be16 hw_protocol; /* hw protocol (network order) */ - u_int8_t hook; /* netfilter hook */ + __u8 hook; /* netfilter hook */ } __attribute__ ((packed)); struct nfqnl_msg_packet_hw { __be16 hw_addrlen; - u_int16_t _pad; - u_int8_t hw_addr[8]; + __u16 _pad; + __u8 hw_addr[8]; }; struct nfqnl_msg_packet_timestamp { - aligned_be64 sec; - aligned_be64 usec; + __aligned_be64 sec; + __aligned_be64 usec; }; enum nfqnl_attr_type { NFQA_UNSPEC, NFQA_PACKET_HDR, NFQA_VERDICT_HDR, /* nfqnl_msg_verdict_hrd */ - NFQA_MARK, /* u_int32_t nfmark */ + NFQA_MARK, /* __u32 nfmark */ NFQA_TIMESTAMP, /* nfqnl_msg_packet_timestamp */ - NFQA_IFINDEX_INDEV, /* u_int32_t ifindex */ - NFQA_IFINDEX_OUTDEV, /* u_int32_t ifindex */ - NFQA_IFINDEX_PHYSINDEV, /* u_int32_t ifindex */ - NFQA_IFINDEX_PHYSOUTDEV, /* u_int32_t ifindex */ + NFQA_IFINDEX_INDEV, /* __u32 ifindex */ + NFQA_IFINDEX_OUTDEV, /* __u32 ifindex */ + NFQA_IFINDEX_PHYSINDEV, /* __u32 ifindex */ + NFQA_IFINDEX_PHYSOUTDEV, /* __u32 ifindex */ NFQA_HWADDR, /* nfqnl_msg_packet_hw */ NFQA_PAYLOAD, /* opaque data payload */ + NFQA_CT, /* nf_conntrack_netlink.h */ + NFQA_CT_INFO, /* enum ip_conntrack_info */ + NFQA_CAP_LEN, /* __u32 length of captured packet */ __NFQA_MAX }; @@ -65,8 +69,8 @@ enum nfqnl_msg_config_cmds { }; struct nfqnl_msg_config_cmd { - u_int8_t command; /* nfqnl_msg_config_cmds */ - u_int8_t _pad; + __u8 command; /* nfqnl_msg_config_cmds */ + __u8 _pad; __be16 pf; /* AF_xxx for PF_[UN]BIND */ }; @@ -78,7 +82,7 @@ enum nfqnl_config_mode { struct nfqnl_msg_config_params { __be32 copy_range; - u_int8_t copy_mode; /* enum nfqnl_config_mode */ + __u8 copy_mode; /* enum nfqnl_config_mode */ } __attribute__ ((packed)); @@ -86,9 +90,16 @@ enum nfqnl_attr_config { NFQA_CFG_UNSPEC, NFQA_CFG_CMD, /* nfqnl_msg_config_cmd */ NFQA_CFG_PARAMS, /* nfqnl_msg_config_params */ - NFQA_CFG_QUEUE_MAXLEN, /* u_int32_t */ + NFQA_CFG_QUEUE_MAXLEN, /* __u32 */ + NFQA_CFG_MASK, /* identify which flags to change */ + NFQA_CFG_FLAGS, /* value of these flags (__u32) */ __NFQA_CFG_MAX }; #define NFQA_CFG_MAX (__NFQA_CFG_MAX-1) +/* Flags for NFQA_CFG_FLAGS */ +#define NFQA_CFG_F_FAIL_OPEN (1 << 0) +#define NFQA_CFG_F_CONNTRACK (1 << 1) +#define NFQA_CFG_F_MAX (1 << 2) + #endif /* _NFNETLINK_QUEUE_H */ diff --git a/include/linux/netlink.h b/include/linux/netlink.h index 12f9fbd..d427fcc 100644 --- a/include/linux/netlink.h +++ b/include/linux/netlink.h @@ -148,130 +148,4 @@ struct nlattr { #define NLA_ALIGN(len) (((len) + NLA_ALIGNTO - 1) & ~(NLA_ALIGNTO - 1)) #define NLA_HDRLEN ((int) NLA_ALIGN(sizeof(struct nlattr))) -#ifdef __KERNEL__ - -#include -#include - -struct net; - -static inline struct nlmsghdr *nlmsg_hdr(const struct sk_buff *skb) -{ - return (struct nlmsghdr *)skb->data; -} - -struct netlink_skb_parms { - struct ucred creds; /* Skb credentials */ - __u32 pid; - __u32 dst_group; -}; - -#define NETLINK_CB(skb) (*(struct netlink_skb_parms*)&((skb)->cb)) -#define NETLINK_CREDS(skb) (&NETLINK_CB((skb)).creds) - - -extern void netlink_table_grab(void); -extern void netlink_table_ungrab(void); - -extern struct sock *netlink_kernel_create(struct net *net, - int unit,unsigned int groups, - void (*input)(struct sk_buff *skb), - struct mutex *cb_mutex, - struct module *module); -extern void netlink_kernel_release(struct sock *sk); -extern int __netlink_change_ngroups(struct sock *sk, unsigned int groups); -extern int netlink_change_ngroups(struct sock *sk, unsigned int groups); -extern void __netlink_clear_multicast_users(struct sock *sk, unsigned int group); -extern void netlink_clear_multicast_users(struct sock *sk, unsigned int group); -extern void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err); -extern int netlink_has_listeners(struct sock *sk, unsigned int group); -extern int netlink_unicast(struct sock *ssk, struct sk_buff *skb, __u32 pid, int nonblock); -extern int netlink_broadcast(struct sock *ssk, struct sk_buff *skb, __u32 pid, - __u32 group, gfp_t allocation); -extern int netlink_broadcast_filtered(struct sock *ssk, struct sk_buff *skb, - __u32 pid, __u32 group, gfp_t allocation, - int (*filter)(struct sock *dsk, struct sk_buff *skb, void *data), - void *filter_data); -extern int netlink_set_err(struct sock *ssk, __u32 pid, __u32 group, int code); -extern int netlink_register_notifier(struct notifier_block *nb); -extern int netlink_unregister_notifier(struct notifier_block *nb); - -/* finegrained unicast helpers: */ -struct sock *netlink_getsockbyfilp(struct file *filp); -int netlink_attachskb(struct sock *sk, struct sk_buff *skb, - long *timeo, struct sock *ssk); -void netlink_detachskb(struct sock *sk, struct sk_buff *skb); -int netlink_sendskb(struct sock *sk, struct sk_buff *skb); - -/* - * skb should fit one page. This choice is good for headerless malloc. - * But we should limit to 8K so that userspace does not have to - * use enormous buffer sizes on recvmsg() calls just to avoid - * MSG_TRUNC when PAGE_SIZE is very large. - */ -#if PAGE_SIZE < 8192UL -#define NLMSG_GOODSIZE SKB_WITH_OVERHEAD(PAGE_SIZE) -#else -#define NLMSG_GOODSIZE SKB_WITH_OVERHEAD(8192UL) -#endif - -#define NLMSG_DEFAULT_SIZE (NLMSG_GOODSIZE - NLMSG_HDRLEN) - - -struct netlink_callback { - struct sk_buff *skb; - const struct nlmsghdr *nlh; - int (*dump)(struct sk_buff * skb, - struct netlink_callback *cb); - int (*done)(struct netlink_callback *cb); - u16 family; - u16 min_dump_alloc; - unsigned int prev_seq, seq; - long args[6]; -}; - -struct netlink_notify { - struct net *net; - int pid; - int protocol; -}; - -static __inline__ struct nlmsghdr * -__nlmsg_put(struct sk_buff *skb, u32 pid, u32 seq, int type, int len, int flags) -{ - struct nlmsghdr *nlh; - int size = NLMSG_LENGTH(len); - - nlh = (struct nlmsghdr*)skb_put(skb, NLMSG_ALIGN(size)); - nlh->nlmsg_type = type; - nlh->nlmsg_len = size; - nlh->nlmsg_flags = flags; - nlh->nlmsg_pid = pid; - nlh->nlmsg_seq = seq; - if (!__builtin_constant_p(size) || NLMSG_ALIGN(size) - size != 0) - memset(NLMSG_DATA(nlh) + len, 0, NLMSG_ALIGN(size) - size); - return nlh; -} - -#define NLMSG_NEW(skb, pid, seq, type, len, flags) \ -({ if (unlikely(skb_tailroom(skb) < (int)NLMSG_SPACE(len))) \ - goto nlmsg_failure; \ - __nlmsg_put(skb, pid, seq, type, len, flags); }) - -#define NLMSG_PUT(skb, pid, seq, type, len) \ - NLMSG_NEW(skb, pid, seq, type, len, 0) - -extern int netlink_dump_start(struct sock *ssk, struct sk_buff *skb, - const struct nlmsghdr *nlh, - int (*dump)(struct sk_buff *skb, struct netlink_callback*), - int (*done)(struct netlink_callback*), - u16 min_dump_alloc); - - -#define NL_NONROOT_RECV 0x1 -#define NL_NONROOT_SEND 0x2 -extern void netlink_set_nonroot(int protocol, unsigned flag); - -#endif /* __KERNEL__ */ - #endif /* __LINUX_NETLINK_H */ diff --git a/include/linux/pkt_cls.h b/include/linux/pkt_cls.h index 7f6ba86..defbde2 100644 --- a/include/linux/pkt_cls.h +++ b/include/linux/pkt_cls.h @@ -332,6 +332,7 @@ enum { FLOW_KEY_SKUID, FLOW_KEY_SKGID, FLOW_KEY_VLAN_TAG, + FLOW_KEY_RXHASH, __FLOW_KEY_MAX, }; diff --git a/include/linux/pkt_sched.h b/include/linux/pkt_sched.h index 2cfa4bc..7ccc1fd 100644 --- a/include/linux/pkt_sched.h +++ b/include/linux/pkt_sched.h @@ -127,6 +127,27 @@ struct tc_multiq_qopt { __u16 max_bands; /* Maximum number of queues */ }; +/* PLUG section */ + +#define TCQ_PLUG_BUFFER 0 +#define TCQ_PLUG_RELEASE_ONE 1 +#define TCQ_PLUG_RELEASE_INDEFINITE 2 +#define TCQ_PLUG_LIMIT 3 + +struct tc_plug_qopt { + /* TCQ_PLUG_BUFFER: Inset a plug into the queue and + * buffer any incoming packets + * TCQ_PLUG_RELEASE_ONE: Dequeue packets from queue head + * to beginning of the next plug. + * TCQ_PLUG_RELEASE_INDEFINITE: Dequeue all packets from queue. + * Stop buffering packets until the next TCQ_PLUG_BUFFER + * command is received (just act as a pass-thru queue). + * TCQ_PLUG_LIMIT: Increase/decrease queue size + */ + int action; + __u32 limit; +}; + /* TBF section */ struct tc_tbf_qopt { @@ -223,7 +244,7 @@ struct tc_gred_qopt { __u32 limit; /* HARD maximal queue length (bytes) */ __u32 qth_min; /* Min average length threshold (bytes) */ __u32 qth_max; /* Max average length threshold (bytes) */ - __u32 DP; /* upto 2^32 DPs */ + __u32 DP; /* up to 2^32 DPs */ __u32 backlog; __u32 qave; __u32 forced; @@ -247,6 +268,35 @@ struct tc_gred_sopt { __u16 pad1; }; +/* CHOKe section */ + +enum { + TCA_CHOKE_UNSPEC, + TCA_CHOKE_PARMS, + TCA_CHOKE_STAB, + __TCA_CHOKE_MAX, +}; + +#define TCA_CHOKE_MAX (__TCA_CHOKE_MAX - 1) + +struct tc_choke_qopt { + __u32 limit; /* Hard queue length (packets) */ + __u32 qth_min; /* Min average threshold (packets) */ + __u32 qth_max; /* Max average threshold (packets) */ + unsigned char Wlog; /* log(W) */ + unsigned char Plog; /* log(P_max/(qth_max-qth_min)) */ + unsigned char Scell_log; /* cell size for idle damping */ + unsigned char flags; /* see RED flags */ +}; + +struct tc_choke_xstats { + __u32 early; /* Early drops */ + __u32 pdrop; /* Drops due to queue limits */ + __u32 other; /* Drops due to drop() calls */ + __u32 marked; /* Marked packets */ + __u32 matched; /* Drops due to flow match */ +}; + /* HTB section */ #define TC_HTB_NUMPRIO 8 #define TC_HTB_MAXDEPTH 8 @@ -435,6 +485,7 @@ enum { TCA_NETEM_DELAY_DIST, TCA_NETEM_REORDER, TCA_NETEM_CORRUPT, + TCA_NETEM_LOSS, __TCA_NETEM_MAX, }; @@ -465,7 +516,33 @@ struct tc_netem_corrupt { __u32 correlation; }; +enum { + NETEM_LOSS_UNSPEC, + NETEM_LOSS_GI, /* General Intuitive - 4 state model */ + NETEM_LOSS_GE, /* Gilbert Elliot models */ + __NETEM_LOSS_MAX +}; +#define NETEM_LOSS_MAX (__NETEM_LOSS_MAX - 1) + +/* State transition probablities for 4 state model */ +struct tc_netem_gimodel { + __u32 p13; + __u32 p31; + __u32 p32; + __u32 p14; + __u32 p23; +}; + +/* Gilbert-Elliot models */ +struct tc_netem_gemodel { + __u32 p; + __u32 r; + __u32 h; + __u32 k1; +}; + #define NETEM_DIST_SCALE 8192 +#define NETEM_DIST_MAX 16384 /* DRR */ @@ -481,4 +558,70 @@ struct tc_drr_stats { __u32 deficit; }; +/* MQPRIO */ +#define TC_QOPT_BITMASK 15 +#define TC_QOPT_MAX_QUEUE 16 + +struct tc_mqprio_qopt { + __u8 num_tc; + __u8 prio_tc_map[TC_QOPT_BITMASK + 1]; + __u8 hw; + __u16 count[TC_QOPT_MAX_QUEUE]; + __u16 offset[TC_QOPT_MAX_QUEUE]; +}; + +/* SFB */ + +enum { + TCA_SFB_UNSPEC, + TCA_SFB_PARMS, + __TCA_SFB_MAX, +}; + +#define TCA_SFB_MAX (__TCA_SFB_MAX - 1) + +/* + * Note: increment, decrement are Q0.16 fixed-point values. + */ +struct tc_sfb_qopt { + __u32 rehash_interval; /* delay between hash move, in ms */ + __u32 warmup_time; /* double buffering warmup time in ms (warmup_time < rehash_interval) */ + __u32 max; /* max len of qlen_min */ + __u32 bin_size; /* maximum queue length per bin */ + __u32 increment; /* probability increment, (d1 in Blue) */ + __u32 decrement; /* probability decrement, (d2 in Blue) */ + __u32 limit; /* max SFB queue length */ + __u32 penalty_rate; /* inelastic flows are rate limited to 'rate' pps */ + __u32 penalty_burst; +}; + +struct tc_sfb_xstats { + __u32 earlydrop; + __u32 penaltydrop; + __u32 bucketdrop; + __u32 queuedrop; + __u32 childdrop; /* drops in child qdisc */ + __u32 marked; + __u32 maxqlen; + __u32 maxprob; + __u32 avgprob; +}; + +#define SFB_MAX_PROB 0xFFFF + +/* QFQ */ +enum { + TCA_QFQ_UNSPEC, + TCA_QFQ_WEIGHT, + TCA_QFQ_LMAX, + __TCA_QFQ_MAX +}; + +#define TCA_QFQ_MAX (__TCA_QFQ_MAX - 1) + +struct tc_qfq_stats { + __u32 weight; + __u32 lmax; +}; + #endif diff --git a/include/linux/snmp.h b/include/linux/snmp.h index ebb0c80..12b2b18 100644 --- a/include/linux/snmp.h +++ b/include/linux/snmp.h @@ -230,6 +230,7 @@ enum LINUX_MIB_TCPMINTTLDROP, /* RFC 5082 */ LINUX_MIB_TCPDEFERACCEPTDROP, LINUX_MIB_IPRPFILTER, /* IP Reverse Path Filter (rp_filter) */ + LINUX_MIB_TCPTIMEWAITOVERFLOW, /* TCPTimeWaitOverflow */ __LINUX_MIB_MAX }; diff --git a/include/netlink-private/cache-api.h b/include/netlink-private/cache-api.h new file mode 100644 index 0000000..f3d9f01 --- /dev/null +++ b/include/netlink-private/cache-api.h @@ -0,0 +1,270 @@ +/* + * netlink-private/cache-api.h Caching API + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2013 Thomas Graf + */ + +#ifndef NETLINK_CACHE_API_H_ +#define NETLINK_CACHE_API_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @ingroup cache + * @defgroup cache_api Cache Implementation + * @brief + * + * @par 1) Cache Definition + * @code + * struct nl_cache_ops my_cache_ops = { + * .co_name = "route/link", + * .co_protocol = NETLINK_ROUTE, + * .co_hdrsize = sizeof(struct ifinfomsg), + * .co_obj_ops = &my_obj_ops, + * }; + * @endcode + * + * @par 2) + * @code + * // The simplest way to fill a cache is by providing a request-update + * // function which must trigger a complete dump on the kernel-side of + * // whatever the cache covers. + * static int my_request_update(struct nl_cache *cache, + * struct nl_sock *socket) + * { + * // In this example, we request a full dump of the interface table + * return nl_rtgen_request(socket, RTM_GETLINK, AF_UNSPEC, NLM_F_DUMP); + * } + * + * // The resulting netlink messages sent back will be fed into a message + * // parser one at a time. The message parser has to extract all relevant + * // information from the message and create an object reflecting the + * // contents of the message and pass it on to the parser callback function + * // provide which will add the object to the cache. + * static int my_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, + * struct nlmsghdr *nlh, struct nl_parser_param *pp) + * { + * struct my_obj *obj; + * + * obj = my_obj_alloc(); + * obj->ce_msgtype = nlh->nlmsg_type; + * + * // Parse the netlink message and continue creating the object. + * + * err = pp->pp_cb((struct nl_object *) obj, pp); + * if (err < 0) + * goto errout; + * } + * + * struct nl_cache_ops my_cache_ops = { + * ... + * .co_request_update = my_request_update, + * .co_msg_parser = my_msg_parser, + * }; + * @endcode + * + * @par 3) Notification based Updates + * @code + * // Caches can be kept up-to-date based on notifications if the kernel + * // sends out notifications whenever an object is added/removed/changed. + * // + * // It is trivial to support this, first a list of groups needs to be + * // defined which are required to join in order to receive all necessary + * // notifications. The groups are separated by address family to support + * // the common situation where a separate group is used for each address + * // family. If there is only one group, simply specify AF_UNSPEC. + * static struct nl_af_group addr_groups[] = { + * { AF_INET, RTNLGRP_IPV4_IFADDR }, + * { AF_INET6, RTNLGRP_IPV6_IFADDR }, + * { END_OF_GROUP_LIST }, + * }; + * + * // In order for the caching system to know the meaning of each message + * // type it requires a table which maps each supported message type to + * // a cache action, e.g. RTM_NEWADDR means address has been added or + * // updated, RTM_DELADDR means address has been removed. + * static struct nl_cache_ops rtnl_addr_ops = { + * ... + * .co_msgtypes = { + * { RTM_NEWADDR, NL_ACT_NEW, "new" }, + * { RTM_DELADDR, NL_ACT_DEL, "del" }, + * { RTM_GETADDR, NL_ACT_GET, "get" }, + * END_OF_MSGTYPES_LIST, + * }, + * .co_groups = addr_groups, + * }; + * + * // It is now possible to keep the cache up-to-date using the cache manager. + * @endcode + * @{ + */ + +#define END_OF_MSGTYPES_LIST { -1, -1, NULL } + +/** + * Message type to cache action association + */ +struct nl_msgtype +{ + /** Netlink message type */ + int mt_id; + + /** Cache action to take */ + int mt_act; + + /** Name of operation for human-readable printing */ + char * mt_name; +}; + +/** + * Address family to netlink group association + */ +struct nl_af_group +{ + /** Address family */ + int ag_family; + + /** Netlink group identifier */ + int ag_group; +}; + +#define END_OF_GROUP_LIST AF_UNSPEC, 0 + +/** + * Parser parameters + * + * This structure is used to configure what kind of parser to use + * when parsing netlink messages to create objects. + */ +struct nl_parser_param +{ + /** Function to parse netlink messages into objects */ + int (*pp_cb)(struct nl_object *, struct nl_parser_param *); + + /** Arbitary argument to be passed to the parser */ + void * pp_arg; +}; + +/** + * Cache Operations + * + * This structure defines the characterstics of a cache type. It contains + * pointers to functions which implement the specifics of the object type + * the cache can hold. + */ +struct nl_cache_ops +{ + /** Name of cache type (must be unique) */ + char * co_name; + + /** Size of family specific netlink header */ + int co_hdrsize; + + /** Netlink protocol */ + int co_protocol; + + /** cache object hash size **/ + int co_hash_size; + + /** cache flags */ + unsigned int co_flags; + + /** Reference counter */ + unsigned int co_refcnt; + + /** Group definition */ + struct nl_af_group * co_groups; + + /** + * Called whenever an update of the cache is required. Must send + * a request message to the kernel requesting a complete dump. + */ + int (*co_request_update)(struct nl_cache *, struct nl_sock *); + + /** + * Called whenever a message was received that needs to be parsed. + * Must parse the message and call the paser callback function + * (nl_parser_param) provided via the argument. + */ + int (*co_msg_parser)(struct nl_cache_ops *, struct sockaddr_nl *, + struct nlmsghdr *, struct nl_parser_param *); + + /** + * The function registered under this callback is called after a + * netlink notification associated with this cache type has been + * parsed into an object and is being considered for inclusio into + * the specified cache. + * + * The purpose of this function is to filter out notifications + * which should be ignored when updating caches. + * + * The function must return NL_SKIP to prevent the object from + * being included, or NL_OK to include it. + * + * @code + * int my_filter(struct nl_cache *cache, struct nl_object *obj) + * { + * if (reason_to_not_include_obj(obj)) + * return NL_SKIP; + * + * return NL_OK; + * } + * @endcode + */ + int (*co_event_filter)(struct nl_cache *, struct nl_object *obj); + + /** + * The function registered under this callback is called when an + * object formed from a notification event needs to be included in + * a cache. + * + * For each modified object, the change callback \c change_cb must + * be called with the \c data argument provided. + * + * If no function is registered, the function nl_cache_include() + * will be used for this purpose. + * + * @see nl_cache_include() + */ + int (*co_include_event)(struct nl_cache *cache, struct nl_object *obj, + change_func_t change_cb, void *data); + + void (*reserved_1)(void); + void (*reserved_2)(void); + void (*reserved_3)(void); + void (*reserved_4)(void); + void (*reserved_5)(void); + void (*reserved_6)(void); + void (*reserved_7)(void); + void (*reserved_8)(void); + + /** Object operations */ + struct nl_object_ops * co_obj_ops; + + /** Internal, do not touch! */ + struct nl_cache_ops *co_next; + + struct nl_cache *co_major_cache; + struct genl_ops * co_genl; + + /* Message type definition */ + struct nl_msgtype co_msgtypes[]; +}; + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/netlink-generic.h b/include/netlink-private/genl.h similarity index 71% rename from include/netlink-generic.h rename to include/netlink-private/genl.h index 10aa2f0..0aca6d7 100644 --- a/include/netlink-generic.h +++ b/include/netlink-private/genl.h @@ -1,18 +1,18 @@ /* - * netlink-generic.h Local Generic Netlink Interface + * netlink-private/genl.h Local Generic Netlink Interface * * 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 version 2.1 * of the License. * - * Copyright (c) 2003-2006 Thomas Graf + * Copyright (c) 2003-2013 Thomas Graf */ #ifndef NETLINK_GENL_PRIV_H_ #define NETLINK_GENL_PRIV_H_ -#include +#include #include #define GENL_HDRSIZE(hdrlen) (GENL_HDRLEN + (hdrlen)) diff --git a/include/netlink-local.h b/include/netlink-private/netlink.h similarity index 65% rename from include/netlink-local.h rename to include/netlink-private/netlink.h index 32e8302..6ae6d17 100644 --- a/include/netlink-local.h +++ b/include/netlink-private/netlink.h @@ -1,12 +1,12 @@ /* - * netlink-local.h Local Netlink Interface + * netlink-private/netlink.h Local Netlink Interface * * 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 version 2.1 * of the License. * - * Copyright (c) 2003-2008 Thomas Graf + * Copyright (c) 2003-2013 Thomas Graf */ #ifndef NETLINK_LOCAL_H_ @@ -33,6 +33,8 @@ #include #include +#include + #ifndef SOL_NETLINK #define SOL_NETLINK 270 #endif @@ -43,6 +45,7 @@ #include #include #include +#include #include #include #include @@ -52,13 +55,17 @@ #include #include +#ifndef DISABLE_PTHREADS +#include +#endif + #include #include #include #include -#include -#include -#include +#include +#include +#include struct trans_tbl { int i; @@ -81,13 +88,20 @@ struct trans_list { fprintf(stderr, "DBG<" #LVL ">: " FMT, ##ARG); \ } while (0) -#define BUG() \ - do { \ - fprintf(stderr, "BUG: %s:%d\n", \ - __FILE__, __LINE__); \ - assert(0); \ +#define BUG() \ + do { \ + NL_DBG(1, "BUG: %s:%d\n", \ + __FILE__, __LINE__); \ + assert(0); \ } while (0) +#define APPBUG(msg) \ + do { \ + NL_DBG(1, "APPLICATION BUG: %s:%d:%s: %s\n", \ + __FILE__, __LINE__, __PRETTY_FUNCTION__, msg); \ + assert(0); \ + } while(0) + extern int __nl_read_num_str_file(const char *path, int (*cb)(long, const char *)); @@ -105,21 +119,17 @@ extern int __str2flags(const char *, const struct trans_tbl *, size_t); extern void dump_from_ops(struct nl_object *, struct nl_dump_params *); -static inline struct nl_cache *dp_cache(struct nl_object *obj) -{ - if (obj->ce_cache == NULL) - return nl_cache_mngt_require(obj->ce_ops->oo_name); - - return obj->ce_cache; -} - static inline int nl_cb_call(struct nl_cb *cb, int type, struct nl_msg *msg) { return cb->cb_set[type](msg, cb->cb_args[type]); } #define ARRAY_SIZE(X) (sizeof(X) / sizeof((X)[0])) + +/* This is also defined in stddef.h */ +#ifndef offsetof #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) +#endif #define __init __attribute__ ((constructor)) #define __exit __attribute__ ((destructor)) @@ -186,4 +196,62 @@ static inline int wait_for_ack(struct nl_sock *sk) return nl_wait_for_ack(sk); } +static inline int build_sysconf_path(char **strp, const char *filename) +{ + char *sysconfdir; + + sysconfdir = getenv("NLSYSCONFDIR"); + + if (!sysconfdir) + sysconfdir = SYSCONFDIR; + + return asprintf(strp, "%s/%s", sysconfdir, filename); +} + +#ifndef DISABLE_PTHREADS +#define NL_LOCK(NAME) pthread_mutex_t (NAME) = PTHREAD_MUTEX_INITIALIZER +#define NL_RW_LOCK(NAME) pthread_rwlock_t (NAME) = PTHREAD_RWLOCK_INITIALIZER + +static inline void nl_lock(pthread_mutex_t *lock) +{ + pthread_mutex_lock(lock); +} + +static inline void nl_unlock(pthread_mutex_t *lock) +{ + pthread_mutex_unlock(lock); +} + +static inline void nl_read_lock(pthread_rwlock_t *lock) +{ + pthread_rwlock_rdlock(lock); +} + +static inline void nl_read_unlock(pthread_rwlock_t *lock) +{ + pthread_rwlock_unlock(lock); +} + +static inline void nl_write_lock(pthread_rwlock_t *lock) +{ + pthread_rwlock_wrlock(lock); +} + +static inline void nl_write_unlock(pthread_rwlock_t *lock) +{ + pthread_rwlock_unlock(lock); +} + +#else +#define NL_LOCK(NAME) int __unused_lock_ ##NAME __attribute__((unused)) +#define NL_RW_LOCK(NAME) int __unused_lock_ ##NAME __attribute__((unused)) + +#define nl_lock(LOCK) do { } while(0) +#define nl_unlock(LOCK) do { } while(0) +#define nl_read_lock(LOCK) do { } while(0) +#define nl_read_unlock(LOCK) do { } while(0) +#define nl_write_lock(LOCK) do { } while(0) +#define nl_write_unlock(LOCK) do { } while(0) +#endif + #endif diff --git a/include/netlink-private/object-api.h b/include/netlink-private/object-api.h new file mode 100644 index 0000000..f4fd71e --- /dev/null +++ b/include/netlink-private/object-api.h @@ -0,0 +1,376 @@ +/* + * netlink-private/object-api.c Object API + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2013 Thomas Graf + */ + +#ifndef NETLINK_OBJECT_API_H_ +#define NETLINK_OBJECT_API_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @ingroup object + * @defgroup object_api Object API + * @brief + * + * @par 1) Object Definition + * @code + * // Define your object starting with the common object header + * struct my_obj { + * NLHDR_COMMON + * int my_data; + * }; + * + * // Fill out the object operations structure + * struct nl_object_ops my_ops = { + * .oo_name = "my_obj", + * .oo_size = sizeof(struct my_obj), + * }; + * + * // At this point the object can be allocated, you may want to provide a + * // separate _alloc() function to ease allocting objects of this kind. + * struct nl_object *obj = nl_object_alloc(&my_ops); + * + * // And release it again... + * nl_object_put(obj); + * @endcode + * + * @par 2) Allocating additional data + * @code + * // You may require to allocate additional data and store it inside + * // object, f.e. assuming there is a field `ptr'. + * struct my_obj { + * NLHDR_COMMON + * void * ptr; + * }; + * + * // And at some point you may assign allocated data to this field: + * my_obj->ptr = calloc(1, ...); + * + * // In order to not introduce any memory leaks you have to release + * // this data again when the last reference is given back. + * static void my_obj_free_data(struct nl_object *obj) + * { + * struct my_obj *my_obj = nl_object_priv(obj); + * + * free(my_obj->ptr); + * } + * + * // Also when the object is cloned, you must ensure for your pointer + * // stay valid even if one of the clones is freed by either making + * // a clone as well or increase the reference count. + * static int my_obj_clone(struct nl_object *src, struct nl_object *dst) + * { + * struct my_obj *my_src = nl_object_priv(src); + * struct my_obj *my_dst = nl_object_priv(dst); + * + * if (src->ptr) { + * dst->ptr = calloc(1, ...); + * memcpy(dst->ptr, src->ptr, ...); + * } + * } + * + * struct nl_object_ops my_ops = { + * ... + * .oo_free_data = my_obj_free_data, + * .oo_clone = my_obj_clone, + * }; + * @endcode + * + * @par 3) Object Dumping + * @code + * static int my_obj_dump_detailed(struct nl_object *obj, + * struct nl_dump_params *params) + * { + * struct my_obj *my_obj = nl_object_priv(obj); + * + * // It is absolutely essential to use nl_dump() when printing + * // any text to make sure the dumping parameters are respected. + * nl_dump(params, "Obj Integer: %d\n", my_obj->my_int); + * + * // Before we can dump the next line, make sure to prefix + * // this line correctly. + * nl_new_line(params); + * + * // You may also split a line into multiple nl_dump() calls. + * nl_dump(params, "String: %s ", my_obj->my_string); + * nl_dump(params, "String-2: %s\n", my_obj->another_string); + * } + * + * struct nl_object_ops my_ops = { + * ... + * .oo_dump[NL_DUMP_FULL] = my_obj_dump_detailed, + * }; + * @endcode + * + * @par 4) Object Attributes + * @code + * // The concept of object attributes is optional but can ease the typical + * // case of objects that have optional attributes, e.g. a route may have a + * // nexthop assigned but it is not required to. + * + * // The first step to define your object specific bitmask listing all + * // attributes + * #define MY_ATTR_FOO (1<<0) + * #define MY_ATTR_BAR (1<<1) + * + * // When assigning an optional attribute to the object, make sure + * // to mark its availability. + * my_obj->foo = 123123; + * my_obj->ce_mask |= MY_ATTR_FOO; + * + * // At any time you may use this mask to check for the availability + * // of the attribute, e.g. while dumping + * if (my_obj->ce_mask & MY_ATTR_FOO) + * nl_dump(params, "foo %d ", my_obj->foo); + * + * // One of the big advantages of this concept is that it allows for + * // standardized comparisons which make it trivial for caches to + * // identify unique objects by use of unified comparison functions. + * // In order for it to work, your object implementation must provide + * // a comparison function and define a list of attributes which + * // combined together make an object unique. + * + * static int my_obj_compare(struct nl_object *_a, struct nl_object *_b, + * uint32_t attrs, int flags) + * { + * struct my_obj *a = nl_object_priv(_a): + * struct my_obj *b = nl_object_priv(_b): + * int diff = 0; + * + * // We help ourselves in defining our own DIFF macro which will + * // call ATTR_DIFF() on both objects which will make sure to only + * // compare the attributes if required. + * #define MY_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, MY_ATTR_##ATTR, a, b, EXPR) + * + * // Call our own diff macro for each attribute to build a bitmask + * // representing the attributes which mismatch. + * diff |= MY_DIFF(FOO, a->foo != b->foo) + * diff |= MY_DIFF(BAR, strcmp(a->bar, b->bar)) + * + * return diff; + * } + * + * // In order to identify identical objects with differing attributes + * // you must specify the attributes required to uniquely identify + * // your object. Make sure to not include too many attributes, this + * // list is used when caches look for an old version of an object. + * struct nl_object_ops my_ops = { + * ... + * .oo_id_attrs = MY_ATTR_FOO, + * .oo_compare = my_obj_compare, + * }; + * @endcode + * @{ + */ + +/** + * Common Object Header + * + * This macro must be included as first member in every object + * definition to allow objects to be cached. + */ +#define NLHDR_COMMON \ + int ce_refcnt; \ + struct nl_object_ops * ce_ops; \ + struct nl_cache * ce_cache; \ + struct nl_list_head ce_list; \ + int ce_msgtype; \ + int ce_flags; \ + uint32_t ce_mask; + +struct nl_object +{ + NLHDR_COMMON +}; + + +/** + * Return true if attribute is available in both objects + * @arg A an object + * @arg B another object + * @arg ATTR attribute bit + * + * @return True if the attribute is available, otherwise false is returned. + */ +#define AVAILABLE(A, B, ATTR) (((A)->ce_mask & (B)->ce_mask) & (ATTR)) + +/** + * Return true if attribute is available in only one of both objects + * @arg A an object + * @arg B another object + * @arg ATTR attribute bit + * + * @return True if the attribute is available in only one of both objects, + * otherwise false is returned. + */ +#define AVAILABLE_MISMATCH(A, B, ATTR) (((A)->ce_mask ^ (B)->ce_mask) & (ATTR)) + +/** + * Return true if attributes mismatch + * @arg A an object + * @arg B another object + * @arg ATTR attribute bit + * @arg EXPR Comparison expression + * + * This function will check if the attribute in question is available + * in both objects, if not this will count as a mismatch. + * + * If available the function will execute the expression which must + * return true if the attributes mismatch. + * + * @return True if the attribute mismatch, or false if they match. + */ +#define ATTR_MISMATCH(A, B, ATTR, EXPR) (AVAILABLE_MISMATCH(A, B, ATTR) || \ + (AVAILABLE(A, B, ATTR) && (EXPR))) + +/** + * Return attribute bit if attribute does not match + * @arg LIST list of attributes to be compared + * @arg ATTR attribute bit + * @arg A an object + * @arg B another object + * @arg EXPR Comparison expression + * + * This function will check if the attribute in question is available + * in both objects, if not this will count as a mismatch. + * + * If available the function will execute the expression which must + * return true if the attributes mismatch. + * + * In case the attributes mismatch, the attribute is returned, otherwise + * 0 is returned. + * + * @code + * diff |= ATTR_DIFF(attrs, MY_ATTR_FOO, a, b, a->foo != b->foo); + * @endcode + */ +#define ATTR_DIFF(LIST, ATTR, A, B, EXPR) \ +({ int diff = 0; \ + if (((LIST) & (ATTR)) && ATTR_MISMATCH(A, B, ATTR, EXPR)) \ + diff = ATTR; \ + diff; }) + +/** + * Object Operations + */ +struct nl_object_ops +{ + /** + * Unique name of object type + * + * Must be in the form family/name, e.g. "route/addr" + */ + char * oo_name; + + /** Size of object including its header */ + size_t oo_size; + + /* List of attributes needed to uniquely identify the object */ + uint32_t oo_id_attrs; + + /** + * Constructor function + * + * Will be called when a new object of this type is allocated. + * Can be used to initialize members such as lists etc. + */ + void (*oo_constructor)(struct nl_object *); + + /** + * Destructor function + * + * Will be called when an object is freed. Must free all + * resources which may have been allocated as part of this + * object. + */ + void (*oo_free_data)(struct nl_object *); + + /** + * Cloning function + * + * Will be called when an object needs to be cloned. Please + * note that the generic object code will make an exact + * copy of the object first, therefore you only need to take + * care of members which require reference counting etc. + * + * May return a negative error code to abort cloning. + */ + int (*oo_clone)(struct nl_object *, struct nl_object *); + + /** + * Dumping functions + * + * Will be called when an object is dumped. The implementations + * have to use nl_dump(), nl_dump_line(), and nl_new_line() to + * dump objects. + * + * The functions must return the number of lines printed. + */ + void (*oo_dump[NL_DUMP_MAX+1])(struct nl_object *, + struct nl_dump_params *); + + /** + * Comparison function + * + * Will be called when two objects of the same type are + * compared. It takes the two objects in question, an object + * specific bitmask defining which attributes should be + * compared and flags to control the behaviour. + * + * The function must return a bitmask with the relevant bit + * set for each attribute that mismatches. + */ + int (*oo_compare)(struct nl_object *, struct nl_object *, + uint32_t, int); + + + /** + * update function + * + * Will be called when the object given by first argument + * needs to be updated with the contents of the second object + * + * The function must return 0 for success and error for failure + * to update. In case of failure its assumed that the original + * object is not touched + */ + int (*oo_update)(struct nl_object *, struct nl_object *); + + /** + * Hash Key generator function + * + * When called returns a hash key for the object being + * referenced. This key will be used by higher level hash functions + * to build association lists. Each object type gets to specify + * it's own key formulation + */ + void (*oo_keygen)(struct nl_object *, uint32_t *, uint32_t); + + char *(*oo_attrs2str)(int, char *, size_t); + + /** + * Get key attributes by family function + */ + uint32_t (*oo_id_attrs_get)(struct nl_object *); +}; + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/netlink-private/route/link/api.h b/include/netlink-private/route/link/api.h new file mode 100644 index 0000000..bb98ccc --- /dev/null +++ b/include/netlink-private/route/link/api.h @@ -0,0 +1,153 @@ +/* + * netlink-private/route/link/api.h Link Modules API + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2013 Thomas Graf + */ + +#ifndef NETLINK_LINK_API_H_ +#define NETLINK_LINK_API_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @ingroup link_api + * + * Available operations to modules implementing a link info type. + */ +struct rtnl_link_info_ops +{ + /** Name of link info type, must match name on kernel side */ + char * io_name; + + /** Reference count, DO NOT MODIFY */ + int io_refcnt; + + /** Called to assign an info type to a link. + * Has to allocate enough resources to hold attributes. Can + * use link->l_info to store a pointer. */ + int (*io_alloc)(struct rtnl_link *); + + /** Called to parse the link info attribute. + * Must parse the attribute and assign all values to the link. + */ + int (*io_parse)(struct rtnl_link *, + struct nlattr *, + struct nlattr *); + + /** Called when the link object is dumped. + * Must dump the info type specific attributes. */ + void (*io_dump[NL_DUMP_MAX+1])(struct rtnl_link *, + struct nl_dump_params *); + + /** Called when a link object is cloned. + * Must clone all info type specific attributes. */ + int (*io_clone)(struct rtnl_link *, struct rtnl_link *); + + /** Called when construction a link netlink message. + * Must append all info type specific attributes to the message. */ + int (*io_put_attrs)(struct nl_msg *, struct rtnl_link *); + + /** Called to release all resources previously allocated + * in either io_alloc() or io_parse(). */ + void (*io_free)(struct rtnl_link *); + + struct nl_list_head io_list; +}; + +extern struct rtnl_link_info_ops *rtnl_link_info_ops_lookup(const char *); +extern void rtnl_link_info_ops_put(struct rtnl_link_info_ops *); +extern int rtnl_link_register_info(struct rtnl_link_info_ops *); +extern int rtnl_link_unregister_info(struct rtnl_link_info_ops *); + + +/** + * @ingroup link_api + * + * Available operations to modules implementing a link address family. + */ +struct rtnl_link_af_ops +{ + /** The address family this operations set implements */ + const unsigned int ao_family; + + /** Number of users of this operations, DO NOT MODIFY. */ + int ao_refcnt; + + /** Validation policy for IFLA_PROTINFO attribute. This pointer + * can be set to a nla_policy structure describing the minimal + * requirements the attribute must meet. Failure of meeting these + * requirements will result in a parsing error. */ + const struct nla_policy *ao_protinfo_policy; + + /** Called after address family has been assigned to link. Must + * allocate data buffer to hold address family specific data and + * store it in link->l_af_data. */ + void * (*ao_alloc)(struct rtnl_link *); + + /** Called when the link is cloned, must allocate a clone of the + * address family specific buffer and return it. */ + void * (*ao_clone)(struct rtnl_link *, void *); + + /** Called when the link gets freed. Must free all allocated data */ + void (*ao_free)(struct rtnl_link *, void *); + + /** Called if a IFLA_PROTINFO attribute needs to be parsed. Typically + * stores the parsed data in the address family specific buffer. */ + int (*ao_parse_protinfo)(struct rtnl_link *, + struct nlattr *, void *); + + /** Called if a IFLA_AF_SPEC attribute needs to be parsed. Typically + * stores the parsed data in the address family specific buffer. */ + int (*ao_parse_af)(struct rtnl_link *, + struct nlattr *, void *); + + /** Called if a link message is sent to the kernel. Must append the + * link address family specific attributes to the message. */ + int (*ao_fill_af)(struct rtnl_link *, + struct nl_msg *msg, void *); + + /** Dump address family specific link attributes */ + void (*ao_dump[NL_DUMP_MAX+1])(struct rtnl_link *, + struct nl_dump_params *, + void *); + + /** Comparison function + * + * Will be called when two links are compared for their af data. It + * takes two link objects in question, an object specific bitmask + * defining which attributes should be compared and flags to control + * the behaviour + * + * The function must return a bitmask with the relevant bit set for + * each attribute that mismatches + */ + int (*ao_compare)(struct rtnl_link *, + struct rtnl_link *, int, uint32_t, int); +}; + +extern struct rtnl_link_af_ops *rtnl_link_af_ops_lookup(unsigned int); +extern void rtnl_link_af_ops_put(struct rtnl_link_af_ops *); +extern void * rtnl_link_af_alloc(struct rtnl_link *, + const struct rtnl_link_af_ops *); +extern void * rtnl_link_af_data(const struct rtnl_link *, + const struct rtnl_link_af_ops *); +extern int rtnl_link_af_register(struct rtnl_link_af_ops *); +extern int rtnl_link_af_unregister(struct rtnl_link_af_ops *); +extern int rtnl_link_af_data_compare(struct rtnl_link *a, + struct rtnl_link *b, + int family); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/netlink-private/route/tc-api.h b/include/netlink-private/route/tc-api.h new file mode 100644 index 0000000..bf0c8a3 --- /dev/null +++ b/include/netlink-private/route/tc-api.h @@ -0,0 +1,134 @@ +/* + * netlink-private/route/tc-api.h Traffic Control API + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2011-2013 Thomas Graf + */ + +#ifndef NETLINK_TC_API_H_ +#define NETLINK_TC_API_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Traffic control object operations + * @ingroup tc + * + * This structure holds function pointers and settings implementing + * the features of each traffic control object implementation. + */ +struct rtnl_tc_ops +{ + /** + * Name of traffic control module + */ + char *to_kind; + + /** + * Type of traffic control object + */ + enum rtnl_tc_type to_type; + + + /** + * Size of private data + */ + size_t to_size; + + /** + * Dump callbacks + */ + void (*to_dump[NL_DUMP_MAX+1])(struct rtnl_tc *, void *, + struct nl_dump_params *); + /** + * Used to fill the contents of TCA_OPTIONS + */ + int (*to_msg_fill)(struct rtnl_tc *, void *, struct nl_msg *); + + /** + * Uesd to to fill tc related messages, unlike with to_msg_fill, + * the contents is not encapsulated with a TCA_OPTIONS nested + * attribute. + */ + int (*to_msg_fill_raw)(struct rtnl_tc *, void *, struct nl_msg *); + + /** + * TCA_OPTIONS message parser + */ + int (*to_msg_parser)(struct rtnl_tc *, void *); + + /** + * Called before a tc object is destroyed + */ + void (*to_free_data)(struct rtnl_tc *, void *); + + /** + * Called whenever a classifier object needs to be cloned + */ + int (*to_clone)(void *, void *); + + /** + * Internal, don't touch + */ + struct nl_list_head to_list; +}; + +struct rtnl_tc_type_ops +{ + enum rtnl_tc_type tt_type; + + char *tt_dump_prefix; + + /** + * Dump callbacks + */ + void (*tt_dump[NL_DUMP_MAX+1])(struct rtnl_tc *, + struct nl_dump_params *); +}; + +extern int rtnl_tc_msg_parse(struct nlmsghdr *, + struct rtnl_tc *); +extern int rtnl_tc_msg_build(struct rtnl_tc *, int, + int, struct nl_msg **); + +extern void rtnl_tc_free_data(struct nl_object *); +extern int rtnl_tc_clone(struct nl_object *, + struct nl_object *); +extern void rtnl_tc_dump_line(struct nl_object *, + struct nl_dump_params *); +extern void rtnl_tc_dump_details(struct nl_object *, + struct nl_dump_params *); +extern void rtnl_tc_dump_stats(struct nl_object *, + struct nl_dump_params *); +extern int rtnl_tc_compare(struct nl_object *, + struct nl_object *, + uint32_t, int); + +extern void * rtnl_tc_data(struct rtnl_tc *); +extern void * rtnl_tc_data_check(struct rtnl_tc *, + struct rtnl_tc_ops *); + +extern struct rtnl_tc_ops * rtnl_tc_lookup_ops(enum rtnl_tc_type, + const char *); +extern struct rtnl_tc_ops * rtnl_tc_get_ops(struct rtnl_tc *); +extern int rtnl_tc_register(struct rtnl_tc_ops *); +extern void rtnl_tc_unregister(struct rtnl_tc_ops *); + +extern void rtnl_tc_type_register(struct rtnl_tc_type_ops *); +extern void rtnl_tc_type_unregister(struct rtnl_tc_type_ops *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/netlink-tc.h b/include/netlink-private/tc.h similarity index 88% rename from include/netlink-tc.h rename to include/netlink-private/tc.h index 3e44642..182adaa 100644 --- a/include/netlink-tc.h +++ b/include/netlink-private/tc.h @@ -1,18 +1,18 @@ /* - * netlink-tc.h Local Traffic Control Interface + * netlink-private/tc.h Local Traffic Control Interface * * 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 version 2.1 * of the License. * - * Copyright (c) 2003-2010 Thomas Graf + * Copyright (c) 2003-2013 Thomas Graf */ #ifndef NETLINK_TC_PRIV_H_ #define NETLINK_TC_PRIV_H_ -#include +#include #ifdef __cplusplus extern "C" { diff --git a/include/netlink-types.h b/include/netlink-private/types.h similarity index 92% rename from include/netlink-types.h rename to include/netlink-private/types.h index 2a6612b..6e2fa68 100644 --- a/include/netlink-types.h +++ b/include/netlink-private/types.h @@ -1,12 +1,12 @@ /* - * netlink-types.h Netlink Types (Private) + * netlink-private/types.h Netlink Types (Private) * * 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 version 2.1 * of the License. * - * Copyright (c) 2003-2008 Thomas Graf + * Copyright (c) 2003-2013 Thomas Graf */ #ifndef NETLINK_LOCAL_TYPES_H_ @@ -17,7 +17,7 @@ #include #include #include -#include +#include #define NL_SOCK_BUFSIZE_SET (1<<0) #define NL_SOCK_PASSCRED (1<<1) @@ -30,6 +30,7 @@ struct nl_cache_ops; struct nl_sock; struct nl_object; +struct nl_hash_table; struct nl_cb { @@ -69,6 +70,7 @@ struct nl_sock unsigned int s_seq_expect; int s_flags; struct nl_cb * s_cb; + size_t s_bufsize; }; struct nl_cache @@ -77,6 +79,9 @@ struct nl_cache int c_nitems; int c_iarg1; int c_iarg2; + int c_refcnt; + unsigned int c_flags; + struct nl_hash_table * hashtable; struct nl_cache_ops * c_ops; }; @@ -92,7 +97,8 @@ struct nl_cache_mngr int cm_protocol; int cm_flags; int cm_nassocs; - struct nl_sock * cm_handle; + struct nl_sock * cm_sock; + struct nl_sock * cm_sync_sock; struct nl_cache_assoc * cm_assocs; }; @@ -167,10 +173,18 @@ struct rtnl_link uint8_t l_operstate; uint8_t l_linkmode; /* 2 byte hole */ + char * l_info_kind; struct rtnl_link_info_ops * l_info_ops; void * l_af_data[AF_MAX]; void * l_info; char * l_ifalias; + uint32_t l_promiscuity; + uint32_t l_num_tx_queues; + uint32_t l_num_rx_queues; + uint32_t l_group; + uint8_t l_carrier; + /* 3 byte hole */ + struct rtnl_link_af_ops * l_af_ops; }; struct rtnl_ncacheinfo @@ -196,6 +210,7 @@ struct rtnl_neigh struct rtnl_ncacheinfo n_cacheinfo; uint32_t n_state_mask; uint32_t n_flag_mask; + uint32_t n_master; }; @@ -234,6 +249,7 @@ struct rtnl_addr char a_label[IFNAMSIZ]; uint32_t a_flag_mask; + struct rtnl_link *a_link; }; struct rtnl_nexthop @@ -510,6 +526,7 @@ struct rtnl_fw struct nl_data * cf_act; struct nl_data * cf_police; char cf_indev[IFNAMSIZ]; + uint32_t cf_fwmask; int cf_mask; }; @@ -630,6 +647,7 @@ struct rtnl_htb_qdisc uint32_t qh_rate2quantum; uint32_t qh_defcls; uint32_t qh_mask; + uint32_t qh_direct_pkts; }; struct rtnl_htb_class @@ -641,6 +659,7 @@ struct rtnl_htb_class uint32_t ch_cbuffer; uint32_t ch_quantum; uint32_t ch_mask; + uint32_t ch_level; }; struct rtnl_cbq @@ -665,6 +684,12 @@ struct rtnl_red uint32_t qr_mask; }; +struct rtnl_plug +{ + int action; + uint32_t limit; +}; + struct flnl_request { NLHDR_COMMON @@ -768,6 +793,50 @@ struct nfnl_ct { struct nfnl_ct_dir ct_repl; }; +union nfnl_exp_protodata { + struct { + uint16_t src; + uint16_t dst; + } port; + struct { + uint16_t id; + uint8_t type; + uint8_t code; + } icmp; +}; + +// Allow for different master/expect l4 protocols +struct nfnl_exp_proto +{ + uint8_t l4protonum; + union nfnl_exp_protodata l4protodata; +}; + +struct nfnl_exp_dir { + struct nl_addr * src; + struct nl_addr * dst; + struct nfnl_exp_proto proto; +}; + +struct nfnl_exp { + NLHDR_COMMON + + uint8_t exp_family; + uint32_t exp_timeout; + uint32_t exp_id; + uint16_t exp_zone; + uint32_t exp_class; + uint32_t exp_flags; + char * exp_helper_name; + char * exp_fn; + uint8_t exp_nat_dir; + + struct nfnl_exp_dir exp_expect; + struct nfnl_exp_dir exp_master; + struct nfnl_exp_dir exp_mask; + struct nfnl_exp_dir exp_nat; +}; + struct nfnl_log { NLHDR_COMMON diff --git a/include/netlink/addr.h b/include/netlink/addr.h index 794c4ff..db3e4c2 100644 --- a/include/netlink/addr.h +++ b/include/netlink/addr.h @@ -6,7 +6,7 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2010 Thomas Graf + * Copyright (c) 2003-2013 Thomas Graf */ #ifndef NETLINK_ADDR_H_ @@ -40,7 +40,7 @@ extern int nl_addr_guess_family(struct nl_addr *); extern int nl_addr_fill_sockaddr(struct nl_addr *, struct sockaddr *, socklen_t *); extern int nl_addr_info(struct nl_addr *, struct addrinfo **); -extern int nl_addr_resolve(struct nl_addr *addr, char *host, size_t hostlen); +extern int nl_addr_resolve(struct nl_addr *, char *, size_t); /* Access Functions */ extern void nl_addr_set_family(struct nl_addr *, int); diff --git a/include/netlink/attr.h b/include/netlink/attr.h index 8479c23..0ed3da3 100644 --- a/include/netlink/attr.h +++ b/include/netlink/attr.h @@ -6,7 +6,7 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2008 Thomas Graf + * Copyright (c) 2003-2013 Thomas Graf */ #ifndef NETLINK_ATTR_H_ @@ -28,12 +28,12 @@ struct nl_msg; * @{ */ - /** - * @ingroup attr - * Basic attribute data types - * - * See \ref attr_datatypes for more details. - */ +/** + * @ingroup attr + * Basic attribute data types + * + * See section @core_doc{core_attr_parse,Attribute Parsing} for more details. + */ enum { NLA_UNSPEC, /**< Unspecified type, binary data chunk */ NLA_U8, /**< 8 bit integer */ @@ -55,7 +55,7 @@ enum { * @ingroup attr * Attribute validation policy. * - * See \ref attr_datatypes for more details. + * See section @core_doc{core_attr_parse,Attribute Parsing} for more details. */ struct nla_policy { /** Type of attribute or NLA_UNSPEC */ @@ -124,8 +124,10 @@ extern int nla_put_msecs(struct nl_msg *, int, unsigned long); extern int nla_put_nested(struct nl_msg *, int, struct nl_msg *); extern struct nlattr * nla_nest_start(struct nl_msg *, int); extern int nla_nest_end(struct nl_msg *, struct nlattr *); +extern void nla_nest_cancel(struct nl_msg *, struct nlattr *); extern int nla_parse_nested(struct nlattr **, int, struct nlattr *, struct nla_policy *); +extern int nla_is_nested(struct nlattr *); /** * @name Attribute Construction (Exception Based) diff --git a/include/netlink/cache-api.h b/include/netlink/cache-api.h index 22fc449..e43c7ca 100644 --- a/include/netlink/cache-api.h +++ b/include/netlink/cache-api.h @@ -6,194 +6,15 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2006 Thomas Graf + * Copyright (c) 2013 Thomas Graf */ -#ifndef NETLINK_CACHE_API_H_ -#define NETLINK_CACHE_API_H_ +#ifndef NETLINK_DUMMY_CACHE_API_H_ +#define NETLINK_DUMMY_CACHE_API_H_ #include +#include -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @ingroup cache - * @defgroup cache_api Cache Implementation - * @brief - * - * @par 1) Cache Definition - * @code - * struct nl_cache_ops my_cache_ops = { - * .co_name = "route/link", - * .co_protocol = NETLINK_ROUTE, - * .co_hdrsize = sizeof(struct ifinfomsg), - * .co_obj_ops = &my_obj_ops, - * }; - * @endcode - * - * @par 2) - * @code - * // The simplest way to fill a cache is by providing a request-update - * // function which must trigger a complete dump on the kernel-side of - * // whatever the cache covers. - * static int my_request_update(struct nl_cache *cache, - * struct nl_sock *socket) - * { - * // In this example, we request a full dump of the interface table - * return nl_rtgen_request(socket, RTM_GETLINK, AF_UNSPEC, NLM_F_DUMP); - * } - * - * // The resulting netlink messages sent back will be fed into a message - * // parser one at a time. The message parser has to extract all relevant - * // information from the message and create an object reflecting the - * // contents of the message and pass it on to the parser callback function - * // provide which will add the object to the cache. - * static int my_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, - * struct nlmsghdr *nlh, struct nl_parser_param *pp) - * { - * struct my_obj *obj; - * - * obj = my_obj_alloc(); - * obj->ce_msgtype = nlh->nlmsg_type; - * - * // Parse the netlink message and continue creating the object. - * - * err = pp->pp_cb((struct nl_object *) obj, pp); - * if (err < 0) - * goto errout; - * } - * - * struct nl_cache_ops my_cache_ops = { - * ... - * .co_request_update = my_request_update, - * .co_msg_parser = my_msg_parser, - * }; - * @endcode - * - * @par 3) Notification based Updates - * @code - * // Caches can be kept up-to-date based on notifications if the kernel - * // sends out notifications whenever an object is added/removed/changed. - * // - * // It is trivial to support this, first a list of groups needs to be - * // defined which are required to join in order to receive all necessary - * // notifications. The groups are separated by address family to support - * // the common situation where a separate group is used for each address - * // family. If there is only one group, simply specify AF_UNSPEC. - * static struct nl_af_group addr_groups[] = { - * { AF_INET, RTNLGRP_IPV4_IFADDR }, - * { AF_INET6, RTNLGRP_IPV6_IFADDR }, - * { END_OF_GROUP_LIST }, - * }; - * - * // In order for the caching system to know the meaning of each message - * // type it requires a table which maps each supported message type to - * // a cache action, e.g. RTM_NEWADDR means address has been added or - * // updated, RTM_DELADDR means address has been removed. - * static struct nl_cache_ops rtnl_addr_ops = { - * ... - * .co_msgtypes = { - * { RTM_NEWADDR, NL_ACT_NEW, "new" }, - * { RTM_DELADDR, NL_ACT_DEL, "del" }, - * { RTM_GETADDR, NL_ACT_GET, "get" }, - * END_OF_MSGTYPES_LIST, - * }, - * .co_groups = addr_groups, - * }; - * - * // It is now possible to keep the cache up-to-date using the cache manager. - * @endcode - * @{ - */ - -enum { - NL_ACT_UNSPEC, - NL_ACT_NEW, - NL_ACT_DEL, - NL_ACT_GET, - NL_ACT_SET, - NL_ACT_CHANGE, - __NL_ACT_MAX, -}; - -#define NL_ACT_MAX (__NL_ACT_MAX - 1) - -#define END_OF_MSGTYPES_LIST { -1, -1, NULL } - -/** - * Message type to cache action association - */ -struct nl_msgtype -{ - /** Netlink message type */ - int mt_id; - - /** Cache action to take */ - int mt_act; - - /** Name of operation for human-readable printing */ - char * mt_name; -}; - -/** - * Address family to netlink group association - */ -struct nl_af_group -{ - /** Address family */ - int ag_family; - - /** Netlink group identifier */ - int ag_group; -}; - -#define END_OF_GROUP_LIST AF_UNSPEC, 0 - -struct nl_parser_param -{ - int (*pp_cb)(struct nl_object *, struct nl_parser_param *); - void * pp_arg; -}; - -/** - * Cache Operations - */ -struct nl_cache_ops -{ - char * co_name; - - int co_hdrsize; - int co_protocol; - struct nl_af_group * co_groups; - - /** - * Called whenever an update of the cache is required. Must send - * a request message to the kernel requesting a complete dump. - */ - int (*co_request_update)(struct nl_cache *, struct nl_sock *); - - /** - * Called whenever a message was received that needs to be parsed. - * Must parse the message and call the paser callback function - * (nl_parser_param) provided via the argument. - */ - int (*co_msg_parser)(struct nl_cache_ops *, struct sockaddr_nl *, - struct nlmsghdr *, struct nl_parser_param *); - - struct nl_object_ops * co_obj_ops; - - struct nl_cache_ops *co_next; - struct nl_cache *co_major_cache; - struct genl_ops * co_genl; - struct nl_msgtype co_msgtypes[]; -}; - -/** @} */ - -#ifdef __cplusplus -} -#endif +#warning "You are including a deprecated header file, include ." #endif diff --git a/include/netlink/cache.h b/include/netlink/cache.h index c752920..415bb00 100644 --- a/include/netlink/cache.h +++ b/include/netlink/cache.h @@ -6,7 +6,7 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2008 Thomas Graf + * Copyright (c) 2003-2012 Thomas Graf */ #ifndef NETLINK_CACHE_H_ @@ -16,16 +16,32 @@ #include #include #include -#include #ifdef __cplusplus extern "C" { #endif -struct nl_cache; +enum { + NL_ACT_UNSPEC, + NL_ACT_NEW, + NL_ACT_DEL, + NL_ACT_GET, + NL_ACT_SET, + NL_ACT_CHANGE, + __NL_ACT_MAX, +}; +#define NL_ACT_MAX (__NL_ACT_MAX - 1) + +struct nl_cache; typedef void (*change_func_t)(struct nl_cache *, struct nl_object *, int, void *); +/** + * @ingroup cache + * Explicitely iterate over all address families when updating the cache + */ +#define NL_CACHE_AF_ITER 0x0001 + /* Access Functions */ extern int nl_cache_nitems(struct nl_cache *); extern int nl_cache_nitems_filter(struct nl_cache *, @@ -44,8 +60,11 @@ extern int nl_cache_alloc_name(const char *, struct nl_cache **); extern struct nl_cache * nl_cache_subset(struct nl_cache *, struct nl_object *); +extern struct nl_cache * nl_cache_clone(struct nl_cache *); extern void nl_cache_clear(struct nl_cache *); +extern void nl_cache_get(struct nl_cache *); extern void nl_cache_free(struct nl_cache *); +extern void nl_cache_put(struct nl_cache *cache); /* Cache modification */ extern int nl_cache_add(struct nl_cache *, @@ -65,9 +84,16 @@ extern int nl_cache_include(struct nl_cache *, struct nl_object *, change_func_t, void *); +extern void nl_cache_set_arg1(struct nl_cache *, int); +extern void nl_cache_set_arg2(struct nl_cache *, int); +extern void nl_cache_set_flags(struct nl_cache *, unsigned int); /* General */ extern int nl_cache_is_empty(struct nl_cache *); +extern struct nl_object * nl_cache_search(struct nl_cache *, + struct nl_object *); +extern struct nl_object * nl_cache_lookup(struct nl_cache *, + struct nl_object *); extern void nl_cache_mark_all(struct nl_cache *); /* Dumping */ @@ -93,7 +119,9 @@ extern void nl_cache_foreach_filter(struct nl_cache *, /* Cache type management */ extern struct nl_cache_ops * nl_cache_ops_lookup(const char *); +extern struct nl_cache_ops * nl_cache_ops_lookup_safe(const char *); extern struct nl_cache_ops * nl_cache_ops_associate(int, int); +extern struct nl_cache_ops * nl_cache_ops_associate_safe(int, int); extern struct nl_msgtype * nl_msgtype_lookup(struct nl_cache_ops *, int); extern void nl_cache_ops_foreach(void (*cb)(struct nl_cache_ops *, void *), void *); extern int nl_cache_mngt_register(struct nl_cache_ops *); @@ -103,10 +131,13 @@ extern int nl_cache_mngt_unregister(struct nl_cache_ops *); extern void nl_cache_mngt_provide(struct nl_cache *); extern void nl_cache_mngt_unprovide(struct nl_cache *); extern struct nl_cache * nl_cache_mngt_require(const char *); +extern struct nl_cache * nl_cache_mngt_require_safe(const char *); +extern struct nl_cache * __nl_cache_mngt_require(const char *); struct nl_cache_mngr; #define NL_AUTO_PROVIDE 1 +#define NL_ALLOCATED_SOCK 2 /* For internal use only, do not use */ extern int nl_cache_mngr_alloc(struct nl_sock *, int, int, @@ -116,12 +147,20 @@ extern int nl_cache_mngr_add(struct nl_cache_mngr *, change_func_t, void *, struct nl_cache **); +extern int nl_cache_mngr_add_cache(struct nl_cache_mngr *mngr, + struct nl_cache *cache, + change_func_t cb, void *data); extern int nl_cache_mngr_get_fd(struct nl_cache_mngr *); extern int nl_cache_mngr_poll(struct nl_cache_mngr *, int); extern int nl_cache_mngr_data_ready(struct nl_cache_mngr *); +extern void nl_cache_mngr_info(struct nl_cache_mngr *, + struct nl_dump_params *); extern void nl_cache_mngr_free(struct nl_cache_mngr *); +extern void nl_cache_ops_get(struct nl_cache_ops *); +extern void nl_cache_ops_put(struct nl_cache_ops *); + #ifdef __cplusplus } #endif diff --git a/include/netlink/cli/exp.h b/include/netlink/cli/exp.h new file mode 100644 index 0000000..b2418f8 --- /dev/null +++ b/include/netlink/cli/exp.h @@ -0,0 +1,42 @@ +/* + * netlink/cli/exp.h CLI Expectation Helper + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2012 Rich Fought + * Copyright (c) 2008-2009 Thomas Graf + */ + +#ifndef __NETLINK_CLI_EXP_H_ +#define __NETLINK_CLI_EXP_H_ + +#include +#include + +extern struct nfnl_exp *nl_cli_exp_alloc(void); +extern struct nl_cache *nl_cli_exp_alloc_cache(struct nl_sock *); + +extern void nl_cli_exp_parse_family(struct nfnl_exp *, char *); +extern void nl_cli_exp_parse_timeout(struct nfnl_exp *, char *); +extern void nl_cli_exp_parse_id(struct nfnl_exp *, char *); +extern void nl_cli_exp_parse_helper_name(struct nfnl_exp *, char *); +extern void nl_cli_exp_parse_zone(struct nfnl_exp *, char *); +extern void nl_cli_exp_parse_flags(struct nfnl_exp *, char *); +extern void nl_cli_exp_parse_class(struct nfnl_exp *, char *); +extern void nl_cli_exp_parse_nat_dir(struct nfnl_exp *, char *); +extern void nl_cli_exp_parse_fn(struct nfnl_exp *, char *); + +extern void nl_cli_exp_parse_src(struct nfnl_exp *, int, char *); +extern void nl_cli_exp_parse_dst(struct nfnl_exp *, int, char *); +extern void nl_cli_exp_parse_l4protonum(struct nfnl_exp *, int, char *); +extern void nl_cli_exp_parse_src_port(struct nfnl_exp *, int, char *); +extern void nl_cli_exp_parse_dst_port(struct nfnl_exp *, int, char *); +extern void nl_cli_exp_parse_icmp_id(struct nfnl_exp *, int, char *); +extern void nl_cli_exp_parse_icmp_type(struct nfnl_exp *, int, char *); +extern void nl_cli_exp_parse_icmp_code(struct nfnl_exp *, int, char *); + + +#endif diff --git a/include/netlink/cli/tc.h b/include/netlink/cli/tc.h index 85d2e30..77042c7 100644 --- a/include/netlink/cli/tc.h +++ b/include/netlink/cli/tc.h @@ -12,7 +12,9 @@ #ifndef __NETLINK_CLI_TC_H_ #define __NETLINK_CLI_TC_H_ -#include +#include + +struct rtnl_tc_ops; extern void nl_cli_tc_parse_dev(struct rtnl_tc *, struct nl_cache *, char *); extern void nl_cli_tc_parse_parent(struct rtnl_tc *, char *); diff --git a/include/netlink/errno.h b/include/netlink/errno.h index dde12b7..f8b5130 100644 --- a/include/netlink/errno.h +++ b/include/netlink/errno.h @@ -47,8 +47,11 @@ extern "C" { #define NLE_PERM 28 #define NLE_PKTLOC_FILE 29 #define NLE_PARSE_ERR 30 +#define NLE_NODEV 31 +#define NLE_IMMUTABLE 32 +#define NLE_DUMP_INTR 33 -#define NLE_MAX NLE_PARSE_ERR +#define NLE_MAX NLE_DUMP_INTR extern const char * nl_geterror(int); extern void nl_perror(int, const char *); diff --git a/include/netlink/genl/ctrl.h b/include/netlink/genl/ctrl.h index 26a0a99..017b8fd 100644 --- a/include/netlink/genl/ctrl.h +++ b/include/netlink/genl/ctrl.h @@ -6,7 +6,7 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2008 Thomas Graf + * Copyright (c) 2003-2012 Thomas Graf */ #ifndef NETLINK_GENL_CTRL_H_ diff --git a/include/netlink/genl/family.h b/include/netlink/genl/family.h index 721dc13..5432b59 100644 --- a/include/netlink/genl/family.h +++ b/include/netlink/genl/family.h @@ -6,7 +6,7 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2006 Thomas Graf + * Copyright (c) 2003-2012 Thomas Graf */ #ifndef NETLINK_GENL_FAMILY_H_ @@ -25,25 +25,19 @@ extern struct genl_family * genl_family_alloc(void); extern void genl_family_put(struct genl_family *); extern unsigned int genl_family_get_id(struct genl_family *); -extern void genl_family_set_id(struct genl_family *, - unsigned int); +extern void genl_family_set_id(struct genl_family *, unsigned int); extern char * genl_family_get_name(struct genl_family *); -extern void genl_family_set_name(struct genl_family *, - const char *name); +extern void genl_family_set_name(struct genl_family *, const char *); extern uint8_t genl_family_get_version(struct genl_family *); -extern void genl_family_set_version(struct genl_family *, - uint8_t); +extern void genl_family_set_version(struct genl_family *, uint8_t); extern uint32_t genl_family_get_hdrsize(struct genl_family *); -extern void genl_family_set_hdrsize(struct genl_family *, - uint32_t); +extern void genl_family_set_hdrsize(struct genl_family *, uint32_t); extern uint32_t genl_family_get_maxattr(struct genl_family *); -extern void genl_family_set_maxattr(struct genl_family *, - uint32_t); +extern void genl_family_set_maxattr(struct genl_family *, uint32_t); -extern int genl_family_add_op(struct genl_family *, - int, int); -extern int genl_family_add_grp(struct genl_family *, - uint32_t , const char *); +extern int genl_family_add_op(struct genl_family *, int, int); +extern int genl_family_add_grp(struct genl_family *, uint32_t , + const char *); #ifdef __cplusplus diff --git a/include/netlink/genl/genl.h b/include/netlink/genl/genl.h index 364a471..e455581 100644 --- a/include/netlink/genl/genl.h +++ b/include/netlink/genl/genl.h @@ -6,7 +6,7 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2008 Thomas Graf + * Copyright (c) 2003-2012 Thomas Graf */ #ifndef NETLINK_GENL_H_ @@ -32,7 +32,13 @@ extern int genlmsg_validate(struct nlmsghdr *, int, int, struct nla_policy *); extern int genlmsg_parse(struct nlmsghdr *, int, struct nlattr **, int, struct nla_policy *); +extern struct genlmsghdr * + genlmsg_hdr(struct nlmsghdr *); extern void * genlmsg_data(const struct genlmsghdr *); +extern void * genlmsg_user_hdr(const struct genlmsghdr *); +extern void * genlmsg_user_data(const struct genlmsghdr *, const int); +extern int genlmsg_user_datalen(const struct genlmsghdr *, + const int); extern int genlmsg_len(const struct genlmsghdr *); extern struct nlattr * genlmsg_attrdata(const struct genlmsghdr *, int); extern int genlmsg_attrlen(const struct genlmsghdr *, int); diff --git a/include/netlink/genl/mngt.h b/include/netlink/genl/mngt.h index 8b0244f..8a51ccd 100644 --- a/include/netlink/genl/mngt.h +++ b/include/netlink/genl/mngt.h @@ -6,7 +6,7 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2006 Thomas Graf + * Copyright (c) 2003-2012 Thomas Graf */ #ifndef NETLINK_GENL_MNGT_H_ @@ -22,64 +22,153 @@ extern "C" { struct nl_cache_ops; +/** + * @ingroup genl_mngt + * @struct genl_info netlink/genl/mngt.h + * + * Informative structure passed on to message parser callbacks + * + * This structure is passed on to all message parser callbacks and contains + * information about the sender of the message as well as pointers to all + * relevant sections of the parsed message. + * + * @see genl_cmd::c_msg_parser + */ struct genl_info { + /** Socket address of sender */ struct sockaddr_nl * who; + + /** Pointer to Netlink message header */ struct nlmsghdr * nlh; + + /** Pointer to Generic Netlink message header */ struct genlmsghdr * genlhdr; + + /** Pointer to user header */ void * userhdr; + + /** Pointer to array of parsed attributes */ struct nlattr ** attrs; }; /** * @ingroup genl_mngt - * Generic Netlink Command + * @struct genl_cmd netlink/genl/mngt.h + * + * Definition of a Generic Netlink command. + * + * This structure is used to define the list of available commands on the + * receiving side. + * + * @par Example: + * @code + * static struct genl_cmd foo_cmds[] = { + * { + * .c_id = FOO_CMD_NEW, + * .c_name = "NEWFOO" , + * .c_maxattr = FOO_ATTR_MAX, + * .c_attr_policy = foo_policy, + * .c_msg_parser = foo_msg_parser, + * }, + * { + * .c_id = FOO_CMD_DEL, + * .c_name = "DELFOO" , + * }, + * }; + * + * static struct genl_ops my_genl_ops = { + * [...] + * .o_cmds = foo_cmds, + * .o_ncmds = ARRAY_SIZE(foo_cmds), + * }; + * @endcode */ struct genl_cmd { - /** Unique command identifier */ + /** Numeric command identifier (required) */ int c_id; - /** Name/description of command */ + /** Human readable name (required) */ char * c_name; - /** - * Maximum attribute identifier, must be provided if - * a message parser is available. - */ + /** Maximum attribute identifier that the command is prepared to handle. */ int c_maxattr; + /** Called whenever a message for this command is received */ int (*c_msg_parser)(struct nl_cache_ops *, struct genl_cmd *, struct genl_info *, void *); - /** - * Attribute validation policy (optional) - */ + /** Attribute validation policy, enforced before the callback is called */ struct nla_policy * c_attr_policy; }; /** * @ingroup genl_mngt - * Generic Netlink Operations + * @struct genl_ops netlink/genl/mngt.h + * + * Definition of a Generic Netlink family + * + * @par Example: + * @code + * static struct genl_cmd foo_cmds[] = { + * [...] + * }; + * + * static struct genl_ops my_genl_ops = { + * .o_name = "foo", + * .o_hdrsize = sizeof(struct my_hdr), + * .o_cmds = foo_cmds, + * .o_ncmds = ARRAY_SIZE(foo_cmds), + * }; + * + * if ((err = genl_register_family(&my_genl_ops)) < 0) + * // ERROR + * @endcode + * + * @see genl_cmd */ struct genl_ops { - int o_family; + /** Length of user header */ + unsigned int o_hdrsize; + + /** Numeric identifier, automatically filled in by genl_ops_resolve() */ int o_id; + + /** Human readable name, used by genl_ops_resolve() to resolve numeric id */ char * o_name; + + /** + * If registered via genl_register(), will point to the related + * cache operations. + */ struct nl_cache_ops * o_cache_ops; + + /** Optional array defining the available Generic Netlink commands */ struct genl_cmd * o_cmds; + + /** Number of elements in \c o_cmds array */ int o_ncmds; - /* linked list of all genl cache operations */ + /** + * @private + * Used internally to link together all registered operations. + */ struct nl_list_head o_list; }; +extern int genl_register_family(struct genl_ops *); +extern int genl_unregister_family(struct genl_ops *); +extern int genl_handle_msg(struct nl_msg *, void *); extern int genl_register(struct nl_cache_ops *); extern void genl_unregister(struct nl_cache_ops *); +extern int genl_ops_resolve(struct nl_sock *, struct genl_ops *); +extern int genl_mngt_resolve(struct nl_sock *); + #ifdef __cplusplus } #endif diff --git a/include/netlink/handlers.h b/include/netlink/handlers.h index f373f58..dfa2809 100644 --- a/include/netlink/handlers.h +++ b/include/netlink/handlers.h @@ -108,6 +108,8 @@ enum nl_cb_type { NL_CB_SEQ_CHECK, /** Sending of an acknowledge message has been requested */ NL_CB_SEND_ACK, + /** Flag NLM_F_DUMP_INTR is set in message */ + NL_CB_DUMP_INTR, __NL_CB_TYPE_MAX, }; diff --git a/include/netlink/hash.h b/include/netlink/hash.h new file mode 100644 index 0000000..0bda74e --- /dev/null +++ b/include/netlink/hash.h @@ -0,0 +1,69 @@ +/* + * This file was taken from http://ccodearchive.net/info/hash.html + * Changes to the original file include cleanups and removal of unwanted code + * and also code that depended on build_asert + */ +#ifndef CCAN_HASH_H +#define CCAN_HASH_H +#include +#include +#include + +/* Stolen mostly from: lookup3.c, by Bob Jenkins, May 2006, Public Domain. + * + * http://burtleburtle.net/bob/c/lookup3.c + */ + +#ifdef __LITTLE_ENDIAN +# define HAVE_LITTLE_ENDIAN 1 +#elif __BIG_ENDIAN +# define HAVE_BIG_ENDIAN 1 +#else +#error Unknown endianness. Failure in endian.h +#endif + +/** + * hash - fast hash of an array for internal use + * @p: the array or pointer to first element + * @num: the number of elements to hash + * @base: the base number to roll into the hash (usually 0) + * + * The memory region pointed to by p is combined with the base to form + * a 32-bit hash. + * + * This hash will have different results on different machines, so is + * only useful for internal hashes (ie. not hashes sent across the + * network or saved to disk). + * + * It may also change with future versions: it could even detect at runtime + * what the fastest hash to use is. + * + * See also: hash64, hash_stable. + * + * Example: + * #include + * #include + * #include + * #include + * + * // Simple demonstration: idential strings will have the same hash, but + * // two different strings will probably not. + * int main(int argc, char *argv[]) + * { + * uint32_t hash1, hash2; + * + * if (argc != 3) + * err(1, "Usage: %s ", argv[0]); + * + * hash1 = __nl_hash(argv[1], strlen(argv[1]), 0); + * hash2 = __nl_hash(argv[2], strlen(argv[2]), 0); + * printf("Hash is %s\n", hash1 == hash2 ? "same" : "different"); + * return 0; + * } + */ +#define __nl_hash(p, num, base) nl_hash_any((p), (num)*sizeof(*(p)), (base)) + +/* Our underlying operations. */ +uint32_t nl_hash_any(const void *key, size_t length, uint32_t base); + +#endif /* HASH_H */ diff --git a/include/netlink/hashtable.h b/include/netlink/hashtable.h new file mode 100644 index 0000000..d9e6ee4 --- /dev/null +++ b/include/netlink/hashtable.h @@ -0,0 +1,52 @@ +/* + * netlink/hashtable.h Netlink hashtable Utilities + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2012 Cumulus Networks, Inc + */ + +#ifndef NETLINK_HASHTABLE_H_ +#define NETLINK_HASHTABLE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct nl_hash_node { + uint32_t key; + uint32_t key_size; + struct nl_object * obj; + struct nl_hash_node * next; +} nl_hash_node_t; + +typedef struct nl_hash_table { + int size; + nl_hash_node_t ** nodes; +} nl_hash_table_t; + +/* Default hash table size */ +#define NL_MAX_HASH_ENTRIES 1024 + +/* Access Functions */ +extern nl_hash_table_t * nl_hash_table_alloc(int size); +extern void nl_hash_table_free(nl_hash_table_t *ht); + +extern int nl_hash_table_add(nl_hash_table_t *ht, + struct nl_object *obj); +extern int nl_hash_table_del(nl_hash_table_t *ht, + struct nl_object *obj); + +extern struct nl_object * nl_hash_table_lookup(nl_hash_table_t *ht, + struct nl_object *obj); +extern uint32_t nl_hash(void *k, size_t length, + uint32_t initval); + +#ifdef __cplusplus +} +#endif + +#endif /* NETLINK_HASHTABLE_H_ */ diff --git a/include/netlink/netfilter/exp.h b/include/netlink/netfilter/exp.h new file mode 100644 index 0000000..4e95014 --- /dev/null +++ b/include/netlink/netfilter/exp.h @@ -0,0 +1,129 @@ +/* + * netlink/netfilter/exp.h Conntrack Expectation + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2008 Thomas Graf + * Copyright (c) 2007 Philip Craig + * Copyright (c) 2007 Secure Computing Corporation + * Copyright (c) 2012 Rich Fought + */ + +#ifndef NETLINK_EXP_H_ +#define NETLINK_EXP_H_ + +#include +#include +#include +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct nfnl_exp; + +enum nfnl_exp_tuples { + NFNL_EXP_TUPLE_EXPECT, + NFNL_EXP_TUPLE_MASTER, + NFNL_EXP_TUPLE_MASK, + NFNL_EXP_TUPLE_NAT, + NFNL_EXP_TUPLE_MAX +}; + +extern struct nl_object_ops exp_obj_ops; + +extern struct nfnl_exp * nfnl_exp_alloc(void); +extern int nfnl_exp_alloc_cache(struct nl_sock *, struct nl_cache **); + +extern int nfnlmsg_exp_group(struct nlmsghdr *); +extern int nfnlmsg_exp_parse(struct nlmsghdr *, struct nfnl_exp **); + +extern void nfnl_exp_get(struct nfnl_exp *); +extern void nfnl_exp_put(struct nfnl_exp *); + +extern int nfnl_exp_dump_request(struct nl_sock *); + +extern int nfnl_exp_build_add_request(const struct nfnl_exp *, int, + struct nl_msg **); +extern int nfnl_exp_add(struct nl_sock *, const struct nfnl_exp *, int); + +extern int nfnl_exp_build_delete_request(const struct nfnl_exp *, int, + struct nl_msg **); +extern int nfnl_exp_del(struct nl_sock *, const struct nfnl_exp *, int); + +extern int nfnl_exp_build_query_request(const struct nfnl_exp *, int, + struct nl_msg **); +extern int nfnl_exp_query(struct nl_sock *, const struct nfnl_exp *, int); + +extern void nfnl_exp_set_family(struct nfnl_exp *, uint8_t); +extern uint8_t nfnl_exp_get_family(const struct nfnl_exp *); + +extern void nfnl_exp_set_timeout(struct nfnl_exp *, uint32_t); +extern int nfnl_exp_test_timeout(const struct nfnl_exp *); +extern uint32_t nfnl_exp_get_timeout(const struct nfnl_exp *); + +extern void nfnl_exp_set_id(struct nfnl_exp *, uint32_t); +extern int nfnl_exp_test_id(const struct nfnl_exp *); +extern uint32_t nfnl_exp_get_id(const struct nfnl_exp *); + +extern int nfnl_exp_set_helper_name(struct nfnl_exp *, void *); +extern int nfnl_exp_test_helper_name(const struct nfnl_exp *); +extern const char * nfnl_exp_get_helper_name(const struct nfnl_exp *); + +extern void nfnl_exp_set_zone(struct nfnl_exp *, uint16_t); +extern int nfnl_exp_test_zone(const struct nfnl_exp *); +extern uint16_t nfnl_exp_get_zone(const struct nfnl_exp *); + +extern void nfnl_exp_set_flags(struct nfnl_exp *, uint32_t); +extern int nfnl_exp_test_flags(const struct nfnl_exp *); +extern uint32_t nfnl_exp_get_flags(const struct nfnl_exp *); + +extern void nfnl_exp_set_class(struct nfnl_exp *, uint32_t); +extern int nfnl_exp_test_class(const struct nfnl_exp *); +extern uint32_t nfnl_exp_get_class(const struct nfnl_exp *); + +extern int nfnl_exp_set_fn(struct nfnl_exp *, void *); +extern int nfnl_exp_test_fn(const struct nfnl_exp *); +extern const char * nfnl_exp_get_fn(const struct nfnl_exp *); + +extern void nfnl_exp_set_nat_dir(struct nfnl_exp *, uint8_t); +extern int nfnl_exp_test_nat_dir(const struct nfnl_exp *); +extern uint8_t nfnl_exp_get_nat_dir(const struct nfnl_exp *); + +// The int argument specifies which nfnl_exp_dir (expect, master, mask or nat) +// Expectation objects only use orig, not reply + +extern int nfnl_exp_set_src(struct nfnl_exp *, int, struct nl_addr *); +extern int nfnl_exp_test_src(const struct nfnl_exp *, int); +extern struct nl_addr * nfnl_exp_get_src(const struct nfnl_exp *, int); + +extern int nfnl_exp_set_dst(struct nfnl_exp *, int, struct nl_addr *); +extern int nfnl_exp_test_dst(const struct nfnl_exp *, int); +extern struct nl_addr * nfnl_exp_get_dst(const struct nfnl_exp *, int); + +extern void nfnl_exp_set_l4protonum(struct nfnl_exp *, int, uint8_t); +extern int nfnl_exp_test_l4protonum(const struct nfnl_exp *, int); +extern uint8_t nfnl_exp_get_l4protonum(const struct nfnl_exp *, int); + +extern void nfnl_exp_set_ports(struct nfnl_exp *, int, uint16_t, uint16_t); +extern int nfnl_exp_test_ports(const struct nfnl_exp *, int); +extern uint16_t nfnl_exp_get_src_port(const struct nfnl_exp *, int); +extern uint16_t nfnl_exp_get_dst_port(const struct nfnl_exp *, int); + +extern void nfnl_exp_set_icmp(struct nfnl_exp *, int, uint16_t, uint8_t, uint8_t); +extern int nfnl_exp_test_icmp(const struct nfnl_exp *, int); +extern uint16_t nfnl_exp_get_icmp_id(const struct nfnl_exp *, int); +extern uint8_t nfnl_exp_get_icmp_type(const struct nfnl_exp *, int); +extern uint8_t nfnl_exp_get_icmp_code(const struct nfnl_exp *, int); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/netlink/netlink.h b/include/netlink/netlink.h index 8127001..c65c303 100644 --- a/include/netlink/netlink.h +++ b/include/netlink/netlink.h @@ -6,7 +6,7 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2006 Thomas Graf + * Copyright (c) 2003-2013 Thomas Graf */ #ifndef NETLINK_NETLINK_H_ @@ -32,12 +32,17 @@ #include #include #include +#include #ifdef __cplusplus extern "C" { #endif struct ucred; +struct nl_cache_ops; +struct nl_parser_param; +struct nl_object; +struct nl_sock; extern int nl_debug; extern struct nl_dump_params nl_debug_dp; @@ -60,6 +65,7 @@ extern void nl_auto_complete(struct nl_sock *, extern int nl_send_auto(struct nl_sock *, struct nl_msg *); extern int nl_send_auto_complete(struct nl_sock *, struct nl_msg *); +extern int nl_send_sync(struct nl_sock *, struct nl_msg *); extern int nl_send_simple(struct nl_sock *, int, int, void *, size_t); @@ -69,11 +75,18 @@ extern int nl_recv(struct nl_sock *, struct ucred **); extern int nl_recvmsgs(struct nl_sock *, struct nl_cb *); +extern int nl_recvmsgs_report(struct nl_sock *, struct nl_cb *); extern int nl_recvmsgs_default(struct nl_sock *); extern int nl_wait_for_ack(struct nl_sock *); +extern int nl_pickup(struct nl_sock *, + int (*parser)(struct nl_cache_ops *, + struct sockaddr_nl *, + struct nlmsghdr *, + struct nl_parser_param *), + struct nl_object **); /* Netlink Family Translations */ extern char * nl_nlfamily2str(int, char *, size_t); extern int nl_str2nlfamily(const char *); diff --git a/include/netlink/object-api.h b/include/netlink/object-api.h index 70a4ddd..75f29cb 100644 --- a/include/netlink/object-api.h +++ b/include/netlink/object-api.h @@ -1,348 +1,19 @@ /* - * netlink/object-api.c Object API + * netlink/object-api.h Object API * * 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 version 2.1 * of the License. * - * Copyright (c) 2003-2007 Thomas Graf + * Copyright (c) 2013 Thomas Graf */ -#ifndef NETLINK_OBJECT_API_H_ -#define NETLINK_OBJECT_API_H_ +#ifndef NETLINK_DUMMY_OBJECT_API_H_ +#define NETLINK_DUMMY_OBJECT_API_H_ #include #include - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @ingroup object - * @defgroup object_api Object API - * @brief - * - * @par 1) Object Definition - * @code - * // Define your object starting with the common object header - * struct my_obj { - * NLHDR_COMMON - * int my_data; - * }; - * - * // Fill out the object operations structure - * struct nl_object_ops my_ops = { - * .oo_name = "my_obj", - * .oo_size = sizeof(struct my_obj), - * }; - * - * // At this point the object can be allocated, you may want to provide a - * // separate _alloc() function to ease allocting objects of this kind. - * struct nl_object *obj = nl_object_alloc(&my_ops); - * - * // And release it again... - * nl_object_put(obj); - * @endcode - * - * @par 2) Allocating additional data - * @code - * // You may require to allocate additional data and store it inside - * // object, f.e. assuming there is a field `ptr'. - * struct my_obj { - * NLHDR_COMMON - * void * ptr; - * }; - * - * // And at some point you may assign allocated data to this field: - * my_obj->ptr = calloc(1, ...); - * - * // In order to not introduce any memory leaks you have to release - * // this data again when the last reference is given back. - * static void my_obj_free_data(struct nl_object *obj) - * { - * struct my_obj *my_obj = nl_object_priv(obj); - * - * free(my_obj->ptr); - * } - * - * // Also when the object is cloned, you must ensure for your pointer - * // stay valid even if one of the clones is freed by either making - * // a clone as well or increase the reference count. - * static int my_obj_clone(struct nl_object *src, struct nl_object *dst) - * { - * struct my_obj *my_src = nl_object_priv(src); - * struct my_obj *my_dst = nl_object_priv(dst); - * - * if (src->ptr) { - * dst->ptr = calloc(1, ...); - * memcpy(dst->ptr, src->ptr, ...); - * } - * } - * - * struct nl_object_ops my_ops = { - * ... - * .oo_free_data = my_obj_free_data, - * .oo_clone = my_obj_clone, - * }; - * @endcode - * - * @par 3) Object Dumping - * @code - * static int my_obj_dump_detailed(struct nl_object *obj, - * struct nl_dump_params *params) - * { - * struct my_obj *my_obj = nl_object_priv(obj); - * - * // It is absolutely essential to use nl_dump() when printing - * // any text to make sure the dumping parameters are respected. - * nl_dump(params, "Obj Integer: %d\n", my_obj->my_int); - * - * // Before we can dump the next line, make sure to prefix - * // this line correctly. - * nl_new_line(params); - * - * // You may also split a line into multiple nl_dump() calls. - * nl_dump(params, "String: %s ", my_obj->my_string); - * nl_dump(params, "String-2: %s\n", my_obj->another_string); - * } - * - * struct nl_object_ops my_ops = { - * ... - * .oo_dump[NL_DUMP_FULL] = my_obj_dump_detailed, - * }; - * @endcode - * - * @par 4) Object Attributes - * @code - * // The concept of object attributes is optional but can ease the typical - * // case of objects that have optional attributes, e.g. a route may have a - * // nexthop assigned but it is not required to. - * - * // The first step to define your object specific bitmask listing all - * // attributes - * #define MY_ATTR_FOO (1<<0) - * #define MY_ATTR_BAR (1<<1) - * - * // When assigning an optional attribute to the object, make sure - * // to mark its availability. - * my_obj->foo = 123123; - * my_obj->ce_mask |= MY_ATTR_FOO; - * - * // At any time you may use this mask to check for the availability - * // of the attribute, e.g. while dumping - * if (my_obj->ce_mask & MY_ATTR_FOO) - * nl_dump(params, "foo %d ", my_obj->foo); - * - * // One of the big advantages of this concept is that it allows for - * // standardized comparisons which make it trivial for caches to - * // identify unique objects by use of unified comparison functions. - * // In order for it to work, your object implementation must provide - * // a comparison function and define a list of attributes which - * // combined together make an object unique. - * - * static int my_obj_compare(struct nl_object *_a, struct nl_object *_b, - * uint32_t attrs, int flags) - * { - * struct my_obj *a = nl_object_priv(_a): - * struct my_obj *b = nl_object_priv(_b): - * int diff = 0; - * - * // We help ourselves in defining our own DIFF macro which will - * // call ATTR_DIFF() on both objects which will make sure to only - * // compare the attributes if required. - * #define MY_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, MY_ATTR_##ATTR, a, b, EXPR) - * - * // Call our own diff macro for each attribute to build a bitmask - * // representing the attributes which mismatch. - * diff |= MY_DIFF(FOO, a->foo != b->foo) - * diff |= MY_DIFF(BAR, strcmp(a->bar, b->bar)) - * - * return diff; - * } - * - * // In order to identify identical objects with differing attributes - * // you must specify the attributes required to uniquely identify - * // your object. Make sure to not include too many attributes, this - * // list is used when caches look for an old version of an object. - * struct nl_object_ops my_ops = { - * ... - * .oo_id_attrs = MY_ATTR_FOO, - * .oo_compare = my_obj_compare, - * }; - * @endcode - * @{ - */ - -/** - * Common Object Header - * - * This macro must be included as first member in every object - * definition to allow objects to be cached. - */ -#define NLHDR_COMMON \ - int ce_refcnt; \ - struct nl_object_ops * ce_ops; \ - struct nl_cache * ce_cache; \ - struct nl_list_head ce_list; \ - int ce_msgtype; \ - int ce_flags; \ - uint32_t ce_mask; - -struct nl_object -{ - NLHDR_COMMON -}; - - -/** - * Return true if attribute is available in both objects - * @arg A an object - * @arg B another object - * @arg ATTR attribute bit - * - * @return True if the attribute is available, otherwise false is returned. - */ -#define AVAILABLE(A, B, ATTR) (((A)->ce_mask & (B)->ce_mask) & (ATTR)) - -/** - * Return true if attribute is available in only one of both objects - * @arg A an object - * @arg B another object - * @arg ATTR attribute bit - * - * @return True if the attribute is available in only one of both objects, - * otherwise false is returned. - */ -#define AVAILABLE_MISMATCH(A, B, ATTR) (((A)->ce_mask ^ (B)->ce_mask) & (ATTR)) - -/** - * Return true if attributes mismatch - * @arg A an object - * @arg B another object - * @arg ATTR attribute bit - * @arg EXPR Comparison expression - * - * This function will check if the attribute in question is available - * in both objects, if not this will count as a mismatch. - * - * If available the function will execute the expression which must - * return true if the attributes mismatch. - * - * @return True if the attribute mismatch, or false if they match. - */ -#define ATTR_MISMATCH(A, B, ATTR, EXPR) (AVAILABLE_MISMATCH(A, B, ATTR) || \ - (AVAILABLE(A, B, ATTR) && (EXPR))) - -/** - * Return attribute bit if attribute does not match - * @arg LIST list of attributes to be compared - * @arg ATTR attribute bit - * @arg A an object - * @arg B another object - * @arg EXPR Comparison expression - * - * This function will check if the attribute in question is available - * in both objects, if not this will count as a mismatch. - * - * If available the function will execute the expression which must - * return true if the attributes mismatch. - * - * In case the attributes mismatch, the attribute is returned, otherwise - * 0 is returned. - * - * @code - * diff |= ATTR_DIFF(attrs, MY_ATTR_FOO, a, b, a->foo != b->foo); - * @endcode - */ -#define ATTR_DIFF(LIST, ATTR, A, B, EXPR) \ -({ int diff = 0; \ - if (((LIST) & (ATTR)) && ATTR_MISMATCH(A, B, ATTR, EXPR)) \ - diff = ATTR; \ - diff; }) - -/** - * Object Operations - */ -struct nl_object_ops -{ - /** - * Unique name of object type - * - * Must be in the form family/name, e.g. "route/addr" - */ - char * oo_name; - - /** Size of object including its header */ - size_t oo_size; - - /* List of attributes needed to uniquely identify the object */ - uint32_t oo_id_attrs; - - /** - * Constructor function - * - * Will be called when a new object of this type is allocated. - * Can be used to initialize members such as lists etc. - */ - void (*oo_constructor)(struct nl_object *); - - /** - * Destructor function - * - * Will be called when an object is freed. Must free all - * resources which may have been allocated as part of this - * object. - */ - void (*oo_free_data)(struct nl_object *); - - /** - * Cloning function - * - * Will be called when an object needs to be cloned. Please - * note that the generic object code will make an exact - * copy of the object first, therefore you only need to take - * care of members which require reference counting etc. - * - * May return a negative error code to abort cloning. - */ - int (*oo_clone)(struct nl_object *, struct nl_object *); - - /** - * Dumping functions - * - * Will be called when an object is dumped. The implementations - * have to use nl_dump(), nl_dump_line(), and nl_new_line() to - * dump objects. - * - * The functions must return the number of lines printed. - */ - void (*oo_dump[NL_DUMP_MAX+1])(struct nl_object *, - struct nl_dump_params *); - - /** - * Comparison function - * - * Will be called when two objects of the same type are - * compared. It takes the two objects in question, an object - * specific bitmask defining which attributes should be - * compared and flags to control the behaviour. - * - * The function must return a bitmask with the relevant bit - * set for each attribute that mismatches. - */ - int (*oo_compare)(struct nl_object *, struct nl_object *, - uint32_t, int); - - - char *(*oo_attrs2str)(int, char *, size_t); -}; - -/** @} */ - -#ifdef __cplusplus -} -#endif +#include #endif diff --git a/include/netlink/object.h b/include/netlink/object.h index ef1ed9f..a95feda 100644 --- a/include/netlink/object.h +++ b/include/netlink/object.h @@ -6,7 +6,7 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2008 Thomas Graf + * Copyright (c) 2003-2012 Thomas Graf */ #ifndef NETLINK_OBJECT_H_ @@ -31,11 +31,14 @@ extern int nl_object_alloc_name(const char *, struct nl_object **); extern void nl_object_free(struct nl_object *); extern struct nl_object * nl_object_clone(struct nl_object *obj); +extern int nl_object_update(struct nl_object *dst, + struct nl_object *src); extern void nl_object_get(struct nl_object *); extern void nl_object_put(struct nl_object *); extern int nl_object_shared(struct nl_object *); extern void nl_object_dump(struct nl_object *, struct nl_dump_params *); +extern void nl_object_dump_buf(struct nl_object *, char *, size_t); extern int nl_object_identical(struct nl_object *, struct nl_object *); extern uint32_t nl_object_diff(struct nl_object *, @@ -47,6 +50,8 @@ extern char * nl_object_attrs2str(struct nl_object *, size_t); extern char * nl_object_attr_list(struct nl_object *, char *, size_t); +extern void nl_object_keygen(struct nl_object *, + uint32_t *, uint32_t); /* Marks */ extern void nl_object_mark(struct nl_object *); @@ -56,6 +61,12 @@ extern int nl_object_is_marked(struct nl_object *); /* Access Functions */ extern int nl_object_get_refcnt(struct nl_object *); extern struct nl_cache * nl_object_get_cache(struct nl_object *); +extern const char * nl_object_get_type(const struct nl_object *); +extern int nl_object_get_msgtype(const struct nl_object *); +struct nl_object_ops * nl_object_get_ops(const struct nl_object *); +uint32_t nl_object_get_id_attrs(struct nl_object *obj); + + static inline void * nl_object_priv(struct nl_object *obj) { return obj; diff --git a/include/netlink/route/addr.h b/include/netlink/route/addr.h index 1381486..56c12e7 100644 --- a/include/netlink/route/addr.h +++ b/include/netlink/route/addr.h @@ -6,7 +6,7 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2008 Thomas Graf + * Copyright (c) 2003-2011 Thomas Graf * Copyright (c) 2003-2006 Baruch Even , * Mediatrix Telecom, inc. */ @@ -17,6 +17,7 @@ #include #include #include +#include #ifdef __cplusplus extern "C" { @@ -29,6 +30,8 @@ extern struct rtnl_addr *rtnl_addr_alloc(void); extern void rtnl_addr_put(struct rtnl_addr *); extern int rtnl_addr_alloc_cache(struct nl_sock *, struct nl_cache **); +extern struct rtnl_addr * + rtnl_addr_get(struct nl_cache *, int, struct nl_addr *); extern int rtnl_addr_build_add_request(struct rtnl_addr *, int, struct nl_msg **); @@ -48,6 +51,10 @@ extern char * rtnl_addr_get_label(struct rtnl_addr *); extern void rtnl_addr_set_ifindex(struct rtnl_addr *, int); extern int rtnl_addr_get_ifindex(struct rtnl_addr *); +extern void rtnl_addr_set_link(struct rtnl_addr *, struct rtnl_link *); +extern struct rtnl_link * + rtnl_addr_get_link(struct rtnl_addr *); + extern void rtnl_addr_set_family(struct rtnl_addr *, int); extern int rtnl_addr_get_family(struct rtnl_addr *); diff --git a/include/netlink/route/class.h b/include/netlink/route/class.h index ad3bacf..e73b60a 100644 --- a/include/netlink/route/class.h +++ b/include/netlink/route/class.h @@ -1,12 +1,12 @@ /* - * netlink/route/class.h Classes + * netlink/route/class.h Traffic Classes * * 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 version 2.1 * of the License. * - * Copyright (c) 2003-2008 Thomas Graf + * Copyright (c) 2003-2011 Thomas Graf */ #ifndef NETLINK_CLASS_H_ @@ -22,14 +22,17 @@ extern "C" { struct rtnl_class; -extern struct rtnl_class * rtnl_class_alloc(void); +extern struct rtnl_class * + rtnl_class_alloc(void); extern void rtnl_class_put(struct rtnl_class *); + extern int rtnl_class_alloc_cache(struct nl_sock *, int, struct nl_cache **); -extern struct rtnl_class *rtnl_class_get(struct nl_cache *, int, uint32_t); +extern struct rtnl_class * + rtnl_class_get(struct nl_cache *, int, uint32_t); -/* leaf qdisc access */ -extern struct rtnl_qdisc * rtnl_class_leaf_qdisc(struct rtnl_class *, +extern struct rtnl_qdisc * + rtnl_class_leaf_qdisc(struct rtnl_class *, struct nl_cache *); extern int rtnl_class_build_add_request(struct rtnl_class *, int, @@ -37,23 +40,24 @@ extern int rtnl_class_build_add_request(struct rtnl_class *, int, extern int rtnl_class_add(struct nl_sock *, struct rtnl_class *, int); -extern int rtnl_class_build_delete_request(struct rtnl_class *, - struct nl_msg **); -extern int rtnl_class_delete(struct nl_sock *, struct rtnl_class *); +extern int rtnl_class_build_delete_request(struct rtnl_class *, + struct nl_msg **); +extern int rtnl_class_delete(struct nl_sock *, + struct rtnl_class *); -extern void rtnl_class_set_kind(struct rtnl_class *, const char *); - -/* iterators */ +/* deprecated functions */ extern void rtnl_class_foreach_child(struct rtnl_class *, struct nl_cache *, void (*cb)(struct nl_object *, void *), - void *); + void *) + __attribute__((deprecated)); extern void rtnl_class_foreach_cls(struct rtnl_class *, struct nl_cache *, void (*cb)(struct nl_object *, void *), - void *); + void *) + __attribute__((deprecated)); #ifdef __cplusplus } diff --git a/include/netlink/route/classifier.h b/include/netlink/route/classifier.h index 23af837..647bf1e 100644 --- a/include/netlink/route/classifier.h +++ b/include/netlink/route/classifier.h @@ -6,7 +6,7 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2010 Thomas Graf + * Copyright (c) 2003-2011 Thomas Graf */ #ifndef NETLINK_CLASSIFIER_H_ @@ -22,26 +22,27 @@ extern "C" { #endif extern struct rtnl_cls *rtnl_cls_alloc(void); -extern void rtnl_cls_put(struct rtnl_cls *); +extern void rtnl_cls_put(struct rtnl_cls *); -extern int rtnl_cls_alloc_cache(struct nl_sock *, int, uint32_t, - struct nl_cache **); +extern int rtnl_cls_alloc_cache(struct nl_sock *, int, uint32_t, + struct nl_cache **); -extern int rtnl_cls_build_add_request(struct rtnl_cls *, int, - struct nl_msg **); -extern int rtnl_cls_add(struct nl_sock *, struct rtnl_cls *, int); +extern int rtnl_cls_build_add_request(struct rtnl_cls *, int, + struct nl_msg **); +extern int rtnl_cls_add(struct nl_sock *, struct rtnl_cls *, int); -extern int rtnl_cls_build_change_request(struct rtnl_cls *, int, - struct nl_msg **); -extern int rtnl_cls_build_delete_request(struct rtnl_cls *, int, - struct nl_msg **); -extern int rtnl_cls_delete(struct nl_sock *, struct rtnl_cls *, int); +extern int rtnl_cls_build_change_request(struct rtnl_cls *, int, + struct nl_msg **); +extern int rtnl_cls_build_delete_request(struct rtnl_cls *, int, + struct nl_msg **); +extern int rtnl_cls_delete(struct nl_sock *, struct rtnl_cls *, + int); -extern void rtnl_cls_set_prio(struct rtnl_cls *, uint16_t); -extern uint16_t rtnl_cls_get_prio(struct rtnl_cls *); +extern void rtnl_cls_set_prio(struct rtnl_cls *, uint16_t); +extern uint16_t rtnl_cls_get_prio(struct rtnl_cls *); -extern void rtnl_cls_set_protocol(struct rtnl_cls *, uint16_t); -extern uint16_t rtnl_cls_get_protocol(struct rtnl_cls *); +extern void rtnl_cls_set_protocol(struct rtnl_cls *, uint16_t); +extern uint16_t rtnl_cls_get_protocol(struct rtnl_cls *); #ifdef __cplusplus } diff --git a/include/netlink/route/cls/basic.h b/include/netlink/route/cls/basic.h index 8b58c1e..603fa88 100644 --- a/include/netlink/route/cls/basic.h +++ b/include/netlink/route/cls/basic.h @@ -13,6 +13,8 @@ #define NETLINK_BASIC_H_ #include +#include +#include #ifdef __cplusplus extern "C" { diff --git a/include/netlink/route/cls/cgroup.h b/include/netlink/route/cls/cgroup.h index 20346ff..9cd4845 100644 --- a/include/netlink/route/cls/cgroup.h +++ b/include/netlink/route/cls/cgroup.h @@ -14,6 +14,8 @@ #include #include +#include +#include #ifdef __cplusplus extern "C" { diff --git a/include/netlink/route/cls/fw.h b/include/netlink/route/cls/fw.h index 39878de..2e1bade 100644 --- a/include/netlink/route/cls/fw.h +++ b/include/netlink/route/cls/fw.h @@ -15,12 +15,14 @@ #define NETLINK_FW_H_ #include +#include #ifdef __cplusplus extern "C" { #endif extern int rtnl_fw_set_classid(struct rtnl_cls *, uint32_t); +extern int rtnl_fw_set_mask(struct rtnl_cls *, uint32_t); #ifdef __cplusplus } diff --git a/include/netlink/route/cls/u32.h b/include/netlink/route/cls/u32.h index cf35e26..59bed33 100644 --- a/include/netlink/route/cls/u32.h +++ b/include/netlink/route/cls/u32.h @@ -14,6 +14,7 @@ #include #include +#include #ifdef __cplusplus extern "C" { @@ -21,6 +22,11 @@ extern "C" { extern void rtnl_u32_set_handle(struct rtnl_cls *, int, int, int); extern int rtnl_u32_set_classid(struct rtnl_cls *, uint32_t); +extern int rtnl_u32_set_divisor(struct rtnl_cls *, uint32_t); +extern int rtnl_u32_set_link(struct rtnl_cls *, uint32_t); +extern int rtnl_u32_set_hashtable(struct rtnl_cls *, uint32_t); +extern int rtnl_u32_set_hashmask(struct rtnl_cls *, uint32_t, uint32_t); +extern int rtnl_u32_set_cls_terminal(struct rtnl_cls *); extern int rtnl_u32_set_flags(struct rtnl_cls *, int); extern int rtnl_u32_add_key(struct rtnl_cls *, uint32_t, uint32_t, diff --git a/include/netlink/route/link.h b/include/netlink/route/link.h index dcd32f3..321a80d 100644 --- a/include/netlink/route/link.h +++ b/include/netlink/route/link.h @@ -6,7 +6,7 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2010 Thomas Graf + * Copyright (c) 2003-2012 Thomas Graf */ #ifndef NETLINK_LINK_H_ @@ -15,93 +15,113 @@ #include #include #include +#include #ifdef __cplusplus extern "C" { #endif +/** + * @struct rtnl_link link.h "netlink/route/link.h" + * @brief Link object + * @implements nl_object + * @ingroup link + * + * @copydoc private_struct + */ struct rtnl_link; -enum rtnl_link_st { - RTNL_LINK_RX_PACKETS, - RTNL_LINK_TX_PACKETS, - RTNL_LINK_RX_BYTES, - RTNL_LINK_TX_BYTES, - RTNL_LINK_RX_ERRORS, - RTNL_LINK_TX_ERRORS, - RTNL_LINK_RX_DROPPED, - RTNL_LINK_TX_DROPPED, - RTNL_LINK_RX_COMPRESSED, - RTNL_LINK_TX_COMPRESSED, - RTNL_LINK_RX_FIFO_ERR, - RTNL_LINK_TX_FIFO_ERR, - RTNL_LINK_RX_LEN_ERR, - RTNL_LINK_RX_OVER_ERR, - RTNL_LINK_RX_CRC_ERR, - RTNL_LINK_RX_FRAME_ERR, - RTNL_LINK_RX_MISSED_ERR, - RTNL_LINK_TX_ABORT_ERR, - RTNL_LINK_TX_CARRIER_ERR, - RTNL_LINK_TX_HBEAT_ERR, - RTNL_LINK_TX_WIN_ERR, - RTNL_LINK_COLLISIONS, - RTNL_LINK_MULTICAST, - RTNL_LINK_IP6_INPKTS, /* InReceives */ - RTNL_LINK_IP6_INHDRERRORS, /* InHdrErrors */ - RTNL_LINK_IP6_INTOOBIGERRORS, /* InTooBigErrors */ - RTNL_LINK_IP6_INNOROUTES, /* InNoRoutes */ - RTNL_LINK_IP6_INADDRERRORS, /* InAddrErrors */ - RTNL_LINK_IP6_INUNKNOWNPROTOS, /* InUnknownProtos */ - RTNL_LINK_IP6_INTRUNCATEDPKTS, /* InTruncatedPkts */ - RTNL_LINK_IP6_INDISCARDS, /* InDiscards */ - RTNL_LINK_IP6_INDELIVERS, /* InDelivers */ - RTNL_LINK_IP6_OUTFORWDATAGRAMS, /* OutForwDatagrams */ - RTNL_LINK_IP6_OUTPKTS, /* OutRequests */ - RTNL_LINK_IP6_OUTDISCARDS, /* OutDiscards */ - RTNL_LINK_IP6_OUTNOROUTES, /* OutNoRoutes */ - RTNL_LINK_IP6_REASMTIMEOUT, /* ReasmTimeout */ - RTNL_LINK_IP6_REASMREQDS, /* ReasmReqds */ - RTNL_LINK_IP6_REASMOKS, /* ReasmOKs */ - RTNL_LINK_IP6_REASMFAILS, /* ReasmFails */ - RTNL_LINK_IP6_FRAGOKS, /* FragOKs */ - RTNL_LINK_IP6_FRAGFAILS, /* FragFails */ - RTNL_LINK_IP6_FRAGCREATES, /* FragCreates */ - RTNL_LINK_IP6_INMCASTPKTS, /* InMcastPkts */ - RTNL_LINK_IP6_OUTMCASTPKTS, /* OutMcastPkts */ - RTNL_LINK_IP6_INBCASTPKTS, /* InBcastPkts */ - RTNL_LINK_IP6_OUTBCASTPKTS, /* OutBcastPkts */ - RTNL_LINK_IP6_INOCTETS, /* InOctets */ - RTNL_LINK_IP6_OUTOCTETS, /* OutOctets */ - RTNL_LINK_IP6_INMCASTOCTETS, /* InMcastOctets */ - RTNL_LINK_IP6_OUTMCASTOCTETS, /* OutMcastOctets */ - RTNL_LINK_IP6_INBCASTOCTETS, /* InBcastOctets */ - RTNL_LINK_IP6_OUTBCASTOCTETS, /* OutBcastOctets */ - RTNL_LINK_ICMP6_INMSGS, /* InMsgs */ - RTNL_LINK_ICMP6_INERRORS, /* InErrors */ - RTNL_LINK_ICMP6_OUTMSGS, /* OutMsgs */ - RTNL_LINK_ICMP6_OUTERRORS, /* OutErrors */ +/** + * @ingroup link + */ +typedef enum { + RTNL_LINK_RX_PACKETS, /*!< Packets received */ + RTNL_LINK_TX_PACKETS, /*!< Packets sent */ + RTNL_LINK_RX_BYTES, /*!< Bytes received */ + RTNL_LINK_TX_BYTES, /*!< Bytes sent */ + RTNL_LINK_RX_ERRORS, /*!< Receive errors */ + RTNL_LINK_TX_ERRORS, /*!< Send errors */ + RTNL_LINK_RX_DROPPED, /*!< Received packets dropped */ + RTNL_LINK_TX_DROPPED, /*!< Packets dropped during transmit */ + RTNL_LINK_RX_COMPRESSED, /*!< Compressed packets received */ + RTNL_LINK_TX_COMPRESSED, /*!< Compressed packets sent */ + RTNL_LINK_RX_FIFO_ERR, /*!< Receive FIFO errors */ + RTNL_LINK_TX_FIFO_ERR, /*!< Send FIFO errors */ + RTNL_LINK_RX_LEN_ERR, /*!< Length errors */ + RTNL_LINK_RX_OVER_ERR, /*!< Over errors */ + RTNL_LINK_RX_CRC_ERR, /*!< CRC errors */ + RTNL_LINK_RX_FRAME_ERR, /*!< Frame errors */ + RTNL_LINK_RX_MISSED_ERR, /*!< Missed errors */ + RTNL_LINK_TX_ABORT_ERR, /*!< Aborted errors */ + RTNL_LINK_TX_CARRIER_ERR, /*!< Carrier errors */ + RTNL_LINK_TX_HBEAT_ERR, /*!< Heartbeat errors */ + RTNL_LINK_TX_WIN_ERR, /*!< Window errors */ + RTNL_LINK_COLLISIONS, /*!< Send collisions */ + RTNL_LINK_MULTICAST, /*!< Multicast */ + RTNL_LINK_IP6_INPKTS, /*!< IPv6 SNMP InReceives */ + RTNL_LINK_IP6_INHDRERRORS, /*!< IPv6 SNMP InHdrErrors */ + RTNL_LINK_IP6_INTOOBIGERRORS, /*!< IPv6 SNMP InTooBigErrors */ + RTNL_LINK_IP6_INNOROUTES, /*!< IPv6 SNMP InNoRoutes */ + RTNL_LINK_IP6_INADDRERRORS, /*!< IPv6 SNMP InAddrErrors */ + RTNL_LINK_IP6_INUNKNOWNPROTOS, /*!< IPv6 SNMP InUnknownProtos */ + RTNL_LINK_IP6_INTRUNCATEDPKTS, /*!< IPv6 SNMP InTruncatedPkts */ + RTNL_LINK_IP6_INDISCARDS, /*!< IPv6 SNMP InDiscards */ + RTNL_LINK_IP6_INDELIVERS, /*!< IPv6 SNMP InDelivers */ + RTNL_LINK_IP6_OUTFORWDATAGRAMS, /*!< IPv6 SNMP OutForwDatagrams */ + RTNL_LINK_IP6_OUTPKTS, /*!< IPv6 SNMP OutRequests */ + RTNL_LINK_IP6_OUTDISCARDS, /*!< IPv6 SNMP OutDiscards */ + RTNL_LINK_IP6_OUTNOROUTES, /*!< IPv6 SNMP OutNoRoutes */ + RTNL_LINK_IP6_REASMTIMEOUT, /*!< IPv6 SNMP ReasmTimeout */ + RTNL_LINK_IP6_REASMREQDS, /*!< IPv6 SNMP ReasmReqds */ + RTNL_LINK_IP6_REASMOKS, /*!< IPv6 SNMP ReasmOKs */ + RTNL_LINK_IP6_REASMFAILS, /*!< IPv6 SNMP ReasmFails */ + RTNL_LINK_IP6_FRAGOKS, /*!< IPv6 SNMP FragOKs */ + RTNL_LINK_IP6_FRAGFAILS, /*!< IPv6 SNMP FragFails */ + RTNL_LINK_IP6_FRAGCREATES, /*!< IPv6 SNMP FragCreates */ + RTNL_LINK_IP6_INMCASTPKTS, /*!< IPv6 SNMP InMcastPkts */ + RTNL_LINK_IP6_OUTMCASTPKTS, /*!< IPv6 SNMP OutMcastPkts */ + RTNL_LINK_IP6_INBCASTPKTS, /*!< IPv6 SNMP InBcastPkts */ + RTNL_LINK_IP6_OUTBCASTPKTS, /*!< IPv6 SNMP OutBcastPkts */ + RTNL_LINK_IP6_INOCTETS, /*!< IPv6 SNMP InOctets */ + RTNL_LINK_IP6_OUTOCTETS, /*!< IPv6 SNMP OutOctets */ + RTNL_LINK_IP6_INMCASTOCTETS, /*!< IPv6 SNMP InMcastOctets */ + RTNL_LINK_IP6_OUTMCASTOCTETS, /*!< IPv6 SNMP OutMcastOctets */ + RTNL_LINK_IP6_INBCASTOCTETS, /*!< IPv6 SNMP InBcastOctets */ + RTNL_LINK_IP6_OUTBCASTOCTETS, /*!< IPv6 SNMP OutBcastOctets */ + RTNL_LINK_ICMP6_INMSGS, /*!< ICMPv6 SNMP InMsgs */ + RTNL_LINK_ICMP6_INERRORS, /*!< ICMPv6 SNMP InErrors */ + RTNL_LINK_ICMP6_OUTMSGS, /*!< ICMPv6 SNMP OutMsgs */ + RTNL_LINK_ICMP6_OUTERRORS, /*!< ICMPv6 SNMP OutErrors */ __RTNL_LINK_STATS_MAX, -}; +} rtnl_link_stat_id_t; #define RTNL_LINK_STATS_MAX (__RTNL_LINK_STATS_MAX - 1) -/* link object allocation/freeage */ extern struct rtnl_link *rtnl_link_alloc(void); extern void rtnl_link_put(struct rtnl_link *); -extern void rtnl_link_free(struct rtnl_link *); -/* link cache management */ extern int rtnl_link_alloc_cache(struct nl_sock *, int, struct nl_cache **); extern struct rtnl_link *rtnl_link_get(struct nl_cache *, int); extern struct rtnl_link *rtnl_link_get_by_name(struct nl_cache *, const char *); +extern int rtnl_link_build_add_request(struct rtnl_link *, int, + struct nl_msg **); +extern int rtnl_link_add(struct nl_sock *, struct rtnl_link *, int); extern int rtnl_link_build_change_request(struct rtnl_link *, struct rtnl_link *, int, struct nl_msg **); extern int rtnl_link_change(struct nl_sock *, struct rtnl_link *, struct rtnl_link *, int); +extern int rtnl_link_build_delete_request(const struct rtnl_link *, + struct nl_msg **); +extern int rtnl_link_delete(struct nl_sock *, const struct rtnl_link *); +extern int rtnl_link_build_get_request(int, const char *, + struct nl_msg **); +extern int rtnl_link_get_kernel(struct nl_sock *, int, const char *, + struct rtnl_link **); + /* Name <-> Index Translations */ extern char * rtnl_link_i2name(struct nl_cache *, int, char *, size_t); extern int rtnl_link_name2i(struct nl_cache *, const char *); @@ -114,12 +134,16 @@ extern int rtnl_link_str2stat(const char *); extern char * rtnl_link_flags2str(int, char *, size_t); extern int rtnl_link_str2flags(const char *); -extern char * rtnl_link_operstate2str(int, char *, size_t); +extern char * rtnl_link_operstate2str(uint8_t, char *, size_t); extern int rtnl_link_str2operstate(const char *); -extern char * rtnl_link_mode2str(int, char *, size_t); +extern char * rtnl_link_mode2str(uint8_t, char *, size_t); extern int rtnl_link_str2mode(const char *); +/* Carrier State Translations */ +extern char * rtnl_link_carrier2str(uint8_t, char *, size_t); +extern int rtnl_link_str2carrier(const char *); + /* Access Functions */ extern void rtnl_link_set_qdisc(struct rtnl_link *, const char *); extern char * rtnl_link_get_qdisc(struct rtnl_link *); @@ -127,6 +151,9 @@ extern char * rtnl_link_get_qdisc(struct rtnl_link *); extern void rtnl_link_set_name(struct rtnl_link *, const char *); extern char * rtnl_link_get_name(struct rtnl_link *); +extern void rtnl_link_set_group(struct rtnl_link *, uint32_t); +extern uint32_t rtnl_link_get_group(struct rtnl_link *); + extern void rtnl_link_set_flags(struct rtnl_link *, unsigned int); extern void rtnl_link_unset_flags(struct rtnl_link *, unsigned int); extern unsigned int rtnl_link_get_flags(struct rtnl_link *); @@ -137,9 +164,6 @@ extern unsigned int rtnl_link_get_mtu(struct rtnl_link *); extern void rtnl_link_set_txqlen(struct rtnl_link *, unsigned int); extern unsigned int rtnl_link_get_txqlen(struct rtnl_link *); -extern void rtnl_link_set_weight(struct rtnl_link *, unsigned int); -extern unsigned int rtnl_link_get_weight(struct rtnl_link *); - extern void rtnl_link_set_ifindex(struct rtnl_link *, int); extern int rtnl_link_get_ifindex(struct rtnl_link *); @@ -161,6 +185,9 @@ extern int rtnl_link_get_link(struct rtnl_link *); extern void rtnl_link_set_master(struct rtnl_link *, int); extern int rtnl_link_get_master(struct rtnl_link *); +extern void rtnl_link_set_carrier(struct rtnl_link *, uint8_t); +extern uint8_t rtnl_link_get_carrier(struct rtnl_link *); + extern void rtnl_link_set_operstate(struct rtnl_link *, uint8_t); extern uint8_t rtnl_link_get_operstate(struct rtnl_link *); @@ -172,12 +199,34 @@ extern void rtnl_link_set_ifalias(struct rtnl_link *, const char *); extern int rtnl_link_get_num_vf(struct rtnl_link *, uint32_t *); -extern uint64_t rtnl_link_get_stat(struct rtnl_link *, int); -extern int rtnl_link_set_stat(struct rtnl_link *, const unsigned int, +extern uint64_t rtnl_link_get_stat(struct rtnl_link *, rtnl_link_stat_id_t); +extern int rtnl_link_set_stat(struct rtnl_link *, rtnl_link_stat_id_t, const uint64_t); -extern int rtnl_link_set_info_type(struct rtnl_link *, const char *); -extern char * rtnl_link_get_info_type(struct rtnl_link *); +extern int rtnl_link_set_type(struct rtnl_link *, const char *); +extern char * rtnl_link_get_type(struct rtnl_link *); + +extern void rtnl_link_set_promiscuity(struct rtnl_link *, uint32_t); +extern uint32_t rtnl_link_get_promiscuity(struct rtnl_link *); + +extern void rtnl_link_set_num_tx_queues(struct rtnl_link *, uint32_t); +extern uint32_t rtnl_link_get_num_tx_queues(struct rtnl_link *); + +extern void rtnl_link_set_num_rx_queues(struct rtnl_link *, uint32_t); +extern uint32_t rtnl_link_get_num_rx_queues(struct rtnl_link *); + +extern int rtnl_link_enslave_ifindex(struct nl_sock *, int, int); +extern int rtnl_link_enslave(struct nl_sock *, struct rtnl_link *, + struct rtnl_link *); +extern int rtnl_link_release_ifindex(struct nl_sock *, int); +extern int rtnl_link_release(struct nl_sock *, struct rtnl_link *); + +/* deprecated */ +extern int rtnl_link_set_info_type(struct rtnl_link *, const char *) __attribute__((deprecated)); +extern char * rtnl_link_get_info_type(struct rtnl_link *) __attribute__((deprecated)); +extern void rtnl_link_set_weight(struct rtnl_link *, unsigned int) __attribute__((deprecated)); +extern unsigned int rtnl_link_get_weight(struct rtnl_link *) __attribute__((deprecated)); + #ifdef __cplusplus } diff --git a/include/netlink/route/link/api.h b/include/netlink/route/link/api.h index 960d3f1..03b1e5e 100644 --- a/include/netlink/route/link/api.h +++ b/include/netlink/route/link/api.h @@ -6,129 +6,15 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2010 Thomas Graf + * Copyright (c) 2013 Thomas Graf */ -#ifndef NETLINK_LINK_API_H_ -#define NETLINK_LINK_API_H_ +#ifndef NETLINK_DUMMY_LINK_API_H_ +#define NETLINK_DUMMY_LINK_API_H_ #include +#include -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @ingroup link_api - * - * Available operations to modules implementing a link info type. - */ -struct rtnl_link_info_ops -{ - /** Name of link info type, must match name on kernel side */ - char * io_name; - - /** Reference count, DO NOT MODIFY */ - int io_refcnt; - - /** Called to assign an info type to a link. - * Has to allocate enough resources to hold attributes. Can - * use link->l_info to store a pointer. */ - int (*io_alloc)(struct rtnl_link *); - - /** Called to parse the link info attribute. - * Must parse the attribute and assign all values to the link. - */ - int (*io_parse)(struct rtnl_link *, - struct nlattr *, - struct nlattr *); - - /** Called when the link object is dumped. - * Must dump the info type specific attributes. */ - void (*io_dump[NL_DUMP_MAX+1])(struct rtnl_link *, - struct nl_dump_params *); - - /** Called when a link object is cloned. - * Must clone all info type specific attributes. */ - int (*io_clone)(struct rtnl_link *, struct rtnl_link *); - - /** Called when construction a link netlink message. - * Must append all info type specific attributes to the message. */ - int (*io_put_attrs)(struct nl_msg *, struct rtnl_link *); - - /** Called to release all resources previously allocated - * in either io_alloc() or io_parse(). */ - void (*io_free)(struct rtnl_link *); - - struct nl_list_head io_list; -}; - -extern struct rtnl_link_info_ops *rtnl_link_info_ops_lookup(const char *); -extern void rtnl_link_info_ops_put(struct rtnl_link_info_ops *); -extern int rtnl_link_register_info(struct rtnl_link_info_ops *); -extern int rtnl_link_unregister_info(struct rtnl_link_info_ops *); - - -/** - * @ingroup link_api - * - * Available operations to modules implementing a link address family. - */ -struct rtnl_link_af_ops -{ - /** The address family this operations set implements */ - const unsigned int ao_family; - - /** Number of users of this operations, DO NOT MODIFY. */ - int ao_refcnt; - - /** Validation policy for IFLA_PROTINFO attribute. This pointer - * can be set to a nla_policy structure describing the minimal - * requirements the attribute must meet. Failure of meeting these - * requirements will result in a parsing error. */ - const struct nla_policy *ao_protinfo_policy; - - /** Called after address family has been assigned to link. Must - * allocate data buffer to hold address family specific data and - * store it in link->l_af_data. */ - void * (*ao_alloc)(struct rtnl_link *); - - /** Called when the link is cloned, must allocate a clone of the - * address family specific buffer and return it. */ - void * (*ao_clone)(struct rtnl_link *, void *); - - /** Called when the link gets freed. Must free all allocated data */ - void (*ao_free)(struct rtnl_link *, void *); - - /** Called if a IFLA_PROTINFO attribute needs to be parsed. Typically - * stores the parsed data in the address family specific buffer. */ - int (*ao_parse_protinfo)(struct rtnl_link *, - struct nlattr *, void *); - - /** Called if a IFLA_AF_SPEC attribute needs to be parsed. Typically - * stores the parsed data in the address family specific buffer. */ - int (*ao_parse_af)(struct rtnl_link *, - struct nlattr *, void *); - - /** Called if a link message is sent to the kernel. Must append the - * link address family specific attributes to the message. */ - int (*ao_fill_af)(struct rtnl_link *, - struct nl_msg *msg, void *); - - /** Dump address family specific link attributes */ - void (*ao_dump[NL_DUMP_MAX+1])(struct rtnl_link *, - struct nl_dump_params *, - void *); -}; - -extern struct rtnl_link_af_ops *rtnl_link_af_ops_lookup(unsigned int); -extern void rtnl_link_af_ops_put(struct rtnl_link_af_ops *); -extern void * rtnl_link_af_alloc(struct rtnl_link *, - const struct rtnl_link_af_ops *); -extern void * rtnl_link_af_data(const struct rtnl_link *, - const struct rtnl_link_af_ops *); -extern int rtnl_link_af_register(struct rtnl_link_af_ops *); -extern int rtnl_link_af_unregister(struct rtnl_link_af_ops *); - +#warning "You are including a deprecated header file, include ." #endif diff --git a/include/netlink/route/link/bonding.h b/include/netlink/route/link/bonding.h new file mode 100644 index 0000000..5c34662 --- /dev/null +++ b/include/netlink/route/link/bonding.h @@ -0,0 +1,39 @@ +/* + * netlink/route/link/bonding.h Bonding Interface + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2011-2013 Thomas Graf + */ + +#ifndef NETLINK_LINK_BONDING_H_ +#define NETLINK_LINK_BONDING_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern struct rtnl_link *rtnl_link_bond_alloc(void); + +extern int rtnl_link_bond_add(struct nl_sock *, const char *, + struct rtnl_link *); + +extern int rtnl_link_bond_enslave_ifindex(struct nl_sock *, int, int); +extern int rtnl_link_bond_enslave(struct nl_sock *, struct rtnl_link *, + struct rtnl_link *); + +extern int rtnl_link_bond_release_ifindex(struct nl_sock *, int); +extern int rtnl_link_bond_release(struct nl_sock *, struct rtnl_link *); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/include/netlink/route/link/bridge.h b/include/netlink/route/link/bridge.h new file mode 100644 index 0000000..71dcc24 --- /dev/null +++ b/include/netlink/route/link/bridge.h @@ -0,0 +1,56 @@ +/* + * netlink/route/link/bridge.h Bridge + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2013 Thomas Graf + */ + +#ifndef NETLINK_LINK_BRIDGE_H_ +#define NETLINK_LINK_BRIDGE_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Bridge flags + * @ingroup bridge + */ +enum rtnl_link_bridge_flags { + RTNL_BRIDGE_HAIRPIN_MODE = 0x0001, + RTNL_BRIDGE_BPDU_GUARD = 0x0002, + RTNL_BRIDGE_ROOT_BLOCK = 0x0004, + RTNL_BRIDGE_FAST_LEAVE = 0x0008, +}; + +extern struct rtnl_link *rtnl_link_bridge_alloc(void); + +extern int rtnl_link_is_bridge(struct rtnl_link *); +extern int rtnl_link_bridge_has_ext_info(struct rtnl_link *); + +extern int rtnl_link_bridge_set_port_state(struct rtnl_link *, uint8_t ); +extern int rtnl_link_bridge_get_port_state(struct rtnl_link *); + +extern int rtnl_link_bridge_set_priority(struct rtnl_link *, uint16_t); +extern int rtnl_link_bridge_get_priority(struct rtnl_link *); + +extern int rtnl_link_bridge_set_cost(struct rtnl_link *, uint32_t); +extern int rtnl_link_bridge_get_cost(struct rtnl_link *, uint32_t *); + +extern int rtnl_link_bridge_unset_flags(struct rtnl_link *, unsigned int); +extern int rtnl_link_bridge_set_flags(struct rtnl_link *, unsigned int); +extern int rtnl_link_bridge_get_flags(struct rtnl_link *); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/include/netlink/route/link/can.h b/include/netlink/route/link/can.h new file mode 100644 index 0000000..61c9f47 --- /dev/null +++ b/include/netlink/route/link/can.h @@ -0,0 +1,60 @@ +/* + * netlink/route/link/can.h CAN interface + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2012 Benedikt Spranger + */ + +#ifndef NETLINK_LINK_CAN_H_ +#define NETLINK_LINK_CAN_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern int rtnl_link_is_can(struct rtnl_link *link); + +extern char *rtnl_link_can_ctrlmode2str(int, char *, size_t); +extern int rtnl_link_can_str2ctrlmode(const char *); + +extern int rtnl_link_can_restart(struct rtnl_link *); +extern int rtnl_link_can_freq(struct rtnl_link *, uint32_t *); +extern int rtnl_link_can_state(struct rtnl_link *, uint32_t *); + +extern int rtnl_link_can_berr_rx(struct rtnl_link *); +extern int rtnl_link_can_berr_tx(struct rtnl_link *); +extern int rtnl_link_can_berr(struct rtnl_link *, struct can_berr_counter *); + +extern int rtnl_link_can_get_bt_const(struct rtnl_link *, + struct can_bittiming_const *); +extern int rtnl_link_can_get_bittiming(struct rtnl_link *, + struct can_bittiming *); +extern int rtnl_link_can_set_bittiming(struct rtnl_link *, + struct can_bittiming *); + +extern int rtnl_link_can_get_bitrate(struct rtnl_link *, uint32_t *); +extern int rtnl_link_can_set_bitrate(struct rtnl_link *, uint32_t); + +extern int rtnl_link_can_get_sample_point(struct rtnl_link *, uint32_t *); +extern int rtnl_link_can_set_sample_point(struct rtnl_link *, uint32_t); + +extern int rtnl_link_can_get_restart_ms(struct rtnl_link *, uint32_t *); +extern int rtnl_link_can_set_restart_ms(struct rtnl_link *, uint32_t); + +extern int rtnl_link_can_get_ctrlmode(struct rtnl_link *, uint32_t *); +extern int rtnl_link_can_set_ctrlmode(struct rtnl_link *, uint32_t); +extern int rtnl_link_can_unset_ctrlmode(struct rtnl_link *, uint32_t); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/netlink/route/link/inet.h b/include/netlink/route/link/inet.h index 9feff37..506542f 100644 --- a/include/netlink/route/link/inet.h +++ b/include/netlink/route/link/inet.h @@ -19,11 +19,15 @@ extern "C" { #endif extern const char * rtnl_link_inet_devconf2str(int, char *, size_t); -extern unsigned int rtnl_link_inet_str2devconf(const char *); +extern int rtnl_link_inet_str2devconf(const char *); extern int rtnl_link_inet_get_conf(struct rtnl_link *, const unsigned int, uint32_t *); extern int rtnl_link_inet_set_conf(struct rtnl_link *, const unsigned int, uint32_t); +#ifdef __cplusplus +} +#endif + #endif diff --git a/include/netlink/route/link/info-api.h b/include/netlink/route/link/info-api.h index 4750e18..1087ad4 100644 --- a/include/netlink/route/link/info-api.h +++ b/include/netlink/route/link/info-api.h @@ -1,20 +1,20 @@ /* - * netlink/route/link/info-api.h Link Info API + * netlink/route/link/info-api.h Link Modules API * * 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 version 2.1 * of the License. * - * Copyright (c) 2003-2010 Thomas Graf + * Copyright (c) 2013 Thomas Graf */ -#ifndef NETLINK_LINK_INFO_API_H_ -#define NETLINK_LINK_INFO_API_H_ +#ifndef NETLINK_DUMMY_LINK_INFO_API_H_ +#define NETLINK_DUMMY_LINK_INFO_API_H_ -#warning " is obsolete and may be removed in the future." -#warning "include instead. +#include +#include -#include +#warning "You are including a deprecated header file, include ." #endif diff --git a/include/netlink/route/link/vlan.h b/include/netlink/route/link/vlan.h index a3ad76d..f274163 100644 --- a/include/netlink/route/link/vlan.h +++ b/include/netlink/route/link/vlan.h @@ -6,7 +6,7 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2008 Thomas Graf + * Copyright (c) 2003-2013 Thomas Graf */ #ifndef NETLINK_LINK_VLAN_H_ @@ -27,17 +27,21 @@ struct vlan_map #define VLAN_PRIO_MAX 7 +extern struct rtnl_link *rtnl_link_vlan_alloc(void); + +extern int rtnl_link_is_vlan(struct rtnl_link *); + extern char * rtnl_link_vlan_flags2str(int, char *, size_t); extern int rtnl_link_vlan_str2flags(const char *); -extern int rtnl_link_vlan_set_id(struct rtnl_link *, int); +extern int rtnl_link_vlan_set_id(struct rtnl_link *, uint16_t); extern int rtnl_link_vlan_get_id(struct rtnl_link *); extern int rtnl_link_vlan_set_flags(struct rtnl_link *, unsigned int); extern int rtnl_link_vlan_unset_flags(struct rtnl_link *, unsigned int); -extern unsigned int rtnl_link_vlan_get_flags(struct rtnl_link *); +extern int rtnl_link_vlan_get_flags(struct rtnl_link *); extern int rtnl_link_vlan_set_ingress_map(struct rtnl_link *, int, uint32_t); diff --git a/include/netlink/route/neighbour.h b/include/netlink/route/neighbour.h index 698539a..1d1179b 100644 --- a/include/netlink/route/neighbour.h +++ b/include/netlink/route/neighbour.h @@ -29,6 +29,8 @@ extern int rtnl_neigh_alloc_cache(struct nl_sock *, struct nl_cache **); extern struct rtnl_neigh *rtnl_neigh_get(struct nl_cache *, int, struct nl_addr *); +extern int rtnl_neigh_parse(struct nlmsghdr *, struct rtnl_neigh **); + extern char * rtnl_neigh_state2str(int, char *, size_t); extern int rtnl_neigh_str2state(const char *); diff --git a/include/netlink/route/qdisc.h b/include/netlink/route/qdisc.h index 966eb44..10b85c5 100644 --- a/include/netlink/route/qdisc.h +++ b/include/netlink/route/qdisc.h @@ -6,7 +6,7 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2008 Thomas Graf + * Copyright (c) 2003-2011 Thomas Graf */ #ifndef NETLINK_QDISC_H_ @@ -22,41 +22,49 @@ extern "C" { struct rtnl_qdisc; -extern struct nl_object_ops qdisc_obj_ops; - -extern struct rtnl_qdisc *rtnl_qdisc_alloc(void); +extern struct rtnl_qdisc * + rtnl_qdisc_alloc(void); extern void rtnl_qdisc_put(struct rtnl_qdisc *); extern int rtnl_qdisc_alloc_cache(struct nl_sock *, struct nl_cache **); -extern struct rtnl_qdisc *rtnl_qdisc_get(struct nl_cache *, int, uint32_t); -extern struct rtnl_qdisc *rtnl_qdisc_get_by_parent(struct nl_cache *, - int, uint32_t); + +extern struct rtnl_qdisc * + rtnl_qdisc_get(struct nl_cache *, int, uint32_t); + +extern struct rtnl_qdisc * + rtnl_qdisc_get_by_parent(struct nl_cache *, int, uint32_t); extern int rtnl_qdisc_build_add_request(struct rtnl_qdisc *, int, struct nl_msg **); extern int rtnl_qdisc_add(struct nl_sock *, struct rtnl_qdisc *, int); -extern int rtnl_qdisc_build_change_request(struct rtnl_qdisc *, +extern int rtnl_qdisc_build_update_request(struct rtnl_qdisc *, struct rtnl_qdisc *, - struct nl_msg **); -extern int rtnl_qdisc_change(struct nl_sock *, struct rtnl_qdisc *, - struct rtnl_qdisc *); + int, struct nl_msg **); + +extern int rtnl_qdisc_update(struct nl_sock *, struct rtnl_qdisc *, + struct rtnl_qdisc *, int); extern int rtnl_qdisc_build_delete_request(struct rtnl_qdisc *, struct nl_msg **); extern int rtnl_qdisc_delete(struct nl_sock *, struct rtnl_qdisc *); -extern void rtnl_qdisc_set_kind(struct rtnl_qdisc *, const char *); +/* Deprecated functions */ +extern void rtnl_qdisc_foreach_child(struct rtnl_qdisc *, struct nl_cache *, + void (*cb)(struct nl_object *, void *), + void *) __attribute__ ((deprecated)); -extern void rtnl_qdisc_foreach_child(struct rtnl_qdisc *, struct nl_cache *, - void (*cb)(struct nl_object *, void *), - void *); +extern void rtnl_qdisc_foreach_cls(struct rtnl_qdisc *, struct nl_cache *, + void (*cb)(struct nl_object *, void *), + void *) __attribute__ ((deprecated)); -extern void rtnl_qdisc_foreach_cls(struct rtnl_qdisc *, struct nl_cache *, - void (*cb)(struct nl_object *, void *), - void *); +extern int rtnl_qdisc_build_change_request(struct rtnl_qdisc *, + struct rtnl_qdisc *, + struct nl_msg **) + __attribute__ ((deprecated)); -extern struct nl_msg * rtnl_qdisc_get_opts(struct rtnl_qdisc *); +extern int rtnl_qdisc_change(struct nl_sock *, struct rtnl_qdisc *, + struct rtnl_qdisc *) __attribute__ ((deprecated)); #ifdef __cplusplus } diff --git a/include/netlink/route/qdisc/dsmark.h b/include/netlink/route/qdisc/dsmark.h index de65496..06bd9d3 100644 --- a/include/netlink/route/qdisc/dsmark.h +++ b/include/netlink/route/qdisc/dsmark.h @@ -6,20 +6,22 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2006 Thomas Graf + * Copyright (c) 2003-2011 Thomas Graf */ #ifndef NETLINK_DSMARK_H_ #define NETLINK_DSMARK_H_ #include +#include +#include #ifdef __cplusplus extern "C" { #endif -extern int rtnl_class_dsmark_set_bmask(struct rtnl_class *, uint8_t); -extern int rtnl_class_dsmark_get_bmask(struct rtnl_class *); +extern int rtnl_class_dsmark_set_bitmask(struct rtnl_class *, uint8_t); +extern int rtnl_class_dsmark_get_bitmask(struct rtnl_class *); extern int rtnl_class_dsmark_set_value(struct rtnl_class *, uint8_t); extern int rtnl_class_dsmark_get_value(struct rtnl_class *); diff --git a/include/netlink/route/qdisc/fifo.h b/include/netlink/route/qdisc/fifo.h index c18dd79..c033427 100644 --- a/include/netlink/route/qdisc/fifo.h +++ b/include/netlink/route/qdisc/fifo.h @@ -13,6 +13,7 @@ #define NETLINK_FIFO_H_ #include +#include #ifdef __cplusplus extern "C" { diff --git a/include/netlink/route/qdisc/htb.h b/include/netlink/route/qdisc/htb.h index 9061b9b..c909f84 100644 --- a/include/netlink/route/qdisc/htb.h +++ b/include/netlink/route/qdisc/htb.h @@ -6,7 +6,7 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2010 Thomas Graf + * Copyright (c) 2003-2011 Thomas Graf * Copyright (c) 2005 Petr Gotthard * Copyright (c) 2005 Siemens AG Oesterreich */ @@ -16,21 +16,31 @@ #include #include +#include +#include #ifdef __cplusplus extern "C" { #endif -extern void rtnl_htb_set_rate2quantum(struct rtnl_qdisc *, uint32_t); -extern void rtnl_htb_set_defcls(struct rtnl_qdisc *, uint32_t); +extern uint32_t rtnl_htb_get_rate2quantum(struct rtnl_qdisc *); +extern int rtnl_htb_set_rate2quantum(struct rtnl_qdisc *, uint32_t); +extern uint32_t rtnl_htb_get_defcls(struct rtnl_qdisc *); +extern int rtnl_htb_set_defcls(struct rtnl_qdisc *, uint32_t); -extern void rtnl_htb_set_prio(struct rtnl_class *, uint32_t); -extern void rtnl_htb_set_rate(struct rtnl_class *, uint32_t); -extern uint32_t rtnl_htb_get_rate(struct rtnl_class *); -extern void rtnl_htb_set_ceil(struct rtnl_class *, uint32_t); -extern void rtnl_htb_set_rbuffer(struct rtnl_class *, uint32_t); -extern void rtnl_htb_set_cbuffer(struct rtnl_class *, uint32_t); -extern void rtnl_htb_set_quantum(struct rtnl_class *, uint32_t quantum); +extern uint32_t rtnl_htb_get_prio(struct rtnl_class *); +extern int rtnl_htb_set_prio(struct rtnl_class *, uint32_t); +extern uint32_t rtnl_htb_get_rate(struct rtnl_class *); +extern int rtnl_htb_set_rate(struct rtnl_class *, uint32_t); +extern uint32_t rtnl_htb_get_ceil(struct rtnl_class *); +extern int rtnl_htb_set_ceil(struct rtnl_class *, uint32_t); +extern uint32_t rtnl_htb_get_rbuffer(struct rtnl_class *); +extern int rtnl_htb_set_rbuffer(struct rtnl_class *, uint32_t); +extern uint32_t rtnl_htb_get_cbuffer(struct rtnl_class *); +extern int rtnl_htb_set_cbuffer(struct rtnl_class *, uint32_t); +extern uint32_t rtnl_htb_get_quantum(struct rtnl_class *); +extern int rtnl_htb_set_quantum(struct rtnl_class *, uint32_t); +extern int rtnl_htb_get_level(struct rtnl_class *); #ifdef __cplusplus } diff --git a/include/netlink/route/qdisc/netem.h b/include/netlink/route/qdisc/netem.h index ce56ee7..4b071bf 100644 --- a/include/netlink/route/qdisc/netem.h +++ b/include/netlink/route/qdisc/netem.h @@ -13,6 +13,7 @@ #define NETLINK_NETEM_H_ #include +#include #ifdef __cplusplus extern "C" { diff --git a/include/netlink/route/qdisc/plug.h b/include/netlink/route/qdisc/plug.h new file mode 100644 index 0000000..40f7e53 --- /dev/null +++ b/include/netlink/route/qdisc/plug.h @@ -0,0 +1,31 @@ +/* + * netlink/route/qdisc/plug.c PLUG Qdisc + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2012 Shriram Rajagopalan + */ + +#ifndef NETLINK_PLUG_H_ +#define NETLINK_PLUG_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern int rtnl_qdisc_plug_set_limit(struct rtnl_qdisc *, int); +extern int rtnl_qdisc_plug_buffer(struct rtnl_qdisc *); +extern int rtnl_qdisc_plug_release_one(struct rtnl_qdisc *); +extern int rtnl_qdisc_plug_release_indefinite(struct rtnl_qdisc *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/netlink/route/qdisc/prio.h b/include/netlink/route/qdisc/prio.h index f6fdc86..636a8f9 100644 --- a/include/netlink/route/qdisc/prio.h +++ b/include/netlink/route/qdisc/prio.h @@ -13,6 +13,7 @@ #define NETLINK_PRIO_H_ #include +#include #ifdef __cplusplus extern "C" { diff --git a/include/netlink/route/qdisc/sfq.h b/include/netlink/route/qdisc/sfq.h index 7cc0b3e..77d2e29 100644 --- a/include/netlink/route/qdisc/sfq.h +++ b/include/netlink/route/qdisc/sfq.h @@ -13,6 +13,7 @@ #define NETLINK_SFQ_H_ #include +#include #ifdef __cplusplus extern "C" { diff --git a/include/netlink/route/qdisc/tbf.h b/include/netlink/route/qdisc/tbf.h index 8a2144a..ce31c54 100644 --- a/include/netlink/route/qdisc/tbf.h +++ b/include/netlink/route/qdisc/tbf.h @@ -14,6 +14,7 @@ #include #include +#include #ifdef __cplusplus extern "C" { diff --git a/include/netlink/route/route.h b/include/netlink/route/route.h index 5729cd7..477250d 100644 --- a/include/netlink/route/route.h +++ b/include/netlink/route/route.h @@ -6,7 +6,7 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2008 Thomas Graf + * Copyright (c) 2003-2012 Thomas Graf */ #ifndef NETLINK_ROUTE_H_ @@ -24,7 +24,12 @@ extern "C" { #endif -/* flags */ +/** + * @ingroup route + * When passed to rtnl_route_alloc_cache() the cache will + * correspond to the contents of the routing cache instead + * of the actual routes. + */ #define ROUTE_CACHE_CONTENT 1 struct rtnl_route; @@ -49,7 +54,6 @@ extern int rtnl_route_alloc_cache(struct nl_sock *, int, int, struct nl_cache **); extern void rtnl_route_get(struct rtnl_route *); -extern void rtnl_route_put(struct rtnl_route *); extern int rtnl_route_parse(struct nlmsghdr *, struct rtnl_route **); extern int rtnl_route_build_msg(struct nl_msg *, struct rtnl_route *); diff --git a/include/netlink/route/tc-api.h b/include/netlink/route/tc-api.h index 8ed940a..b7771b5 100644 --- a/include/netlink/route/tc-api.h +++ b/include/netlink/route/tc-api.h @@ -6,136 +6,16 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2011 Thomas Graf + * Copyright (c) 2013 Thomas Graf */ -#ifndef NETLINK_TC_API_H_ -#define NETLINK_TC_API_H_ +#ifndef NETLINK_DUMMY_TC_API_H_ +#define NETLINK_DUMMY_TC_API_H_ #include #include #include -#ifdef __cplusplus -extern "C" { -#endif - -enum rtnl_tc_type { - RTNL_TC_TYPE_QDISC, - RTNL_TC_TYPE_CLASS, - RTNL_TC_TYPE_CLS, - __RTNL_TC_TYPE_MAX, -}; - -#define RTNL_TC_TYPE_MAX (__RTNL_TC_TYPE_MAX - 1) - -/** - * Traffic control object operations - * @ingroup tc - * - * This structure holds function pointers and settings implementing - * the features of each traffic control object implementation. - */ -struct rtnl_tc_ops -{ - /** - * Name of traffic control module - */ - char *to_kind; - - /** - * Type of traffic control object - */ - enum rtnl_tc_type to_type; - - - /** - * Size of private data - */ - size_t to_size; - - /** - * Dump callbacks - */ - void (*to_dump[NL_DUMP_MAX+1])(struct rtnl_tc *, void *, - struct nl_dump_params *); - /** - * Used to fill the contents of TCA_OPTIONS - */ - int (*to_msg_fill)(struct rtnl_tc *, void *, struct nl_msg *); - - /** - * Uesd to to fill tc related messages, unlike with to_msg_fill, - * the contents is not encapsulated with a TCA_OPTIONS nested - * attribute. - */ - int (*to_msg_fill_raw)(struct rtnl_tc *, void *, struct nl_msg *); - - /** - * TCA_OPTIONS message parser - */ - int (*to_msg_parser)(struct rtnl_tc *, void *); - - /** - * Called before a tc object is destroyed - */ - void (*to_free_data)(struct rtnl_tc *, void *); - - /** - * Called whenever a classifier object needs to be cloned - */ - int (*to_clone)(void *, void *); - - /** - * Internal, don't touch - */ - struct nl_list_head to_list; -}; - -struct rtnl_tc_type_ops -{ - enum rtnl_tc_type tt_type; - - char *tt_dump_prefix; - - /** - * Dump callbacks - */ - void (*tt_dump[NL_DUMP_MAX+1])(struct rtnl_tc *, - struct nl_dump_params *); -}; - -extern int rtnl_tc_msg_parse(struct nlmsghdr *, - struct rtnl_tc *); -extern int rtnl_tc_msg_build(struct rtnl_tc *, int, - int, struct nl_msg **); - -extern void rtnl_tc_free_data(struct nl_object *); -extern int rtnl_tc_clone(struct nl_object *, - struct nl_object *); -extern void rtnl_tc_dump_line(struct nl_object *, - struct nl_dump_params *); -extern void rtnl_tc_dump_details(struct nl_object *, - struct nl_dump_params *); -extern void rtnl_tc_dump_stats(struct nl_object *, - struct nl_dump_params *); -extern int rtnl_tc_compare(struct nl_object *, - struct nl_object *, - uint32_t, int); - -extern void * rtnl_tc_data(struct rtnl_tc *); - -extern struct rtnl_tc_ops * rtnl_tc_lookup_ops(enum rtnl_tc_type, - const char *); -extern struct rtnl_tc_ops * rtnl_tc_get_ops(struct rtnl_tc *); -extern int rtnl_tc_register(struct rtnl_tc_ops *); -extern void rtnl_tc_unregister(struct rtnl_tc_ops *); - -extern void rtnl_tc_type_register(struct rtnl_tc_type_ops *); -extern void rtnl_tc_type_unregister(struct rtnl_tc_type_ops *); - -#ifdef __cplusplus -} -#endif +#warning "You are including a deprecated header file, include ." #endif diff --git a/include/netlink/route/tc.h b/include/netlink/route/tc.h index 17021be..836f7b1 100644 --- a/include/netlink/route/tc.h +++ b/include/netlink/route/tc.h @@ -23,6 +23,21 @@ extern "C" { #endif +enum rtnl_tc_type { + RTNL_TC_TYPE_QDISC, + RTNL_TC_TYPE_CLASS, + RTNL_TC_TYPE_CLS, + __RTNL_TC_TYPE_MAX, +}; + +#define RTNL_TC_TYPE_MAX (__RTNL_TC_TYPE_MAX - 1) + +/** + * Compute tc handle based on major and minor parts + * @ingroup tc + */ +#define TC_HANDLE(maj, min) (TC_H_MAJ((maj) << 16) | TC_H_MIN(min)) + /** * Traffic control object * @ingroup tc @@ -65,6 +80,7 @@ enum rtnl_tc_stat { extern void rtnl_tc_set_ifindex(struct rtnl_tc *, int); extern int rtnl_tc_get_ifindex(struct rtnl_tc *); extern void rtnl_tc_set_link(struct rtnl_tc *, struct rtnl_link *); +extern struct rtnl_link *rtnl_tc_get_link(struct rtnl_tc *); extern void rtnl_tc_set_mtu(struct rtnl_tc *, uint32_t); extern uint32_t rtnl_tc_get_mtu(struct rtnl_tc *); extern void rtnl_tc_set_mpu(struct rtnl_tc *, uint32_t); diff --git a/include/netlink/socket.h b/include/netlink/socket.h index f0062b5..1007eba 100644 --- a/include/netlink/socket.h +++ b/include/netlink/socket.h @@ -45,8 +45,12 @@ extern void nl_socket_set_cb(struct nl_sock *, extern int nl_socket_modify_cb(struct nl_sock *, enum nl_cb_type, enum nl_cb_kind, nl_recvmsg_msg_cb_t, void *); +extern int nl_socket_modify_err_cb(struct nl_sock *, enum nl_cb_kind, + nl_recvmsg_err_cb_t, void *); extern int nl_socket_set_buffer_size(struct nl_sock *, int, int); +extern int nl_socket_set_msg_buf_size(struct nl_sock *, size_t); +extern size_t nl_socket_get_msg_buf_size(struct nl_sock *); extern int nl_socket_set_passcred(struct nl_sock *, int); extern int nl_socket_recv_pktinfo(struct nl_sock *, int); diff --git a/include/netlink/types.h b/include/netlink/types.h index f6dade3..09cc5bd 100644 --- a/include/netlink/types.h +++ b/include/netlink/types.h @@ -1,12 +1,12 @@ /* - * netlink/netlink-types.h Netlink Types + * netlink/types.h Definition of public types * * 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 version 2.1 * of the License. * - * Copyright (c) 2003-2006 Thomas Graf + * Copyright (c) 2003-2012 Thomas Graf */ #ifndef __NETLINK_TYPES_H_ @@ -15,8 +15,8 @@ #include /** - * Dumping types (dp_type) * @ingroup utils + * Enumeration of dumping variations (dp_type) */ enum nl_dump_type { NL_DUMP_LINE, /**< Dump object briefly on one line */ @@ -27,8 +27,8 @@ enum nl_dump_type { #define NL_DUMP_MAX (__NL_DUMP_MAX - 1) /** - * Dumping parameters * @ingroup utils + * Dumping parameters */ struct nl_dump_params { diff --git a/include/netlink/utils.h b/include/netlink/utils.h index 309e02f..502341a 100644 --- a/include/netlink/utils.h +++ b/include/netlink/utils.h @@ -6,7 +6,7 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2008 Thomas Graf + * Copyright (c) 2003-2012 Thomas Graf */ #ifndef NETLINK_UTILS_H_ @@ -31,16 +31,22 @@ extern "C" { #define NL_PROB_MIN 0x0 /** - * Upper probability limit + * Upper probability limit nl_dump_type * @ingroup utils */ #define NL_PROB_MAX 0xffffffff /** @} */ +enum { + NL_BYTE_RATE, + NL_BIT_RATE, +}; + /* unit pretty-printing */ extern double nl_cancel_down_bytes(unsigned long long, char **); extern double nl_cancel_down_bits(unsigned long long, char **); +extern int nl_rate2str(unsigned long long, int, char *, size_t); extern double nl_cancel_down_us(uint32_t, char **); /* generic unit translations */ @@ -50,6 +56,7 @@ extern long nl_prob2int(const char *); /* time translations */ extern int nl_get_user_hz(void); +extern int nl_get_psched_hz(void); extern uint32_t nl_us2ticks(uint32_t); extern uint32_t nl_ticks2us(uint32_t); extern int nl_str2msec(const char *, uint64_t *); diff --git a/include/netlink/version.h.in b/include/netlink/version.h.in index 9deb365..35bf2aa 100644 --- a/include/netlink/version.h.in +++ b/include/netlink/version.h.in @@ -1,5 +1,5 @@ /* - * netlink/version.h Compile Time Versioning Information + * netlink/version.h Versioning Information * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -12,12 +12,26 @@ #ifndef NETLINK_VERSION_H_ #define NETLINK_VERSION_H_ +/* Compile Time Versioning Information */ + #define LIBNL_STRING "@PACKAGE_STRING@" #define LIBNL_VERSION "@PACKAGE_VERSION@" #define LIBNL_VER_MAJ @MAJ_VERSION@ #define LIBNL_VER_MIN @MIN_VERSION@ +#define LIBNL_VER_MIC @MIC_VERSION@ #define LIBNL_VER(maj,min) ((maj) << 8 | (min)) #define LIBNL_VER_NUM LIBNL_VER(LIBNL_VER_MAJ, LIBNL_VER_MIN) +#define LIBNL_CURRENT @LT_CURRENT@ +#define LIBNL_REVISION @LT_REVISION@ +#define LIBNL_AGE @LT_AGE@ + +/* Run-time version information */ + +extern const int nl_ver_num; +extern const int nl_ver_maj; +extern const int nl_ver_min; +extern const int nl_ver_mic; + #endif diff --git a/lib/.gitignore b/lib/.gitignore index 2a450e8..e1abf18 100644 --- a/lib/.gitignore +++ b/lib/.gitignore @@ -1,2 +1,3 @@ libnl.so* libnl-*.so* +lex.yy.c diff --git a/lib/Makefile.am b/lib/Makefile.am index ce881fa..438b3eb 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -1,25 +1,42 @@ # -*- Makefile -*- -AM_CPPFLAGS = -Wall -I${top_srcdir}/include -I${top_builddir}/include -D_GNU_SOURCE -DSYSCONFDIR=\"$(sysconfdir)/libnl\" -AM_LDFLAGS = -version-info 3:0:0 +AM_CPPFLAGS = \ + -Wall \ + -I${top_srcdir}/include \ + -I${top_builddir}/include \ + -I${builddir}/route \ + -I${builddir}/route/cls \ + -D_GNU_SOURCE \ + -DSYSCONFDIR=\"$(sysconfdir)/libnl\" + +AM_LDFLAGS = \ + -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) lib_LTLIBRARIES = \ - libnl.la libnl-genl.la libnl-route.la libnl-nf.la libnl-dect.la + libnl-3.la libnl-genl-3.la libnl-route-3.la libnl-nf-3.la libnl-dect-3.la -libnl_la_SOURCES = \ +libnl_3_la_SOURCES = \ addr.c attr.c cache.c cache_mngr.c cache_mngt.c data.c \ - error.c handlers.c msg.c nl.c object.c socket.c utils.c + error.c handlers.c msg.c nl.c object.c socket.c utils.c \ + version.c hash.c hashtable.c -libnl_genl_la_LIBADD = libnl.la -libnl_genl_la_SOURCES = \ +libnl_genl_3_la_LIBADD = libnl-3.la +libnl_genl_3_la_SOURCES = \ genl/ctrl.c genl/family.c genl/genl.c genl/mngt.c -libnl_nf_la_LIBADD = libnl-route.la -libnl_nf_la_SOURCES = \ +libnl_nf_3_la_LIBADD = libnl-route-3.la +libnl_nf_3_la_SOURCES = \ netfilter/ct.c netfilter/ct_obj.c netfilter/log.c \ netfilter/log_msg.c netfilter/log_msg_obj.c netfilter/log_obj.c \ netfilter/netfilter.c netfilter/nfnl.c netfilter/queue.c \ - netfilter/queue_msg.c netfilter/queue_msg_obj.c netfilter/queue_obj.c + netfilter/queue_msg.c netfilter/queue_msg_obj.c netfilter/queue_obj.c \ + netfilter/exp.c netfilter/exp_obj.c + +libnl_dect_3_la_LIBADD = libnl-3.la +libnl_dect_3_la_SOURCES = \ + dect/ari.c dect/cell.c dect/cell_obj.c \ + dect/cluster.c dect/cluster_obj.c dect/dect.c dect/llme.c \ + dect/transceiver.c dect/transceiver_obj.c CLEANFILES = \ route/pktloc_grammar.c route/pktloc_grammar.h \ @@ -30,19 +47,19 @@ CLEANFILES = \ # Hack to avoid using ylwrap. It does not function correctly in combination # with --header-file= route/pktloc_grammar.c: route/pktloc_grammar.l - $(AM_V_GEN) $(LEX) --header-file=route/pktloc_grammar.h $(LFLAGS) -o $@ $^ + $(AM_V_GEN) $(FLEX) --header-file=route/pktloc_grammar.h $(LFLAGS) -o $@ $^ route/pktloc_syntax.c: route/pktloc_syntax.y $(AM_V_GEN) $(YACC) -d $(YFLAGS) -o $@ $^ route/cls/ematch_grammar.c: route/cls/ematch_grammar.l - $(AM_V_GEN) $(LEX) --header-file=route/cls/ematch_grammar.h $(LFLAGS) -o $@ $^ + $(AM_V_GEN) $(FLEX) --header-file=route/cls/ematch_grammar.h $(LFLAGS) -o $@ $^ route/cls/ematch_syntax.c: route/cls/ematch_syntax.y $(AM_V_GEN) $(YACC) -d $(YFLAGS) -o $@ $^ -libnl_route_la_LIBADD = libnl.la -libnl_route_la_SOURCES = \ +libnl_route_3_la_LIBADD = libnl-3.la +libnl_route_3_la_SOURCES = \ route/addr.c route/class.c route/cls.c route/link.c \ route/neigh.c route/neightbl.c route/nexthop.c route/qdisc.c \ route/route.c route/route_obj.c route/route_utils.c route/rtnl.c \ @@ -51,43 +68,57 @@ libnl_route_la_SOURCES = \ route/cls/fw.c route/cls/police.c route/cls/u32.c route/cls/basic.c \ route/cls/cgroup.c \ \ - route/cls/ematch_syntax.c route/cls/ematch_grammar.c \ route/cls/ematch.c \ route/cls/ematch/container.c route/cls/ematch/cmp.c \ route/cls/ematch/nbyte.c route/cls/ematch/text.c \ route/cls/ematch/meta.c \ \ - route/link/api.c route/link/vlan.c \ + route/link/api.c route/link/vlan.c route/link/dummy.c \ route/link/bridge.c route/link/inet6.c route/link/inet.c \ + route/link/bonding.c route/link/can.c \ \ route/qdisc/blackhole.c route/qdisc/cbq.c route/qdisc/dsmark.c \ route/qdisc/fifo.c route/qdisc/htb.c route/qdisc/netem.c \ route/qdisc/prio.c route/qdisc/red.c route/qdisc/sfq.c \ - route/qdisc/tbf.c \ + route/qdisc/tbf.c route/qdisc/plug.c \ \ fib_lookup/lookup.c fib_lookup/request.c \ \ - route/pktloc_syntax.c route/pktloc_grammar.c route/pktloc.c + route/pktloc.c -libnl_dect_la_LIBADD = libnl.la -libnl_dect_la_SOURCES = \ - dect/ari.c dect/cell.c dect/cell_obj.c \ - dect/cluster.c dect/cluster_obj.c dect/dect.c dect/llme.c \ - dect/transceiver.c dect/transceiver_obj.c +nodist_libnl_route_3_la_SOURCES = \ + route/pktloc_syntax.c route/pktloc_syntax.h \ + route/pktloc_grammar.c route/pktloc_grammar.h \ + route/cls/ematch_syntax.c route/cls/ematch_syntax.h \ + route/cls/ematch_grammar.c route/cls/ematch_grammar.h + +BUILT_SOURCES = \ + route/cls/ematch_grammar.c \ + route/cls/ematch_syntax.c \ + route/pktloc_grammar.c \ + route/pktloc_syntax.c + +EXTRA_DIST = \ + route/pktloc_grammar.l \ + route/pktloc_syntax.y \ + route/cls/ematch_grammar.l \ + route/cls/ematch_syntax.y if ENABLE_CLI nobase_pkglib_LTLIBRARIES = \ cli/qdisc/htb.la \ cli/qdisc/blackhole.la \ cli/qdisc/pfifo.la \ + cli/qdisc/plug.la \ cli/qdisc/bfifo.la \ cli/cls/basic.la \ cli/cls/cgroup.la -cli_qdisc_htb_la_LDFLAGS = -module -version-info 0:0:0 -cli_qdisc_blackhole_la_LDFLAGS = -module -version-info 0:0:0 -cli_qdisc_pfifo_la_LDFLAGS = -module -version-info 0:0:0 -cli_qdisc_bfifo_la_LDFLAGS = -module -version-info 0:0:0 -cli_cls_basic_la_LDFLAGS = -module -version-info 0:0:0 -cli_cls_cgroup_la_LDFLAGS = -module -version-info 0:0:0 +cli_qdisc_htb_la_LDFLAGS = -module -avoid-version +cli_qdisc_blackhole_la_LDFLAGS = -module -avoid-version +cli_qdisc_pfifo_la_LDFLAGS = -module -avoid-version +cli_qdisc_plug_la_LDFLAGS = -module -avoid-version +cli_qdisc_bfifo_la_LDFLAGS = -module -avoid-version +cli_cls_basic_la_LDFLAGS = -module -avoid-version +cli_cls_cgroup_la_LDFLAGS = -module -avoid-version endif diff --git a/lib/addr.c b/lib/addr.c index c8c4ca4..557b0ad 100644 --- a/lib/addr.c +++ b/lib/addr.c @@ -1,31 +1,33 @@ /* - * lib/addr.c Abstract Address + * lib/addr.c Network Address * * 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 version 2.1 * of the License. * - * Copyright (c) 2003-2010 Thomas Graf + * Copyright (c) 2003-2013 Thomas Graf */ /** - * @ingroup core - * @defgroup addr Abstract Address + * @ingroup core_types + * @defgroup addr Network Address + * + * Abstract data type representing any kind of network address + * + * Related sections in the development guide: + * - @core_doc{_abstract_address, Network Addresses} * - * @par 1) Transform character string to abstract address - * @code - * struct nl_addr *a = nl_addr_parse("::1", AF_UNSPEC); - * printf("Address family: %s\n", nl_af2str(nl_addr_get_family(a))); - * nl_addr_put(a); - * a = nl_addr_parse("11:22:33:44:55:66", AF_UNSPEC); - * printf("Address family: %s\n", nl_af2str(nl_addr_get_family(a))); - * nl_addr_put(a); - * @endcode * @{ + * + * Header + * ------ + * ~~~~{.c} + * #include + * ~~~~ */ -#include +#include #include #include #include @@ -163,14 +165,22 @@ static void addr_destroy(struct nl_addr *addr) } /** - * @name Creating Abstract Addresses + * @name Creating Abstract Network Addresses * @{ */ /** - * Allocate new abstract address object. - * @arg maxsize Maximum size of the binary address. - * @return Newly allocated address object or NULL + * Allocate empty abstract address + * @arg maxsize Upper limit of the binary address to be stored + * + * The new address object will be empty with a prefix length of 0 and will + * be capable of holding binary addresses up to the specified limit. + * + * @see nl_addr_build() + * @see nl_addr_parse() + * @see nl_addr_put() + * + * @return Allocated address object or NULL upon failure. */ struct nl_addr *nl_addr_alloc(size_t maxsize) { @@ -187,11 +197,21 @@ struct nl_addr *nl_addr_alloc(size_t maxsize) } /** - * Allocate new abstract address object based on a binary address. - * @arg family Address family. - * @arg buf Buffer containing the binary address. - * @arg size Length of binary address buffer. - * @return Newly allocated address handle or NULL + * Allocate abstract address based on a binary address. + * @arg family Address family + * @arg buf Binary address + * @arg size Length of binary address + * + * This function will allocate an abstract address capable of holding the + * binary address specified. The prefix length will be set to the full + * length of the binary address provided. + * + * @see nl_addr_alloc() + * @see nl_addr_alloc_attr() + * @see nl_addr_parse() + * @see nl_addr_put() + * + * @return Allocated address object or NULL upon failure. */ struct nl_addr *nl_addr_build(int family, void *buf, size_t size) { @@ -212,14 +232,25 @@ struct nl_addr *nl_addr_build(int family, void *buf, size_t size) } /** - * Allocate abstract address based on netlink attribute. - * @arg nla Netlink attribute of unspecific type. + * Allocate abstract address based on Netlink attribute. + * @arg nla Netlink attribute * @arg family Address family. * - * Considers the netlink attribute payload a address of the specified - * family and allocates a new abstract address based on it. + * Allocates an abstract address based on the specified Netlink attribute + * by interpreting the payload of the Netlink attribute as the binary + * address. * - * @return Newly allocated address handle or NULL. + * This function is identical to: + * @code + * nl_addr_build(family, nla_data(nla), nla_len(nla)); + * @endcode + * + * @see nl_addr_alloc() + * @see nl_addr_build() + * @see nl_addr_parse() + * @see nl_addr_put() + * + * @return Allocated address object or NULL upon failure. */ struct nl_addr *nl_addr_alloc_attr(struct nlattr *nla, int family) { @@ -227,13 +258,13 @@ struct nl_addr *nl_addr_alloc_attr(struct nlattr *nla, int family) } /** - * Allocate abstract address object based on a character string + * Allocate abstract address based on character string * @arg addrstr Address represented as character string. * @arg hint Address family hint or AF_UNSPEC. * @arg result Pointer to store resulting address. * * Regognizes the following address formats: - *@code + * @code * Format Len Family * ---------------------------------------------------------------- * IPv6 address format 16 AF_INET6 @@ -251,6 +282,10 @@ struct nl_addr *nl_addr_alloc_attr(struct nlattr *nla, int family) * The prefix length may be appened at the end prefixed with a * slash, e.g. 10.0.0.0/8. * + * @see nl_addr_alloc() + * @see nl_addr_build() + * @see nl_addr_put() + * * @return 0 on success or a negative error code. */ int nl_addr_parse(const char *addrstr, int hint, struct nl_addr **result) @@ -277,7 +312,9 @@ int nl_addr_parse(const char *addrstr, int hint, struct nl_addr **result) if (!strcasecmp(str, "default") || !strcasecmp(str, "all") || !strcasecmp(str, "any")) { - + + len = 0; + switch (hint) { case AF_INET: case AF_UNSPEC: @@ -285,17 +322,14 @@ int nl_addr_parse(const char *addrstr, int hint, struct nl_addr **result) * no hint given the user wants to have a IPv4 * address given back. */ family = AF_INET; - len = 4; goto prefix; case AF_INET6: family = AF_INET6; - len = 16; goto prefix; case AF_LLC: family = AF_LLC; - len = 6; goto prefix; default: @@ -366,7 +400,7 @@ int nl_addr_parse(const char *addrstr, int hint, struct nl_addr **result) } if (hint == AF_UNSPEC && strchr(str, ':')) { - int i = 0; + size_t i = 0; char *s = str, *p; for (;;) { long l = strtol(s, &p, 16); @@ -423,10 +457,16 @@ errout: } /** - * Clone existing abstract address object. - * @arg addr Abstract address object. - * @return Newly allocated abstract address object being a duplicate of the - * specified address object or NULL if a failure occured. + * Clone existing abstract address object + * @arg addr Abstract address object + * + * Allocates new abstract address representing an identical clone of an + * existing address. + * + * @see nl_addr_alloc() + * @see nl_addr_put() + * + * @return Allocated abstract address or NULL upon failure. */ struct nl_addr *nl_addr_clone(struct nl_addr *addr) { @@ -446,6 +486,18 @@ struct nl_addr *nl_addr_clone(struct nl_addr *addr) * @{ */ +/** + * Increase the reference counter of an abstract address + * @arg addr Abstract address + * + * Increases the reference counter of the address and thus prevents the + * release of the memory resources until the reference is given back + * using the function nl_addr_put(). + * + * @see nl_addr_put() + * + * @return Pointer to the existing abstract address + */ struct nl_addr *nl_addr_get(struct nl_addr *addr) { addr->a_refcnt++; @@ -453,6 +505,15 @@ struct nl_addr *nl_addr_get(struct nl_addr *addr) return addr; } +/** + * Decrease the reference counter of an abstract address + * @arg addr Abstract addr + * + * @note The resources of the abstract address will be freed after the + * last reference to the address has been returned. + * + * @see nl_addr_get() + */ void nl_addr_put(struct nl_addr *addr) { if (!addr) @@ -465,9 +526,10 @@ void nl_addr_put(struct nl_addr *addr) } /** - * Check whether an abstract address object is shared. + * Check whether an abstract address is shared. * @arg addr Abstract address object. - * @return Non-zero if the abstract address object is shared, otherwise 0. + * + * @return Non-zero if the abstract address is shared, otherwise 0. */ int nl_addr_shared(struct nl_addr *addr) { @@ -482,12 +544,21 @@ int nl_addr_shared(struct nl_addr *addr) */ /** - * Compares two abstract address objects. - * @arg a A abstract address object. - * @arg b Another abstract address object. + * Compare abstract addresses + * @arg a An abstract address + * @arg b Another abstract address * - * @return Integer less than, equal to or greather than zero if \c is found, - * respectively to be less than, to, or be greater than \c b. + * Verifies whether the address family, address length, prefix length, and + * binary addresses of two abstract addresses matches. + * + * @note This function will *not* respect the prefix length in the sense + * that only the actual prefix will be compared. Please refer to the + * nl_addr_cmp_prefix() function if you require this functionality. + * + * @see nl_addr_cmp_prefix() + * + * @return Integer less than, equal to or greather than zero if the two + * addresses match. */ int nl_addr_cmp(struct nl_addr *a, struct nl_addr *b) { @@ -496,20 +567,29 @@ int nl_addr_cmp(struct nl_addr *a, struct nl_addr *b) if (d == 0) { d = a->a_len - b->a_len; - if (a->a_len && d == 0) - return memcmp(a->a_addr, b->a_addr, a->a_len); + if (a->a_len && d == 0) { + d = memcmp(a->a_addr, b->a_addr, a->a_len); + + if (d == 0) + return (a->a_prefixlen - b->a_prefixlen); + } } return d; } /** - * Compares the prefix of two abstract address objects. - * @arg a A abstract address object. - * @arg b Another abstract address object. + * Compare the prefix of two abstract addresses + * @arg a An abstract address + * @arg b Another abstract address * - * @return Integer less than, equal to or greather than zero if \c is found, - * respectively to be less than, to, or be greater than \c b. + * Verifies whether the address family and the binary address covered by + * the smaller prefix length of the two abstract addresses matches. + * + * @see nl_addr_cmp() + * + * @return Integer less than, equal to or greather than zero if the two + * addresses match. */ int nl_addr_cmp_prefix(struct nl_addr *a, struct nl_addr *b) { @@ -520,8 +600,8 @@ int nl_addr_cmp_prefix(struct nl_addr *a, struct nl_addr *b) int bytes = len / 8; d = memcmp(a->a_addr, b->a_addr, bytes); - if (d == 0) { - int mask = (1UL << (len % 8)) - 1UL; + if (d == 0 && (len % 8) != 0) { + int mask = (0xFF00 >> (len % 8)) & 0xFF; d = (a->a_addr[bytes] & mask) - (b->a_addr[bytes] & mask); @@ -533,11 +613,13 @@ int nl_addr_cmp_prefix(struct nl_addr *a, struct nl_addr *b) /** * Returns true if the address consists of all zeros - * @arg addr Address to look at. + * @arg addr Abstract address + * + * @return 1 if the binary address consists of all zeros, 0 otherwise. */ int nl_addr_iszero(struct nl_addr *addr) { - int i; + unsigned int i; for (i = 0; i < addr->a_len; i++) if (addr->a_addr[i]) @@ -547,11 +629,11 @@ int nl_addr_iszero(struct nl_addr *addr) } /** - * Check if an address matches a certain family. + * Check if address string is parseable for a specific address family * @arg addr Address represented as character string. * @arg family Desired address family. * - * @return 1 if the address is of the desired address family, + * @return 1 if the address is parseable assuming the specified address family, * otherwise 0 is returned. */ int nl_addr_valid(char *addr, int family) @@ -583,9 +665,10 @@ int nl_addr_valid(char *addr, int family) } /** - * Guess address family of an abstract address object based on address size. + * Guess address family of abstract address based on address size * @arg addr Abstract address object. - * @return Address family or AF_UNSPEC if guessing wasn't successful. + * + * @return Numeric address family or AF_UNSPEC */ int nl_addr_guess_family(struct nl_addr *addr) { @@ -661,7 +744,7 @@ int nl_addr_fill_sockaddr(struct nl_addr *addr, struct sockaddr *sa, * Call getaddrinfo() for an abstract address object. * @arg addr Abstract address object. * @arg result Pointer to store resulting address list. - * + * * Calls getaddrinfo() for the specified abstract address in AI_NUMERICHOST * mode. * @@ -739,11 +822,26 @@ int nl_addr_resolve(struct nl_addr *addr, char *host, size_t hostlen) * @{ */ +/** + * Set address family + * @arg addr Abstract address object + * @arg family Address family + * + * @see nl_addr_get_family() + */ void nl_addr_set_family(struct nl_addr *addr, int family) { addr->a_family = family; } +/** + * Return address family + * @arg addr Abstract address object + * + * @see nl_addr_set_family() + * + * @return The numeric address family or `AF_UNSPEC` + */ int nl_addr_get_family(struct nl_addr *addr) { return addr->a_family; @@ -754,6 +852,20 @@ int nl_addr_get_family(struct nl_addr *addr) * @arg addr Abstract address object. * @arg buf Buffer containing binary address. * @arg len Length of buffer containing binary address. + * + * Modifies the binary address portion of the abstract address. The + * abstract address must be capable of holding the required amount + * or this function will fail. + * + * @note This function will *not* modify the prefix length. It is within + * the responsibility of the caller to set the prefix length to the + * desirable length. + * + * @see nl_addr_alloc() + * @see nl_addr_get_binary_addr() + * @see nl_addr_get_len() + * + * @return 0 on success or a negative error code. */ int nl_addr_set_binary_addr(struct nl_addr *addr, void *buf, size_t len) { @@ -761,7 +873,10 @@ int nl_addr_set_binary_addr(struct nl_addr *addr, void *buf, size_t len) return -NLE_RANGE; addr->a_len = len; - memcpy(addr->a_addr, buf, len); + memset(addr->a_addr, 0, addr->a_maxsize); + + if (len) + memcpy(addr->a_addr, buf, len); return 0; } @@ -769,6 +884,11 @@ int nl_addr_set_binary_addr(struct nl_addr *addr, void *buf, size_t len) /** * Get binary address of abstract address object. * @arg addr Abstract address object. + * + * @see nl_addr_set_binary_addr() + * @see nl_addr_get_len() + * + * @return Pointer to binary address of length nl_addr_get_len() */ void *nl_addr_get_binary_addr(struct nl_addr *addr) { @@ -778,20 +898,32 @@ void *nl_addr_get_binary_addr(struct nl_addr *addr) /** * Get length of binary address of abstract address object. * @arg addr Abstract address object. + * + * @see nl_addr_get_binary_addr() + * @see nl_addr_set_binary_addr() */ unsigned int nl_addr_get_len(struct nl_addr *addr) { return addr->a_len; } +/** + * Set the prefix length of an abstract address + * @arg addr Abstract address object + * @arg prefixlen New prefix length + * + * @see nl_addr_get_prefixlen() + */ void nl_addr_set_prefixlen(struct nl_addr *addr, int prefixlen) { addr->a_prefixlen = prefixlen; } /** - * Get prefix length of abstract address object. - * @arg addr Abstract address object. + * Return prefix length of abstract address object. + * @arg addr Abstract address object + * + * @see nl_addr_set_prefixlen() */ unsigned int nl_addr_get_prefixlen(struct nl_addr *addr) { @@ -818,7 +950,7 @@ unsigned int nl_addr_get_prefixlen(struct nl_addr *addr) */ char *nl_addr2str(struct nl_addr *addr, char *buf, size_t size) { - int i; + unsigned int i; char tmp[16]; if (!addr || !addr->a_len) { @@ -873,7 +1005,6 @@ prefix: static const struct trans_tbl afs[] = { __ADD(AF_UNSPEC,unspec) __ADD(AF_UNIX,unix) - __ADD(AF_LOCAL,local) __ADD(AF_INET,inet) __ADD(AF_AX25,ax25) __ADD(AF_IPX,ipx) @@ -889,17 +1020,27 @@ static const struct trans_tbl afs[] = { __ADD(AF_SECURITY,security) __ADD(AF_KEY,key) __ADD(AF_NETLINK,netlink) - __ADD(AF_ROUTE,route) __ADD(AF_PACKET,packet) __ADD(AF_ASH,ash) __ADD(AF_ECONET,econet) __ADD(AF_ATMSVC,atmsvc) + __ADD(AF_RDS,rds) __ADD(AF_SNA,sna) __ADD(AF_IRDA,irda) __ADD(AF_PPPOX,pppox) __ADD(AF_WANPIPE,wanpipe) __ADD(AF_LLC,llc) + __ADD(AF_CAN,can) + __ADD(AF_TIPC,tipc) __ADD(AF_BLUETOOTH,bluetooth) + __ADD(AF_IUCV,iucv) + __ADD(AF_RXRPC,rxrpc) + __ADD(AF_ISDN,isdn) + __ADD(AF_PHONET,phonet) + __ADD(AF_IEEE802154,ieee802154) + __ADD(AF_CAIF,caif) + __ADD(AF_ALG,alg) + __ADD(AF_NFC,nfc) }; char *nl_af2str(int family, char *buf, size_t size) diff --git a/lib/attr.c b/lib/attr.c index cccd50d..e6efe4e 100644 --- a/lib/attr.c +++ b/lib/attr.c @@ -6,10 +6,10 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2008 Thomas Graf + * Copyright (c) 2003-2013 Thomas Graf */ -#include +#include #include #include #include @@ -22,356 +22,16 @@ * @defgroup attr Attributes * Netlink Attributes Construction/Parsing Interface * - * \section attr_sec Netlink Attributes - * Netlink attributes allow for data chunks of arbitary length to be - * attached to a netlink message. Each attribute is encoded with a - * type and length field, both 16 bits, stored in the attribute header - * preceding the attribute data. The main advantage of using attributes - * over packing everything into the family header is that the interface - * stays extendable as new attributes can supersede old attributes while - * remaining backwards compatible. Also attributes can be defined optional - * thus avoiding the transmission of unnecessary empty data blocks. - * Special nested attributes allow for more complex data structures to - * be transmitted, e.g. trees, lists, etc. - * - * While not required, netlink attributes typically follow the family - * header of a netlink message and must be properly aligned to NLA_ALIGNTO: - * @code - * +----------------+- - -+---------------+- - -+------------+- - -+ - * | Netlink Header | Pad | Family Header | Pad | Attributes | Pad | - * +----------------+- - -+---------------+- - -+------------+- - -+ - * @endcode - * - * The actual attributes are chained together each separately aligned to - * NLA_ALIGNTO. The position of an attribute is defined based on the - * length field of the preceding attributes: - * @code - * +-------------+- - -+-------------+- - -+------ - * | Attribute 1 | Pad | Attribute 2 | Pad | ... - * +-------------+- - -+-------------+- - -+------ - * nla_next(attr1)------^ - * @endcode - * - * The attribute itself consists of the attribute header followed by - * the actual payload also aligned to NLA_ALIGNTO. The function nla_data() - * returns a pointer to the start of the payload while nla_len() returns - * the length of the payload in bytes. - * - * \b Note: Be aware, NLA_ALIGNTO equals to 4 bytes, therefore it is not - * safe to dereference any 64 bit data types directly. - * - * @code - * <----------- nla_total_size(payload) -----------> - * <-------- nla_attr_size(payload) ---------> - * +------------------+- - -+- - - - - - - - - +- - -+ - * | Attribute Header | Pad | Payload | Pad | - * +------------------+- - -+- - - - - - - - - +- - -+ - * nla_data(nla)-------------^ - * <- nla_len(nla) -> - * @endcode - * - * @subsection attr_datatypes Attribute Data Types - * A number of basic data types are supported to simplify access and - * validation of netlink attributes. This data type information is - * not encoded in the attribute, both the kernel and userspace part - * are required to share this information on their own. - * - * One of the major advantages of these basic types is the automatic - * validation of each attribute based on an attribute policy. The - * validation covers most of the checks required to safely use - * attributes and thus keeps the individual sanity check to a minimum. - * - * Never access attribute payload without ensuring basic validation - * first, attributes may: - * - not be present even though required - * - contain less actual payload than expected - * - fake a attribute length which exceeds the end of the message - * - contain unterminated character strings - * - * Policies are defined as array of the struct nla_policy. The array is - * indexed with the attribute type, therefore the array must be sized - * accordingly. - * @code - * static struct nla_policy my_policy[ATTR_MAX+1] = { - * [ATTR_FOO] = { .type = ..., .minlen = ..., .maxlen = ... }, - * }; - * - * err = nla_validate(attrs, attrlen, ATTR_MAX, &my_policy); - * @endcode - * - * Some basic validations are performed on every attribute, regardless of type. - * - If the attribute type exceeds the maximum attribute type specified or - * the attribute type is lesser-or-equal than zero, the attribute will - * be silently ignored. - * - If the payload length falls below the \a minlen value the attribute - * will be rejected. - * - If \a maxlen is non-zero and the payload length exceeds the \a maxlen - * value the attribute will be rejected. - * - * - * @par Unspecific Attribute (NLA_UNSPEC) - * This is the standard type if no type is specified. It is used for - * binary data of arbitary length. Typically this attribute carries - * a binary structure or a stream of bytes. - * @par - * @code - * // In this example, we will assume a binary structure requires to - * // be transmitted. The definition of the structure will typically - * // go into a header file available to both the kernel and userspace - * // side. - * // - * // Note: Be careful when putting 64 bit data types into a structure. - * // The attribute payload is only aligned to 4 bytes, dereferencing - * // the member may fail. - * struct my_struct { - * int a; - * int b; - * }; - * - * // The validation function will not enforce an exact length match to - * // allow structures to grow as required. Note: While it is allowed - * // to add members to the end of the structure, changing the order or - * // inserting members in the middle of the structure will break your - * // binary interface. - * static struct nla_policy my_policy[ATTR_MAX+1] = { - * [ATTR_MY_STRICT] = { .type = NLA_UNSPEC, - * .minlen = sizeof(struct my_struct) }, - * - * // The binary structure is appened to the message using nla_put() - * struct my_struct foo = { .a = 1, .b = 2 }; - * nla_put(msg, ATTR_MY_STRUCT, sizeof(foo), &foo); - * - * // On the receiving side, a pointer to the structure pointing inside - * // the message payload is returned by nla_get(). - * if (attrs[ATTR_MY_STRUCT]) - * struct my_struct *foo = nla_get(attrs[ATTR_MY_STRUCT]); - * @endcode - * - * @par Integers (NLA_U8, NLA_U16, NLA_U32, NLA_U64) - * Integers come in different sizes from 8 bit to 64 bit. However, since the - * payload length is aligned to 4 bytes, integers smaller than 32 bit are - * only useful to enforce the maximum range of values. - * @par - * \b Note: There is no difference made between signed and unsigned integers. - * The validation only enforces the minimal payload length required to store - * an integer of specified type. - * @par - * @code - * // Even though possible, it does not make sense to specify .minlen or - * // .maxlen for integer types. The data types implies the corresponding - * // minimal payload length. - * static struct nla_policy my_policy[ATTR_MAX+1] = { - * [ATTR_FOO] = { .type = NLA_U32 }, - * - * // Numeric values can be appended directly using the respective - * // nla_put_uxxx() function - * nla_put_u32(msg, ATTR_FOO, 123); - * - * // Same for the receiving side. - * if (attrs[ATTR_FOO]) - * uint32_t foo = nla_get_u32(attrs[ATTR_FOO]); - * @endcode - * - * @par Character string (NLA_STRING) - * This data type represents a NUL terminated character string of variable - * length. For binary data streams the type NLA_UNSPEC is recommended. - * @par - * @code - * // Enforce a NUL terminated character string of at most 4 characters - * // including the NUL termination. - * static struct nla_policy my_policy[ATTR_MAX+1] = { - * [ATTR_BAR] = { .type = NLA_STRING, maxlen = 4 }, - * - * // nla_put_string() creates a string attribute of the necessary length - * // and appends it to the message including the NUL termination. - * nla_put_string(msg, ATTR_BAR, "some text"); - * - * // It is safe to use the returned character string directly if the - * // attribute has been validated as the validation enforces the proper - * // termination of the string. - * if (attrs[ATTR_BAR]) - * char *text = nla_get_string(attrs[ATTR_BAR]); - * @endcode - * - * @par Flag (NLA_FLAG) - * This attribute type may be used to indicate the presence of a flag. The - * attribute is only valid if the payload length is zero. The presence of - * the attribute header indicates the presence of the flag. - * @par - * @code - * // This attribute type is special as .minlen and .maxlen have no effect. - * static struct nla_policy my_policy[ATTR_MAX+1] = { - * [ATTR_FLAG] = { .type = NLA_FLAG }, - * - * // nla_put_flag() appends a zero sized attribute to the message. - * nla_put_flag(msg, ATTR_FLAG); - * - * // There is no need for a receival function, the presence is the value. - * if (attrs[ATTR_FLAG]) - * // flag is present - * @endcode - * - * @par Micro Seconds (NLA_MSECS) - * - * @par Nested Attribute (NLA_NESTED) - * Attributes can be nested and put into a container to create groups, lists - * or to construct trees of attributes. Nested attributes are often used to - * pass attributes to a subsystem where the top layer has no knowledge of the - * configuration possibilities of each subsystem. - * @par - * \b Note: When validating the attributes using nlmsg_validate() or - * nlmsg_parse() it will only affect the top level attributes. Each - * level of nested attributes must be validated seperately using - * nla_parse_nested() or nla_validate(). - * @par - * @code - * // The minimal length policy may be used to enforce the presence of at - * // least one attribute. - * static struct nla_policy my_policy[ATTR_MAX+1] = { - * [ATTR_OPTS] = { .type = NLA_NESTED, minlen = NLA_HDRLEN }, - * - * // Nested attributes are constructed by enclosing the attributes - * // to be nested with calls to nla_nest_start() respetively nla_nest_end(). - * struct nlattr *opts = nla_nest_start(msg, ATTR_OPTS); - * nla_put_u32(msg, ATTR_FOO, 123); - * nla_put_string(msg, ATTR_BAR, "some text"); - * nla_nest_end(msg, opts); - * - * // Various methods exist to parse nested attributes, the easiest being - * // nla_parse_nested() which also allows validation in the same step. - * if (attrs[ATTR_OPTS]) { - * struct nlattr *nested[ATTR_MAX+1]; - * - * nla_parse_nested(nested, ATTR_MAX, attrs[ATTR_OPTS], &policy); - * - * if (nested[ATTR_FOO]) - * uint32_t foo = nla_get_u32(nested[ATTR_FOO]); - * } - * @endcode - * - * @subsection attr_exceptions Exception Based Attribute Construction - * Often a large number of attributes are added to a message in a single - * function. In order to simplify error handling, a second set of - * construction functions exist which jump to a error label when they - * fail instead of returning an error code. This second set consists - * of macros which are named after their error code based counterpart - * except that the name is written all uppercase. - * - * All of the macros jump to the target \c nla_put_failure if they fail. - * @code - * void my_func(struct nl_msg *msg) - * { - * NLA_PUT_U32(msg, ATTR_FOO, 10); - * NLA_PUT_STRING(msg, ATTR_BAR, "bar"); - * - * return 0; - * - * nla_put_failure: - * return -NLE_NOMEM; - * } - * @endcode - * - * @subsection attr_examples Examples - * @par Example 1.1 Constructing a netlink message with attributes. - * @code - * struct nl_msg *build_msg(int ifindex, struct nl_addr *lladdr, int mtu) - * { - * struct nl_msg *msg; - * struct nlattr *info, *vlan; - * struct ifinfomsg ifi = { - * .ifi_family = AF_INET, - * .ifi_index = ifindex, - * }; - * - * // Allocate a new netlink message, type=RTM_SETLINK, flags=NLM_F_ECHO - * if (!(msg = nlmsg_alloc_simple(RTM_SETLINK, NLM_F_ECHO))) - * return NULL; - * - * // Append the family specific header (struct ifinfomsg) - * if (nlmsg_append(msg, &ifi, sizeof(ifi), NLMSG_ALIGNTO) < 0) - * goto nla_put_failure - * - * // Append a 32 bit integer attribute to carry the MTU - * NLA_PUT_U32(msg, IFLA_MTU, mtu); - * - * // Append a unspecific attribute to carry the link layer address - * NLA_PUT_ADDR(msg, IFLA_ADDRESS, lladdr); - * - * // Append a container for nested attributes to carry link information - * if (!(info = nla_nest_start(msg, IFLA_LINKINFO))) - * goto nla_put_failure; - * - * // Put a string attribute into the container - * NLA_PUT_STRING(msg, IFLA_INFO_KIND, "vlan"); - * - * // Append another container inside the open container to carry - * // vlan specific attributes - * if (!(vlan = nla_nest_start(msg, IFLA_INFO_DATA))) - * goto nla_put_failure; - * - * // add vlan specific info attributes here... - * - * // Finish nesting the vlan attributes and close the second container. - * nla_nest_end(msg, vlan); - * - * // Finish nesting the link info attribute and close the first container. - * nla_nest_end(msg, info); - * - * return msg; - * - * // If any of the construction macros fails, we end up here. - * nla_put_failure: - * nlmsg_free(msg); - * return NULL; - * } - * @endcode - * - * @par Example 2.1 Parsing a netlink message with attributes. - * @code - * int parse_message(struct nl_msg *msg) - * { - * // The policy defines two attributes: a 32 bit integer and a container - * // for nested attributes. - * struct nla_policy attr_policy[ATTR_MAX+1] = { - * [ATTR_FOO] = { .type = NLA_U32 }, - * [ATTR_BAR] = { .type = NLA_NESTED }, - * }; - * struct nlattr *attrs[ATTR_MAX+1]; - * int err; - * - * // The nlmsg_parse() function will make sure that the message contains - * // enough payload to hold the header (struct my_hdr), validates any - * // attributes attached to the messages and stores a pointer to each - * // attribute in the attrs[] array accessable by attribute type. - * if ((err = nlmsg_parse(nlmsg_hdr(msg), sizeof(struct my_hdr), attrs, - * ATTR_MAX, attr_policy)) < 0) - * goto errout; - * - * if (attrs[ATTR_FOO]) { - * // It is safe to directly access the attribute payload without - * // any further checks since nlmsg_parse() enforced the policy. - * uint32_t foo = nla_get_u32(attrs[ATTR_FOO]); - * } - * - * if (attrs[ATTR_BAR]) { - * struct nlattr *nested[NESTED_MAX+1]; - * - * // Attributes nested in a container can be parsed the same way - * // as top level attributes. - * if ((err = nla_parse_nested(nested, NESTED_MAX, attrs[ATTR_BAR], - * nested_policy)) < 0) - * goto errout; - * - * // Process nested attributes here. - * } - * - * err = 0; - * errout: - * return err; - * } - * @endcode + * Related sections in the development guide: + * - @core_doc{core_attr,Netlink Attributes} * * @{ + * + * Header + * ------ + * ~~~~{.c} + * #include + * ~~~~ */ /** @@ -522,13 +182,15 @@ static uint16_t nla_attr_minlen[NLA_TYPE_MAX+1] = { [NLA_U32] = sizeof(uint32_t), [NLA_U64] = sizeof(uint64_t), [NLA_STRING] = 1, + [NLA_FLAG] = 0, }; static int validate_nla(struct nlattr *nla, int maxtype, struct nla_policy *policy) { struct nla_policy *pt; - int minlen = 0, type = nla_type(nla); + unsigned int minlen = 0; + int type = nla_type(nla); if (type <= 0 || type > maxtype) return 0; @@ -543,9 +205,6 @@ static int validate_nla(struct nlattr *nla, int maxtype, else if (pt->type != NLA_UNSPEC) minlen = nla_attr_minlen[pt->type]; - if (pt->type == NLA_FLAG && nla_len(nla) > 0) - return -NLE_RANGE; - if (nla_len(nla) < minlen) return -NLE_RANGE; @@ -591,24 +250,28 @@ int nla_parse(struct nlattr *tb[], int maxtype, struct nlattr *head, int len, nla_for_each_attr(nla, head, len, rem) { int type = nla_type(nla); - if (type == 0) { - fprintf(stderr, "Illegal nla->nla_type == 0\n"); + /* Padding attributes */ + if (type == 0) continue; + + if (type > maxtype) + continue; + + if (policy) { + err = validate_nla(nla, maxtype, policy); + if (err < 0) + goto errout; } - if (type <= maxtype) { - if (policy) { - err = validate_nla(nla, maxtype, policy); - if (err < 0) - goto errout; - } + if (tb[type]) + NL_DBG(1, "Attribute of type %#x found multiple times in message, " + "previous attribute is being ignored.\n", type); - tb[type] = nla; - } + tb[type] = nla; } if (rem > 0) - fprintf(stderr, "netlink: %d bytes leftover after parsing " + NL_DBG(1, "netlink: %d bytes leftover after parsing " "attributes.\n", rem); err = 0; @@ -628,8 +291,7 @@ errout: * than the maximum type specified will be silently ignored in order to * maintain backwards compatibility. * - * See \ref attr_datatypes for more details on what kind of validation - * checks are performed on each attribute data type. + * See section @core_doc{core_attr_parse,Attribute Parsing} for more details. * * @return 0 on success or a negative error code. */ @@ -1151,10 +813,21 @@ struct nlattr *nla_nest_start(struct nl_msg *msg, int attrtype) */ int nla_nest_end(struct nl_msg *msg, struct nlattr *start) { - size_t pad; + size_t pad, len; - start->nla_len = (unsigned char *) nlmsg_tail(msg->nm_nlh) - - (unsigned char *) start; + len = (void *) nlmsg_tail(msg->nm_nlh) - (void *) start; + + if (len == NLA_HDRLEN) { + /* + * Kernel can't handle empty nested attributes, trim the + * attribute header again + */ + nla_nest_cancel(msg, start); + + return 0; + } + + start->nla_len = len; pad = NLMSG_ALIGN(msg->nm_nlh->nlmsg_len) - msg->nm_nlh->nlmsg_len; if (pad > 0) { @@ -1177,6 +850,28 @@ int nla_nest_end(struct nl_msg *msg, struct nlattr *start) return 0; } +/** + * Cancel the addition of a nested attribute + * @arg msg Netlink message + * @arg attr Nested netlink attribute + * + * Removes any partially added nested Netlink attribute from the message + * by resetting the message to the size before the call to nla_nest_start() + * and by overwriting any potentially touched message segments with 0. + */ +void nla_nest_cancel(struct nl_msg *msg, struct nlattr *attr) +{ + ssize_t len; + + len = (void *) nlmsg_tail(msg->nm_nlh) - (void *) attr; + if (len < 0) + BUG(); + else if (len > 0) { + msg->nm_nlh->nlmsg_len -= len; + memset(nlmsg_tail(msg->nm_nlh), 0, len); + } +} + /** * Create attribute index based on nested attribute * @arg tb Index array to be filled (maxtype+1 elements). @@ -1196,6 +891,17 @@ int nla_parse_nested(struct nlattr *tb[], int maxtype, struct nlattr *nla, return nla_parse(tb, maxtype, nla_data(nla), nla_len(nla), policy); } +/** + * Return true if attribute has NLA_F_NESTED flag set + * @arg attr Netlink attribute + * + * @return True if attribute has NLA_F_NESTED flag set, oterhwise False. + */ +int nla_is_nested(struct nlattr *attr) +{ + return !!(nla_type(attr) & NLA_F_NESTED); +} + /** @} */ /** @} */ diff --git a/lib/cache.c b/lib/cache.c index 601dadc..fafc023 100644 --- a/lib/cache.c +++ b/lib/cache.c @@ -6,7 +6,7 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2008 Thomas Graf + * Copyright (c) 2003-2012 Thomas Graf */ /** @@ -37,13 +37,23 @@ * | | Core Netlink * @endcode * + * Related sections in the development guide: + * - @core_doc{core_cache, Caching System} + * * @{ + * + * Header + * ------ + * ~~~~{.c} + * #include + * ~~~~ */ -#include +#include #include #include #include +#include #include /** @@ -67,15 +77,12 @@ int nl_cache_nitems(struct nl_cache *cache) */ int nl_cache_nitems_filter(struct nl_cache *cache, struct nl_object *filter) { - struct nl_object_ops *ops; struct nl_object *obj; int nitems = 0; if (cache->c_ops == NULL) BUG(); - ops = cache->c_ops->co_obj_ops; - nl_list_for_each_entry(obj, &cache->c_items, ce_list) { if (filter && !nl_object_match_filter(obj, filter)) continue; @@ -160,15 +167,18 @@ struct nl_object *nl_cache_get_prev(struct nl_object *obj) /** @} */ /** - * @name Cache Creation/Deletion + * @name Cache Allocation/Deletion * @{ */ /** - * Allocate an empty cache - * @arg ops cache operations to base the cache on - * - * @return A newly allocated and initialized cache. + * Allocate new cache + * @arg ops Cache operations + * + * Allocate and initialize a new cache based on the cache operations + * provided. + * + * @return Allocated cache or NULL if allocation failed. */ struct nl_cache *nl_cache_alloc(struct nl_cache_ops *ops) { @@ -180,12 +190,46 @@ struct nl_cache *nl_cache_alloc(struct nl_cache_ops *ops) nl_init_list_head(&cache->c_items); cache->c_ops = ops; + cache->c_flags |= ops->co_flags; + cache->c_refcnt = 1; + + /* + * If object type provides a hash keygen + * functions, allocate a hash table for the + * cache objects for faster lookups + */ + if (ops->co_obj_ops->oo_keygen) { + int hashtable_size; + + if (ops->co_hash_size) + hashtable_size = ops->co_hash_size; + else + hashtable_size = NL_MAX_HASH_ENTRIES; + + cache->hashtable = nl_hash_table_alloc(hashtable_size); + } NL_DBG(2, "Allocated cache %p <%s>.\n", cache, nl_cache_name(cache)); return cache; } +/** + * Allocate new cache and fill it + * @arg ops Cache operations + * @arg sock Netlink socket + * @arg result Result pointer + * + * Allocate new cache and fill it. Equivalent to calling: + * @code + * cache = nl_cache_alloc(ops); + * nl_cache_refill(sock, cache); + * @endcode + * + * @see nl_cache_alloc + * + * @return 0 on success or a negative error code. + */ int nl_cache_alloc_and_fill(struct nl_cache_ops *ops, struct nl_sock *sock, struct nl_cache **result) { @@ -205,20 +249,30 @@ int nl_cache_alloc_and_fill(struct nl_cache_ops *ops, struct nl_sock *sock, } /** - * Allocate an empty cache based on type name + * Allocate new cache based on type name * @arg kind Name of cache type - * @return A newly allocated and initialized cache. + * @arg result Result pointer + * + * Lookup cache ops via nl_cache_ops_lookup() and allocate the cache + * by calling nl_cache_alloc(). Stores the allocated cache in the + * result pointer provided. + * + * @see nl_cache_alloc + * + * @return 0 on success or a negative error code. */ int nl_cache_alloc_name(const char *kind, struct nl_cache **result) { struct nl_cache_ops *ops; struct nl_cache *cache; - ops = nl_cache_ops_lookup(kind); + ops = nl_cache_ops_lookup_safe(kind); if (!ops) return -NLE_NOCACHE; - if (!(cache = nl_cache_alloc(ops))) + cache = nl_cache_alloc(ops); + nl_cache_ops_put(ops); + if (!cache) return -NLE_NOMEM; *result = cache; @@ -226,16 +280,24 @@ int nl_cache_alloc_name(const char *kind, struct nl_cache **result) } /** - * Allocate a new cache containing a subset of a cache - * @arg orig Original cache to be based on - * @arg filter Filter defining the subset to be filled into new cache + * Allocate new cache containing a subset of an existing cache + * @arg orig Original cache to base new cache on + * @arg filter Filter defining the subset to be filled into the new cache + * + * Allocates a new cache matching the type of the cache specified by + * \p orig. Iterates over the \p orig cache applying the specified + * \p filter and copies all objects that match to the new cache. + * + * The copied objects are clones but do not contain a reference to each + * other. Later modifications to objects in the original cache will + * not affect objects in the new cache. + * * @return A newly allocated cache or NULL. */ struct nl_cache *nl_cache_subset(struct nl_cache *orig, struct nl_object *filter) { struct nl_cache *cache; - struct nl_object_ops *ops; struct nl_object *obj; if (!filter) @@ -245,8 +307,6 @@ struct nl_cache *nl_cache_subset(struct nl_cache *orig, if (!cache) return NULL; - ops = orig->c_ops->co_obj_ops; - nl_list_for_each_entry(obj, &orig->c_items, ce_list) { if (!nl_object_match_filter(obj, filter)) continue; @@ -258,10 +318,45 @@ struct nl_cache *nl_cache_subset(struct nl_cache *orig, } /** - * Clear a cache. - * @arg cache cache to clear + * Allocate new cache and copy the contents of an existing cache + * @arg cache Original cache to base new cache on * - * Removes all elements of a cache. + * Allocates a new cache matching the type of the cache specified by + * \p cache. Iterates over the \p cache cache and copies all objects + * to the new cache. + * + * The copied objects are clones but do not contain a reference to each + * other. Later modifications to objects in the original cache will + * not affect objects in the new cache. + * + * @return A newly allocated cache or NULL. + */ +struct nl_cache *nl_cache_clone(struct nl_cache *cache) +{ + struct nl_cache_ops *ops = nl_cache_get_ops(cache); + struct nl_cache *clone; + struct nl_object *obj; + + clone = nl_cache_alloc(ops); + if (!clone) + return NULL; + + nl_list_for_each_entry(obj, &cache->c_items, ce_list) + nl_cache_add(clone, obj); + + return clone; +} + +/** + * Remove all objects of a cache. + * @arg cache Cache to clear + * + * The objects are unliked/removed from the cache by calling + * nl_cache_remove() on each object in the cache. If any of the objects + * to not contain any further references to them, those objects will + * be freed. + * + * Unlike with nl_cache_free(), the cache is not freed just emptied. */ void nl_cache_clear(struct nl_cache *cache) { @@ -273,22 +368,51 @@ void nl_cache_clear(struct nl_cache *cache) nl_cache_remove(obj); } +static void __nl_cache_free(struct nl_cache *cache) +{ + nl_cache_clear(cache); + + if (cache->hashtable) + nl_hash_table_free(cache->hashtable); + + NL_DBG(1, "Freeing cache %p <%s>...\n", cache, nl_cache_name(cache)); + free(cache); +} + +/** + * Increase reference counter of cache + * @arg cache Cache + */ +void nl_cache_get(struct nl_cache *cache) +{ + cache->c_refcnt++; +} + /** * Free a cache. * @arg cache Cache to free. * - * Removes all elements of a cache and frees all memory. + * Calls nl_cache_clear() to remove all objects associated with the + * cache and frees the cache afterwards. * - * @note Use this function if you are working with allocated caches. + * @see nl_cache_clear() */ void nl_cache_free(struct nl_cache *cache) { if (!cache) return; - nl_cache_clear(cache); - NL_DBG(1, "Freeing cache %p <%s>...\n", cache, nl_cache_name(cache)); - free(cache); + cache->c_refcnt--; + NL_DBG(4, "Returned cache reference %p, %d remaining\n", + cache, cache->c_refcnt); + + if (cache->c_refcnt <= 0) + __nl_cache_free(cache); +} + +void nl_cache_put(struct nl_cache *cache) +{ + return nl_cache_free(cache); } /** @} */ @@ -300,8 +424,18 @@ void nl_cache_free(struct nl_cache *cache) static int __cache_add(struct nl_cache *cache, struct nl_object *obj) { + int ret; + obj->ce_cache = cache; + if (cache->hashtable) { + ret = nl_hash_table_add(cache->hashtable, obj); + if (ret < 0) { + obj->ce_cache = NULL; + return ret; + } + } + nl_list_add_tail(&obj->ce_list, &cache->c_items); cache->c_nitems++; @@ -312,18 +446,31 @@ static int __cache_add(struct nl_cache *cache, struct nl_object *obj) } /** - * Add object to a cache. - * @arg cache Cache to add object to + * Add object to cache. + * @arg cache Cache * @arg obj Object to be added to the cache * - * Adds the given object to the specified cache. The object is cloned - * if it has been added to another cache already. + * Adds the object \p obj to the specified \p cache. In case the object + * is already associated with another cache, the object is cloned before + * adding it to the cache. In this case, the sole reference to the object + * will be the one of the cache. Therefore clearing/freeing the cache + * will result in the object being freed again. + * + * If the object has not been associated with a cache yet, the reference + * counter of the object is incremented to account for the additional + * reference. + * + * The type of the object and cache must match, otherwise an error is + * returned (-NLE_OBJ_MISMATCH). + * + * @see nl_cache_move() * * @return 0 or a negative error code. */ int nl_cache_add(struct nl_cache *cache, struct nl_object *obj) { struct nl_object *new; + int ret = 0; if (cache->c_ops->co_obj_ops != obj->ce_ops) return -NLE_OBJ_MISMATCH; @@ -337,7 +484,11 @@ int nl_cache_add(struct nl_cache *cache, struct nl_object *obj) new = obj; } - return __cache_add(cache, new); + ret = __cache_add(cache, new); + if (ret < 0) + nl_object_put(new); + + return ret; } /** @@ -345,8 +496,16 @@ int nl_cache_add(struct nl_cache *cache, struct nl_object *obj) * @arg cache Cache to move object to. * @arg obj Object subject to be moved * - * Removes the given object from its associated cache if needed - * and adds it to the new cache. + * Removes the the specified object \p obj from its associated cache + * and moves it to another cache. + * + * If the object is not associated with a cache, the function behaves + * just like nl_cache_add(). + * + * The type of the object and cache must match, otherwise an error is + * returned (-NLE_OBJ_MISMATCH). + * + * @see nl_cache_add() * * @return 0 on success or a negative error code. */ @@ -368,20 +527,30 @@ int nl_cache_move(struct nl_cache *cache, struct nl_object *obj) } /** - * Removes an object from a cache. - * @arg obj Object to remove from its cache + * Remove object from cache. + * @arg obj Object to remove from cache * - * Removes the object \c obj from the cache it is assigned to, since - * an object can only be assigned to one cache at a time, the cache - * must ne be passed along with it. + * Removes the object \c obj from the cache it is associated with. The + * reference counter of the object will be decremented. If the reference + * to the object was the only one remaining, the object will be freed. + * + * If no cache is associated with the object, this function is a NOP. */ void nl_cache_remove(struct nl_object *obj) { + int ret; struct nl_cache *cache = obj->ce_cache; if (cache == NULL) return; + if (cache->hashtable) { + ret = nl_hash_table_del(cache->hashtable, obj); + if (ret < 0) + NL_DBG(3, "Failed to delete %p from cache %p <%s>.\n", + obj, cache, nl_cache_name(cache)); + } + nl_list_del(&obj->ce_list); obj->ce_cache = NULL; nl_object_put(obj); @@ -391,33 +560,6 @@ void nl_cache_remove(struct nl_object *obj) obj, cache, nl_cache_name(cache)); } -/** - * Search for an object in a cache - * @arg cache Cache to search in. - * @arg needle Object to look for. - * - * Iterates over the cache and looks for an object with identical - * identifiers as the needle. - * - * @return Reference to object or NULL if not found. - * @note The returned object must be returned via nl_object_put(). - */ -struct nl_object *nl_cache_search(struct nl_cache *cache, - struct nl_object *needle) -{ - struct nl_object *obj; - - nl_list_for_each_entry(obj, &cache->c_items, ce_list) { - if (nl_object_identical(obj, needle)) { - nl_object_get(obj); - return obj; - } - } - - return NULL; -} - - /** @} */ /** @@ -426,17 +568,67 @@ struct nl_object *nl_cache_search(struct nl_cache *cache, */ /** - * Request a full dump from the kernel to fill a cache - * @arg sk Netlink socket. - * @arg cache Cache subjected to be filled. + * Set synchronization arg1 of cache + * @arg cache Cache + * @arg arg argument * - * Send a dumping request to the kernel causing it to dump all objects - * related to the specified cache to the netlink socket. - * - * Use nl_cache_pickup() to read the objects from the socket and fill them - * into a cache. + * Synchronization arguments are used to specify filters when + * requesting dumps from the kernel. */ -int nl_cache_request_full_dump(struct nl_sock *sk, struct nl_cache *cache) +void nl_cache_set_arg1(struct nl_cache *cache, int arg) +{ + cache->c_iarg1 = arg; +} + +/** + * Set synchronization arg2 of cache + * @arg cache Cache + * @arg arg argument + * + * Synchronization arguments are used to specify filters when + * requesting dumps from the kernel. + */ +void nl_cache_set_arg2(struct nl_cache *cache, int arg) +{ + cache->c_iarg2 = arg; +} + +/** + * Set cache flags + * @arg cache Cache + * @arg flags Flags + */ +void nl_cache_set_flags(struct nl_cache *cache, unsigned int flags) +{ + cache->c_flags |= flags; +} + +/** + * Invoke the request-update operation + * @arg sk Netlink socket. + * @arg cache Cache + * + * This function causes the \e request-update function of the cache + * operations to be invoked. This usually causes a dump request to + * be sent over the netlink socket which triggers the kernel to dump + * all objects of a specific type to be dumped onto the netlink + * socket for pickup. + * + * The behaviour of this function depends on the implemenation of + * the \e request_update function of each individual type of cache. + * + * This function will not have any effects on the cache (unless the + * request_update implementation of the cache operations does so). + * + * Use nl_cache_pickup() to pick-up (read) the objects from the socket + * and fill them into the cache. + * + * @see nl_cache_pickup(), nl_cache_resync() + * + * @return 0 on success or a negative error code. + */ +static int nl_cache_request_full_dump(struct nl_sock *sk, + struct nl_cache *cache) { NL_DBG(2, "Requesting dump from kernel for cache %p <%s>...\n", cache, nl_cache_name(cache)); @@ -456,13 +648,24 @@ struct update_xdata { static int update_msg_parser(struct nl_msg *msg, void *arg) { struct update_xdata *x = arg; - - return nl_cache_parse(x->ops, &msg->nm_src, msg->nm_nlh, x->params); + int ret = 0; + + ret = nl_cache_parse(x->ops, &msg->nm_src, msg->nm_nlh, x->params); + if (ret == -NLE_EXIST) + return NL_SKIP; + else + return ret; } /** @endcond */ -int __cache_pickup(struct nl_sock *sk, struct nl_cache *cache, - struct nl_parser_param *param) +/** + * Pick-up a netlink request-update with your own parser + * @arg sk Netlink socket + * @arg cache Cache + * @arg param Parser parameters + */ +static int __cache_pickup(struct nl_sock *sk, struct nl_cache *cache, + struct nl_parser_param *param) { int err; struct nl_cb *cb; @@ -493,7 +696,21 @@ int __cache_pickup(struct nl_sock *sk, struct nl_cache *cache, static int pickup_cb(struct nl_object *c, struct nl_parser_param *p) { - return nl_cache_add((struct nl_cache *) p->pp_arg, c); + struct nl_cache *cache = (struct nl_cache *)p->pp_arg; + struct nl_object *old; + + old = nl_cache_search(cache, c); + if (old) { + if (nl_object_update(old, c) == 0) { + nl_object_put(old); + return 0; + } + + nl_cache_remove(old); + nl_object_put(old); + } + + return nl_cache_add(cache, c); } /** @@ -502,7 +719,10 @@ static int pickup_cb(struct nl_object *c, struct nl_parser_param *p) * @arg cache Cache to put items into. * * Waits for netlink messages to arrive, parses them and puts them into - * the specified cache. + * the specified cache. If an old object with same key attributes is + * present in the cache, it is replaced with the new object. + * If the old object type supports an update operation, an update is + * attempted before a replace. * * @return 0 on success or a negative error code. */ @@ -526,6 +746,18 @@ static int cache_include(struct nl_cache *cache, struct nl_object *obj, case NL_ACT_DEL: old = nl_cache_search(cache, obj); if (old) { + /* + * Some objects types might support merging the new + * object with the old existing cache object. + * Handle them first. + */ + if (nl_object_update(old, obj) == 0) { + if (cb) + cb(cache, old, NL_ACT_CHANGE, data); + nl_object_put(old); + return 0; + } + nl_cache_remove(old); if (type->mt_act == NL_ACT_DEL) { if (cb) @@ -582,6 +814,7 @@ int nl_cache_resync(struct nl_sock *sk, struct nl_cache *cache, change_func_t change_cb, void *data) { struct nl_object *obj, *next; + struct nl_af_group *grp; struct nl_cache_assoc ca = { .ca_cache = cache, .ca_change = change_cb, @@ -598,13 +831,27 @@ int nl_cache_resync(struct nl_sock *sk, struct nl_cache *cache, /* Mark all objects so we can see if some of them are obsolete */ nl_cache_mark_all(cache); - err = nl_cache_request_full_dump(sk, cache); - if (err < 0) - goto errout; + grp = cache->c_ops->co_groups; + do { + if (grp && grp->ag_group && + (cache->c_flags & NL_CACHE_AF_ITER)) + nl_cache_set_arg1(cache, grp->ag_family); - err = __cache_pickup(sk, cache, &p); - if (err < 0) - goto errout; +restart: + err = nl_cache_request_full_dump(sk, cache); + if (err < 0) + goto errout; + + err = __cache_pickup(sk, cache, &p); + if (err == -NLE_DUMP_INTR) + goto restart; + else if (err < 0) + goto errout; + + if (grp) + grp++; + } while (grp && grp->ag_group && + (cache->c_flags & NL_CACHE_AF_ITER)); nl_list_for_each_entry_safe(obj, next, &cache->c_items, ce_list) { if (nl_object_is_marked(obj)) { @@ -660,7 +907,10 @@ errout: * @arg msg netlink message * * Parses a netlink message by calling the cache specific message parser - * and adds the new element to the cache. + * and adds the new element to the cache. If an old object with same key + * attributes is present in the cache, it is replaced with the new object. + * If the old object type supports an update operation, an update is + * attempted before a replace. * * @return 0 or a negative error code. */ @@ -686,17 +936,37 @@ int nl_cache_parse_and_add(struct nl_cache *cache, struct nl_msg *msg) */ int nl_cache_refill(struct nl_sock *sk, struct nl_cache *cache) { + struct nl_af_group *grp; int err; - err = nl_cache_request_full_dump(sk, cache); - if (err < 0) - return err; + nl_cache_clear(cache); + grp = cache->c_ops->co_groups; + do { + if (grp && grp->ag_group && + (cache->c_flags & NL_CACHE_AF_ITER)) + nl_cache_set_arg1(cache, grp->ag_family); + +restart: + err = nl_cache_request_full_dump(sk, cache); + if (err < 0) + return err; + + err = nl_cache_pickup(sk, cache); + if (err == -NLE_DUMP_INTR) { + NL_DBG(1, "dump interrupted, restarting!\n"); + goto restart; + } else if (err < 0) + break; + + if (grp) + grp++; + } while (grp && grp->ag_group && + (cache->c_flags & NL_CACHE_AF_ITER)); NL_DBG(2, "Upading cache %p <%s>, request sent, waiting for dump...\n", cache, nl_cache_name(cache)); - nl_cache_clear(cache); - return nl_cache_pickup(sk, cache); + return err; } /** @} */ @@ -705,10 +975,98 @@ int nl_cache_refill(struct nl_sock *sk, struct nl_cache *cache) * @name Utillities * @{ */ +static struct nl_object *__cache_fast_lookup(struct nl_cache *cache, + struct nl_object *needle) +{ + struct nl_object *obj; + + obj = nl_hash_table_lookup(cache->hashtable, needle); + if (obj) { + nl_object_get(obj); + return obj; + } + + return NULL; +} /** - * Mark all objects in a cache - * @arg cache Cache to mark all objects in + * Search object in cache + * @arg cache Cache + * @arg needle Object to look for. + * + * Searches the cache for an object which matches the object \p needle. + * The function nl_object_identical() is used to determine if the + * objects match. If a matching object is found, the reference counter + * is incremented and the object is returned. + * + * Therefore, if an object is returned, the reference to the object + * must be returned by calling nl_object_put() after usage. + * + * @return Reference to object or NULL if not found. + */ +struct nl_object *nl_cache_search(struct nl_cache *cache, + struct nl_object *needle) +{ + struct nl_object *obj; + + if (cache->hashtable) + return __cache_fast_lookup(cache, needle); + + nl_list_for_each_entry(obj, &cache->c_items, ce_list) { + if (nl_object_identical(obj, needle)) { + nl_object_get(obj); + return obj; + } + } + + return NULL; +} + +/** + * Find object in cache + * @arg cache Cache + * @arg filter object acting as a filter + * + * Searches the cache for an object which matches the object filter. + * If the filter attributes matches the object type id attributes, + * and the cache supports hash lookups, a faster hashtable lookup + * is used to return the object. Else, function nl_object_match_filter() is + * used to determine if the objects match. If a matching object is + * found, the reference counter is incremented and the object is returned. + * + * Therefore, if an object is returned, the reference to the object + * must be returned by calling nl_object_put() after usage. + * + * @return Reference to object or NULL if not found. + */ +struct nl_object *nl_cache_find(struct nl_cache *cache, + struct nl_object *filter) +{ + struct nl_object *obj; + + if (cache->c_ops == NULL) + BUG(); + + if ((nl_object_get_id_attrs(filter) == filter->ce_mask) + && cache->hashtable) + return __cache_fast_lookup(cache, filter); + + nl_list_for_each_entry(obj, &cache->c_items, ce_list) { + if (nl_object_match_filter(obj, filter)) { + nl_object_get(obj); + return obj; + } + } + + return NULL; +} + +/** + * Mark all objects of a cache + * @arg cache Cache + * + * Marks all objects of a cache by calling nl_object_mark() on each + * object associated with the cache. */ void nl_cache_mark_all(struct nl_cache *cache) { @@ -770,6 +1128,9 @@ void nl_cache_dump_filter(struct nl_cache *cache, if (!ops->oo_dump[type]) return; + if (params->dp_buf) + memset(params->dp_buf, 0, params->dp_buflen); + nl_list_for_each_entry(obj, &cache->c_items, ce_list) { if (filter && !nl_object_match_filter(obj, filter)) continue; @@ -816,13 +1177,10 @@ void nl_cache_foreach_filter(struct nl_cache *cache, struct nl_object *filter, void (*cb)(struct nl_object *, void *), void *arg) { struct nl_object *obj, *tmp; - struct nl_object_ops *ops; if (cache->c_ops == NULL) BUG(); - ops = cache->c_ops->co_obj_ops; - nl_list_for_each_entry_safe(obj, tmp, &cache->c_items, ce_list) { if (filter) { int diff = nl_object_match_filter(obj, filter); diff --git a/lib/cache_mngr.c b/lib/cache_mngr.c index 81052aa..f8a65e3 100644 --- a/lib/cache_mngr.c +++ b/lib/cache_mngr.c @@ -6,96 +6,62 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2008 Thomas Graf + * Copyright (c) 2003-2012 Thomas Graf */ /** * @ingroup cache_mngt * @defgroup cache_mngr Manager - * @brief Helps keeping caches up to date. + * @brief Manager keeping caches up to date automatically. * - * The purpose of a cache manager is to keep track of caches and - * automatically receive event notifications to keep the caches - * up to date with the kernel state. Each manager has exactly one - * netlink socket assigned which limits the scope of each manager - * to exactly one netlink family. Therefore all caches committed - * to a manager must be part of the same netlink family. Due to the - * nature of a manager, it is not possible to have a cache maintain - * two instances of the same cache type. The socket is subscribed - * to the event notification group of each cache and also put into - * non-blocking mode. Functions exist to poll() on the socket to - * wait for new events to be received. + * The cache manager keeps caches up to date automatically by listening to + * netlink notifications and integrating the received information into the + * existing cache. * - * @code - * App libnl Kernel - * | | - * +-----------------+ [ notification, link change ] - * | | Cache Manager | | [ (IFF_UP | IFF_RUNNING) ] - * | | | - * | | +------------+| | | [ notification, new addr ] - * <-------|---| route/link |<-------(async)--+ [ 10.0.1.1/32 dev eth1 ] - * | | +------------+| | | - * | +------------+| | - * <---|---|---| route/addr |<------|-(async)--------------+ - * | +------------+| - * | | +------------+| | - * <-------|---| ... || - * | | +------------+| | - * +-----------------+ - * | | - * @endcode + * @note This functionality is still considered experimental. * - * @par 1) Creating a new cache manager - * @code - * struct nl_cache_mngr *mngr; + * Related sections in the development guide: + * - @core_doc{_cache_manager,Cache Manager} * - * // Allocate a new cache manager for RTNETLINK and automatically - * // provide the caches added to the manager. - * mngr = nl_cache_mngr_alloc(NETLINK_ROUTE, NL_AUTO_PROVIDE); - * @endcode - * - * @par 2) Keep track of a cache - * @code - * struct nl_cache *cache; - * - * // Create a new cache for links/interfaces and ask the manager to - * // keep it up to date for us. This will trigger a full dump request - * // to initially fill the cache. - * cache = nl_cache_mngr_add(mngr, "route/link"); - * @endcode - * - * @par 3) Make the manager receive updates - * @code - * // Give the manager the ability to receive updates, will call poll() - * // with a timeout of 5 seconds. - * if (nl_cache_mngr_poll(mngr, 5000) > 0) { - * // Manager received at least one update, dump cache? - * nl_cache_dump(cache, ...); - * } - * @endcode - * - * @par 4) Release cache manager - * @code - * nl_cache_mngr_free(mngr); - * @endcode * @{ + * + * Header + * ------ + * ~~~~{.c} + * #include + * ~~~~ */ -#include +#include #include #include #include +/** @cond SKIP */ +#define NASSOC_INIT 16 +#define NASSOC_EXPAND 8 +/** @endcond */ + static int include_cb(struct nl_object *obj, struct nl_parser_param *p) { struct nl_cache_assoc *ca = p->pp_arg; + struct nl_cache_ops *ops = ca->ca_cache->c_ops; NL_DBG(2, "Including object %p into cache %p\n", obj, ca->ca_cache); #ifdef NL_DEBUG if (nl_debug >= 4) nl_object_dump(obj, &nl_debug_dp); #endif - return nl_cache_include(ca->ca_cache, obj, ca->ca_change, ca->ca_change_data); + + if (ops->co_event_filter) + if (ops->co_event_filter(ca->ca_cache, obj) != NL_OK) + return 0; + + if (ops->co_include_event) + return ops->co_include_event(ca->ca_cache, obj, ca->ca_change, + ca->ca_change_data); + else + return nl_cache_include(ca->ca_cache, obj, ca->ca_change, ca->ca_change_data); } static int event_input(struct nl_msg *msg, void *arg) @@ -140,11 +106,31 @@ found: /** * Allocate new cache manager - * @arg sk Netlink socket. - * @arg protocol Netlink Protocol this manager is used for - * @arg flags Flags + * @arg sk Netlink socket or NULL to auto allocate + * @arg protocol Netlink protocol this manager is used for + * @arg flags Flags (\c NL_AUTO_PROVIDE) + * @arg result Result pointer * - * @return Newly allocated cache manager or NULL on failure. + * Allocates a new cache manager for the specified netlink protocol. + * + * 1. If sk is not specified (\c NULL) a netlink socket matching the + * specified protocol will be automatically allocated. + * + * 2. The socket will be put in non-blocking mode and sequence checking + * will be disabled regardless of whether the socket was provided by + * the caller or automatically allocated. + * + * 3. The socket will be connected. + * + * If the flag \c NL_AUTO_PROVIDE is specified, any cache added to the + * manager will automatically be made available to other users using + * nl_cache_mngt_provide(). + * + * @note If the socket is provided by the caller, it is NOT recommended + * to use the socket for anything else besides receiving netlink + * notifications. + * + * @return 0 on success or a negative error code. */ int nl_cache_mngr_alloc(struct nl_sock *sk, int protocol, int flags, struct nl_cache_mngr **result) @@ -152,15 +138,23 @@ int nl_cache_mngr_alloc(struct nl_sock *sk, int protocol, int flags, struct nl_cache_mngr *mngr; int err = -NLE_NOMEM; - if (sk == NULL) + /* Catch abuse of flags */ + if (flags & NL_ALLOCATED_SOCK) BUG(); mngr = calloc(1, sizeof(*mngr)); if (!mngr) - goto errout; + return -NLE_NOMEM; - mngr->cm_handle = sk; - mngr->cm_nassocs = 32; + if (!sk) { + if (!(sk = nl_socket_alloc())) + goto errout; + + flags |= NL_ALLOCATED_SOCK; + } + + mngr->cm_sock = sk; + mngr->cm_nassocs = NASSOC_INIT; mngr->cm_protocol = protocol; mngr->cm_flags = flags; mngr->cm_assocs = calloc(mngr->cm_nassocs, @@ -168,55 +162,70 @@ int nl_cache_mngr_alloc(struct nl_sock *sk, int protocol, int flags, if (!mngr->cm_assocs) goto errout; - nl_socket_modify_cb(mngr->cm_handle, NL_CB_VALID, NL_CB_CUSTOM, - event_input, mngr); - /* Required to receive async event notifications */ - nl_socket_disable_seq_check(mngr->cm_handle); + nl_socket_disable_seq_check(mngr->cm_sock); - if ((err = nl_connect(mngr->cm_handle, protocol) < 0)) + if ((err = nl_connect(mngr->cm_sock, protocol) < 0)) goto errout; - if ((err = nl_socket_set_nonblocking(mngr->cm_handle) < 0)) + if ((err = nl_socket_set_nonblocking(mngr->cm_sock) < 0)) goto errout; + /* Create and allocate socket for sync cache fills */ + mngr->cm_sync_sock = nl_socket_alloc(); + if (!mngr->cm_sync_sock) + goto errout; + if ((err = nl_connect(mngr->cm_sync_sock, protocol)) < 0) + goto errout_free_sync_sock; + NL_DBG(1, "Allocated cache manager %p, protocol %d, %d caches\n", mngr, protocol, mngr->cm_nassocs); *result = mngr; return 0; +errout_free_sync_sock: + nl_socket_free(mngr->cm_sync_sock); errout: nl_cache_mngr_free(mngr); return err; } /** - * Add cache responsibility to cache manager + * Add cache to cache manager * @arg mngr Cache manager. - * @arg name Name of cache to keep track of + * @arg cache Cache to be added to cache manager * @arg cb Function to be called upon changes. - * @arg result Pointer to store added cache. + * @arg data Argument passed on to change callback * - * Allocates a new cache of the specified type and adds it to the manager. - * The operation will trigger a full dump request from the kernel to - * initially fill the contents of the cache. The manager will subscribe - * to the notification group of the cache to keep track of any further - * changes. + * Adds cache to the manager. The operation will trigger a full + * dump request from the kernel to initially fill the contents + * of the cache. The manager will subscribe to the notification group + * of the cache and keep track of any further changes. + * + * The user is responsible for calling nl_cache_mngr_poll() or monitor + * the socket and call nl_cache_mngr_data_ready() to allow the library + * to process netlink notification events. + * + * @see nl_cache_mngr_poll() + * @see nl_cache_mngr_data_ready() * * @return 0 on success or a negative error code. + * @return -NLE_PROTO_MISMATCH Protocol mismatch between cache manager and + * cache type + * @return -NLE_OPNOTSUPP Cache type does not support updates + * @return -NLE_EXIST Cache of this type already being managed */ -int nl_cache_mngr_add(struct nl_cache_mngr *mngr, const char *name, - change_func_t cb, void *data, struct nl_cache **result) +int nl_cache_mngr_add_cache(struct nl_cache_mngr *mngr, struct nl_cache *cache, + change_func_t cb, void *data) { struct nl_cache_ops *ops; - struct nl_cache *cache; struct nl_af_group *grp; int err, i; - ops = nl_cache_ops_lookup(name); + ops = cache->c_ops; if (!ops) - return -NLE_NOCACHE; + return -NLE_INVAL; if (ops->co_protocol != mngr->cm_protocol) return -NLE_PROTO_MISMATCH; @@ -235,30 +244,28 @@ retry: break; if (i >= mngr->cm_nassocs) { - mngr->cm_nassocs += 16; + mngr->cm_nassocs += NASSOC_EXPAND; mngr->cm_assocs = realloc(mngr->cm_assocs, mngr->cm_nassocs * sizeof(struct nl_cache_assoc)); if (mngr->cm_assocs == NULL) return -NLE_NOMEM; - else { - NL_DBG(1, "Increased capacity of cache manager %p " \ - "to %d\n", mngr, mngr->cm_nassocs); - goto retry; - } - } - cache = nl_cache_alloc(ops); - if (!cache) - return -NLE_NOMEM; + memset(mngr->cm_assocs + (mngr->cm_nassocs - NASSOC_EXPAND), 0, + NASSOC_EXPAND * sizeof(struct nl_cache_assoc)); + + NL_DBG(1, "Increased capacity of cache manager %p " \ + "to %d\n", mngr, mngr->cm_nassocs); + goto retry; + } for (grp = ops->co_groups; grp->ag_group; grp++) { - err = nl_socket_add_membership(mngr->cm_handle, grp->ag_group); + err = nl_socket_add_membership(mngr->cm_sock, grp->ag_group); if (err < 0) - goto errout_free_cache; + return err; } - err = nl_cache_refill(mngr->cm_handle, cache); + err = nl_cache_refill(mngr->cm_sync_sock, cache); if (err < 0) goto errout_drop_membership; @@ -272,12 +279,66 @@ retry: NL_DBG(1, "Added cache %p <%s> to cache manager %p\n", cache, nl_cache_name(cache), mngr); - *result = cache; return 0; errout_drop_membership: for (grp = ops->co_groups; grp->ag_group; grp++) - nl_socket_drop_membership(mngr->cm_handle, grp->ag_group); + nl_socket_drop_membership(mngr->cm_sock, grp->ag_group); + + return err; +} + +/** + * Add cache to cache manager + * @arg mngr Cache manager. + * @arg name Name of cache to keep track of + * @arg cb Function to be called upon changes. + * @arg data Argument passed on to change callback + * @arg result Pointer to store added cache (optional) + * + * Allocates a new cache of the specified type and adds it to the manager. + * The operation will trigger a full dump request from the kernel to + * initially fill the contents of the cache. The manager will subscribe + * to the notification group of the cache and keep track of any further + * changes. + * + * The user is responsible for calling nl_cache_mngr_poll() or monitor + * the socket and call nl_cache_mngr_data_ready() to allow the library + * to process netlink notification events. + * + * @see nl_cache_mngr_poll() + * @see nl_cache_mngr_data_ready() + * + * @return 0 on success or a negative error code. + * @return -NLE_NOCACHE Unknown cache type + * @return -NLE_PROTO_MISMATCH Protocol mismatch between cache manager and + * cache type + * @return -NLE_OPNOTSUPP Cache type does not support updates + * @return -NLE_EXIST Cache of this type already being managed + */ +int nl_cache_mngr_add(struct nl_cache_mngr *mngr, const char *name, + change_func_t cb, void *data, struct nl_cache **result) +{ + struct nl_cache_ops *ops; + struct nl_cache *cache; + int err; + + ops = nl_cache_ops_lookup_safe(name); + if (!ops) + return -NLE_NOCACHE; + + cache = nl_cache_alloc(ops); + nl_cache_ops_put(ops); + if (!cache) + return -NLE_NOMEM; + + err = nl_cache_mngr_add_cache(mngr, cache, cb, data); + if (err < 0) + goto errout_free_cache; + + *result = cache; + return 0; + errout_free_cache: nl_cache_free(cache); @@ -285,16 +346,17 @@ errout_free_cache: } /** - * Get file descriptor + * Get socket file descriptor * @arg mngr Cache Manager * - * Get the file descriptor of the socket associated to the manager. - * This can be used to change socket options or monitor activity - * using poll()/select(). + * Get the file descriptor of the socket associated with the manager. + * + * @note Do not use the socket for anything besides receiving + * notifications. */ int nl_cache_mngr_get_fd(struct nl_cache_mngr *mngr) { - return nl_socket_get_fd(mngr->cm_handle); + return nl_socket_get_fd(mngr->cm_sock); } /** @@ -303,20 +365,24 @@ int nl_cache_mngr_get_fd(struct nl_cache_mngr *mngr) * @arg timeout Upper limit poll() will block, in milliseconds. * * Causes poll() to be called to check for new event notifications - * being available. Automatically receives and handles available - * notifications. + * being available. Calls nl_cache_mngr_data_ready() to process + * available data. * * This functionally is ideally called regularly during an idle * period. * - * @return A positive value if at least one update was handled, 0 - * for none, or a negative error code. + * A timeout can be specified in milliseconds to limit the time the + * function will wait for updates. + * + * @see nl_cache_mngr_data_ready() + * + * @return The number of messages processed or a negative error code. */ int nl_cache_mngr_poll(struct nl_cache_mngr *mngr, int timeout) { int ret; struct pollfd fds = { - .fd = nl_socket_get_fd(mngr->cm_handle), + .fd = nl_socket_get_fd(mngr->cm_sock), .events = POLLIN, }; @@ -326,6 +392,7 @@ int nl_cache_mngr_poll(struct nl_cache_mngr *mngr, int timeout) if (ret < 0) return -nl_syserr2nlerr(errno); + /* No events, return */ if (ret == 0) return 0; @@ -337,28 +404,90 @@ int nl_cache_mngr_poll(struct nl_cache_mngr *mngr, int timeout) * @arg mngr Cache manager * * This function can be called if the socket associated to the manager - * contains updates to be received. This function should not be used - * if nl_cache_mngr_poll() is used. + * contains updates to be received. This function should only be used + * if nl_cache_mngr_poll() is not used. * - * @return A positive value if at least one update was handled, 0 - * for none, or a negative error code. + * The function will process messages until there is no more data to + * be read from the socket. + * + * @see nl_cache_mngr_poll() + * + * @return The number of messages processed or a negative error code. */ int nl_cache_mngr_data_ready(struct nl_cache_mngr *mngr) { - int err; + int err, nread = 0; + struct nl_cb *cb; - err = nl_recvmsgs_default(mngr->cm_handle); + NL_DBG(2, "Cache manager %p, reading new data from fd %d\n", + mngr, nl_socket_get_fd(mngr->cm_sock)); + + cb = nl_cb_clone(mngr->cm_sock->s_cb); + if (cb == NULL) + return -NLE_NOMEM; + + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, event_input, mngr); + + while ((err = nl_recvmsgs_report(mngr->cm_sock, cb)) > 0) { + NL_DBG(2, "Cache manager %p, recvmsgs read %d messages\n", + mngr, err); + nread += err; + } + + nl_cb_put(cb); if (err < 0) return err; - return 1; + return nread; +} + +/** + * Print information about cache manager + * @arg mngr Cache manager + * @arg p Dumping parameters + * + * Prints information about the cache manager including all managed caches. + * + * @note This is a debugging function. + */ +void nl_cache_mngr_info(struct nl_cache_mngr *mngr, struct nl_dump_params *p) +{ + char buf[128]; + int i; + + nl_dump_line(p, "cache-manager <%p>\n", mngr); + nl_dump_line(p, " .protocol = %s\n", + nl_nlfamily2str(mngr->cm_protocol, buf, sizeof(buf))); + nl_dump_line(p, " .flags = %#x\n", mngr->cm_flags); + nl_dump_line(p, " .nassocs = %u\n", mngr->cm_nassocs); + nl_dump_line(p, " .sock = <%p>\n", mngr->cm_sock); + + for (i = 0; i < mngr->cm_nassocs; i++) { + struct nl_cache_assoc *assoc = &mngr->cm_assocs[i]; + + if (assoc->ca_cache) { + nl_dump_line(p, " .cache[%d] = <%p> {\n", i, assoc->ca_cache); + nl_dump_line(p, " .name = %s\n", assoc->ca_cache->c_ops->co_name); + nl_dump_line(p, " .change_func = <%p>\n", assoc->ca_change); + nl_dump_line(p, " .change_data = <%p>\n", assoc->ca_change_data); + nl_dump_line(p, " .nitems = %u\n", nl_cache_nitems(assoc->ca_cache)); + nl_dump_line(p, " .objects = {\n"); + + p->dp_prefix += 6; + nl_cache_dump(assoc->ca_cache, p); + p->dp_prefix -= 6; + + nl_dump_line(p, " }\n"); + nl_dump_line(p, " }\n"); + } + } } /** * Free cache manager and all caches. * @arg mngr Cache manager. * - * Release all resources after usage of a cache manager. + * Release all resources held by a cache manager. */ void nl_cache_mngr_free(struct nl_cache_mngr *mngr) { @@ -367,12 +496,23 @@ void nl_cache_mngr_free(struct nl_cache_mngr *mngr) if (!mngr) return; - if (mngr->cm_handle) - nl_close(mngr->cm_handle); + if (mngr->cm_sock) + nl_close(mngr->cm_sock); - for (i = 0; i < mngr->cm_nassocs; i++) - if (mngr->cm_assocs[i].ca_cache) + if (mngr->cm_sync_sock) { + nl_close(mngr->cm_sync_sock); + nl_socket_free(mngr->cm_sync_sock); + } + + if (mngr->cm_flags & NL_ALLOCATED_SOCK) + nl_socket_free(mngr->cm_sock); + + for (i = 0; i < mngr->cm_nassocs; i++) { + if (mngr->cm_assocs[i].ca_cache) { + nl_cache_mngt_unprovide(mngr->cm_assocs[i].ca_cache); nl_cache_free(mngr->cm_assocs[i].ca_cache); + } + } free(mngr->cm_assocs); free(mngr); diff --git a/lib/cache_mngt.c b/lib/cache_mngt.c index d57d836..4d3d6ff 100644 --- a/lib/cache_mngt.c +++ b/lib/cache_mngt.c @@ -6,35 +6,39 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2008 Thomas Graf + * Copyright (c) 2003-2012 Thomas Graf */ /** * @ingroup core - * @defgroup cache_mngt Caching + * @defgroup cache_mngt Caching System + * + * Related sections in the development guide: + * - @core_doc{core_cache, Caching System} + * * @{ + * + * Header + * ------ + * ~~~~{.c} + * #include + * ~~~~ */ -#include +#include #include #include #include static struct nl_cache_ops *cache_ops; +static NL_RW_LOCK(cache_ops_lock); /** * @name Cache Operations Sets * @{ */ -/** - * Lookup the set cache operations of a certain cache type - * @arg name name of the cache type - * - * @return The cache operations or NULL if no operations - * have been registered under the specified name. - */ -struct nl_cache_ops *nl_cache_ops_lookup(const char *name) +struct nl_cache_ops *__nl_cache_ops_lookup(const char *name) { struct nl_cache_ops *ops; @@ -46,17 +50,65 @@ struct nl_cache_ops *nl_cache_ops_lookup(const char *name) } /** - * Associate a message type to a set of cache operations - * @arg protocol netlink protocol - * @arg msgtype netlink message type - * - * Associates the specified netlink message type with - * a registered set of cache operations. - * - * @return The cache operations or NULL if no association - * could be made. + * Increment reference counter + * @arg ops Cache operations */ -struct nl_cache_ops *nl_cache_ops_associate(int protocol, int msgtype) +void nl_cache_ops_get(struct nl_cache_ops *ops) +{ + ops->co_refcnt++; +} + +/** + * Decrement reference counter + * @arg ops Cache operations + */ +void nl_cache_ops_put(struct nl_cache_ops *ops) +{ + ops->co_refcnt--; +} + +/** + * Lookup cache operations by name + * @arg name name of the cache type + * + * @attention This function is not safe, it does not increment the reference + * counter. Please use nl_cache_ops_lookup_safe(). + * + * @return The cache operations or NULL if not found. + */ +struct nl_cache_ops *nl_cache_ops_lookup(const char *name) +{ + struct nl_cache_ops *ops; + + nl_read_lock(&cache_ops_lock); + ops = __nl_cache_ops_lookup(name); + nl_read_unlock(&cache_ops_lock); + + return ops; +} + +/** + * Lookup cache operations by name + * @arg name name of the cache type + * + * @note The reference counter of the returned cache operation is incremented + * and must be decremented after use with nl_cache_ops_put(). + * + * @return The cache operations or NULL if not found. + */ +struct nl_cache_ops *nl_cache_ops_lookup_safe(const char *name) +{ + struct nl_cache_ops *ops; + + nl_write_lock(&cache_ops_lock); + if ((ops = __nl_cache_ops_lookup(name))) + nl_cache_ops_get(ops); + nl_write_unlock(&cache_ops_lock); + + return ops; +} + +static struct nl_cache_ops *__cache_ops_associate(int protocol, int msgtype) { int i; struct nl_cache_ops *ops; @@ -73,6 +125,54 @@ struct nl_cache_ops *nl_cache_ops_associate(int protocol, int msgtype) return NULL; } +/** + * Associate protocol and message type to cache operations + * @arg protocol netlink protocol + * @arg msgtype netlink message type + * + * @attention This function is not safe, it does not increment the reference + * counter. Please use nl_cache_ops_associate_safe(). + * + * @see nl_cache_ops_associate_safe() + * + * @return The cache operations or NULL if no match found. + */ +struct nl_cache_ops *nl_cache_ops_associate(int protocol, int msgtype) +{ + struct nl_cache_ops *ops; + + nl_read_lock(&cache_ops_lock); + ops = __cache_ops_associate(protocol, msgtype); + nl_read_unlock(&cache_ops_lock); + + return ops; +} + +/** + * Associate protocol and message type to cache operations + * @arg protocol netlink protocol + * @arg msgtype netlink message type + * + * Searches the registered cache operations for a matching protocol + * and message type. + * + * @note The reference counter of the returned cache operation is incremented + * and must be decremented after use with nl_cache_ops_put(). + * + * @return The cache operations or NULL if no no match was found. + */ +struct nl_cache_ops *nl_cache_ops_associate_safe(int protocol, int msgtype) +{ + struct nl_cache_ops *ops; + + nl_write_lock(&cache_ops_lock); + if ((ops = __cache_ops_associate(protocol, msgtype))) + nl_cache_ops_get(ops); + nl_write_unlock(&cache_ops_lock); + + return ops; +} + /** * Lookup message type cache association * @arg ops cache operations @@ -81,6 +181,9 @@ struct nl_cache_ops *nl_cache_ops_associate(int protocol, int msgtype) * Searches for a matching message type association ing the specified * cache operations. * + * @attention The guranteed lifetime of the returned message type is bound + * to the lifetime of the underlying cache operations. + * * @return A message type association or NULL. */ struct nl_msgtype *nl_msgtype_lookup(struct nl_cache_ops *ops, int msgtype) @@ -94,6 +197,7 @@ struct nl_msgtype *nl_msgtype_lookup(struct nl_cache_ops *ops, int msgtype) return NULL; } +/* Must hold cache_ops_lock */ static struct nl_cache_ops *cache_ops_lookup_for_obj(struct nl_object_ops *obj_ops) { struct nl_cache_ops *ops; @@ -115,8 +219,25 @@ void nl_cache_ops_foreach(void (*cb)(struct nl_cache_ops *, void *), void *arg) { struct nl_cache_ops *ops; + nl_read_lock(&cache_ops_lock); for (ops = cache_ops; ops; ops = ops->co_next) cb(ops, arg); + nl_read_unlock(&cache_ops_lock); +} + +/** + * Set default flags for caches of this type + * @arg ops Cache ops + * @arg flags Flags to set + * + * The cache operation flags will be derived to all caches allocates + * based on this set of cache operations. + */ +void nl_cache_ops_set_flags(struct nl_cache_ops *ops, unsigned int flags) +{ + nl_write_lock(&cache_ops_lock); + ops->co_flags |= flags; + nl_write_unlock(&cache_ops_lock); } /** @@ -133,11 +254,16 @@ int nl_cache_mngt_register(struct nl_cache_ops *ops) if (!ops->co_name || !ops->co_obj_ops) return -NLE_INVAL; - if (nl_cache_ops_lookup(ops->co_name)) + nl_write_lock(&cache_ops_lock); + if (__nl_cache_ops_lookup(ops->co_name)) { + nl_write_unlock(&cache_ops_lock); return -NLE_EXIST; + } + ops->co_refcnt = 0; ops->co_next = cache_ops; cache_ops = ops; + nl_write_unlock(&cache_ops_lock); NL_DBG(1, "Registered cache operations %s\n", ops->co_name); @@ -158,18 +284,31 @@ int nl_cache_mngt_register(struct nl_cache_ops *ops) int nl_cache_mngt_unregister(struct nl_cache_ops *ops) { struct nl_cache_ops *t, **tp; + int err = 0; + + nl_write_lock(&cache_ops_lock); + + if (ops->co_refcnt > 0) { + err = -NLE_BUSY; + goto errout; + } for (tp = &cache_ops; (t=*tp) != NULL; tp = &t->co_next) if (t == ops) break; - if (!t) - return -NLE_NOCACHE; + if (!t) { + err = -NLE_NOCACHE; + goto errout; + } NL_DBG(1, "Unregistered cache operations %s\n", ops->co_name); *tp = t->co_next; - return 0; +errout: + nl_write_unlock(&cache_ops_lock); + + return err; } /** @} */ @@ -191,11 +330,25 @@ void nl_cache_mngt_provide(struct nl_cache *cache) { struct nl_cache_ops *ops; + nl_write_lock(&cache_ops_lock); + ops = cache_ops_lookup_for_obj(cache->c_ops->co_obj_ops); if (!ops) BUG(); - else + else { + nl_cache_get(cache); + + /* + * Hold a reference to the cache operations to ensure the + * ops don't go away while we use it to store the cache pointer. + */ + if (!ops->co_major_cache) + nl_cache_ops_get(ops); + ops->co_major_cache = cache; + } + + nl_write_unlock(&cache_ops_lock); } /** @@ -210,38 +363,75 @@ void nl_cache_mngt_unprovide(struct nl_cache *cache) { struct nl_cache_ops *ops; + nl_write_lock(&cache_ops_lock); + ops = cache_ops_lookup_for_obj(cache->c_ops->co_obj_ops); if (!ops) BUG(); - else if (ops->co_major_cache == cache) + else if (ops->co_major_cache == cache) { + nl_cache_free(ops->co_major_cache); + nl_cache_ops_put(ops); ops->co_major_cache = NULL; + } + + nl_write_unlock(&cache_ops_lock); +} + +struct nl_cache *__nl_cache_mngt_require(const char *name) +{ + struct nl_cache_ops *ops; + struct nl_cache *cache = NULL; + + ops = nl_cache_ops_lookup_safe(name); + if (ops) { + cache = ops->co_major_cache; + nl_cache_ops_put(ops); + } + + return cache; } /** - * Demand the use of a global cache - * @arg name name of the required object type + * Return cache previously provided via nl_cache_mngt_provide() + * @arg name Name of cache to lookup * - * Trys to find a cache of the specified type for global - * use. + * @attention This function is not safe, it does not increment the reference + * counter. Please use nl_cache_mngt_require_safe(). * - * @return A cache provided by another subsystem of the - * specified type marked to be available. + * @see nl_cache_mngt_require_safe() + * + * @return Pointer to cache or NULL if none registered */ struct nl_cache *nl_cache_mngt_require(const char *name) { - struct nl_cache_ops *ops; + struct nl_cache *cache; - ops = nl_cache_ops_lookup(name); - if (!ops || !ops->co_major_cache) { - fprintf(stderr, "Application BUG: Your application must " - "call nl_cache_mngt_provide() and\nprovide a valid " - "%s cache to be used for internal lookups.\nSee the " - " API documentation for more details.\n", name); - - return NULL; - } + if (!(cache = __nl_cache_mngt_require(name))) + NL_DBG(1, "Application BUG: Your application must " + "call nl_cache_mngt_provide() and\nprovide a valid " + "%s cache to be used for internal lookups.\nSee the " + " API documentation for more details.\n", name); - return ops->co_major_cache; + return cache; +} + +/** + * Return cache previously provided via nl_cache_mngt_provide() + * @arg name Name of cache to lookup + * + * @note The reference counter of the returned cache is incremented + * and must be decremented after use with nl_cache_put(). + * + * @return Pointer to cache or NULL if none registered + */ +struct nl_cache *nl_cache_mngt_require_safe(const char *name) +{ + struct nl_cache *cache; + + if ((cache = nl_cache_mngt_require(name))) + nl_cache_get(cache); + + return cache; } /** @} */ diff --git a/lib/cli/qdisc/plug.c b/lib/cli/qdisc/plug.c new file mode 100644 index 0000000..2b8d5d6 --- /dev/null +++ b/lib/cli/qdisc/plug.c @@ -0,0 +1,113 @@ + +/* + * src/lib/cli/qdisc/plug.c plug module for CLI lib + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2012 Shriram Rajagopalan + */ + +#include +#include +#include + +static void print_usage(void) +{ + printf( +"Usage: nl-qdisc-add [...] plug [OPTIONS]...\n" +"\n" +"OPTIONS\n" +" --help Show this help text.\n" +" --limit Maximum queue length in bytes.\n" +" --buffer create a new buffer(plug) and queue incoming traffic into it.\n" +" --release-one release traffic from previous buffer.\n" +" --release-indefinite stop buffering and release all (buffered and new) packets.\n" +"\n" +"EXAMPLE" +" # Attach plug qdisc with 32KB queue size to ifb0\n" +" nl-qdisc-add --dev=ifb0 --parent=root plug --limit=32768\n" +" # Plug network traffic arriving at ifb0\n" +" nl-qdisc-add --dev=ifb0 --parent=root --update plug --buffer\n" +" # Unplug traffic arriving at ifb0 indefinitely\n" +" nl-qdisc-add --dev=ifb0 --parent=root --update plug --release-indefinite\n\n" +" # If operating in output buffering mode:\n" +" # at time t=t0, create a new output buffer b0 to hold network output\n" +" nl-qdisc-add --dev=ifb0 --parent=root --update plug --buffer\n\n" +" # at time t=t1, take a checkpoint c0, create a new output buffer b1\n" +" nl-qdisc-add --dev=ifb0 --parent=root --update plug --buffer\n" +" # at time t=t1+r, after c0 is committed, release b0\n" +" nl-qdisc-add --dev=ifb0 --parent=root --update plug --release-one\n\n" +" # at time t=t2, take a checkpoint c1, create a new output buffer b2\n" +" nl-qdisc-add --dev=ifb0 --parent=root --update plug --buffer\n" +" # at time t=t2+r, after c1 is committed, release b1\n" +" nl-qdisc-add --dev=ifb0 --parent=root --update plug --release-one\n"); +} + +static void plug_parse_argv(struct rtnl_tc *tc, int argc, char **argv) +{ + struct rtnl_qdisc *qdisc = (struct rtnl_qdisc *) tc; + + for (;;) { + int c, optidx = 0; + enum { + ARG_LIMIT = 257, + ARG_BUFFER = 258, + ARG_RELEASE_ONE = 259, + ARG_RELEASE_INDEFINITE = 260, + }; + static struct option long_opts[] = { + { "help", 0, 0, 'h' }, + { "limit", 1, 0, ARG_LIMIT }, + { "buffer", 0, 0, ARG_BUFFER }, + { "release-one", 0, 0, ARG_RELEASE_ONE }, + { "release-indefinite", 0, 0, ARG_RELEASE_INDEFINITE }, + { 0, 0, 0, 0 } + }; + + c = getopt_long(argc, argv, "h", long_opts, &optidx); + if (c == -1) + break; + + switch (c) { + case 'h': + print_usage(); + return; + + case ARG_LIMIT: + rtnl_qdisc_plug_set_limit(qdisc, nl_cli_parse_u32(optarg)); + break; + + case ARG_BUFFER: + rtnl_qdisc_plug_buffer(qdisc); + break; + + case ARG_RELEASE_ONE: + rtnl_qdisc_plug_release_one(qdisc); + break; + + case ARG_RELEASE_INDEFINITE: + rtnl_qdisc_plug_release_indefinite(qdisc); + break; + } + } +} + +static struct nl_cli_tc_module plug_module = +{ + .tm_name = "plug", + .tm_type = RTNL_TC_TYPE_QDISC, + .tm_parse_argv = plug_parse_argv, +}; + +static void __init plug_init(void) +{ + nl_cli_tc_register(&plug_module); +} + +static void __exit plug_exit(void) +{ + nl_cli_tc_unregister(&plug_module); +} diff --git a/lib/data.c b/lib/data.c index 03cd9fe..1a3a3fb 100644 --- a/lib/data.c +++ b/lib/data.c @@ -6,16 +6,28 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2008 Thomas Graf + * Copyright (c) 2003-2012 Thomas Graf */ /** - * @ingroup core + * @ingroup core_types * @defgroup data Abstract Data + * + * Abstract data type representing a binary data blob. + * + * Related sections in the development guide: + * - @core_doc{_abstract_data, Abstract Data} + * * @{ + * + * Header + * ------ + * ~~~~{.c} + * #include + * ~~~~ */ -#include +#include #include #include #include @@ -98,9 +110,6 @@ struct nl_data *nl_data_clone(struct nl_data *src) */ int nl_data_append(struct nl_data *data, void *buf, size_t size) { - if (size < 0) - BUG(); - if (size > 0) { data->d_data = realloc(data->d_data, data->d_size + size); if (!data->d_data) diff --git a/lib/dect/ari.c b/lib/dect/ari.c index 44fbe80..d17d4f8 100644 --- a/lib/dect/ari.c +++ b/lib/dect/ari.c @@ -1,4 +1,4 @@ -#include +#include #include #include #include diff --git a/lib/dect/cell.c b/lib/dect/cell.c index 8d8cec9..f7081d9 100644 --- a/lib/dect/cell.c +++ b/lib/dect/cell.c @@ -16,7 +16,7 @@ * @{ */ -#include +#include #include #include #include diff --git a/lib/dect/cell_obj.c b/lib/dect/cell_obj.c index b369602..603fef0 100644 --- a/lib/dect/cell_obj.c +++ b/lib/dect/cell_obj.c @@ -9,7 +9,7 @@ * Copyright (c) 2009 Patrick McHardy */ -#include +#include #include #include #include diff --git a/lib/dect/cluster.c b/lib/dect/cluster.c index d5d19d0..29ac522 100644 --- a/lib/dect/cluster.c +++ b/lib/dect/cluster.c @@ -16,7 +16,7 @@ * @{ */ -#include +#include #include #include #include diff --git a/lib/dect/cluster_obj.c b/lib/dect/cluster_obj.c index 492192b..043c677 100644 --- a/lib/dect/cluster_obj.c +++ b/lib/dect/cluster_obj.c @@ -9,7 +9,7 @@ * Copyright (c) 2009 Patrick McHardy */ -#include +#include #include #include #include diff --git a/lib/dect/dect.c b/lib/dect/dect.c index 40382cd..c7fbc18 100644 --- a/lib/dect/dect.c +++ b/lib/dect/dect.c @@ -9,7 +9,7 @@ * Copyright (c) 2009 Patrick McHardy */ -#include +#include #include #include #include diff --git a/lib/dect/llme.c b/lib/dect/llme.c index 572d59e..baed29e 100644 --- a/lib/dect/llme.c +++ b/lib/dect/llme.c @@ -16,7 +16,7 @@ * @{ */ -#include +#include #include #include #include diff --git a/lib/dect/transceiver.c b/lib/dect/transceiver.c index 34c666b..330fbd4 100644 --- a/lib/dect/transceiver.c +++ b/lib/dect/transceiver.c @@ -16,7 +16,7 @@ * @{ */ -#include +#include #include #include #include diff --git a/lib/dect/transceiver_obj.c b/lib/dect/transceiver_obj.c index 1a6018a..1638471 100644 --- a/lib/dect/transceiver_obj.c +++ b/lib/dect/transceiver_obj.c @@ -9,7 +9,7 @@ * Copyright (c) 2009 Patrick McHardy */ -#include +#include #include #include #include diff --git a/lib/error.c b/lib/error.c index 4c4e7d4..f30b9a5 100644 --- a/lib/error.c +++ b/lib/error.c @@ -9,7 +9,7 @@ * Copyright (c) 2008 Thomas Graf */ -#include +#include #include static const char *errmsg[NLE_MAX+1] = { @@ -44,6 +44,9 @@ static const char *errmsg[NLE_MAX+1] = { [NLE_PERM] = "Operation not permitted", [NLE_PKTLOC_FILE] = "Unable to open packet location file", [NLE_PARSE_ERR] = "Unable to parse object", +[NLE_NODEV] = "No such device", +[NLE_IMMUTABLE] = "Immutable attribute", +[NLE_DUMP_INTR] = "Dump inconsistency detected, interrupted", }; /** @@ -87,7 +90,7 @@ int nl_syserr2nlerr(int error) case EADDRINUSE: return NLE_EXIST; case EEXIST: return NLE_EXIST; case EADDRNOTAVAIL: return NLE_NOADDR; - case ENODEV: + case ESRCH: /* fall through */ case ENOENT: return NLE_OBJ_NOTFOUND; case EINTR: return NLE_INTR; case EAGAIN: return NLE_AGAIN; @@ -104,6 +107,7 @@ int nl_syserr2nlerr(int error) case EPERM: return NLE_PERM; case EBUSY: return NLE_BUSY; case ERANGE: return NLE_RANGE; + case ENODEV: return NLE_NODEV; default: return NLE_FAILURE; } } diff --git a/lib/fib_lookup/lookup.c b/lib/fib_lookup/lookup.c index ce9c027..3d07317 100644 --- a/lib/fib_lookup/lookup.c +++ b/lib/fib_lookup/lookup.c @@ -6,16 +6,17 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2008 Thomas Graf + * Copyright (c) 2003-2012 Thomas Graf */ /** + * @ingroup rtnl * @defgroup fib_lookup FIB Lookup * @brief * @{ */ -#include +#include #include #include #include @@ -123,7 +124,7 @@ errout: static void result_dump_line(struct nl_object *obj, struct nl_dump_params *p) { struct flnl_result *res = (struct flnl_result *) obj; - char buf[128]; + char buf[256]; nl_dump_line(p, "table %s prefixlen %u next-hop-selector %u\n", rtnl_route_table2str(res->fr_table_id, buf, sizeof(buf)), @@ -132,7 +133,7 @@ static void result_dump_line(struct nl_object *obj, struct nl_dump_params *p) nl_rtntype2str(res->fr_type, buf, sizeof(buf))); nl_dump(p, "scope %s error %s (%d)\n", rtnl_scope2str(res->fr_scope, buf, sizeof(buf)), - strerror(-res->fr_error), res->fr_error); + strerror_r(-res->fr_error, buf, sizeof(buf)), res->fr_error); } static void result_dump_details(struct nl_object *obj, struct nl_dump_params *p) @@ -192,6 +193,7 @@ struct nl_cache *flnl_result_alloc_cache(void) * Builds a netlink request message to do a lookup * @arg req Requested match. * @arg flags additional netlink message flags + * @arg result Result pointer * * Builds a new netlink message requesting a change of link attributes. * The netlink message header isn't fully equipped with all relevant @@ -201,9 +203,7 @@ struct nl_cache *flnl_result_alloc_cache(void) * and \a tmpl must contain the attributes to be changed set via * \c rtnl_link_set_* functions. * - * @return New netlink message - * @note Not all attributes can be changed, see - * \ref link_changeable "Changeable Attributes" for more details. + * @return 0 on success or a negative error code. */ int flnl_lookup_build_request(struct flnl_request *req, int flags, struct nl_msg **result) diff --git a/lib/fib_lookup/request.c b/lib/fib_lookup/request.c index ffcf8f5..1b021b6 100644 --- a/lib/fib_lookup/request.c +++ b/lib/fib_lookup/request.c @@ -16,7 +16,7 @@ * @{ */ -#include +#include #include #include #include diff --git a/lib/genl/ctrl.c b/lib/genl/ctrl.c index 3871795..c6db742 100644 --- a/lib/genl/ctrl.c +++ b/lib/genl/ctrl.c @@ -6,18 +6,22 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2008 Thomas Graf + * Copyright (c) 2003-2012 Thomas Graf */ /** - * @ingroup genl_mngt - * @defgroup ctrl Controller - * @brief + * @ingroup genl + * @defgroup genl_ctrl Controller (Resolver) * + * Resolves Generic Netlink family names to numeric identifiers. + * + * The controller is a component in the kernel that resolves Generic Netlink + * family names to their numeric identifiers. This module provides functions + * to query the controller to access the resolving functionality. * @{ */ -#include +#include #include #include #include @@ -29,7 +33,6 @@ #define CTRL_VERSION 0x0001 static struct nl_cache_ops genl_ctrl_ops; -/** @endcond */ static int ctrl_request_update(struct nl_cache *c, struct nl_sock *h) { @@ -58,6 +61,47 @@ static struct nla_policy family_grp_policy[CTRL_ATTR_MCAST_GRP_MAX+1] = { [CTRL_ATTR_MCAST_GRP_ID] = { .type = NLA_U32 }, }; +static int parse_mcast_grps(struct genl_family *family, struct nlattr *grp_attr) +{ + struct nlattr *nla; + int remaining, err; + + if (!grp_attr) + BUG(); + + nla_for_each_nested(nla, grp_attr, remaining) { + struct nlattr *tb[CTRL_ATTR_MCAST_GRP_MAX+1]; + int id; + const char * name; + + err = nla_parse_nested(tb, CTRL_ATTR_MCAST_GRP_MAX, nla, + family_grp_policy); + if (err < 0) + goto errout; + + if (tb[CTRL_ATTR_MCAST_GRP_ID] == NULL) { + err = -NLE_MISSING_ATTR; + goto errout; + } + id = nla_get_u32(tb[CTRL_ATTR_MCAST_GRP_ID]); + + if (tb[CTRL_ATTR_MCAST_GRP_NAME] == NULL) { + err = -NLE_MISSING_ATTR; + goto errout; + } + name = nla_get_string(tb[CTRL_ATTR_MCAST_GRP_NAME]); + + err = genl_family_add_grp(family, id, name); + if (err < 0) + goto errout; + } + + err = 0; + +errout: + return err; +} + static int ctrl_msg_parser(struct nl_cache_ops *ops, struct genl_cmd *cmd, struct genl_info *info, void *arg) { @@ -134,37 +178,9 @@ static int ctrl_msg_parser(struct nl_cache_ops *ops, struct genl_cmd *cmd, } if (info->attrs[CTRL_ATTR_MCAST_GROUPS]) { - struct nlattr *nla, *nla_grps; - int remaining; - - nla_grps = info->attrs[CTRL_ATTR_MCAST_GROUPS]; - nla_for_each_nested(nla, nla_grps, remaining) { - struct nlattr *tb[CTRL_ATTR_MCAST_GRP_MAX+1]; - int id; - const char * name; - - err = nla_parse_nested(tb, CTRL_ATTR_MCAST_GRP_MAX, nla, - family_grp_policy); - if (err < 0) - goto errout; - - if (tb[CTRL_ATTR_MCAST_GRP_ID] == NULL) { - err = -NLE_MISSING_ATTR; - goto errout; - } - id = nla_get_u32(tb[CTRL_ATTR_MCAST_GRP_ID]); - - if (tb[CTRL_ATTR_MCAST_GRP_NAME] == NULL) { - err = -NLE_MISSING_ATTR; - goto errout; - } - name = nla_get_string(tb[CTRL_ATTR_MCAST_GRP_NAME]); - - err = genl_family_add_grp(family, id, name); - if (err < 0) - goto errout; - } - + err = parse_mcast_grps(family, info->attrs[CTRL_ATTR_MCAST_GROUPS]); + if (err < 0) + goto errout; } err = pp->pp_cb((struct nl_object *) family, pp); @@ -174,27 +190,162 @@ errout: } /** - * @name Cache Management - * @{ + * process responses from from the query sent by genl_ctrl_probe_by_name + * @arg nl_msg Returned message. + * @arg name genl_family structure to fill out. + * + * Process returned messages, filling out the missing informatino in the + * genl_family structure + * + * @return Indicator to keep processing frames or not + * */ - -int genl_ctrl_alloc_cache(struct nl_sock *sock, struct nl_cache **result) +static int probe_response(struct nl_msg *msg, void *arg) { - return nl_cache_alloc_and_fill(&genl_ctrl_ops, sock, result); + struct nlattr *tb[CTRL_ATTR_MAX+1]; + struct nlmsghdr *nlh = nlmsg_hdr(msg); + struct genl_family *ret = (struct genl_family *)arg; + + if (genlmsg_parse(nlh, 0, tb, CTRL_ATTR_MAX, ctrl_policy)) + return NL_SKIP; + + if (tb[CTRL_ATTR_FAMILY_ID]) + genl_family_set_id(ret, nla_get_u16(tb[CTRL_ATTR_FAMILY_ID])); + + if (tb[CTRL_ATTR_MCAST_GROUPS]) + if (parse_mcast_grps(ret, tb[CTRL_ATTR_MCAST_GROUPS]) < 0) + return NL_SKIP; + + return NL_STOP; } /** - * Look up generic netlink family by id in the provided cache. - * @arg cache Generic netlink family cache. - * @arg id Family identifier. + * Look up generic netlink family by family name querying the kernel directly + * @arg sk Socket. + * @arg name Family name. * - * Searches through the cache looking for a registered family - * matching the specified identifier. The caller will own a - * reference on the returned object which needs to be given - * back after usage using genl_family_put(). + * Directly query's the kernel for a given family name. The caller will own a + * reference on the returned object which needsd to be given back after usage + * using genl_family_put. + * + * Note: This API call differs from genl_ctrl_search_by_name in that it querys + * the kernel directly, alowing for module autoload to take place to resolve the + * family request. Using an nl_cache prevents that operation * * @return Generic netlink family object or NULL if no match was found. */ +static struct genl_family *genl_ctrl_probe_by_name(struct nl_sock *sk, + const char *name) +{ + struct nl_msg *msg; + struct genl_family *ret; + struct nl_cb *cb; + int rc; + + ret = genl_family_alloc(); + if (!ret) + goto out; + + genl_family_set_name(ret, name); + + msg = nlmsg_alloc(); + if (!msg) + goto out_fam_free; + + if (!(cb = nl_cb_clone(nl_socket_get_cb(sk)))) + goto out_msg_free; + + if (!genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, GENL_ID_CTRL, + 0, 0, CTRL_CMD_GETFAMILY, 1)) { + BUG(); + goto out_cb_free; + } + + if (nla_put_string(msg, CTRL_ATTR_FAMILY_NAME, name) < 0) + goto out_cb_free; + + rc = nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, probe_response, + (void *) ret); + if (rc < 0) + goto out_cb_free; + + rc = nl_send_auto_complete(sk, msg); + if (rc < 0) + goto out_cb_free; + + rc = nl_recvmsgs(sk, cb); + if (rc < 0) + goto out_cb_free; + + /* If search was successful, request may be ACKed after data */ + rc = wait_for_ack(sk); + if (rc < 0) + goto out_cb_free; + + if (genl_family_get_id(ret) != 0) { + nlmsg_free(msg); + nl_cb_put(cb); + return ret; + } + +out_cb_free: + nl_cb_put(cb); +out_msg_free: + nlmsg_free(msg); +out_fam_free: + genl_family_put(ret); + ret = NULL; +out: + return ret; +} + + +/** @endcond */ + +/** + * @name Controller Cache + * + * The controller cache allows to keep a local copy of the list of all + * kernel side registered Generic Netlink families to quickly resolve + * multiple Generic Netlink family names without requiring to communicate + * with the kernel for each resolving iteration. + * + * @{ + */ + +/** + * Allocate a new controller cache + * @arg sk Generic Netlink socket + * @arg result Pointer to store resulting cache + * + * Allocates a new cache mirroring the state of the controller and stores it + * in \c *result. The allocated cache will contain a list of all currently + * registered kernel side Generic Netlink families. The cache is meant to be + * used to resolve family names locally. + * + * @return 0 on success or a negative error code. + */ +int genl_ctrl_alloc_cache(struct nl_sock *sk, struct nl_cache **result) +{ + return nl_cache_alloc_and_fill(&genl_ctrl_ops, sk, result); +} + +/** + * Search controller cache for a numeric address match + * @arg cache Controller cache + * @arg id Numeric family identifier. + * + * Searches a previously allocated controller cache and looks for an entry + * that matches the specified numeric family identifier \c id. If a match + * is found successfully, the reference count of the matching object is + * increased by one before the objet is returned. + * + * @see genl_ctrl_alloc_cache() + * @see genl_ctrl_search_by_name() + * @see genl_family_put() + * + * @return Generic Netlink family object or NULL if no match was found. + */ struct genl_family *genl_ctrl_search(struct nl_cache *cache, int id) { struct genl_family *fam; @@ -213,24 +364,23 @@ struct genl_family *genl_ctrl_search(struct nl_cache *cache, int id) } /** - * @name Resolver - * @{ - */ - -/** - * Look up generic netlink family by family name in the provided cache. - * @arg cache Generic netlink family cache. - * @arg name Family name. + * Search controller cache for a family name match + * @arg cache Controller cache + * @arg name Name of Generic Netlink family * - * Searches through the cache looking for a registered family - * matching the specified name. The caller will own a reference - * on the returned object which needs to be given back after - * usage using genl_family_put(). + * Searches a previously allocated controller cache and looks for an entry + * that matches the specified family \c name. If a match is found successfully, + * the reference count of the matching object is increased by one before the + * objet is returned. * - * @return Generic netlink family object or NULL if no match was found. + * @see genl_ctrl_alloc_cache() + * @see genl_ctrl_search() + * @see genl_family_put() + * + * @return Generic Netlink family object or NULL if no match was found. */ struct genl_family *genl_ctrl_search_by_name(struct nl_cache *cache, - const char *name) + const char *name) { struct genl_family *fam; @@ -250,25 +400,33 @@ struct genl_family *genl_ctrl_search_by_name(struct nl_cache *cache, /** @} */ /** - * Resolve generic netlink family name to its identifier - * @arg sk Netlink socket. - * @arg name Name of generic netlink family + * @name Direct Resolvers * - * Resolves the generic netlink family name to its identifer and returns - * it. + * These functions communicate directly with the kernel and do not require + * a cache to be kept up to date. * - * @return A positive identifier or a negative error code. + * @{ + */ + +/** + * Resolve Generic Netlink family name to numeric identifier + * @arg sk Generic Netlink socket. + * @arg name Name of Generic Netlink family + * + * Resolves the Generic Netlink family name to the corresponding numeric + * family identifier. This function queries the kernel directly, use + * genl_ctrl_search_by_name() if you need to resolve multiple names. + * + * @see genl_ctrl_search_by_name() + * + * @return The numeric family identifier or a negative error code. */ int genl_ctrl_resolve(struct nl_sock *sk, const char *name) { - struct nl_cache *cache; struct genl_family *family; int err; - if ((err = genl_ctrl_alloc_cache(sk, &cache)) < 0) - return err; - - family = genl_ctrl_search_by_name(cache, name); + family = genl_ctrl_probe_by_name(sk, name); if (family == NULL) { err = -NLE_OBJ_NOTFOUND; goto errout; @@ -277,13 +435,11 @@ int genl_ctrl_resolve(struct nl_sock *sk, const char *name) err = genl_family_get_id(family); genl_family_put(family); errout: - nl_cache_free(cache); - return err; } static int genl_ctrl_grp_by_name(const struct genl_family *family, - const char *grp_name) + const char *grp_name) { struct genl_family_grp *grp; @@ -293,20 +449,28 @@ static int genl_ctrl_grp_by_name(const struct genl_family *family, } } - return 0; + return -NLE_OBJ_NOTFOUND; } +/** + * Resolve Generic Netlink family group name + * @arg sk Generic Netlink socket + * @arg family_name Name of Generic Netlink family + * @arg grp_name Name of group to resolve + * + * Looks up the family object and resolves the group name to the numeric + * group identifier. + * + * @return Numeric group identifier or a negative error code. + */ int genl_ctrl_resolve_grp(struct nl_sock *sk, const char *family_name, - const char *grp_name) + const char *grp_name) { - struct nl_cache *cache; + struct genl_family *family; int err; - if ((err = genl_ctrl_alloc_cache(sk, &cache)) < 0) - return err; - - family = genl_ctrl_search_by_name(cache, family_name); + family = genl_ctrl_probe_by_name(sk, family_name); if (family == NULL) { err = -NLE_OBJ_NOTFOUND; goto errout; @@ -315,13 +479,12 @@ int genl_ctrl_resolve_grp(struct nl_sock *sk, const char *family_name, err = genl_ctrl_grp_by_name(family, grp_name); genl_family_put(family); errout: - nl_cache_free(cache); - return err; } /** @} */ +/** @cond SKIP */ static struct genl_cmd genl_cmds[] = { { .c_id = CTRL_CMD_NEWFAMILY, @@ -353,9 +516,7 @@ static struct genl_ops genl_ops = { .o_ncmds = ARRAY_SIZE(genl_cmds), }; -/** @cond SKIP */ extern struct nl_object_ops genl_family_ops; -/** @endcond */ static struct nl_cache_ops genl_ctrl_ops = { .co_name = "genl/family", @@ -376,5 +537,6 @@ static void __exit ctrl_exit(void) { genl_unregister(&genl_ctrl_ops); } +/** @endcond */ /** @} */ diff --git a/lib/genl/family.c b/lib/genl/family.c index ebeebcb..897c809 100644 --- a/lib/genl/family.c +++ b/lib/genl/family.c @@ -6,18 +6,19 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2006 Thomas Graf + * Copyright (c) 2003-2012 Thomas Graf */ /** - * @ingroup genl - * @defgroup genl_family Generic Netlink Family - * @brief + * @ingroup genl_ctrl + * @defgroup genl_family Generic Netlink Family Object + * + * Object representing a kernel side registered Generic Netlink family * * @{ */ -#include +#include #include #include #include @@ -32,7 +33,6 @@ #define FAMILY_ATTR_OPS 0x20 struct nl_object_ops genl_family_ops; -/** @endcond */ static void family_constructor(struct nl_object *c) { @@ -96,10 +96,10 @@ static void family_dump_line(struct nl_object *obj, struct nl_dump_params *p) } static const struct trans_tbl ops_flags[] = { - __ADD(GENL_ADMIN_PERM, admin-perm) - __ADD(GENL_CMD_CAP_DO, has-doit) - __ADD(GENL_CMD_CAP_DUMP, has-dump) - __ADD(GENL_CMD_CAP_HASPOL, has-policy) + __ADD(GENL_ADMIN_PERM, admin_perm) + __ADD(GENL_CMD_CAP_DO, has_doit) + __ADD(GENL_CMD_CAP_DUMP, has_dump) + __ADD(GENL_CMD_CAP_HASPOL, has_policy) }; static char *ops_flags2str(int flags, char *buf, size_t len) @@ -166,18 +166,32 @@ static int family_compare(struct nl_object *_a, struct nl_object *_b, return diff; } - +/** @endcond */ /** - * @name Family Object + * @name Object Allocation * @{ */ +/** + * Allocate new Generic Netlink family object + * + * @return Newly allocated Generic Netlink family object or NULL. + */ struct genl_family *genl_family_alloc(void) { return (struct genl_family *) nl_object_alloc(&genl_family_ops); } +/** + * Release reference on Generic Netlink family object + * @arg family Generic Netlink family object + * + * Reduces the reference counter of a Generic Netlink family object by one. + * The object is freed after the last user has returned its reference. + * + * @see nl_object_put() + */ void genl_family_put(struct genl_family *family) { nl_object_put((struct nl_object *) family); @@ -186,10 +200,16 @@ void genl_family_put(struct genl_family *family) /** @} */ /** - * @name Attributes + * @name Numeric Identifier * @{ */ +/** + * Return numeric identifier + * @arg family Generic Netlink family object + * + * @return Numeric identifier or 0 if not available. + */ unsigned int genl_family_get_id(struct genl_family *family) { if (family->ce_mask & FAMILY_ATTR_ID) @@ -198,12 +218,30 @@ unsigned int genl_family_get_id(struct genl_family *family) return GENL_ID_GENERATE; } +/** + * Set the numeric identifier + * @arg family Generic Netlink family object + * @arg id New numeric identifier + */ void genl_family_set_id(struct genl_family *family, unsigned int id) { family->gf_id = id; family->ce_mask |= FAMILY_ATTR_ID; } +/** @} */ + +/** + * @name Human Readable Name + * @{ + */ + +/** + * Return human readable name + * @arg family Generic Netlink family object + * + * @return Name of family or NULL if not available + */ char *genl_family_get_name(struct genl_family *family) { if (family->ce_mask & FAMILY_ATTR_NAME) @@ -212,12 +250,28 @@ char *genl_family_get_name(struct genl_family *family) return NULL; } +/** + * Set human readable name + * @arg family Generic Netlink family object + * @arg name New human readable name + */ void genl_family_set_name(struct genl_family *family, const char *name) { strncpy(family->gf_name, name, GENL_NAMSIZ-1); family->ce_mask |= FAMILY_ATTR_NAME; } +/** + * @name Interface Version + * @{ + */ + +/** + * Return interface version + * @arg family Generic Netlink family object + * + * @return Interface version or 0 if not available. + */ uint8_t genl_family_get_version(struct genl_family *family) { if (family->ce_mask & FAMILY_ATTR_VERSION) @@ -226,12 +280,30 @@ uint8_t genl_family_get_version(struct genl_family *family) return 0; } +/** + * Set interface version + * @arg family Generic Netlink family object + * @arg version New interface version + */ void genl_family_set_version(struct genl_family *family, uint8_t version) { family->gf_version = version; family->ce_mask |= FAMILY_ATTR_VERSION; } +/** @} */ + +/** + * @name Header Size + * @{ + */ + +/** + * Return user header size expected by kernel component + * @arg family Generic Netlink family object + * + * @return Expected header length or 0 if not available. + */ uint32_t genl_family_get_hdrsize(struct genl_family *family) { if (family->ce_mask & FAMILY_ATTR_HDRSIZE) @@ -246,6 +318,13 @@ void genl_family_set_hdrsize(struct genl_family *family, uint32_t hdrsize) family->ce_mask |= FAMILY_ATTR_HDRSIZE; } +/** @} */ + +/** + * @name Maximum Expected Attribute + * @{ + */ + uint32_t genl_family_get_maxattr(struct genl_family *family) { if (family->ce_mask & FAMILY_ATTR_MAXATTR) @@ -260,6 +339,13 @@ void genl_family_set_maxattr(struct genl_family *family, uint32_t maxattr) family->ce_mask |= FAMILY_ATTR_MAXATTR; } +/** @} */ + +/** + * @name Operations + * @{ + */ + int genl_family_add_op(struct genl_family *family, int id, int flags) { struct genl_family_op *op; diff --git a/lib/genl/genl.c b/lib/genl/genl.c index 055be91..0c9b11e 100644 --- a/lib/genl/genl.c +++ b/lib/genl/genl.c @@ -6,99 +6,42 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2008 Thomas Graf + * Copyright (c) 2003-2012 Thomas Graf */ /** - * @defgroup genl Generic Netlink + * @defgroup genl Generic Netlink Library (libnl-genl) * - * @par Message Format - * @code - * <------- NLMSG_ALIGN(hlen) ------> <---- NLMSG_ALIGN(len) ---> - * +----------------------------+- - -+- - - - - - - - - - -+- - -+ - * | Header | Pad | Payload | Pad | - * | struct nlmsghdr | | | | - * +----------------------------+- - -+- - - - - - - - - - -+- - -+ - * @endcode - * @code - * <-------- GENL_HDRLEN -------> <--- hdrlen --> - * <------- genlmsg_len(ghdr) ------> - * +------------------------+- - -+---------------+- - -+------------+ - * | Generic Netlink Header | Pad | Family Header | Pad | Attributes | - * | struct genlmsghdr | | | | | - * +------------------------+- - -+---------------+- - -+------------+ - * genlmsg_data(ghdr)--------------^ ^ - * genlmsg_attrdata(ghdr, hdrlen)------------------------- - * @endcode - * - * @par Example - * @code - * #include - * #include - * #include - * - * struct nl_sock *sock; - * struct nl_msg *msg; - * int family; - * - * // Allocate a new netlink socket - * sock = nl_socket_alloc(); - * - * // Connect to generic netlink socket on kernel side - * genl_connect(sock); - * - * // Ask kernel to resolve family name to family id - * family = genl_ctrl_resolve(sock, "generic_netlink_family_name"); - * - * // Construct a generic netlink by allocating a new message, fill in - * // the header and append a simple integer attribute. - * msg = nlmsg_alloc(); - * genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, family, 0, NLM_F_ECHO, - * CMD_FOO_GET, FOO_VERSION); - * nla_put_u32(msg, ATTR_FOO, 123); - * - * // Send message over netlink socket - * nl_send_auto_complete(sock, msg); - * - * // Free message - * nlmsg_free(msg); - * - * // Prepare socket to receive the answer by specifying the callback - * // function to be called for valid messages. - * nl_socket_modify_cb(sock, NL_CB_VALID, NL_CB_CUSTOM, parse_cb, NULL); - * - * // Wait for the answer and receive it - * nl_recvmsgs_default(sock); - * - * static int parse_cb(struct nl_msg *msg, void *arg) - * { - * struct nlmsghdr *nlh = nlmsg_hdr(msg); - * struct nlattr *attrs[ATTR_MAX+1]; - * - * // Validate message and parse attributes - * genlmsg_parse(nlh, 0, attrs, ATTR_MAX, policy); - * - * if (attrs[ATTR_FOO]) { - * uint32_t value = nla_get_u32(attrs[ATTR_FOO]); - * ... - * } - * - * return 0; - * } - * @endcode * @{ */ -#include +#include #include #include #include /** - * @name Socket Creating + * @name Generic Netlink Socket * @{ */ +/** + * Connect a Generic Netlink socket + * @arg sk Unconnected Netlink socket + * + * This function expects a struct nl_socket object previously allocated via + * nl_socket_alloc(). It calls nl_connect() to create the local socket file + * descriptor and binds the socket to the \c NETLINK_GENERIC Netlink protocol. + * + * Using this function is equivalent to: + * @code + * nl_connect(sk, NETLINK_GENERIC); + * @endcode + * + * @see nl_connect() + * + * @return 0 on success or a negative error code. + */ int genl_connect(struct nl_sock *sk) { return nl_connect(sk, NETLINK_GENERIC); @@ -107,20 +50,34 @@ int genl_connect(struct nl_sock *sk) /** @} */ /** - * @name Sending + * @name Sending Data * @{ */ /** - * Send trivial generic netlink message - * @arg sk Netlink socket. - * @arg family Generic netlink family - * @arg cmd Command - * @arg version Version - * @arg flags Additional netlink message flags. + * Send a Generic Netlink message consisting only of a header + * @arg sk Generic Netlink socket + * @arg family Numeric family identifier + * @arg cmd Numeric command identifier + * @arg version Interface version + * @arg flags Additional Netlink message flags (optional) * - * Fills out a routing netlink request message and sends it out - * using nl_send_simple(). + * This function is a shortcut for sending a Generic Netlink message without + * any message payload. The message will only consist of the Netlink and + * Generic Netlink headers. The header is constructed based on the specified + * parameters and passed on to nl_send_simple() to send it on the specified + * socket. + * + * @par Example: + * @code + * #include + * #include + * + * err = genl_send_simple(sk, GENL_ID_CTRL, CTRL_CMD_GETFAMILY, CTRL_VERSION, + * NLM_F_DUMP); + * @endcode + * + * @see nl_send_simple() * * @return 0 on success or a negative error code. */ @@ -137,12 +94,26 @@ int genl_send_simple(struct nl_sock *sk, int family, int cmd, /** @} */ - /** * @name Message Parsing * @{ */ +/** + * Validate Generic Netlink message headers + * @arg nlh Pointer to Netlink message header + * @arg hdrlen Length of user header + * + * Verifies the integrity of the Netlink and Generic Netlink headers by + * enforcing the following requirements: + * - Valid Netlink message header (nlmsg_valid_hdr()) + * - Presence of a complete Generic Netlink header + * - At least \c hdrlen bytes of payload included after the generic + * netlink header. + * + * @return A positive integer (true) if the headers are valid or + * 0 (false) if not. + */ int genlmsg_valid_hdr(struct nlmsghdr *nlh, int hdrlen) { struct genlmsghdr *ghdr; @@ -157,8 +128,28 @@ int genlmsg_valid_hdr(struct nlmsghdr *nlh, int hdrlen) return 1; } +/** + * Validate Generic Netlink message including attributes + * @arg nlh Pointer to Netlink message header + * @arg hdrlen Length of user header + * @arg maxtype Maximum attribtue id expected + * @arg policy Attribute validation policy + * + * Verifies the validity of the Netlink and Generic Netlink headers using + * genlmsg_valid_hdr() and calls nla_validate() on the message payload to + * verify the integrity of eventual attributes. + * + * @note You may call genlmsg_parse() directly to perform validation and + * parsing in a single step. + * + * @see genlmsg_valid_hdr() + * @see nla_validate() + * @see genlmsg_parse() + * + * @return 0 on success or a negative error code. + */ int genlmsg_validate(struct nlmsghdr *nlh, int hdrlen, int maxtype, - struct nla_policy *policy) + struct nla_policy *policy) { struct genlmsghdr *ghdr; @@ -170,6 +161,33 @@ int genlmsg_validate(struct nlmsghdr *nlh, int hdrlen, int maxtype, genlmsg_attrlen(ghdr, hdrlen), maxtype, policy); } +/** + * Parse Generic Netlink message including attributes + * @arg nlh Pointer to Netlink message header + * @arg hdrlen Length of user header + * @arg tb Array to store parsed attributes + * @arg maxtype Maximum attribute id expected + * @arg policy Attribute validation policy + * + * Verifies the validity of the Netlink and Generic Netlink headers using + * genlmsg_valid_hdr() and calls nla_parse() on the message payload to + * parse eventual attributes. + * + * @par Example: + * @code + * struct nlattr *attrs[MY_TYPE_MAX+1]; + * + * if ((err = genlsmg_parse(nlmsg_nlh(msg), sizeof(struct my_hdr), attrs, + * MY_TYPE_MAX, attr_policy)) < 0) + * // ERROR + * @endcode + * + * @see genlmsg_valid_hdr() + * @see genlmsg_validate() + * @see nla_parse() + * + * @return 0 on success or a negative error code. + */ int genlmsg_parse(struct nlmsghdr *nlh, int hdrlen, struct nlattr *tb[], int maxtype, struct nla_policy *policy) { @@ -184,39 +202,102 @@ int genlmsg_parse(struct nlmsghdr *nlh, int hdrlen, struct nlattr *tb[], } /** - * Get head of message payload - * @arg gnlh genetlink messsage header + * Return pointer to Generic Netlink header + * @arg nlh Netlink message header + * + * @return Pointer to Generic Netlink message header */ -void *genlmsg_data(const struct genlmsghdr *gnlh) +struct genlmsghdr *genlmsg_hdr(struct nlmsghdr *nlh) { - return ((unsigned char *) gnlh + GENL_HDRLEN); + return nlmsg_data(nlh); } /** - * Get lenght of message payload - * @arg gnlh genetlink message header + * Return length of message payload including user header + * @arg gnlh Generic Netlink message header + * + * @see genlmsg_data() + * + * @return Length of user payload including an eventual user header in + * number of bytes. */ int genlmsg_len(const struct genlmsghdr *gnlh) { - struct nlmsghdr *nlh = (struct nlmsghdr *)((unsigned char *)gnlh - - NLMSG_HDRLEN); + const struct nlmsghdr *nlh; + + nlh = (const struct nlmsghdr *)((const unsigned char *) gnlh - NLMSG_HDRLEN); return (nlh->nlmsg_len - GENL_HDRLEN - NLMSG_HDRLEN); } + /** - * Get head of attribute data - * @arg gnlh generic netlink message header - * @arg hdrlen length of family specific header + * Return pointer to user header + * @arg gnlh Generic Netlink message header + * + * Calculates the pointer to the user header based on the pointer to + * the Generic Netlink message header. + * + * @return Pointer to the user header */ -struct nlattr *genlmsg_attrdata(const struct genlmsghdr *gnlh, int hdrlen) +void *genlmsg_user_hdr(const struct genlmsghdr *gnlh) { - return genlmsg_data(gnlh) + NLMSG_ALIGN(hdrlen); + return genlmsg_data(gnlh); } /** - * Get length of attribute data - * @arg gnlh generic netlink message header - * @arg hdrlen length of family specific header + * Return pointer to user data + * @arg gnlh Generic netlink message header + * @arg hdrlen Length of user header + * + * Calculates the pointer to the user data based on the pointer to + * the Generic Netlink message header. + * + * @see genlmsg_user_datalen() + * + * @return Pointer to the user data + */ +void *genlmsg_user_data(const struct genlmsghdr *gnlh, const int hdrlen) +{ + return genlmsg_user_hdr(gnlh) + NLMSG_ALIGN(hdrlen); +} + +/** + * Return length of user data + * @arg gnlh Generic Netlink message header + * @arg hdrlen Length of user header + * + * @see genlmsg_user_data() + * + * @return Length of user data in bytes + */ +int genlmsg_user_datalen(const struct genlmsghdr *gnlh, const int hdrlen) +{ + return genlmsg_len(gnlh) - NLMSG_ALIGN(hdrlen); +} + +/** + * Return pointer to message attributes + * @arg gnlh Generic Netlink message header + * @arg hdrlen Length of user header + * + * @see genlmsg_attrlen() + * + * @return Pointer to the start of the message's attributes section. + */ +struct nlattr *genlmsg_attrdata(const struct genlmsghdr *gnlh, int hdrlen) +{ + return genlmsg_user_data(gnlh, hdrlen); +} + +/** + * Return length of message attributes + * @arg gnlh Generic Netlink message header + * @arg hdrlen Length of user header + * + * @see genlmsg_attrdata() + * + * @return Length of the message section containing attributes in number + * of bytes. */ int genlmsg_attrlen(const struct genlmsghdr *gnlh, int hdrlen) { @@ -226,24 +307,45 @@ int genlmsg_attrlen(const struct genlmsghdr *gnlh, int hdrlen) /** @} */ /** - * @name Message Building + * @name Message Construction * @{ */ /** - * Add generic netlink header to netlink message - * @arg msg netlink message - * @arg pid netlink process id or NL_AUTO_PID - * @arg seq sequence number of message or NL_AUTO_SEQ - * @arg family generic netlink family - * @arg hdrlen length of user specific header - * @arg flags message flags - * @arg cmd generic netlink command - * @arg version protocol version + * Add Generic Netlink headers to Netlink message + * @arg msg Netlink message object + * @arg port Netlink port or NL_AUTO_PORT + * @arg seq Sequence number of message or NL_AUTO_SEQ + * @arg family Numeric family identifier + * @arg hdrlen Length of user header + * @arg flags Additional Netlink message flags (optional) + * @arg cmd Numeric command identifier + * @arg version Interface version * - * Returns pointer to user specific header. + * Calls nlmsg_put() on the specified message object to reserve space for + * the Netlink header, the Generic Netlink header, and a user header of + * specified length. Fills out the header fields with the specified + * parameters. + * + * @par Example: + * @code + * struct nl_msg *msg; + * struct my_hdr *user_hdr; + * + * if (!(msg = nlmsg_alloc())) + * // ERROR + * + * user_hdr = genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, family_id, + * sizeof(struct my_hdr), 0, MY_CMD_FOO, 0); + * if (!user_hdr) + * // ERROR + * @endcode + * + * @see nlmsg_put() + * + * Returns Pointer to user header or NULL if an error occurred. */ -void *genlmsg_put(struct nl_msg *msg, uint32_t pid, uint32_t seq, int family, +void *genlmsg_put(struct nl_msg *msg, uint32_t port, uint32_t seq, int family, int hdrlen, int flags, uint8_t cmd, uint8_t version) { struct nlmsghdr *nlh; @@ -252,7 +354,7 @@ void *genlmsg_put(struct nl_msg *msg, uint32_t pid, uint32_t seq, int family, .version = version, }; - nlh = nlmsg_put(msg, pid, seq, family, GENL_HDRLEN + hdrlen, flags); + nlh = nlmsg_put(msg, port, seq, family, GENL_HDRLEN + hdrlen, flags); if (nlh == NULL) return NULL; @@ -265,4 +367,25 @@ void *genlmsg_put(struct nl_msg *msg, uint32_t pid, uint32_t seq, int family, /** @} */ +/** + * @name Deprecated + * @{ + */ + +/** + * Return pointer to message payload + * @arg gnlh Generic Netlink message header + * + * @deprecated This function has been deprecated due to inability to specify + * the length of the user header. Use genlmsg_user_hdr() + * respectively genlmsg_user_data(). + * + * @return Pointer to payload section + */ +void *genlmsg_data(const struct genlmsghdr *gnlh) +{ + return ((unsigned char *) gnlh + GENL_HDRLEN); +} + +/** @} */ /** @} */ diff --git a/lib/genl/mngt.c b/lib/genl/mngt.c index 0ebe74d..a3faaf2 100644 --- a/lib/genl/mngt.c +++ b/lib/genl/mngt.c @@ -6,81 +6,19 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2008 Thomas Graf + * Copyright (c) 2003-2012 Thomas Graf */ /** * @ingroup genl - * @defgroup genl_mngt Management + * @defgroup genl_mngt Family and Command Registration * - * @par 1) Registering a generic netlink module - * @code - * #include + * Registering Generic Netlink Families and Commands * - * // First step is to define all the commands being used in - * // particular generic netlink family. The ID and name are - * // mandatory to be filled out. A callback function and - * // most the attribute policy that comes with it must be - * // defined for commands expected to be issued towards - * // userspace. - * static struct genl_cmd foo_cmds[] = { - * { - * .c_id = FOO_CMD_NEW, - * .c_name = "NEWFOO" , - * .c_maxattr = FOO_ATTR_MAX, - * .c_attr_policy = foo_policy, - * .c_msg_parser = foo_msg_parser, - * }, - * { - * .c_id = FOO_CMD_DEL, - * .c_name = "DELFOO" , - * }, - * }; - * - * // The list of commands must then be integrated into a - * // struct genl_ops serving as handle for this particular - * // family. - * static struct genl_ops my_genl_ops = { - * .o_cmds = foo_cmds, - * .o_ncmds = ARRAY_SIZE(foo_cmds), - * }; - * - * // Using the above struct genl_ops an arbitary number of - * // cache handles can be associated to it. - * // - * // The macro GENL_HDRSIZE() must be used to specify the - * // length of the header to automatically take headers on - * // generic layers into account. - * // - * // The macro GENL_FAMILY() is used to represent the generic - * // netlink family id. - * static struct nl_cache_ops genl_foo_ops = { - * .co_name = "genl/foo", - * .co_hdrsize = GENL_HDRSIZE(sizeof(struct my_hdr)), - * .co_msgtypes = GENL_FAMILY(GENL_ID_GENERATE, "foo"), - * .co_genl = &my_genl_ops, - * .co_protocol = NETLINK_GENERIC, - * .co_request_update = foo_request_update, - * .co_obj_ops = &genl_foo_ops, - * }; - * - * // Finally each cache handle for a generic netlink family - * // must be registered using genl_register(). - * static void __init foo_init(void) - * { - * genl_register(&genl_foo_ops); - * } - * - * // ... respectively unregsted again. - * static void __exit foo_exit(void) - * { - * genl_unregister(&genl_foo_ops); - * } - * @endcode * @{ */ -#include +#include #include #include #include @@ -88,30 +26,38 @@ #include #include +/** @cond SKIP */ + static NL_LIST_HEAD(genl_ops_list); -static int genl_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, - struct nlmsghdr *nlh, struct nl_parser_param *pp) +static struct genl_cmd *lookup_cmd(struct genl_ops *ops, int cmd_id) { - int i, err; + struct genl_cmd *cmd; + int i; + + for (i = 0; i < ops->o_ncmds; i++) { + cmd = &ops->o_cmds[i]; + if (cmd->c_id == cmd_id) + return cmd; + } + + return NULL; +} + +static int cmd_msg_parser(struct sockaddr_nl *who, struct nlmsghdr *nlh, + struct genl_ops *ops, struct nl_cache_ops *cache_ops, void *arg) +{ + int err; struct genlmsghdr *ghdr; struct genl_cmd *cmd; - ghdr = nlmsg_data(nlh); + ghdr = genlmsg_hdr(nlh); - if (ops->co_genl == NULL) - BUG(); - - for (i = 0; i < ops->co_genl->o_ncmds; i++) { - cmd = &ops->co_genl->o_cmds[i]; - if (cmd->c_id == ghdr->cmd) - goto found; + if (!(cmd = lookup_cmd(ops, ghdr->cmd))) { + err = -NLE_MSGTYPE_NOSUPPORT; + goto errout; } - err = -NLE_MSGTYPE_NOSUPPORT; - goto errout; - -found: if (cmd->c_msg_parser == NULL) err = -NLE_OPNOTSUPP; else { @@ -120,37 +66,68 @@ found: .who = who, .nlh = nlh, .genlhdr = ghdr, - .userhdr = genlmsg_data(ghdr), + .userhdr = genlmsg_user_hdr(ghdr), .attrs = tb, }; - err = nlmsg_parse(nlh, ops->co_hdrsize, tb, cmd->c_maxattr, + err = nlmsg_parse(nlh, GENL_HDRSIZE(ops->o_hdrsize), tb, cmd->c_maxattr, cmd->c_attr_policy); if (err < 0) goto errout; - err = cmd->c_msg_parser(ops, cmd, &info, pp); + err = cmd->c_msg_parser(cache_ops, cmd, &info, arg); } errout: return err; } +static int genl_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, + struct nlmsghdr *nlh, struct nl_parser_param *pp) +{ + if (ops->co_genl == NULL) + BUG(); + + return cmd_msg_parser(who, nlh, ops->co_genl, ops, pp); +} + +static struct genl_ops *lookup_family(int family) +{ + struct genl_ops *ops; + + nl_list_for_each_entry(ops, &genl_ops_list, o_list) { + if (ops->o_id == family) + return ops; + } + + return NULL; +} + +static struct genl_ops *lookup_family_by_name(const char *name) +{ + struct genl_ops *ops; + + nl_list_for_each_entry(ops, &genl_ops_list, o_list) { + if (!strcmp(ops->o_name, name)) + return ops; + } + + return NULL; +} + char *genl_op2name(int family, int op, char *buf, size_t len) { struct genl_ops *ops; int i; - nl_list_for_each_entry(ops, &genl_ops_list, o_list) { - if (ops->o_family == family) { - for (i = 0; i < ops->o_ncmds; i++) { - struct genl_cmd *cmd; - cmd = &ops->o_cmds[i]; + if ((ops = lookup_family(family))) { + for (i = 0; i < ops->o_ncmds; i++) { + struct genl_cmd *cmd; + cmd = &ops->o_cmds[i]; - if (cmd->c_id == op) { - strncpy(buf, cmd->c_name, len - 1); - return buf; - } + if (cmd->c_id == op) { + strncpy(buf, cmd->c_name, len - 1); + return buf; } } } @@ -159,15 +136,107 @@ char *genl_op2name(int family, int op, char *buf, size_t len) return NULL; } +/** @endcond */ /** - * @name Register/Unregister + * @name Registration * @{ */ /** - * Register generic netlink operations - * @arg ops cache operations + * Register Generic Netlink family and associated commands + * @arg ops Generic Netlink family definition + * + * Registers the specified Generic Netlink family definition together with + * all associated commands. After registration, received Generic Netlink + * messages can be passed to genl_handle_msg() which will validate the + * messages, look for a matching command and call the respective callback + * function automatically. + * + * @note Consider using genl_register() if the family is used to implement a + * cacheable type. + * + * @see genl_unregister_family(); + * @see genl_register(); + * + * @return 0 on success or a negative error code. + */ +int genl_register_family(struct genl_ops *ops) +{ + if (!ops->o_name) + return -NLE_INVAL; + + if (ops->o_cmds && ops->o_ncmds <= 0) + return -NLE_INVAL; + + if (ops->o_id && lookup_family(ops->o_id)) + return -NLE_EXIST; + + if (lookup_family_by_name(ops->o_name)) + return -NLE_EXIST; + + nl_list_add_tail(&ops->o_list, &genl_ops_list); + + return 0; +} + +/** + * Unregister Generic Netlink family + * @arg ops Generic Netlink family definition + * + * Unregisters a family and all associated commands that were previously + * registered using genl_register_family(). + * + * @see genl_register_family() + * + * @return 0 on success or a negative error code. + */ +int genl_unregister_family(struct genl_ops *ops) +{ + nl_list_del(&ops->o_list); + + return 0; +} + +/** + * Run a received message through the demultiplexer + * @arg msg Generic Netlink message + * @arg arg Argument passed on to the message handler callback + * + * @return 0 on success or a negative error code. + */ +int genl_handle_msg(struct nl_msg *msg, void *arg) +{ + struct nlmsghdr *nlh = nlmsg_hdr(msg); + struct genl_ops *ops; + + if (!genlmsg_valid_hdr(nlh, 0)) + return -NLE_INVAL; + + if (!(ops = lookup_family(nlh->nlmsg_type))) + return -NLE_MSGTYPE_NOSUPPORT; + + return cmd_msg_parser(nlmsg_get_src(msg), nlh, ops, NULL, arg); +} + +/** @} */ + +/** + * @name Registration of Cache Operations + * @{ + */ + +/** + * Register Generic Netlink family backed cache + * @arg ops Cache operations definition + * + * Same as genl_register_family() but additionally registers the specified + * cache operations using nl_cache_mngt_register() and associates it with + * the Generic Netlink family. + * + * @see genl_register_family() + * + * @return 0 on success or a negative error code. */ int genl_register(struct nl_cache_ops *ops) { @@ -189,13 +258,13 @@ int genl_register(struct nl_cache_ops *ops) } ops->co_genl->o_cache_ops = ops; + ops->co_genl->o_hdrsize = ops->co_hdrsize - GENL_HDRLEN; ops->co_genl->o_name = ops->co_msgtypes[0].mt_name; - ops->co_genl->o_family = ops->co_msgtypes[0].mt_id; + ops->co_genl->o_id = ops->co_msgtypes[0].mt_id; ops->co_msg_parser = genl_msg_parser; - /* FIXME: check for dup */ - - nl_list_add_tail(&ops->co_genl->o_list, &genl_ops_list); + if ((err = genl_register_family(ops->co_genl)) < 0) + goto errout; err = nl_cache_mngt_register(ops); errout: @@ -203,22 +272,22 @@ errout: } /** - * Unregister generic netlink operations - * @arg ops cache operations + * Unregister cache based Generic Netlink family + * @arg ops Cache operations definition */ void genl_unregister(struct nl_cache_ops *ops) { + if (!ops) + return; + nl_cache_mngt_unregister(ops); - nl_list_del(&ops->co_genl->o_list); + + genl_unregister_family(ops->co_genl); } /** @} */ -/** - * @name Resolving ID/Name - * @{ - */ - +/** @cond SKIP */ static int __genl_ops_resolve(struct nl_cache *ctrl, struct genl_ops *ops) { struct genl_family *family; @@ -233,7 +302,22 @@ static int __genl_ops_resolve(struct nl_cache *ctrl, struct genl_ops *ops) return -NLE_OBJ_NOTFOUND; } +/** @endcond */ +/** + * @name Resolving the name of registered families + * @{ + */ + +/** + * Resolve a single Generic Netlink family + * @arg sk Generic Netlink socket + * @arg ops Generic Netlink family definition + * + * Resolves the family name to its numeric identifier. + * + * @return 0 on success or a negative error code. + */ int genl_ops_resolve(struct nl_sock *sk, struct genl_ops *ops) { struct nl_cache *ctrl; @@ -249,6 +333,19 @@ errout: return err; } +/** + * Resolve all registered Generic Netlink families + * @arg sk Generic Netlink socket + * + * Walks through all local Generic Netlink families that have been registered + * using genl_register() and resolves the name of each family to the + * corresponding numeric identifier. + * + * @see genl_register() + * @see genl_ops_resolve() + * + * @return 0 on success or a negative error code. + */ int genl_mngt_resolve(struct nl_sock *sk) { struct nl_cache *ctrl; @@ -269,5 +366,4 @@ errout: /** @} */ - /** @} */ diff --git a/lib/handlers.c b/lib/handlers.c index f13b89e..e52c850 100644 --- a/lib/handlers.c +++ b/lib/handlers.c @@ -13,24 +13,19 @@ * @ingroup core * @defgroup cb Callbacks/Customization * - * @details - * @par 1) Setting up a callback set - * @code - * // Allocate a callback set and initialize it to the verbose default set - * struct nl_cb *cb = nl_cb_alloc(NL_CB_VERBOSE); + * Related sections in the development guide: + * - @core_doc{core_cb, Callback Configuration} * - * // Modify the set to call my_func() for all valid messages - * nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, my_func, NULL); - * - * // Set the error message handler to the verbose default implementation - * // and direct it to print all errors to the given file descriptor. - * FILE *file = fopen(...); - * nl_cb_err(cb, NL_CB_VERBOSE, NULL, file); - * @endcode * @{ + * + * Header + * ------ + * ~~~~{.c} + * #include + * ~~~~ */ -#include +#include #include #include #include @@ -84,9 +79,10 @@ static int nl_error_handler_verbose(struct sockaddr_nl *who, struct nlmsgerr *e, void *arg) { FILE *ofd = arg ? arg : stderr; + char buf[256]; fprintf(ofd, "-- Error received: %s\n-- Original message: ", - strerror(-e->error)); + strerror_r(-e->error, buf, sizeof(buf))); print_header_content(ofd, &e->msg); fprintf(ofd, "\n"); diff --git a/lib/hash.c b/lib/hash.c new file mode 100644 index 0000000..47c938b --- /dev/null +++ b/lib/hash.c @@ -0,0 +1,482 @@ +/* + * This code was taken from http://ccodearchive.net/info/hash.html + * The original file was modified to remove unwanted code + * and some changes to fit the current build environment + */ +/* +------------------------------------------------------------------------------- +lookup3.c, by Bob Jenkins, May 2006, Public Domain. + +These are functions for producing 32-bit hashes for hash table lookup. +hash_word(), hashlittle(), hashlittle2(), hashbig(), mix(), and final() +are externally useful functions. Routines to test the hash are included +if SELF_TEST is defined. You can use this free for any purpose. It's in +the public domain. It has no warranty. + +You probably want to use hashlittle(). hashlittle() and hashbig() +hash byte arrays. hashlittle() is is faster than hashbig() on +little-endian machines. Intel and AMD are little-endian machines. +On second thought, you probably want hashlittle2(), which is identical to +hashlittle() except it returns two 32-bit hashes for the price of one. +You could implement hashbig2() if you wanted but I haven't bothered here. + +If you want to find a hash of, say, exactly 7 integers, do + a = i1; b = i2; c = i3; + mix(a,b,c); + a += i4; b += i5; c += i6; + mix(a,b,c); + a += i7; + final(a,b,c); +then use c as the hash value. If you have a variable length array of +4-byte integers to hash, use hash_word(). If you have a byte array (like +a character string), use hashlittle(). If you have several byte arrays, or +a mix of things, see the comments above hashlittle(). + +Why is this so big? I read 12 bytes at a time into 3 4-byte integers, +then mix those integers. This is fast (you can do a lot more thorough +mixing with 12*3 instructions on 3 integers than you can with 3 instructions +on 1 byte), but shoehorning those bytes into integers efficiently is messy. +------------------------------------------------------------------------------- +*/ +#include + +#if HAVE_LITTLE_ENDIAN +#define HASH_LITTLE_ENDIAN 1 +#define HASH_BIG_ENDIAN 0 +#elif HAVE_BIG_ENDIAN +#define HASH_LITTLE_ENDIAN 0 +#define HASH_BIG_ENDIAN 1 +#else +#error Unknown endian +#endif + +#define hashsize(n) ((uint32_t)1<<(n)) +#define hashmask(n) (hashsize(n)-1) +#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k)))) + +/* +------------------------------------------------------------------------------- +mix -- mix 3 32-bit values reversibly. + +This is reversible, so any information in (a,b,c) before mix() is +still in (a,b,c) after mix(). + +If four pairs of (a,b,c) inputs are run through mix(), or through +mix() in reverse, there are at least 32 bits of the output that +are sometimes the same for one pair and different for another pair. +This was tested for: +* pairs that differed by one bit, by two bits, in any combination + of top bits of (a,b,c), or in any combination of bottom bits of + (a,b,c). +* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed + the output delta to a Gray code (a^(a>>1)) so a string of 1's (as + is commonly produced by subtraction) look like a single 1-bit + difference. +* the base values were pseudorandom, all zero but one bit set, or + all zero plus a counter that starts at zero. + +Some k values for my "a-=c; a^=rot(c,k); c+=b;" arrangement that +satisfy this are + 4 6 8 16 19 4 + 9 15 3 18 27 15 + 14 9 3 7 17 3 +Well, "9 15 3 18 27 15" didn't quite get 32 bits diffing +for "differ" defined as + with a one-bit base and a two-bit delta. I +used http://burtleburtle.net/bob/hash/avalanche.html to choose +the operations, constants, and arrangements of the variables. + +This does not achieve avalanche. There are input bits of (a,b,c) +that fail to affect some output bits of (a,b,c), especially of a. The +most thoroughly mixed value is c, but it doesn't really even achieve +avalanche in c. + +This allows some parallelism. Read-after-writes are good at doubling +the number of bits affected, so the goal of mixing pulls in the opposite +direction as the goal of parallelism. I did what I could. Rotates +seem to cost as much as shifts on every machine I could lay my hands +on, and rotates are much kinder to the top and bottom bits, so I used +rotates. +------------------------------------------------------------------------------- +*/ +#define mix(a,b,c) \ +{ \ + a -= c; a ^= rot(c, 4); c += b; \ + b -= a; b ^= rot(a, 6); a += c; \ + c -= b; c ^= rot(b, 8); b += a; \ + a -= c; a ^= rot(c,16); c += b; \ + b -= a; b ^= rot(a,19); a += c; \ + c -= b; c ^= rot(b, 4); b += a; \ +} + +/* +------------------------------------------------------------------------------- +final -- final mixing of 3 32-bit values (a,b,c) into c + +Pairs of (a,b,c) values differing in only a few bits will usually +produce values of c that look totally different. This was tested for +* pairs that differed by one bit, by two bits, in any combination + of top bits of (a,b,c), or in any combination of bottom bits of + (a,b,c). +* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed + the output delta to a Gray code (a^(a>>1)) so a string of 1's (as + is commonly produced by subtraction) look like a single 1-bit + difference. +* the base values were pseudorandom, all zero but one bit set, or + all zero plus a counter that starts at zero. + +These constants passed: + 14 11 25 16 4 14 24 + 12 14 25 16 4 14 24 +and these came close: + 4 8 15 26 3 22 24 + 10 8 15 26 3 22 24 + 11 8 15 26 3 22 24 +------------------------------------------------------------------------------- +*/ +#define final(a,b,c) \ +{ \ + c ^= b; c -= rot(b,14); \ + a ^= c; a -= rot(c,11); \ + b ^= a; b -= rot(a,25); \ + c ^= b; c -= rot(b,16); \ + a ^= c; a -= rot(c,4); \ + b ^= a; b -= rot(a,14); \ + c ^= b; c -= rot(b,24); \ +} + +/* +------------------------------------------------------------------------------- +hashlittle() -- hash a variable-length key into a 32-bit value + k : the key (the unaligned variable-length array of bytes) + length : the length of the key, counting by bytes + val2 : IN: can be any 4-byte value OUT: second 32 bit hash. +Returns a 32-bit value. Every bit of the key affects every bit of +the return value. Two keys differing by one or two bits will have +totally different hash values. Note that the return value is better +mixed than val2, so use that first. + +The best hash table sizes are powers of 2. There is no need to do +mod a prime (mod is sooo slow!). If you need less than 32 bits, +use a bitmask. For example, if you need only 10 bits, do + h = (h & hashmask(10)); +In which case, the hash table should have hashsize(10) elements. + +If you are hashing n strings (uint8_t **)k, do it like this: + for (i=0, h=0; i 12) + { + a += k[0]; + b += k[1]; + c += k[2]; + mix(a,b,c); + length -= 12; + k += 3; + } + + /*----------------------------- handle the last (probably partial) block */ + /* + * "k[2]&0xffffff" actually reads beyond the end of the string, but + * then masks off the part it's not allowed to read. Because the + * string is aligned, the masked-off tail is in the same word as the + * rest of the string. Every machine with memory protection I've seen + * does it on word boundaries, so is OK with this. But VALGRIND will + * still catch it and complain. The masking trick does make the hash + * noticably faster for short strings (like English words). + * + * Not on my testing with gcc 4.5 on an intel i5 CPU, at least --RR. + */ +#if 0 + switch(length) + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break; + case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break; + case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break; + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=k[1]&0xffffff; a+=k[0]; break; + case 6 : b+=k[1]&0xffff; a+=k[0]; break; + case 5 : b+=k[1]&0xff; a+=k[0]; break; + case 4 : a+=k[0]; break; + case 3 : a+=k[0]&0xffffff; break; + case 2 : a+=k[0]&0xffff; break; + case 1 : a+=k[0]&0xff; break; + case 0 : return c; /* zero length strings require no mixing */ + } + +#else /* make valgrind happy */ + + k8 = (const uint8_t *)k; + switch(length) + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ + case 10: c+=((uint32_t)k8[9])<<8; /* fall through */ + case 9 : c+=k8[8]; /* fall through */ + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ + case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */ + case 5 : b+=k8[4]; /* fall through */ + case 4 : a+=k[0]; break; + case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ + case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */ + case 1 : a+=k8[0]; break; + case 0 : return c; + } + +#endif /* !valgrind */ + + } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) { + const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */ + const uint8_t *k8; + + /*--------------- all but last block: aligned reads and different mixing */ + while (length > 12) + { + a += k[0] + (((uint32_t)k[1])<<16); + b += k[2] + (((uint32_t)k[3])<<16); + c += k[4] + (((uint32_t)k[5])<<16); + mix(a,b,c); + length -= 12; + k += 6; + } + + /*----------------------------- handle the last (probably partial) block */ + k8 = (const uint8_t *)k; + switch(length) + { + case 12: c+=k[4]+(((uint32_t)k[5])<<16); + b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ + case 10: c+=k[4]; + b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 9 : c+=k8[8]; /* fall through */ + case 8 : b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ + case 6 : b+=k[2]; + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 5 : b+=k8[4]; /* fall through */ + case 4 : a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ + case 2 : a+=k[0]; + break; + case 1 : a+=k8[0]; + break; + case 0 : return c; /* zero length requires no mixing */ + } + + } else { /* need to read the key one byte at a time */ + const uint8_t *k = (const uint8_t *)key; + + /*--------------- all but the last block: affect some 32 bits of (a,b,c) */ + while (length > 12) + { + a += k[0]; + a += ((uint32_t)k[1])<<8; + a += ((uint32_t)k[2])<<16; + a += ((uint32_t)k[3])<<24; + b += k[4]; + b += ((uint32_t)k[5])<<8; + b += ((uint32_t)k[6])<<16; + b += ((uint32_t)k[7])<<24; + c += k[8]; + c += ((uint32_t)k[9])<<8; + c += ((uint32_t)k[10])<<16; + c += ((uint32_t)k[11])<<24; + mix(a,b,c); + length -= 12; + k += 12; + } + + /*-------------------------------- last block: affect all 32 bits of (c) */ + switch(length) /* all the case statements fall through */ + { + case 12: c+=((uint32_t)k[11])<<24; + case 11: c+=((uint32_t)k[10])<<16; + case 10: c+=((uint32_t)k[9])<<8; + case 9 : c+=k[8]; + case 8 : b+=((uint32_t)k[7])<<24; + case 7 : b+=((uint32_t)k[6])<<16; + case 6 : b+=((uint32_t)k[5])<<8; + case 5 : b+=k[4]; + case 4 : a+=((uint32_t)k[3])<<24; + case 3 : a+=((uint32_t)k[2])<<16; + case 2 : a+=((uint32_t)k[1])<<8; + case 1 : a+=k[0]; + break; + case 0 : return c; + } + } + + final(a,b,c); + *val2 = b; + return c; +} + +/* + * hashbig(): + * This is the same as hash_word() on big-endian machines. It is different + * from hashlittle() on all machines. hashbig() takes advantage of + * big-endian byte ordering. + */ +static uint32_t hashbig( const void *key, size_t length, uint32_t *val2) +{ + uint32_t a,b,c; + union { const void *ptr; size_t i; } u; /* to cast key to (size_t) happily */ + + /* Set up the internal state */ + a = b = c = 0xdeadbeef + ((uint32_t)length) + *val2; + + u.ptr = key; + if (HASH_BIG_ENDIAN && ((u.i & 0x3) == 0)) { + const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */ + const uint8_t *k8; + + /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */ + while (length > 12) + { + a += k[0]; + b += k[1]; + c += k[2]; + mix(a,b,c); + length -= 12; + k += 3; + } + + /*----------------------------- handle the last (probably partial) block */ + /* + * "k[2]<<8" actually reads beyond the end of the string, but + * then shifts out the part it's not allowed to read. Because the + * string is aligned, the illegal read is in the same word as the + * rest of the string. Every machine with memory protection I've seen + * does it on word boundaries, so is OK with this. But VALGRIND will + * still catch it and complain. The masking trick does make the hash + * noticably faster for short strings (like English words). + * + * Not on my testing with gcc 4.5 on an intel i5 CPU, at least --RR. + */ +#if 0 + switch(length) + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=k[2]&0xffffff00; b+=k[1]; a+=k[0]; break; + case 10: c+=k[2]&0xffff0000; b+=k[1]; a+=k[0]; break; + case 9 : c+=k[2]&0xff000000; b+=k[1]; a+=k[0]; break; + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=k[1]&0xffffff00; a+=k[0]; break; + case 6 : b+=k[1]&0xffff0000; a+=k[0]; break; + case 5 : b+=k[1]&0xff000000; a+=k[0]; break; + case 4 : a+=k[0]; break; + case 3 : a+=k[0]&0xffffff00; break; + case 2 : a+=k[0]&0xffff0000; break; + case 1 : a+=k[0]&0xff000000; break; + case 0 : return c; /* zero length strings require no mixing */ + } + +#else /* make valgrind happy */ + + k8 = (const uint8_t *)k; + switch(length) /* all the case statements fall through */ + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=((uint32_t)k8[10])<<8; /* fall through */ + case 10: c+=((uint32_t)k8[9])<<16; /* fall through */ + case 9 : c+=((uint32_t)k8[8])<<24; /* fall through */ + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=((uint32_t)k8[6])<<8; /* fall through */ + case 6 : b+=((uint32_t)k8[5])<<16; /* fall through */ + case 5 : b+=((uint32_t)k8[4])<<24; /* fall through */ + case 4 : a+=k[0]; break; + case 3 : a+=((uint32_t)k8[2])<<8; /* fall through */ + case 2 : a+=((uint32_t)k8[1])<<16; /* fall through */ + case 1 : a+=((uint32_t)k8[0])<<24; break; + case 0 : return c; + } + +#endif /* !VALGRIND */ + + } else { /* need to read the key one byte at a time */ + const uint8_t *k = (const uint8_t *)key; + + /*--------------- all but the last block: affect some 32 bits of (a,b,c) */ + while (length > 12) + { + a += ((uint32_t)k[0])<<24; + a += ((uint32_t)k[1])<<16; + a += ((uint32_t)k[2])<<8; + a += ((uint32_t)k[3]); + b += ((uint32_t)k[4])<<24; + b += ((uint32_t)k[5])<<16; + b += ((uint32_t)k[6])<<8; + b += ((uint32_t)k[7]); + c += ((uint32_t)k[8])<<24; + c += ((uint32_t)k[9])<<16; + c += ((uint32_t)k[10])<<8; + c += ((uint32_t)k[11]); + mix(a,b,c); + length -= 12; + k += 12; + } + + /*-------------------------------- last block: affect all 32 bits of (c) */ + switch(length) /* all the case statements fall through */ + { + case 12: c+=k[11]; + case 11: c+=((uint32_t)k[10])<<8; + case 10: c+=((uint32_t)k[9])<<16; + case 9 : c+=((uint32_t)k[8])<<24; + case 8 : b+=k[7]; + case 7 : b+=((uint32_t)k[6])<<8; + case 6 : b+=((uint32_t)k[5])<<16; + case 5 : b+=((uint32_t)k[4])<<24; + case 4 : a+=k[3]; + case 3 : a+=((uint32_t)k[2])<<8; + case 2 : a+=((uint32_t)k[1])<<16; + case 1 : a+=((uint32_t)k[0])<<24; + break; + case 0 : return c; + } + } + + final(a,b,c); + *val2 = b; + return c; +} + +uint32_t nl_hash_any(const void *key, size_t length, uint32_t base) +{ + if (HASH_BIG_ENDIAN) + return hashbig(key, length, &base); + else + return hashlittle(key, length, &base); +} diff --git a/lib/hashtable.c b/lib/hashtable.c new file mode 100644 index 0000000..8b15925 --- /dev/null +++ b/lib/hashtable.c @@ -0,0 +1,197 @@ +/* + * netlink/hashtable.c Netlink hashtable Utilities + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2012 Cumulus Networks, Inc + */ +#include +#include +#include +#include +#include + +/** + * @ingroup core_types + * @defgroup hashtable Hashtable + * @{ + */ + +/** + * Allocate hashtable + * @arg size Size of hashtable in number of elements + * + * @return Allocated hashtable or NULL. + */ +nl_hash_table_t *nl_hash_table_alloc(int size) +{ + nl_hash_table_t *ht; + + ht = calloc(1, sizeof (*ht)); + if (!ht) + goto errout; + + ht->nodes = calloc(size, sizeof (*ht->nodes)); + if (!ht->nodes) { + free(ht); + goto errout; + } + + ht->size = size; + + return ht; +errout: + return NULL; +} + +/** + * Free hashtable including all nodes + * @arg ht Hashtable + * + * @note Reference counter of all objects in the hashtable will be decremented. + */ +void nl_hash_table_free(nl_hash_table_t *ht) +{ + int i; + + for(i = 0; i < ht->size; i++) { + nl_hash_node_t *node = ht->nodes[i]; + nl_hash_node_t *saved_node; + + while (node) { + saved_node = node; + node = node->next; + nl_object_put(saved_node->obj); + free(saved_node); + } + } + + free(ht->nodes); + free(ht); +} + +/** + * Lookup identical object in hashtable + * @arg ht Hashtable + * @arg obj Object to lookup + * + * Generates hashkey for `obj` and traverses the corresponding chain calling + * `nl_object_identical()` on each trying to find a match. + * + * @return Pointer to object if match was found or NULL. + */ +struct nl_object* nl_hash_table_lookup(nl_hash_table_t *ht, + struct nl_object *obj) +{ + nl_hash_node_t *node; + uint32_t key_hash; + + nl_object_keygen(obj, &key_hash, ht->size); + node = ht->nodes[key_hash]; + + while (node) { + if (nl_object_identical(node->obj, obj)) + return node->obj; + node = node->next; + } + + return NULL; +} + +/** + * Add object to hashtable + * @arg ht Hashtable + * @arg obj Object to add + * + * Adds `obj` to the hashtable. Object type must support hashing, otherwise all + * objects will be added to the chain `0`. + * + * @note The reference counter of the object is incremented. + * + * @return 0 on success or a negative error code + * @retval -NLE_EXIST Identical object already present in hashtable + */ +int nl_hash_table_add(nl_hash_table_t *ht, struct nl_object *obj) +{ + nl_hash_node_t *node; + uint32_t key_hash; + + nl_object_keygen(obj, &key_hash, ht->size); + node = ht->nodes[key_hash]; + + while (node) { + if (nl_object_identical(node->obj, obj)) { + NL_DBG(2, "Warning: Add to hashtable found duplicate...\n"); + return -NLE_EXIST; + } + node = node->next; + } + + NL_DBG (5, "adding cache entry of obj %p in table %p, with hash 0x%x\n", + obj, ht, key_hash); + + node = malloc(sizeof(nl_hash_node_t)); + if (!node) + return -NLE_NOMEM; + nl_object_get(obj); + node->obj = obj; + node->key = key_hash; + node->key_size = sizeof(uint32_t); + node->next = ht->nodes[key_hash]; + ht->nodes[key_hash] = node; + + return 0; +} + +/** + * Remove object from hashtable + * @arg ht Hashtable + * @arg obj Object to remove + * + * Remove `obj` from hashtable if it exists. + * + * @note Reference counter of object will be decremented. + * + * @return 0 on success or a negative error code. + * @retval -NLE_OBJ_NOTFOUND Object not present in hashtable. + */ +int nl_hash_table_del(nl_hash_table_t *ht, struct nl_object *obj) +{ + nl_hash_node_t *node, *prev; + uint32_t key_hash; + + nl_object_keygen(obj, &key_hash, ht->size); + prev = node = ht->nodes[key_hash]; + + while (node) { + if (nl_object_identical(node->obj, obj)) { + nl_object_put(obj); + + NL_DBG (5, "deleting cache entry of obj %p in table %p, with" + " hash 0x%x\n", obj, ht, key_hash); + + if (node == ht->nodes[key_hash]) + ht->nodes[key_hash] = node->next; + else + prev->next = node->next; + + free(node); + + return 0; + } + prev = node; + node = node->next; + } + + return -NLE_OBJ_NOTFOUND; +} + +uint32_t nl_hash(void *k, size_t length, uint32_t initval) +{ + return(__nl_hash(k, length, initval)); +} + +/** @} */ diff --git a/lib/msg.c b/lib/msg.c index 5c852c0..b07de63 100644 --- a/lib/msg.c +++ b/lib/msg.c @@ -6,159 +6,27 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2008 Thomas Graf + * Copyright (c) 2003-2012 Thomas Graf */ /** * @ingroup core - * @defgroup msg Messages + * @defgroup msg Message Construction & Parsing * Netlink Message Construction/Parsing Interface * - * The following information is partly extracted from RFC3549 - * (ftp://ftp.rfc-editor.org/in-notes/rfc3549.txt) + * Related sections in the development guide: + * - @core_doc{_message_parsing_amp_construction,Message Parsing & Construction} * - * @par Message Format - * Netlink messages consist of a byte stream with one or multiple - * Netlink headers and an associated payload. If the payload is too big - * to fit into a single message it, can be split over multiple Netlink - * messages, collectively called a multipart message. For multipart - * messages, the first and all following headers have the \c NLM_F_MULTI - * Netlink header flag set, except for the last header which has the - * Netlink header type \c NLMSG_DONE. - * - * @par - * The Netlink message header (\link nlmsghdr struct nlmsghdr\endlink) is shown below. - * @code - * 0 1 2 3 - * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Length | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Type | Flags | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Sequence Number | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Process ID (PID) | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * @endcode - * - * @par - * The netlink message header and payload must be aligned properly: - * @code - * <------- NLMSG_ALIGN(hlen) ------> <---- NLMSG_ALIGN(len) ---> - * +----------------------------+- - -+- - - - - - - - - - -+- - -+ - * | Header | Pad | Payload | Pad | - * | struct nlmsghdr | | | | - * +----------------------------+- - -+- - - - - - - - - - -+- - -+ - * @endcode - * @par - * Message Format: - * @code - * <--- nlmsg_total_size(payload) ---> - * <-- nlmsg_msg_size(payload) -> - * +----------+- - -+-------------+- - -+-------- - - - * | nlmsghdr | Pad | Payload | Pad | nlmsghdr - * +----------+- - -+-------------+- - -+-------- - - - * nlmsg_data(nlh)---^ ^ - * nlmsg_next(nlh)-----------------------+ - * @endcode - * @par - * The payload may consist of arbitary data but may have strict - * alignment and formatting rules depening on the specific netlink - * families. - * @par - * @code - * <---------------------- nlmsg_len(nlh) ---------------------> - * <------ hdrlen ------> <- nlmsg_attrlen(nlh, hdrlen) -> - * +----------------------+- - -+--------------------------------+ - * | Family Header | Pad | Attributes | - * +----------------------+- - -+--------------------------------+ - * nlmsg_attrdata(nlh, hdrlen)---^ - * @endcode - * @par The ACK Netlink Message - * This message is actually used to denote both an ACK and a NACK. - * Typically, the direction is from FEC to CPC (in response to an ACK - * request message). However, the CPC should be able to send ACKs back - * to FEC when requested. - * @code - * 0 1 2 3 - * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Netlink message header | - * | type = NLMSG_ERROR | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Error code | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | OLD Netlink message header | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * @endcode - * - * @par Example - * @code - * // Various methods exist to create/allocate a new netlink - * // message. - * // - * // nlmsg_alloc() will allocate an empty netlink message with - * // a maximum payload size which defaults to the page size of - * // the system. This default size can be modified using the - * // function nlmsg_set_default_size(). - * struct nl_msg *msg = nlmsg_alloc(); - * - * // Very often, the message type and message flags are known - * // at allocation time while the other fields are auto generated: - * struct nl_msg *msg = nlmsg_alloc_simple(MY_TYPE, MY_FLAGS); - * - * // Alternatively an existing netlink message header can be used - * // to inherit the header values: - * struct nlmsghdr hdr = { - * .nlmsg_type = MY_TYPE, - * .nlmsg_flags = MY_FLAGS, - * }; - * struct nl_msg *msg = nlmsg_inherit(&hdr); - * - * // Last but not least, netlink messages received from netlink sockets - * // can be converted into nl_msg objects using nlmsg_convert(). This - * // will create a message with a maximum payload size which equals the - * // length of the existing netlink message, therefore no more data can - * // be appened without calling nlmsg_expand() first. - * struct nl_msg *msg = nlmsg_convert(nlh_from_nl_sock); - * - * // Payload may be added to the message via nlmsg_append(). The fourth - * // parameter specifies the number of alignment bytes the data should - * // be padding with at the end. Common values are 0 to disable it or - * // NLMSG_ALIGNTO to ensure proper netlink message padding. - * nlmsg_append(msg, &mydata, sizeof(mydata), 0); - * - * // Sometimes it may be necessary to reserve room for data but defer - * // the actual copying to a later point, nlmsg_reserve() can be used - * // for this purpose: - * void *data = nlmsg_reserve(msg, sizeof(mydata), NLMSG_ALIGNTO); - * - * // Attributes may be added using the attributes interface. - * - * // After successful use of the message, the memory must be freed - * // using nlmsg_free() - * nlmsg_free(msg); - * @endcode - * - * @par 4) Parsing messages - * @code - * int n; - * unsigned char *buf; - * struct nlmsghdr *hdr; - * - * n = nl_recv(handle, NULL, &buf); - * - * hdr = (struct nlmsghdr *) buf; - * while (nlmsg_ok(hdr, n)) { - * // Process message here... - * hdr = nlmsg_next(hdr, &n); - * } - * @endcode * @{ + * + * Header + * ------ + * ~~~~{.c} + * #include + * ~~~~ */ -#include +#include #include #include #include @@ -181,8 +49,6 @@ static void __init init_msg_size(void) * Calculates size of netlink message based on payload length. * @arg payload Length of payload * - * See \ref core_msg_fmt_align for more information on alignment. - * * @return size of netlink message without padding. */ int nlmsg_size(int payload) @@ -190,7 +56,7 @@ int nlmsg_size(int payload) return NLMSG_HDRLEN + payload; } -int nlmsg_msg_size(int payload) +static int nlmsg_msg_size(int payload) { return nlmsg_size(payload); } @@ -201,8 +67,6 @@ int nlmsg_msg_size(int payload) * * This function is idential to nlmsg_size() + nlmsg_padlen(). * - * See \ref core_msg_fmt_align for more information on alignment. - * * @return Size of netlink message including padding. */ int nlmsg_total_size(int payload) @@ -218,8 +82,6 @@ int nlmsg_total_size(int payload) * the end of the message to ensure that the next netlink message header begins * properly aligned to NLMSG_ALIGNTO. * - * See \ref core_msg_fmt_align for more information on alignment. - * * @return Number of bytes of padding needed. */ int nlmsg_padlen(int payload) @@ -261,7 +123,7 @@ int nlmsg_datalen(const struct nlmsghdr *nlh) return nlh->nlmsg_len - NLMSG_HDRLEN; } -int nlmsg_len(const struct nlmsghdr *nlh) +static int nlmsg_len(const struct nlmsghdr *nlh) { return nlmsg_datalen(nlh); } @@ -871,14 +733,18 @@ int nl_msg_parse(struct nl_msg *msg, void (*cb)(struct nl_object *, void *), .cb = cb, .arg = arg, }; + int err; - ops = nl_cache_ops_associate(nlmsg_get_proto(msg), - nlmsg_hdr(msg)->nlmsg_type); + ops = nl_cache_ops_associate_safe(nlmsg_get_proto(msg), + nlmsg_hdr(msg)->nlmsg_type); if (ops == NULL) return -NLE_MSGTYPE_NOSUPPORT; p.pp_arg = &x; - return nl_cache_parse(ops, NULL, nlmsg_hdr(msg), &p); + err = nl_cache_parse(ops, NULL, nlmsg_hdr(msg), &p); + nl_cache_ops_put(ops); + + return err; } /** @} */ @@ -939,13 +805,14 @@ static void print_hdr(FILE *ofd, struct nl_msg *msg) fprintf(ofd, " .nlmsg_len = %d\n", nlh->nlmsg_len); - ops = nl_cache_ops_associate(nlmsg_get_proto(msg), nlh->nlmsg_type); + ops = nl_cache_ops_associate_safe(nlmsg_get_proto(msg), nlh->nlmsg_type); if (ops) { mt = nl_msgtype_lookup(ops, nlh->nlmsg_type); if (!mt) BUG(); snprintf(buf, sizeof(buf), "%s::%s", ops->co_name, mt->mt_name); + nl_cache_ops_put(ops); } else nl_nlmsgtype2str(nlh->nlmsg_type, buf, sizeof(buf)); @@ -967,9 +834,13 @@ static void dump_attrs(FILE *ofd, struct nlattr *attrs, int attrlen, int padlen, alen = nla_len(nla); prefix_line(ofd, prefix); - fprintf(ofd, " [ATTR %02d%s] %d octets\n", nla_type(nla), - nla->nla_type & NLA_F_NESTED ? " NESTED" : "", - alen); + + if (nla->nla_type == 0) + fprintf(ofd, " [ATTR PADDING] %d octets\n", alen); + else + fprintf(ofd, " [ATTR %02d%s] %d octets\n", nla_type(nla), + nla->nla_type & NLA_F_NESTED ? " NESTED" : "", + alen); if (nla->nla_type & NLA_F_NESTED) dump_attrs(ofd, nla_data(nla), alen, prefix+1); @@ -992,6 +863,27 @@ static void dump_attrs(FILE *ofd, struct nlattr *attrs, int attrlen, } } +static void dump_error_msg(struct nl_msg *msg, FILE *ofd) +{ + struct nlmsghdr *hdr = nlmsg_hdr(msg); + struct nlmsgerr *err = nlmsg_data(hdr); + + fprintf(ofd, " [ERRORMSG] %zu octets\n", sizeof(*err)); + + if (nlmsg_len(hdr) >= sizeof(*err)) { + char buf[256]; + struct nl_msg *errmsg; + + fprintf(ofd, " .error = %d \"%s\"\n", err->error, + strerror_r(-err->error, buf, sizeof(buf))); + fprintf(ofd, " [ORIGINAL MESSAGE] %zu octets\n", sizeof(*hdr)); + + errmsg = nlmsg_inherit(&err->msg); + print_hdr(ofd, errmsg); + nlmsg_free(errmsg); + } +} + /** * Dump message in human readable format to file descriptor * @arg msg Message to print @@ -1005,29 +897,18 @@ void nl_msg_dump(struct nl_msg *msg, FILE *ofd) "-------------------------- BEGIN NETLINK MESSAGE " "---------------------------\n"); - fprintf(ofd, " [HEADER] %Zu octets\n", sizeof(struct nlmsghdr)); + fprintf(ofd, " [HEADER] %zu octets\n", sizeof(struct nlmsghdr)); print_hdr(ofd, msg); - if (hdr->nlmsg_type == NLMSG_ERROR && - hdr->nlmsg_len >= nlmsg_msg_size(sizeof(struct nlmsgerr))) { - struct nl_msg *errmsg; - struct nlmsgerr *err = nlmsg_data(hdr); - - fprintf(ofd, " [ERRORMSG] %Zu octets\n", sizeof(*err)); - fprintf(ofd, " .error = %d \"%s\"\n", err->error, - strerror(-err->error)); - fprintf(ofd, " [ORIGINAL MESSAGE] %Zu octets\n", sizeof(*hdr)); - - errmsg = nlmsg_inherit(&err->msg); - print_hdr(ofd, errmsg); - nlmsg_free(errmsg); - } else if (nlmsg_len(hdr) > 0) { + if (hdr->nlmsg_type == NLMSG_ERROR) + dump_error_msg(msg, ofd); + else if (nlmsg_len(hdr) > 0) { struct nl_cache_ops *ops; int payloadlen = nlmsg_len(hdr); int attrlen = 0; - ops = nl_cache_ops_associate(nlmsg_get_proto(msg), - hdr->nlmsg_type); + ops = nl_cache_ops_associate_safe(nlmsg_get_proto(msg), + hdr->nlmsg_type); if (ops) { attrlen = nlmsg_attrlen(hdr, ops->co_hdrsize); payloadlen -= attrlen; @@ -1044,6 +925,9 @@ void nl_msg_dump(struct nl_msg *msg, FILE *ofd) attrlen = nlmsg_attrlen(hdr, ops->co_hdrsize); dump_attrs(ofd, attrs, attrlen, 0); } + + if (ops) + nl_cache_ops_put(ops); } fprintf(ofd, diff --git a/lib/netfilter/ct.c b/lib/netfilter/ct.c index 9d61b6c..5dde1d1 100644 --- a/lib/netfilter/ct.c +++ b/lib/netfilter/ct.c @@ -23,7 +23,7 @@ #include #include -#include +#include #include #include #include @@ -38,7 +38,7 @@ static uint64_t ntohll(uint64_t x) #elif __BYTE_ORDER == __LITTLE_ENDIAN static uint64_t ntohll(uint64_t x) { - return __bswap_64(x); + return bswap_64(x); } #endif diff --git a/lib/netfilter/ct_obj.c b/lib/netfilter/ct_obj.c index c205427..bac775b 100644 --- a/lib/netfilter/ct_obj.c +++ b/lib/netfilter/ct_obj.c @@ -16,7 +16,7 @@ #include #include -#include +#include #include #include @@ -121,10 +121,10 @@ static void dump_icmp(struct nl_dump_params *p, struct nfnl_ct *ct, int reply) if (nfnl_ct_test_icmp_type(ct, reply)) nl_dump(p, "icmp type %d ", nfnl_ct_get_icmp_type(ct, reply)); - if (nfnl_ct_test_icmp_type(ct, reply)) + if (nfnl_ct_test_icmp_code(ct, reply)) nl_dump(p, "code %d ", nfnl_ct_get_icmp_code(ct, reply)); - if (nfnl_ct_test_icmp_type(ct, reply)) + if (nfnl_ct_test_icmp_id(ct, reply)) nl_dump(p, "id %d ", nfnl_ct_get_icmp_id(ct, reply)); } @@ -256,18 +256,29 @@ static void ct_dump_stats(struct nl_object *a, struct nl_dump_params *p) struct nfnl_ct *ct = (struct nfnl_ct *) a; double res; char *unit; + uint64_t packets; + const char * const names[] = {"rx", "tx"}; + int i; ct_dump_details(a, p); + if (!nfnl_ct_test_bytes(ct, 0) || + !nfnl_ct_test_packets(ct, 0) || + !nfnl_ct_test_bytes(ct, 1) || + !nfnl_ct_test_packets(ct, 1)) + { + nl_dump_line(p, " Statistics are not available.\n"); + nl_dump_line(p, " Please set sysctl net.netfilter.nf_conntrack_acct=1\n"); + nl_dump_line(p, " (Require kernel 2.6.27)\n"); + return; + } + nl_dump_line(p, " # packets volume\n"); - - res = nl_cancel_down_bytes(nfnl_ct_get_bytes(ct, 1), &unit); - nl_dump_line(p, " rx %10llu %7.2f %s\n", - nfnl_ct_get_packets(ct, 1), res, unit); - - res = nl_cancel_down_bytes(nfnl_ct_get_bytes(ct, 0), &unit); - nl_dump_line(p, " tx %10llu %7.2f %s\n", - nfnl_ct_get_packets(ct, 0), res, unit); + for (i=0; i<=1; i++) { + res = nl_cancel_down_bytes(nfnl_ct_get_bytes(ct, i), &unit); + packets = nfnl_ct_get_packets(ct, i); + nl_dump_line(p, " %s %10" PRIu64 " %7.2f %s\n", names[i], packets, res, unit); + } } static int ct_compare(struct nl_object *_a, struct nl_object *_b, diff --git a/lib/netfilter/exp.c b/lib/netfilter/exp.c new file mode 100644 index 0000000..86ed8d1 --- /dev/null +++ b/lib/netfilter/exp.c @@ -0,0 +1,621 @@ +/* + * lib/netfilter/exp.c Conntrack Expectation + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2008 Thomas Graf + * Copyright (c) 2007 Philip Craig + * Copyright (c) 2007 Secure Computing Corporation + * Copyright (c= 2008 Patrick McHardy + * Copyright (c) 2012 Rich Fought + */ + +/** + * @ingroup nfnl + * @defgroup exp Expectation + * @brief + * @{ + */ + +#include +#include +#include + +#include +#include +#include +#include + +static struct nl_cache_ops nfnl_exp_ops; + +static struct nla_policy exp_policy[CTA_EXPECT_MAX+1] = { + [CTA_EXPECT_MASTER] = { .type = NLA_NESTED }, + [CTA_EXPECT_TUPLE] = { .type = NLA_NESTED }, + [CTA_EXPECT_MASK] = { .type = NLA_NESTED }, + [CTA_EXPECT_TIMEOUT] = { .type = NLA_U32 }, + [CTA_EXPECT_ID] = { .type = NLA_U32 }, + [CTA_EXPECT_HELP_NAME] = { .type = NLA_STRING }, + [CTA_EXPECT_ZONE] = { .type = NLA_U16 }, + [CTA_EXPECT_FLAGS] = { .type = NLA_U32 }, // Added in kernel 2.6.37 + [CTA_EXPECT_CLASS] = { .type = NLA_U32 }, // Added in kernel 3.5 + [CTA_EXPECT_NAT] = { .type = NLA_NESTED }, // Added in kernel 3.5 + [CTA_EXPECT_FN] = { .type = NLA_STRING }, // Added in kernel 3.5 +}; + +static struct nla_policy exp_tuple_policy[CTA_TUPLE_MAX+1] = { + [CTA_TUPLE_IP] = { .type = NLA_NESTED }, + [CTA_TUPLE_PROTO] = { .type = NLA_NESTED }, +}; + +static struct nla_policy exp_ip_policy[CTA_IP_MAX+1] = { + [CTA_IP_V4_SRC] = { .type = NLA_U32 }, + [CTA_IP_V4_DST] = { .type = NLA_U32 }, + [CTA_IP_V6_SRC] = { .minlen = 16 }, + [CTA_IP_V6_DST] = { .minlen = 16 }, +}; + +static struct nla_policy exp_proto_policy[CTA_PROTO_MAX+1] = { + [CTA_PROTO_NUM] = { .type = NLA_U8 }, + [CTA_PROTO_SRC_PORT] = { .type = NLA_U16 }, + [CTA_PROTO_DST_PORT] = { .type = NLA_U16 }, + [CTA_PROTO_ICMP_ID] = { .type = NLA_U16 }, + [CTA_PROTO_ICMP_TYPE] = { .type = NLA_U8 }, + [CTA_PROTO_ICMP_CODE] = { .type = NLA_U8 }, + [CTA_PROTO_ICMPV6_ID] = { .type = NLA_U16 }, + [CTA_PROTO_ICMPV6_TYPE] = { .type = NLA_U8 }, + [CTA_PROTO_ICMPV6_CODE] = { .type = NLA_U8 }, +}; + +static struct nla_policy exp_nat_policy[CTA_EXPECT_NAT_MAX+1] = { + [CTA_EXPECT_NAT_DIR] = { .type = NLA_U32 }, + [CTA_EXPECT_NAT_TUPLE] = { .type = NLA_NESTED }, +}; + +static int exp_parse_ip(struct nfnl_exp *exp, int tuple, struct nlattr *attr) +{ + struct nlattr *tb[CTA_IP_MAX+1]; + struct nl_addr *addr; + int err; + + err = nla_parse_nested(tb, CTA_IP_MAX, attr, exp_ip_policy); + if (err < 0) + goto errout; + + if (tb[CTA_IP_V4_SRC]) { + addr = nl_addr_alloc_attr(tb[CTA_IP_V4_SRC], AF_INET); + if (addr == NULL) + goto errout_enomem; + err = nfnl_exp_set_src(exp, tuple, addr); + nl_addr_put(addr); + if (err < 0) + goto errout; + } + if (tb[CTA_IP_V4_DST]) { + addr = nl_addr_alloc_attr(tb[CTA_IP_V4_DST], AF_INET); + if (addr == NULL) + goto errout_enomem; + err = nfnl_exp_set_dst(exp, tuple, addr); + nl_addr_put(addr); + if (err < 0) + goto errout; + } + if (tb[CTA_IP_V6_SRC]) { + addr = nl_addr_alloc_attr(tb[CTA_IP_V6_SRC], AF_INET6); + if (addr == NULL) + goto errout_enomem; + err = nfnl_exp_set_src(exp, tuple, addr); + nl_addr_put(addr); + if (err < 0) + goto errout; + } + if (tb[CTA_IP_V6_DST]) { + addr = nl_addr_alloc_attr(tb[CTA_IP_V6_DST], AF_INET6); + if (addr == NULL) + goto errout_enomem; + err = nfnl_exp_set_dst(exp, tuple, addr); + nl_addr_put(addr); + if (err < 0) + goto errout; + } + + return 0; + +errout_enomem: + err = -NLE_NOMEM; +errout: + return err; +} + +static int exp_parse_proto(struct nfnl_exp *exp, int tuple, struct nlattr *attr) +{ + struct nlattr *tb[CTA_PROTO_MAX+1]; + int err; + uint16_t srcport = 0, dstport = 0, icmpid = 0; + uint8_t icmptype = 0, icmpcode = 0; + + err = nla_parse_nested(tb, CTA_PROTO_MAX, attr, exp_proto_policy); + if (err < 0) + return err; + + if (tb[CTA_PROTO_NUM]) + nfnl_exp_set_l4protonum(exp, tuple, nla_get_u8(tb[CTA_PROTO_NUM])); + + if (tb[CTA_PROTO_SRC_PORT]) + srcport = ntohs(nla_get_u16(tb[CTA_PROTO_SRC_PORT])); + if (tb[CTA_PROTO_DST_PORT]) + dstport = ntohs(nla_get_u16(tb[CTA_PROTO_DST_PORT])); + if (tb[CTA_PROTO_SRC_PORT] || tb[CTA_PROTO_DST_PORT]) + nfnl_exp_set_ports(exp, tuple, srcport, dstport); + + if (tb[CTA_PROTO_ICMP_ID]) + icmpid = ntohs(nla_get_u16(tb[CTA_PROTO_ICMP_ID])); + if (tb[CTA_PROTO_ICMP_TYPE]) + icmptype = nla_get_u8(tb[CTA_PROTO_ICMP_TYPE]); + if (tb[CTA_PROTO_ICMP_CODE]) + icmpcode = nla_get_u8(tb[CTA_PROTO_ICMP_CODE]); + if (tb[CTA_PROTO_ICMP_ID] || tb[CTA_PROTO_ICMP_TYPE] || tb[CTA_PROTO_ICMP_CODE]) + nfnl_exp_set_icmp(exp, tuple, icmpid, icmptype, icmpcode); + return 0; +} + +static int exp_parse_tuple(struct nfnl_exp *exp, int tuple, struct nlattr *attr) +{ + struct nlattr *tb[CTA_TUPLE_MAX+1]; + int err; + + err = nla_parse_nested(tb, CTA_TUPLE_MAX, attr, exp_tuple_policy); + if (err < 0) + return err; + + if (tb[CTA_TUPLE_IP]) { + err = exp_parse_ip(exp, tuple, tb[CTA_TUPLE_IP]); + if (err < 0) + return err; + } + + if (tb[CTA_TUPLE_PROTO]) { + err = exp_parse_proto(exp, tuple, tb[CTA_TUPLE_PROTO]); + if (err < 0) + return err; + } + + return 0; +} + +static int exp_parse_nat(struct nfnl_exp *exp, struct nlattr *attr) +{ + struct nlattr *tb[CTA_EXPECT_NAT_MAX+1]; + int err; + + err = nla_parse_nested(tb, CTA_EXPECT_NAT_MAX, attr, exp_nat_policy); + if (err < 0) + return err; + + if (tb[CTA_EXPECT_NAT_DIR]) + nfnl_exp_set_nat_dir(exp, nla_get_u32(tb[CTA_EXPECT_NAT_DIR])); + + if (tb[CTA_EXPECT_NAT_TUPLE]) { + err = exp_parse_tuple(exp, NFNL_EXP_TUPLE_NAT, tb[CTA_EXPECT_NAT_TUPLE]); + if (err < 0) + return err; + } + + return 0; +} + +int nfnlmsg_exp_group(struct nlmsghdr *nlh) +{ + switch (nfnlmsg_subtype(nlh)) { + case IPCTNL_MSG_EXP_NEW: + if (nlh->nlmsg_flags & (NLM_F_CREATE|NLM_F_EXCL)) + return NFNLGRP_CONNTRACK_EXP_NEW; + else + return NFNLGRP_CONNTRACK_EXP_UPDATE; + case IPCTNL_MSG_EXP_DELETE: + return NFNLGRP_CONNTRACK_EXP_DESTROY; + default: + return NFNLGRP_NONE; + } +} + +int nfnlmsg_exp_parse(struct nlmsghdr *nlh, struct nfnl_exp **result) +{ + struct nfnl_exp *exp; + struct nlattr *tb[CTA_MAX+1]; + int err; + + exp = nfnl_exp_alloc(); + if (!exp) + return -NLE_NOMEM; + + exp->ce_msgtype = nlh->nlmsg_type; + + err = nlmsg_parse(nlh, sizeof(struct nfgenmsg), tb, CTA_EXPECT_MAX, + exp_policy); + if (err < 0) + goto errout; + + nfnl_exp_set_family(exp, nfnlmsg_family(nlh)); + + if (tb[CTA_EXPECT_TUPLE]) { + err = exp_parse_tuple(exp, NFNL_EXP_TUPLE_EXPECT, tb[CTA_EXPECT_TUPLE]); + if (err < 0) + goto errout; + } + if (tb[CTA_EXPECT_MASTER]) { + err = exp_parse_tuple(exp, NFNL_EXP_TUPLE_MASTER, tb[CTA_EXPECT_MASTER]); + if (err < 0) + goto errout; + } + if (tb[CTA_EXPECT_MASK]) { + err = exp_parse_tuple(exp, NFNL_EXP_TUPLE_MASK, tb[CTA_EXPECT_MASK]); + if (err < 0) + goto errout; + } + + if (tb[CTA_EXPECT_NAT]) { + err = exp_parse_nat(exp, tb[CTA_EXPECT_MASK]); + if (err < 0) + goto errout; + } + + if (tb[CTA_EXPECT_CLASS]) + nfnl_exp_set_class(exp, ntohl(nla_get_u32(tb[CTA_EXPECT_CLASS]))); + + if (tb[CTA_EXPECT_FN]) + nfnl_exp_set_fn(exp, nla_data(tb[CTA_EXPECT_FN])); + + if (tb[CTA_EXPECT_TIMEOUT]) + nfnl_exp_set_timeout(exp, ntohl(nla_get_u32(tb[CTA_EXPECT_TIMEOUT]))); + + if (tb[CTA_EXPECT_ID]) + nfnl_exp_set_id(exp, ntohl(nla_get_u32(tb[CTA_EXPECT_ID]))); + + if (tb[CTA_EXPECT_HELP_NAME]) + nfnl_exp_set_helper_name(exp, nla_data(tb[CTA_EXPECT_HELP_NAME])); + + if (tb[CTA_EXPECT_ZONE]) + nfnl_exp_set_zone(exp, ntohs(nla_get_u16(tb[CTA_EXPECT_ZONE]))); + + if (tb[CTA_EXPECT_FLAGS]) + nfnl_exp_set_flags(exp, ntohl(nla_get_u32(tb[CTA_EXPECT_FLAGS]))); + + *result = exp; + return 0; + +errout: + nfnl_exp_put(exp); + return err; +} + +static int exp_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, + struct nlmsghdr *nlh, struct nl_parser_param *pp) +{ + struct nfnl_exp *exp; + int err; + + if ((err = nfnlmsg_exp_parse(nlh, &exp)) < 0) + goto errout; + + err = pp->pp_cb((struct nl_object *) exp, pp); +errout: + nfnl_exp_put(exp); + return err; +} + +int nfnl_exp_dump_request(struct nl_sock *sk) +{ + return nfnl_send_simple(sk, NFNL_SUBSYS_CTNETLINK_EXP, IPCTNL_MSG_EXP_GET, + NLM_F_DUMP, AF_UNSPEC, 0); +} + +static int exp_request_update(struct nl_cache *cache, struct nl_sock *sk) +{ + return nfnl_exp_dump_request(sk); +} + +static int exp_get_tuple_attr(int tuple) +{ + int attr = 0; + + switch (tuple) { + case CTA_EXPECT_MASTER: + attr = NFNL_EXP_TUPLE_MASTER; + break; + case CTA_EXPECT_MASK: + attr = NFNL_EXP_TUPLE_MASK; + break; + case CTA_EXPECT_NAT: + attr = NFNL_EXP_TUPLE_NAT; + break; + case CTA_EXPECT_TUPLE: + default : + attr = NFNL_EXP_TUPLE_EXPECT; + break; + } + + return attr; +} + +static int nfnl_exp_build_tuple(struct nl_msg *msg, const struct nfnl_exp *exp, + int cta) +{ + struct nlattr *tuple, *ip, *proto; + struct nl_addr *addr; + int family; + + family = nfnl_exp_get_family(exp); + + int type = exp_get_tuple_attr(cta); + + if (cta == CTA_EXPECT_NAT) + tuple = nla_nest_start(msg, CTA_EXPECT_NAT_TUPLE); + else + tuple = nla_nest_start(msg, cta); + + if (!tuple) + goto nla_put_failure; + + ip = nla_nest_start(msg, CTA_TUPLE_IP); + if (!ip) + goto nla_put_failure; + + addr = nfnl_exp_get_src(exp, type); + if (addr) + NLA_PUT_ADDR(msg, + family == AF_INET ? CTA_IP_V4_SRC : CTA_IP_V6_SRC, + addr); + + addr = nfnl_exp_get_dst(exp, type); + if (addr) + NLA_PUT_ADDR(msg, + family == AF_INET ? CTA_IP_V4_DST : CTA_IP_V6_DST, + addr); + + nla_nest_end(msg, ip); + + proto = nla_nest_start(msg, CTA_TUPLE_PROTO); + if (!proto) + goto nla_put_failure; + + if (nfnl_exp_test_l4protonum(exp, type)) + NLA_PUT_U8(msg, CTA_PROTO_NUM, nfnl_exp_get_l4protonum(exp, type)); + + if (nfnl_exp_test_ports(exp, type)) { + NLA_PUT_U16(msg, CTA_PROTO_SRC_PORT, + htons(nfnl_exp_get_src_port(exp, type))); + + NLA_PUT_U16(msg, CTA_PROTO_DST_PORT, + htons(nfnl_exp_get_dst_port(exp, type))); + } + + if (nfnl_exp_test_icmp(exp, type)) { + NLA_PUT_U16(msg, CTA_PROTO_ICMP_ID, + htons(nfnl_exp_get_icmp_id(exp, type))); + + NLA_PUT_U8(msg, CTA_PROTO_ICMP_TYPE, + nfnl_exp_get_icmp_type(exp, type)); + + NLA_PUT_U8(msg, CTA_PROTO_ICMP_CODE, + nfnl_exp_get_icmp_code(exp, type)); + } + + nla_nest_end(msg, proto); + + nla_nest_end(msg, tuple); + return 0; + +nla_put_failure: + return -NLE_MSGSIZE; +} + +static int nfnl_exp_build_nat(struct nl_msg *msg, const struct nfnl_exp *exp) +{ + struct nlattr *nat; + int err; + + nat = nla_nest_start(msg, CTA_EXPECT_NAT); + + if (nfnl_exp_test_nat_dir(exp)) { + NLA_PUT_U32(msg, CTA_EXPECT_NAT_DIR, + nfnl_exp_get_nat_dir(exp)); + } + + if ((err = nfnl_exp_build_tuple(msg, exp, CTA_EXPECT_NAT)) < 0) + goto nla_put_failure; + + nla_nest_end(msg, nat); + return 0; + +nla_put_failure: + return -NLE_MSGSIZE; +} + +static int nfnl_exp_build_message(const struct nfnl_exp *exp, int cmd, int flags, + struct nl_msg **result) +{ + struct nl_msg *msg; + int err; + + msg = nfnlmsg_alloc_simple(NFNL_SUBSYS_CTNETLINK_EXP, cmd, flags, + nfnl_exp_get_family(exp), 0); + if (msg == NULL) + return -NLE_NOMEM; + + if ((err = nfnl_exp_build_tuple(msg, exp, CTA_EXPECT_TUPLE)) < 0) + goto err_out; + + if ((err = nfnl_exp_build_tuple(msg, exp, CTA_EXPECT_MASTER)) < 0) + goto err_out; + + if ((err = nfnl_exp_build_tuple(msg, exp, CTA_EXPECT_MASK)) < 0) + goto err_out; + + if (nfnl_exp_test_src(exp, NFNL_EXP_TUPLE_NAT)) { + if ((err = nfnl_exp_build_nat(msg, exp)) < 0) + goto err_out; + } + + if (nfnl_exp_test_class(exp)) + NLA_PUT_U32(msg, CTA_EXPECT_CLASS, htonl(nfnl_exp_get_class(exp))); + + if (nfnl_exp_test_fn(exp)) + NLA_PUT_STRING(msg, CTA_EXPECT_FN, nfnl_exp_get_fn(exp)); + + if (nfnl_exp_test_id(exp)) + NLA_PUT_U32(msg, CTA_EXPECT_ID, htonl(nfnl_exp_get_id(exp))); + + if (nfnl_exp_test_timeout(exp)) + NLA_PUT_U32(msg, CTA_EXPECT_TIMEOUT, htonl(nfnl_exp_get_timeout(exp))); + + if (nfnl_exp_test_helper_name(exp)) + NLA_PUT_STRING(msg, CTA_EXPECT_HELP_NAME, nfnl_exp_get_helper_name(exp)); + + if (nfnl_exp_test_zone(exp)) + NLA_PUT_U16(msg, CTA_EXPECT_ZONE, htons(nfnl_exp_get_zone(exp))); + + if (nfnl_exp_test_flags(exp)) + NLA_PUT_U32(msg, CTA_EXPECT_FLAGS, htonl(nfnl_exp_get_flags(exp))); + + *result = msg; + return 0; + +nla_put_failure: +err_out: + nlmsg_free(msg); + return err; +} + +int nfnl_exp_build_add_request(const struct nfnl_exp *exp, int flags, + struct nl_msg **result) +{ + return nfnl_exp_build_message(exp, IPCTNL_MSG_EXP_NEW, flags, result); +} + +int nfnl_exp_add(struct nl_sock *sk, const struct nfnl_exp *exp, int flags) +{ + struct nl_msg *msg; + int err; + + if ((err = nfnl_exp_build_add_request(exp, flags, &msg)) < 0) + return err; + + err = nl_send_auto_complete(sk, msg); + nlmsg_free(msg); + if (err < 0) + return err; + + return wait_for_ack(sk); +} + +int nfnl_exp_build_delete_request(const struct nfnl_exp *exp, int flags, + struct nl_msg **result) +{ + return nfnl_exp_build_message(exp, IPCTNL_MSG_EXP_DELETE, flags, result); +} + +int nfnl_exp_del(struct nl_sock *sk, const struct nfnl_exp *exp, int flags) +{ + struct nl_msg *msg; + int err; + + if ((err = nfnl_exp_build_delete_request(exp, flags, &msg)) < 0) + return err; + + err = nl_send_auto_complete(sk, msg); + nlmsg_free(msg); + if (err < 0) + return err; + + return wait_for_ack(sk); +} + +int nfnl_exp_build_query_request(const struct nfnl_exp *exp, int flags, + struct nl_msg **result) +{ + return nfnl_exp_build_message(exp, IPCTNL_MSG_EXP_GET, flags, result); +} + +int nfnl_exp_query(struct nl_sock *sk, const struct nfnl_exp *exp, int flags) +{ + struct nl_msg *msg; + int err; + + if ((err = nfnl_exp_build_query_request(exp, flags, &msg)) < 0) + return err; + + err = nl_send_auto_complete(sk, msg); + nlmsg_free(msg); + if (err < 0) + return err; + + return wait_for_ack(sk); +} + +/** + * @name Cache Management + * @{ + */ + +/** + * Build a expectation cache holding all expectations currently in the kernel + * @arg sk Netlink socket. + * @arg result Pointer to store resulting cache. + * + * Allocates a new cache, initializes it properly and updates it to + * contain all expectations currently in the kernel. + * + * @return 0 on success or a negative error code. + */ +int nfnl_exp_alloc_cache(struct nl_sock *sk, struct nl_cache **result) +{ + return nl_cache_alloc_and_fill(&nfnl_exp_ops, sk, result); +} + +/** @} */ + +/** + * @name Expectation Addition + * @{ + */ + +/** @} */ + +static struct nl_af_group exp_groups[] = { + { AF_UNSPEC, NFNLGRP_CONNTRACK_EXP_NEW }, + { AF_UNSPEC, NFNLGRP_CONNTRACK_EXP_UPDATE }, + { AF_UNSPEC, NFNLGRP_CONNTRACK_EXP_DESTROY }, + { END_OF_GROUP_LIST }, +}; + +#define NFNLMSG_EXP_TYPE(type) NFNLMSG_TYPE(NFNL_SUBSYS_CTNETLINK_EXP, (type)) +static struct nl_cache_ops nfnl_exp_ops = { + .co_name = "netfilter/exp", + .co_hdrsize = NFNL_HDRLEN, + .co_msgtypes = { + { NFNLMSG_EXP_TYPE(IPCTNL_MSG_EXP_NEW), NL_ACT_NEW, "new" }, + { NFNLMSG_EXP_TYPE(IPCTNL_MSG_EXP_GET), NL_ACT_GET, "get" }, + { NFNLMSG_EXP_TYPE(IPCTNL_MSG_EXP_DELETE), NL_ACT_DEL, "del" }, + END_OF_MSGTYPES_LIST, + }, + .co_protocol = NETLINK_NETFILTER, + .co_groups = exp_groups, + .co_request_update = exp_request_update, + .co_msg_parser = exp_msg_parser, + .co_obj_ops = &exp_obj_ops, +}; + +static void __init exp_init(void) +{ + nl_cache_mngt_register(&nfnl_exp_ops); +} + +static void __exit exp_exit(void) +{ + nl_cache_mngt_unregister(&nfnl_exp_ops); +} + +/** @} */ diff --git a/lib/netfilter/exp_obj.c b/lib/netfilter/exp_obj.c new file mode 100644 index 0000000..69b4dd3 --- /dev/null +++ b/lib/netfilter/exp_obj.c @@ -0,0 +1,888 @@ +/* + * lib/netfilter/exp_obj.c Conntrack Expectation Object + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2008 Thomas Graf + * Copyright (c) 2007 Philip Craig + * Copyright (c) 2007 Secure Computing Corporation + * Copyright (c) 2012 Rich Fought + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +// The 32-bit attribute mask in the common object header isn't +// big enough to handle all attributes of an expectation. So +// we'll for sure specify optional attributes + parent attributes +// that are required for valid object comparison. Comparison of +// these parent attributes will include nested attributes. + +/** @cond SKIP */ +#define EXP_ATTR_FAMILY (1UL << 0) // 8-bit +#define EXP_ATTR_TIMEOUT (1UL << 1) // 32-bit +#define EXP_ATTR_ID (1UL << 2) // 32-bit +#define EXP_ATTR_HELPER_NAME (1UL << 3) // string +#define EXP_ATTR_ZONE (1UL << 4) // 16-bit +#define EXP_ATTR_FLAGS (1UL << 5) // 32-bit +#define EXP_ATTR_CLASS (1UL << 6) // 32-bit +#define EXP_ATTR_FN (1UL << 7) // String +// Tuples +#define EXP_ATTR_EXPECT_IP_SRC (1UL << 8) +#define EXP_ATTR_EXPECT_IP_DST (1UL << 9) +#define EXP_ATTR_EXPECT_L4PROTO_NUM (1UL << 10) +#define EXP_ATTR_EXPECT_L4PROTO_PORTS (1UL << 11) +#define EXP_ATTR_EXPECT_L4PROTO_ICMP (1UL << 12) +#define EXP_ATTR_MASTER_IP_SRC (1UL << 13) +#define EXP_ATTR_MASTER_IP_DST (1UL << 14) +#define EXP_ATTR_MASTER_L4PROTO_NUM (1UL << 15) +#define EXP_ATTR_MASTER_L4PROTO_PORTS (1UL << 16) +#define EXP_ATTR_MASTER_L4PROTO_ICMP (1UL << 17) +#define EXP_ATTR_MASK_IP_SRC (1UL << 18) +#define EXP_ATTR_MASK_IP_DST (1UL << 19) +#define EXP_ATTR_MASK_L4PROTO_NUM (1UL << 20) +#define EXP_ATTR_MASK_L4PROTO_PORTS (1UL << 21) +#define EXP_ATTR_MASK_L4PROTO_ICMP (1UL << 22) +#define EXP_ATTR_NAT_IP_SRC (1UL << 23) +#define EXP_ATTR_NAT_IP_DST (1UL << 24) +#define EXP_ATTR_NAT_L4PROTO_NUM (1UL << 25) +#define EXP_ATTR_NAT_L4PROTO_PORTS (1UL << 26) +#define EXP_ATTR_NAT_L4PROTO_ICMP (1UL << 27) +#define EXP_ATTR_NAT_DIR (1UL << 28) +/** @endcond */ + +static void exp_free_data(struct nl_object *c) +{ + struct nfnl_exp *exp = (struct nfnl_exp *) c; + + if (exp == NULL) + return; + + nl_addr_put(exp->exp_expect.src); + nl_addr_put(exp->exp_expect.dst); + nl_addr_put(exp->exp_master.src); + nl_addr_put(exp->exp_master.dst); + nl_addr_put(exp->exp_mask.src); + nl_addr_put(exp->exp_mask.dst); + nl_addr_put(exp->exp_nat.src); + nl_addr_put(exp->exp_nat.dst); + + free(exp->exp_fn); + free(exp->exp_helper_name); +} + +static int exp_clone(struct nl_object *_dst, struct nl_object *_src) +{ + struct nfnl_exp *dst = (struct nfnl_exp *) _dst; + struct nfnl_exp *src = (struct nfnl_exp *) _src; + struct nl_addr *addr; + + // Expectation + if (src->exp_expect.src) { + addr = nl_addr_clone(src->exp_expect.src); + if (!addr) + return -NLE_NOMEM; + dst->exp_expect.src = addr; + } + + if (src->exp_expect.dst) { + addr = nl_addr_clone(src->exp_expect.dst); + if (!addr) + return -NLE_NOMEM; + dst->exp_expect.dst = addr; + } + + // Master CT + if (src->exp_master.src) { + addr = nl_addr_clone(src->exp_master.src); + if (!addr) + return -NLE_NOMEM; + dst->exp_master.src = addr; + } + + if (src->exp_master.dst) { + addr = nl_addr_clone(src->exp_master.dst); + if (!addr) + return -NLE_NOMEM; + dst->exp_master.dst = addr; + } + + // Mask + if (src->exp_mask.src) { + addr = nl_addr_clone(src->exp_mask.src); + if (!addr) + return -NLE_NOMEM; + dst->exp_mask.src = addr; + } + + if (src->exp_mask.dst) { + addr = nl_addr_clone(src->exp_mask.dst); + if (!addr) + return -NLE_NOMEM; + dst->exp_mask.dst = addr; + } + + // NAT + if (src->exp_nat.src) { + addr = nl_addr_clone(src->exp_nat.src); + if (!addr) + return -NLE_NOMEM; + dst->exp_nat.src = addr; + } + + if (src->exp_nat.dst) { + addr = nl_addr_clone(src->exp_nat.dst); + if (!addr) + return -NLE_NOMEM; + dst->exp_nat.dst = addr; + } + + if (src->exp_fn) + dst->exp_fn = strdup(src->exp_fn); + + if (src->exp_helper_name) + dst->exp_helper_name = strdup(src->exp_helper_name); + + return 0; +} + +static void dump_addr(struct nl_dump_params *p, struct nl_addr *addr, int port) +{ + char buf[64]; + + if (addr) + nl_dump(p, "%s", nl_addr2str(addr, buf, sizeof(buf))); + + if (port) + nl_dump(p, ":%u ", port); + else if (addr) + nl_dump(p, " "); +} + +static void dump_icmp(struct nl_dump_params *p, struct nfnl_exp *exp, int tuple) +{ + if (nfnl_exp_test_icmp(exp, tuple)) { + + nl_dump(p, "icmp type %d ", nfnl_exp_get_icmp_type(exp, tuple)); + + nl_dump(p, "code %d ", nfnl_exp_get_icmp_code(exp, tuple)); + + nl_dump(p, "id %d ", nfnl_exp_get_icmp_id(exp, tuple)); + } +} + +static void exp_dump_tuples(struct nfnl_exp *exp, struct nl_dump_params *p) +{ + struct nl_addr *tuple_src, *tuple_dst; + int tuple_sport, tuple_dport; + int i = 0; + char buf[64]; + + for (i = NFNL_EXP_TUPLE_EXPECT; i < NFNL_EXP_TUPLE_MAX; i++) { + tuple_src = NULL; + tuple_dst = NULL; + tuple_sport = 0; + tuple_dport = 0; + + // Test needed for NAT case + if (nfnl_exp_test_src(exp, i)) + tuple_src = nfnl_exp_get_src(exp, i); + if (nfnl_exp_test_dst(exp, i)) + tuple_dst = nfnl_exp_get_dst(exp, i); + + // Don't have tests for individual ports/types/codes/ids, + if (nfnl_exp_test_l4protonum(exp, i)) { + nl_dump(p, "%s ", + nl_ip_proto2str(nfnl_exp_get_l4protonum(exp, i), buf, sizeof(buf))); + } + + if (nfnl_exp_test_ports(exp, i)) { + tuple_sport = nfnl_exp_get_src_port(exp, i); + tuple_dport = nfnl_exp_get_dst_port(exp, i); + } + + dump_addr(p, tuple_src, tuple_sport); + dump_addr(p, tuple_dst, tuple_dport); + dump_icmp(p, exp, 0); + } + + if (nfnl_exp_test_nat_dir(exp)) + nl_dump(p, "nat dir %s ", exp->exp_nat_dir); + +} + +/* FIXME Compatible with /proc/net/nf_conntrack */ +static void exp_dump_line(struct nl_object *a, struct nl_dump_params *p) +{ + struct nfnl_exp *exp = (struct nfnl_exp *) a; + + nl_new_line(p); + + exp_dump_tuples(exp, p); + + nl_dump(p, "\n"); +} + +static void exp_dump_details(struct nl_object *a, struct nl_dump_params *p) +{ + struct nfnl_exp *exp = (struct nfnl_exp *) a; + char buf[64]; + int fp = 0; + + exp_dump_line(a, p); + + nl_dump(p, " id 0x%x ", exp->exp_id); + nl_dump_line(p, "family %s ", + nl_af2str(exp->exp_family, buf, sizeof(buf))); + + if (nfnl_exp_test_timeout(exp)) { + uint64_t timeout_ms = nfnl_exp_get_timeout(exp) * 1000UL; + nl_dump(p, "timeout %s ", + nl_msec2str(timeout_ms, buf, sizeof(buf))); + } + + if (nfnl_exp_test_helper_name(exp)) + nl_dump(p, "helper %s ", exp->exp_helper_name); + + if (nfnl_exp_test_fn(exp)) + nl_dump(p, "fn %s ", exp->exp_fn); + + if (nfnl_exp_test_class(exp)) + nl_dump(p, "class %u ", nfnl_exp_get_class(exp)); + + if (nfnl_exp_test_zone(exp)) + nl_dump(p, "zone %u ", nfnl_exp_get_zone(exp)); + + if (nfnl_exp_test_flags(exp)) + nl_dump(p, "<"); +#define PRINT_FLAG(str) \ + { nl_dump(p, "%s%s", fp++ ? "," : "", (str)); } + + if (exp->exp_flags & NF_CT_EXPECT_PERMANENT) + PRINT_FLAG("PERMANENT"); + if (exp->exp_flags & NF_CT_EXPECT_INACTIVE) + PRINT_FLAG("INACTIVE"); + if (exp->exp_flags & NF_CT_EXPECT_USERSPACE) + PRINT_FLAG("USERSPACE"); +#undef PRINT_FLAG + + if (nfnl_exp_test_flags(exp)) + nl_dump(p, ">"); + + nl_dump(p, "\n"); +} + +static int exp_cmp_l4proto_ports (union nfnl_exp_protodata *a, union nfnl_exp_protodata *b) { + // Must return 0 for match, 1 for mismatch + int d = 0; + d = ( (a->port.src != b->port.src) || + (a->port.dst != b->port.dst) ); + + return d; +} + +static int exp_cmp_l4proto_icmp (union nfnl_exp_protodata *a, union nfnl_exp_protodata *b) { + // Must return 0 for match, 1 for mismatch + int d = 0; + d = ( (a->icmp.code != b->icmp.code) || + (a->icmp.type != b->icmp.type) || + (a->icmp.id != b->icmp.id) ); + + return d; +} + +static int exp_compare(struct nl_object *_a, struct nl_object *_b, + uint32_t attrs, int flags) +{ + struct nfnl_exp *a = (struct nfnl_exp *) _a; + struct nfnl_exp *b = (struct nfnl_exp *) _b; + int diff = 0; + +#define EXP_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, EXP_ATTR_##ATTR, a, b, EXPR) +#define EXP_DIFF_VAL(ATTR, FIELD) EXP_DIFF(ATTR, a->FIELD != b->FIELD) +#define EXP_DIFF_STRING(ATTR, FIELD) EXP_DIFF(ATTR, (strcmp(a->FIELD, b->FIELD) != 0)) +#define EXP_DIFF_ADDR(ATTR, FIELD) \ + ((flags & LOOSE_COMPARISON) \ + ? EXP_DIFF(ATTR, nl_addr_cmp_prefix(a->FIELD, b->FIELD)) \ + : EXP_DIFF(ATTR, nl_addr_cmp(a->FIELD, b->FIELD))) +#define EXP_DIFF_L4PROTO_PORTS(ATTR, FIELD) \ + EXP_DIFF(ATTR, exp_cmp_l4proto_ports(&(a->FIELD), &(b->FIELD))) +#define EXP_DIFF_L4PROTO_ICMP(ATTR, FIELD) \ + EXP_DIFF(ATTR, exp_cmp_l4proto_icmp(&(a->FIELD), &(b->FIELD))) + + diff |= EXP_DIFF_VAL(FAMILY, exp_family); + diff |= EXP_DIFF_VAL(TIMEOUT, exp_timeout); + diff |= EXP_DIFF_VAL(ID, exp_id); + diff |= EXP_DIFF_VAL(ZONE, exp_zone); + diff |= EXP_DIFF_VAL(CLASS, exp_class); + diff |= EXP_DIFF_VAL(FLAGS, exp_flags); + diff |= EXP_DIFF_VAL(NAT_DIR, exp_nat_dir); + + diff |= EXP_DIFF_STRING(FN, exp_fn); + diff |= EXP_DIFF_STRING(HELPER_NAME, exp_helper_name); + + diff |= EXP_DIFF_ADDR(EXPECT_IP_SRC, exp_expect.src); + diff |= EXP_DIFF_ADDR(EXPECT_IP_DST, exp_expect.dst); + diff |= EXP_DIFF_VAL(EXPECT_L4PROTO_NUM, exp_expect.proto.l4protonum); + diff |= EXP_DIFF_L4PROTO_PORTS(EXPECT_L4PROTO_PORTS, exp_expect.proto.l4protodata); + diff |= EXP_DIFF_L4PROTO_ICMP(EXPECT_L4PROTO_ICMP, exp_expect.proto.l4protodata); + + diff |= EXP_DIFF_ADDR(MASTER_IP_SRC, exp_master.src); + diff |= EXP_DIFF_ADDR(MASTER_IP_DST, exp_master.dst); + diff |= EXP_DIFF_VAL(MASTER_L4PROTO_NUM, exp_master.proto.l4protonum); + diff |= EXP_DIFF_L4PROTO_PORTS(MASTER_L4PROTO_PORTS, exp_master.proto.l4protodata); + diff |= EXP_DIFF_L4PROTO_ICMP(MASTER_L4PROTO_ICMP, exp_master.proto.l4protodata); + + diff |= EXP_DIFF_ADDR(MASK_IP_SRC, exp_mask.src); + diff |= EXP_DIFF_ADDR(MASK_IP_DST, exp_mask.dst); + diff |= EXP_DIFF_VAL(MASK_L4PROTO_NUM, exp_mask.proto.l4protonum); + diff |= EXP_DIFF_L4PROTO_PORTS(MASK_L4PROTO_PORTS, exp_mask.proto.l4protodata); + diff |= EXP_DIFF_L4PROTO_ICMP(MASK_L4PROTO_ICMP, exp_mask.proto.l4protodata); + + diff |= EXP_DIFF_ADDR(NAT_IP_SRC, exp_nat.src); + diff |= EXP_DIFF_ADDR(NAT_IP_DST, exp_nat.dst); + diff |= EXP_DIFF_VAL(NAT_L4PROTO_NUM, exp_nat.proto.l4protonum); + diff |= EXP_DIFF_L4PROTO_PORTS(NAT_L4PROTO_PORTS, exp_nat.proto.l4protodata); + diff |= EXP_DIFF_L4PROTO_ICMP(NAT_L4PROTO_ICMP, exp_nat.proto.l4protodata); + +#undef EXP_DIFF +#undef EXP_DIFF_VAL +#undef EXP_DIFF_STRING +#undef EXP_DIFF_ADDR +#undef EXP_DIFF_L4PROTO_PORTS +#undef EXP_DIFF_L4PROTO_ICMP + + return diff; +} + +// CLI arguments? +static const struct trans_tbl exp_attrs[] = { + __ADD(EXP_ATTR_FAMILY, family) + __ADD(EXP_ATTR_TIMEOUT, timeout) + __ADD(EXP_ATTR_ID, id) + __ADD(EXP_ATTR_HELPER_NAME, helpername) + __ADD(EXP_ATTR_ZONE, zone) + __ADD(EXP_ATTR_CLASS, class) + __ADD(EXP_ATTR_FLAGS, flags) + __ADD(EXP_ATTR_FN, function) + __ADD(EXP_ATTR_EXPECT_IP_SRC, expectipsrc) + __ADD(EXP_ATTR_EXPECT_IP_DST, expectipdst) + __ADD(EXP_ATTR_EXPECT_L4PROTO_NUM, expectprotonum) + __ADD(EXP_ATTR_EXPECT_L4PROTO_PORTS, expectports) + __ADD(EXP_ATTR_EXPECT_L4PROTO_ICMP, expecticmp) + __ADD(EXP_ATTR_MASTER_IP_SRC, masteripsrc) + __ADD(EXP_ATTR_MASTER_IP_DST, masteripdst) + __ADD(EXP_ATTR_MASTER_L4PROTO_NUM, masterprotonum) + __ADD(EXP_ATTR_MASTER_L4PROTO_PORTS, masterports) + __ADD(EXP_ATTR_MASTER_L4PROTO_ICMP, mastericmp) + __ADD(EXP_ATTR_MASK_IP_SRC, maskipsrc) + __ADD(EXP_ATTR_MASK_IP_DST, maskipdst) + __ADD(EXP_ATTR_MASK_L4PROTO_NUM, maskprotonum) + __ADD(EXP_ATTR_MASK_L4PROTO_PORTS, maskports) + __ADD(EXP_ATTR_MASK_L4PROTO_ICMP, maskicmp) + __ADD(EXP_ATTR_NAT_IP_SRC, natipsrc) + __ADD(EXP_ATTR_NAT_IP_DST, natipdst) + __ADD(EXP_ATTR_NAT_L4PROTO_NUM, natprotonum) + __ADD(EXP_ATTR_NAT_L4PROTO_PORTS, natports) + __ADD(EXP_ATTR_NAT_L4PROTO_ICMP, naticmp) + __ADD(EXP_ATTR_NAT_DIR, natdir) +}; + +static char *exp_attrs2str(int attrs, char *buf, size_t len) +{ + return __flags2str(attrs, buf, len, exp_attrs, ARRAY_SIZE(exp_attrs)); +} + +/** + * @name Allocation/Freeing + * @{ + */ + +struct nfnl_exp *nfnl_exp_alloc(void) +{ + return (struct nfnl_exp *) nl_object_alloc(&exp_obj_ops); +} + +void nfnl_exp_get(struct nfnl_exp *exp) +{ + nl_object_get((struct nl_object *) exp); +} + +void nfnl_exp_put(struct nfnl_exp *exp) +{ + nl_object_put((struct nl_object *) exp); +} + +/** @} */ + +/** + * @name Attributes + * @{ + */ + +void nfnl_exp_set_family(struct nfnl_exp *exp, uint8_t family) +{ + exp->exp_family = family; + exp->ce_mask |= EXP_ATTR_FAMILY; +} + +uint8_t nfnl_exp_get_family(const struct nfnl_exp *exp) +{ + if (exp->ce_mask & EXP_ATTR_FAMILY) + return exp->exp_family; + else + return AF_UNSPEC; +} + +void nfnl_exp_set_flags(struct nfnl_exp *exp, uint32_t flags) +{ + exp->exp_flags |= flags; + exp->ce_mask |= EXP_ATTR_FLAGS; +} + +int nfnl_exp_test_flags(const struct nfnl_exp *exp) +{ + return !!(exp->ce_mask & EXP_ATTR_FLAGS); +} + +void nfnl_exp_unset_flags(struct nfnl_exp *exp, uint32_t flags) +{ + exp->exp_flags &= ~flags; + exp->ce_mask |= EXP_ATTR_FLAGS; +} + +uint32_t nfnl_exp_get_flags(const struct nfnl_exp *exp) +{ + return exp->exp_flags; +} + +static const struct trans_tbl flag_table[] = { + __ADD(IPS_EXPECTED, expected) + __ADD(IPS_SEEN_REPLY, seen_reply) + __ADD(IPS_ASSURED, assured) +}; + +char * nfnl_exp_flags2str(int flags, char *buf, size_t len) +{ + return __flags2str(flags, buf, len, flag_table, + ARRAY_SIZE(flag_table)); +} + +int nfnl_exp_str2flags(const char *name) +{ + return __str2flags(name, flag_table, ARRAY_SIZE(flag_table)); +} + +void nfnl_exp_set_timeout(struct nfnl_exp *exp, uint32_t timeout) +{ + exp->exp_timeout = timeout; + exp->ce_mask |= EXP_ATTR_TIMEOUT; +} + +int nfnl_exp_test_timeout(const struct nfnl_exp *exp) +{ + return !!(exp->ce_mask & EXP_ATTR_TIMEOUT); +} + +uint32_t nfnl_exp_get_timeout(const struct nfnl_exp *exp) +{ + return exp->exp_timeout; +} + +void nfnl_exp_set_id(struct nfnl_exp *exp, uint32_t id) +{ + exp->exp_id = id; + exp->ce_mask |= EXP_ATTR_ID; +} + +int nfnl_exp_test_id(const struct nfnl_exp *exp) +{ + return !!(exp->ce_mask & EXP_ATTR_ID); +} + +uint32_t nfnl_exp_get_id(const struct nfnl_exp *exp) +{ + return exp->exp_id; +} + +int nfnl_exp_set_helper_name(struct nfnl_exp *exp, void *name) +{ + free(exp->exp_helper_name); + exp->exp_helper_name = strdup(name); + if (!exp->exp_helper_name) + return -NLE_NOMEM; + + exp->ce_mask |= EXP_ATTR_HELPER_NAME; + return 0; +} + +int nfnl_exp_test_helper_name(const struct nfnl_exp *exp) +{ + return !!(exp->ce_mask & EXP_ATTR_HELPER_NAME); +} + +const char * nfnl_exp_get_helper_name(const struct nfnl_exp *exp) +{ + return exp->exp_helper_name; +} + +void nfnl_exp_set_zone(struct nfnl_exp *exp, uint16_t zone) +{ + exp->exp_zone = zone; + exp->ce_mask |= EXP_ATTR_ZONE; +} + +int nfnl_exp_test_zone(const struct nfnl_exp *exp) +{ + return !!(exp->ce_mask & EXP_ATTR_ZONE); +} + +uint16_t nfnl_exp_get_zone(const struct nfnl_exp *exp) +{ + return exp->exp_zone; +} + +void nfnl_exp_set_class(struct nfnl_exp *exp, uint32_t class) +{ + exp->exp_class = class; + exp->ce_mask |= EXP_ATTR_CLASS; +} + +int nfnl_exp_test_class(const struct nfnl_exp *exp) +{ + return !!(exp->ce_mask & EXP_ATTR_CLASS); +} + +uint32_t nfnl_exp_get_class(const struct nfnl_exp *exp) +{ + return exp->exp_class; +} + +int nfnl_exp_set_fn(struct nfnl_exp *exp, void *fn) +{ + free(exp->exp_fn); + exp->exp_fn = strdup(fn); + if (!exp->exp_fn) + return -NLE_NOMEM; + + exp->ce_mask |= EXP_ATTR_FN; + return 0; +} + +int nfnl_exp_test_fn(const struct nfnl_exp *exp) +{ + return !!(exp->ce_mask & EXP_ATTR_FN); +} + +const char * nfnl_exp_get_fn(const struct nfnl_exp *exp) +{ + return exp->exp_fn; +} + +void nfnl_exp_set_nat_dir(struct nfnl_exp *exp, uint8_t nat_dir) +{ + exp->exp_nat_dir = nat_dir; + exp->ce_mask |= EXP_ATTR_NAT_DIR; +} + +int nfnl_exp_test_nat_dir(const struct nfnl_exp *exp) +{ + return !!(exp->ce_mask & EXP_ATTR_NAT_DIR); +} + +uint8_t nfnl_exp_get_nat_dir(const struct nfnl_exp *exp) +{ + return exp->exp_nat_dir; +} + +#define EXP_GET_TUPLE(e, t) \ + (t == NFNL_EXP_TUPLE_MASTER) ? \ + &(e->exp_master) : \ + (t == NFNL_EXP_TUPLE_MASK) ? \ + &(e->exp_mask) : \ + (t == NFNL_EXP_TUPLE_NAT) ? \ + &(e->exp_nat) : &(exp->exp_expect) + +static int exp_get_src_attr(int tuple) +{ + int attr = 0; + + switch (tuple) { + case NFNL_EXP_TUPLE_MASTER: + attr = EXP_ATTR_MASTER_IP_SRC; + break; + case NFNL_EXP_TUPLE_MASK: + attr = EXP_ATTR_MASK_IP_SRC; + break; + case NFNL_EXP_TUPLE_NAT: + attr = EXP_ATTR_NAT_IP_SRC; + break; + case NFNL_EXP_TUPLE_EXPECT: + default : + attr = EXP_ATTR_EXPECT_IP_SRC; + } + + return attr; +} + +static int exp_get_dst_attr(int tuple) +{ + int attr = 0; + + switch (tuple) { + case NFNL_EXP_TUPLE_MASTER: + attr = EXP_ATTR_MASTER_IP_DST; + break; + case NFNL_EXP_TUPLE_MASK: + attr = EXP_ATTR_MASK_IP_DST; + break; + case NFNL_EXP_TUPLE_NAT: + attr = EXP_ATTR_NAT_IP_DST; + break; + case NFNL_EXP_TUPLE_EXPECT: + default : + attr = EXP_ATTR_EXPECT_IP_DST; + } + + return attr; +} + + +static int exp_set_addr(struct nfnl_exp *exp, struct nl_addr *addr, + int attr, struct nl_addr ** exp_addr) +{ + if (exp->ce_mask & EXP_ATTR_FAMILY) { + if (addr->a_family != exp->exp_family) + return -NLE_AF_MISMATCH; + } else + nfnl_exp_set_family(exp, addr->a_family); + + if (*exp_addr) + nl_addr_put(*exp_addr); + + nl_addr_get(addr); + *exp_addr = addr; + exp->ce_mask |= attr; + + return 0; +} + +int nfnl_exp_set_src(struct nfnl_exp *exp, int tuple, struct nl_addr *addr) +{ + struct nfnl_exp_dir *dir = EXP_GET_TUPLE(exp, tuple); + + return exp_set_addr(exp, addr, exp_get_src_attr(tuple), &dir->src); +} + +int nfnl_exp_set_dst(struct nfnl_exp *exp, int tuple, struct nl_addr *addr) +{ + struct nfnl_exp_dir *dir = EXP_GET_TUPLE(exp, tuple); + + return exp_set_addr(exp, addr, exp_get_dst_attr(tuple), &dir->dst); +} + +int nfnl_exp_test_src(const struct nfnl_exp *exp, int tuple) +{ + return !!(exp->ce_mask & exp_get_src_attr(tuple)); +} + +int nfnl_exp_test_dst(const struct nfnl_exp *exp, int tuple) +{ + return !!(exp->ce_mask & exp_get_dst_attr(tuple)); +} + +struct nl_addr *nfnl_exp_get_src(const struct nfnl_exp *exp, int tuple) +{ + const struct nfnl_exp_dir *dir = EXP_GET_TUPLE(exp, tuple); + + if (!(exp->ce_mask & exp_get_src_attr(tuple))) + return NULL; + return dir->src; +} + +struct nl_addr *nfnl_exp_get_dst(const struct nfnl_exp *exp, int tuple) +{ + const struct nfnl_exp_dir *dir = EXP_GET_TUPLE(exp, tuple); + + if (!(exp->ce_mask & exp_get_dst_attr(tuple))) + return NULL; + return dir->dst; +} + +static int exp_get_l4protonum_attr(int tuple) +{ + int attr = 0; + + switch (tuple) { + case NFNL_EXP_TUPLE_MASTER: + attr = EXP_ATTR_MASTER_L4PROTO_NUM; + break; + case NFNL_EXP_TUPLE_MASK: + attr = EXP_ATTR_MASK_L4PROTO_NUM; + break; + case NFNL_EXP_TUPLE_NAT: + attr = EXP_ATTR_NAT_L4PROTO_NUM; + break; + case NFNL_EXP_TUPLE_EXPECT: + default : + attr = EXP_ATTR_EXPECT_L4PROTO_NUM; + } + + return attr; +} + +void nfnl_exp_set_l4protonum(struct nfnl_exp *exp, int tuple, uint8_t l4protonum) +{ + struct nfnl_exp_dir *dir = EXP_GET_TUPLE(exp, tuple); + + dir->proto.l4protonum = l4protonum; + exp->ce_mask |= exp_get_l4protonum_attr(tuple); +} + +int nfnl_exp_test_l4protonum(const struct nfnl_exp *exp, int tuple) +{ + return !!(exp->ce_mask & exp_get_l4protonum_attr(tuple)); +} + +uint8_t nfnl_exp_get_l4protonum(const struct nfnl_exp *exp, int tuple) +{ + const struct nfnl_exp_dir *dir = EXP_GET_TUPLE(exp, tuple); + return dir->proto.l4protonum; +} + +static int exp_get_l4ports_attr(int tuple) +{ + int attr = 0; + + switch (tuple) { + case NFNL_EXP_TUPLE_MASTER: + attr = EXP_ATTR_MASTER_L4PROTO_PORTS; + break; + case NFNL_EXP_TUPLE_MASK: + attr = EXP_ATTR_MASK_L4PROTO_PORTS; + break; + case NFNL_EXP_TUPLE_NAT: + attr = EXP_ATTR_NAT_L4PROTO_PORTS; + break; + case NFNL_EXP_TUPLE_EXPECT: + default : + attr = EXP_ATTR_EXPECT_L4PROTO_PORTS; + } + + return attr; +} + +void nfnl_exp_set_ports(struct nfnl_exp *exp, int tuple, uint16_t srcport, uint16_t dstport) +{ + struct nfnl_exp_dir *dir = EXP_GET_TUPLE(exp, tuple); + + dir->proto.l4protodata.port.src = srcport; + dir->proto.l4protodata.port.dst = dstport; + + exp->ce_mask |= exp_get_l4ports_attr(tuple); +} + +int nfnl_exp_test_ports(const struct nfnl_exp *exp, int tuple) +{ + return !!(exp->ce_mask & exp_get_l4ports_attr(tuple)); +} + +uint16_t nfnl_exp_get_src_port(const struct nfnl_exp *exp, int tuple) +{ + const struct nfnl_exp_dir *dir = EXP_GET_TUPLE(exp, tuple); + return dir->proto.l4protodata.port.src; +} + +uint16_t nfnl_exp_get_dst_port(const struct nfnl_exp *exp, int tuple) +{ + const struct nfnl_exp_dir *dir = EXP_GET_TUPLE(exp, tuple); + + return dir->proto.l4protodata.port.dst; +} + +static int exp_get_l4icmp_attr(int tuple) +{ + int attr = 0; + + switch (tuple) { + case NFNL_EXP_TUPLE_MASTER: + attr = EXP_ATTR_MASTER_L4PROTO_ICMP; + break; + case NFNL_EXP_TUPLE_MASK: + attr = EXP_ATTR_MASK_L4PROTO_ICMP; + break; + case NFNL_EXP_TUPLE_NAT: + attr = EXP_ATTR_NAT_L4PROTO_ICMP; + break; + case NFNL_EXP_TUPLE_EXPECT: + default : + attr = EXP_ATTR_EXPECT_L4PROTO_ICMP; + } + + return attr; +} + +void nfnl_exp_set_icmp(struct nfnl_exp *exp, int tuple, uint16_t id, uint8_t type, uint8_t code) +{ + struct nfnl_exp_dir *dir = EXP_GET_TUPLE(exp, tuple); + + dir->proto.l4protodata.icmp.id = id; + dir->proto.l4protodata.icmp.type = type; + dir->proto.l4protodata.icmp.code = code; + + exp->ce_mask |= exp_get_l4icmp_attr(tuple); +} + +int nfnl_exp_test_icmp(const struct nfnl_exp *exp, int tuple) +{ + int attr = exp_get_l4icmp_attr(tuple); + return !!(exp->ce_mask & attr); +} + +uint16_t nfnl_exp_get_icmp_id(const struct nfnl_exp *exp, int tuple) +{ + const struct nfnl_exp_dir *dir = EXP_GET_TUPLE(exp, tuple); + + return dir->proto.l4protodata.icmp.id; +} + +uint8_t nfnl_exp_get_icmp_type(const struct nfnl_exp *exp, int tuple) +{ + const struct nfnl_exp_dir *dir = EXP_GET_TUPLE(exp, tuple); + + return dir->proto.l4protodata.icmp.type; +} + +uint8_t nfnl_exp_get_icmp_code(const struct nfnl_exp *exp, int tuple) +{ + const struct nfnl_exp_dir *dir = EXP_GET_TUPLE(exp, tuple); + + return dir->proto.l4protodata.icmp.code; +} + +/** @} */ + +struct nl_object_ops exp_obj_ops = { + .oo_name = "netfilter/exp", + .oo_size = sizeof(struct nfnl_exp), + .oo_free_data = exp_free_data, + .oo_clone = exp_clone, + .oo_dump = { + [NL_DUMP_LINE] = exp_dump_line, + [NL_DUMP_DETAILS] = exp_dump_details, + }, + .oo_compare = exp_compare, + .oo_attrs2str = exp_attrs2str, +}; + +/** @} */ diff --git a/lib/netfilter/log.c b/lib/netfilter/log.c index 96ae6c5..1bab9b6 100644 --- a/lib/netfilter/log.c +++ b/lib/netfilter/log.c @@ -21,7 +21,7 @@ #include #include -#include +#include #include #include #include diff --git a/lib/netfilter/log_msg.c b/lib/netfilter/log_msg.c index cad6ddd..6e09da2 100644 --- a/lib/netfilter/log_msg.c +++ b/lib/netfilter/log_msg.c @@ -22,10 +22,11 @@ #include #include -#include +#include #include #include #include +#include #if __BYTE_ORDER == __BIG_ENDIAN static uint64_t ntohll(uint64_t x) @@ -35,7 +36,7 @@ static uint64_t ntohll(uint64_t x) #elif __BYTE_ORDER == __LITTLE_ENDIAN static uint64_t ntohll(uint64_t x) { - return __bswap_64(x); + return bswap_64(x); } #endif diff --git a/lib/netfilter/log_msg_obj.c b/lib/netfilter/log_msg_obj.c index d2cde4e..57db9d4 100644 --- a/lib/netfilter/log_msg_obj.c +++ b/lib/netfilter/log_msg_obj.c @@ -11,7 +11,7 @@ * Copyright (c) 2007 Secure Computing Corporation */ -#include +#include #include #include #include @@ -76,7 +76,7 @@ static void log_msg_dump(struct nl_object *a, struct nl_dump_params *p) struct nl_cache *link_cache; char buf[64]; - link_cache = nl_cache_mngt_require("route/link"); + link_cache = nl_cache_mngt_require_safe("route/link"); nl_new_line(p); @@ -167,6 +167,9 @@ static void log_msg_dump(struct nl_object *a, struct nl_dump_params *p) nl_dump(p, "SEQGLOBAL=%d ", msg->log_msg_seq_global); nl_dump(p, "\n"); + + if (link_cache) + nl_cache_put(link_cache); } /** diff --git a/lib/netfilter/log_obj.c b/lib/netfilter/log_obj.c index 43c4a06..2b11414 100644 --- a/lib/netfilter/log_obj.c +++ b/lib/netfilter/log_obj.c @@ -12,7 +12,7 @@ * Copyright (c) 2008 Patrick McHardy */ -#include +#include #include #include diff --git a/lib/netfilter/netfilter.c b/lib/netfilter/netfilter.c index addecd7..0062994 100644 --- a/lib/netfilter/netfilter.c +++ b/lib/netfilter/netfilter.c @@ -9,7 +9,7 @@ * Copyright (c) 2008 Patrick McHardy */ -#include +#include #include #include diff --git a/lib/netfilter/nfnl.c b/lib/netfilter/nfnl.c index ddce4b9..f028a85 100644 --- a/lib/netfilter/nfnl.c +++ b/lib/netfilter/nfnl.c @@ -6,13 +6,13 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2008 Thomas Graf + * Copyright (c) 2003-2012 Thomas Graf * Copyright (c) 2007 Philip Craig * Copyright (c) 2007 Secure Computing Corporation */ /** - * @defgroup nfnl Netfilter Netlink + * @defgroup nfnl Netfilter Library (libnl-nf) * * @par Message Format * @code @@ -61,7 +61,7 @@ * @{ */ -#include +#include #include #include diff --git a/lib/netfilter/queue.c b/lib/netfilter/queue.c index ff1de0e..5655647 100644 --- a/lib/netfilter/queue.c +++ b/lib/netfilter/queue.c @@ -19,7 +19,7 @@ #include #include -#include +#include #include #include #include diff --git a/lib/netfilter/queue_msg.c b/lib/netfilter/queue_msg.c index c40f8c2..95d8ad3 100644 --- a/lib/netfilter/queue_msg.c +++ b/lib/netfilter/queue_msg.c @@ -20,10 +20,11 @@ #include #include -#include +#include #include #include #include +#include static struct nl_cache_ops nfnl_queue_msg_ops; @@ -35,7 +36,7 @@ static uint64_t ntohll(uint64_t x) #elif __BYTE_ORDER == __LITTLE_ENDIAN static uint64_t ntohll(uint64_t x) { - return __bswap_64(x); + return bswap_64(x); } #endif diff --git a/lib/netfilter/queue_msg_obj.c b/lib/netfilter/queue_msg_obj.c index 33305ed..8085f1b 100644 --- a/lib/netfilter/queue_msg_obj.c +++ b/lib/netfilter/queue_msg_obj.c @@ -9,7 +9,7 @@ * Copyright (c) 2007, 2008 Patrick McHardy */ -#include +#include #include #include #include @@ -66,7 +66,7 @@ static void nfnl_queue_msg_dump(struct nl_object *a, struct nl_dump_params *p) struct nl_cache *link_cache; char buf[64]; - link_cache = nl_cache_mngt_require("route/link"); + link_cache = nl_cache_mngt_require_safe("route/link"); nl_new_line(p); @@ -152,6 +152,9 @@ static void nfnl_queue_msg_dump(struct nl_object *a, struct nl_dump_params *p) buf, sizeof(buf))); nl_dump(p, "\n"); + + if (link_cache) + nl_cache_put(link_cache); } /** diff --git a/lib/netfilter/queue_obj.c b/lib/netfilter/queue_obj.c index 05a9cb8..5edcc68 100644 --- a/lib/netfilter/queue_obj.c +++ b/lib/netfilter/queue_obj.c @@ -16,7 +16,7 @@ * @{ */ -#include +#include #include #include diff --git a/lib/nl.c b/lib/nl.c index 8f6f5f1..fa43c56 100644 --- a/lib/nl.c +++ b/lib/nl.c @@ -6,109 +6,95 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2008 Thomas Graf + * Copyright (c) 2003-2012 Thomas Graf */ /** - * @defgroup core Core + * @defgroup core Core Library (libnl) * - * @details - * @par 1) Connecting the socket - * @code - * // Bind and connect the socket to a protocol, NETLINK_ROUTE in this example. - * nl_connect(sk, NETLINK_ROUTE); - * @endcode + * Socket handling, connection management, sending and receiving of data, + * message construction and parsing, object caching system, ... * - * @par 2) Sending data - * @code - * // The most rudimentary method is to use nl_sendto() simply pushing - * // a piece of data to the other netlink peer. This method is not - * // recommended. - * const char buf[] = { 0x01, 0x02, 0x03, 0x04 }; - * nl_sendto(sk, buf, sizeof(buf)); + * This is the API reference of the core library. It is not meant as a guide + * but as a reference. Please refer to the core library guide for detailed + * documentation on the library architecture and examples: * - * // A more comfortable interface is nl_send() taking a pointer to - * // a netlink message. - * struct nl_msg *msg = my_msg_builder(); - * nl_send(sk, nlmsg_hdr(msg)); + * * @ref_asciidoc{core,_,Netlink Core Library Development Guide} * - * // nl_sendmsg() provides additional control over the sendmsg() message - * // header in order to allow more specific addressing of multiple peers etc. - * struct msghdr hdr = { ... }; - * nl_sendmsg(sk, nlmsg_hdr(msg), &hdr); * - * // You're probably too lazy to fill out the netlink pid, sequence number - * // and message flags all the time. nl_send_auto_complete() automatically - * // extends your message header as needed with an appropriate sequence - * // number, the netlink pid stored in the netlink socket and the message - * // flags NLM_F_REQUEST and NLM_F_ACK (if not disabled in the socket) - * nl_send_auto_complete(sk, nlmsg_hdr(msg)); - * - * // Simple protocols don't require the complex message construction interface - * // and may favour nl_send_simple() to easly send a bunch of payload - * // encapsulated in a netlink message header. - * nl_send_simple(sk, MY_MSG_TYPE, 0, buf, sizeof(buf)); - * @endcode - * - * @par 3) Receiving data - * @code - * // nl_recv() receives a single message allocating a buffer for the message - * // content and gives back the pointer to you. - * struct sockaddr_nl peer; - * unsigned char *msg; - * nl_recv(sk, &peer, &msg); - * - * // nl_recvmsgs() receives a bunch of messages until the callback system - * // orders it to state, usually after receving a compolete multi part - * // message series. - * nl_recvmsgs(sk, my_callback_configuration); - * - * // nl_recvmsgs_default() acts just like nl_recvmsg() but uses the callback - * // configuration stored in the socket. - * nl_recvmsgs_default(sk); - * - * // In case you want to wait for the ACK to be recieved that you requested - * // with your latest message, you can call nl_wait_for_ack() - * nl_wait_for_ack(sk); - * @endcode - * - * @par 4) Closing - * @code - * // Close the socket first to release kernel memory - * nl_close(sk); - * @endcode - * * @{ */ -#include +#include #include #include #include #include #include +/** + * @defgroup core_types Data Types + * + * Core library data types + * @{ + * @} + * + * @defgroup send_recv Send & Receive Data + * + * Connection management, sending & receiving of data + * + * Related sections in the development guide: + * - @core_doc{core_send_recv, Sending & Receiving} + * - @core_doc{core_sockets, Sockets} + * + * @{ + * + * Header + * ------ + * ~~~~{.c} + * #include + * ~~~~ + */ + /** * @name Connection Management * @{ */ /** - * Create and connect netlink socket. - * @arg sk Netlink socket. - * @arg protocol Netlink protocol to use. + * Create file descriptor and bind socket. + * @arg sk Netlink socket (required) + * @arg protocol Netlink protocol to use (required) * - * Creates a netlink socket using the specified protocol, binds the socket - * and issues a connection attempt. + * Creates a new Netlink socket using `socket()` and binds the socket to the + * protocol and local port specified in the `sk` socket object. Fails if + * the socket is already connected. + * + * @note If available, the `close-on-exec` (`SOCK_CLOEXEC`) feature is enabled + * automatically on the new file descriptor. This causes the socket to + * be closed automatically if any of the `exec` family functions succeed. + * This is essential for multi threaded programs. + * + * @see nl_socket_alloc() + * @see nl_close() * * @return 0 on success or a negative error code. + * + * @retval -NLE_BAD_SOCK Socket is already connected */ int nl_connect(struct nl_sock *sk, int protocol) { - int err; + int err, flags = 0; socklen_t addrlen; - sk->s_fd = socket(AF_NETLINK, SOCK_RAW, protocol); +#ifdef SOCK_CLOEXEC + flags |= SOCK_CLOEXEC; +#endif + + if (sk->s_fd != -1) + return -NLE_BAD_SOCK; + + sk->s_fd = socket(AF_NETLINK, SOCK_RAW | flags, protocol); if (sk->s_fd < 0) { err = -nl_syserr2nlerr(errno); goto errout; @@ -149,15 +135,24 @@ int nl_connect(struct nl_sock *sk, int protocol) return 0; errout: - close(sk->s_fd); - sk->s_fd = -1; + if (sk->s_fd != -1) { + close(sk->s_fd); + sk->s_fd = -1; + } return err; } /** - * Close/Disconnect netlink socket. - * @arg sk Netlink socket. + * Close Netlink socket + * @arg sk Netlink socket (required) + * + * Closes the Netlink socket using `close()`. + * + * @note The socket is closed automatically if a `struct nl_sock` object is + * freed using `nl_socket_free()`. + * + * @see nl_connect() */ void nl_close(struct nl_sock *sk) { @@ -177,16 +172,38 @@ void nl_close(struct nl_sock *sk) */ /** - * Send raw data over netlink socket. - * @arg sk Netlink socket. - * @arg buf Data buffer. - * @arg size Size of data buffer. - * @return Number of characters written on success or a negative error code. + * Transmit raw data over Netlink socket. + * @arg sk Netlink socket (required) + * @arg buf Buffer carrying data to send (required) + * @arg size Size of buffer (required) + * + * Transmits "raw" data over the specified Netlink socket. Unlike the other + * transmit functions it does not modify the data in any way. It directly + * passes the buffer \c buf of \c size to sendto(). + * + * The message is addressed to the peer as specified in the socket by either + * the nl_socket_set_peer_port() or nl_socket_set_peer_groups() function. + * + * @note Because there is no indication on the message boundaries of the data + * being sent, the \c NL_CB_MSG_OUT callback handler will not be invoked + * for data that is being sent using this function. + * + * @see nl_socket_set_peer_port() + * @see nl_socket_set_peer_groups() + * @see nl_sendmsg() + * + * @return Number of bytes sent or a negative error code. */ int nl_sendto(struct nl_sock *sk, void *buf, size_t size) { int ret; + if (!buf) + return -NLE_INVAL; + + if (sk->s_fd < 0) + return -NLE_BAD_SOCK; + ret = sendto(sk->s_fd, buf, size, 0, (struct sockaddr *) &sk->s_peer, sizeof(sk->s_peer)); if (ret < 0) @@ -196,17 +213,49 @@ int nl_sendto(struct nl_sock *sk, void *buf, size_t size) } /** - * Send netlink message with control over sendmsg() message header. - * @arg sk Netlink socket. - * @arg msg Netlink message to be sent. - * @arg hdr Sendmsg() message header. - * @return Number of characters sent on sucess or a negative error code. + * Transmit Netlink message using sendmsg() + * @arg sk Netlink socket (required) + * @arg msg Netlink message to be sent (required) + * @arg hdr sendmsg() message header (required) + * + * Transmits the message specified in \c hdr over the Netlink socket using the + * sendmsg() system call. + * + * @attention + * The `msg` argument will *not* be used to derive the message payload that + * is being sent out. The `msg` argument is *only* passed on to the + * `NL_CB_MSG_OUT` callback. The caller is responsible to initialize the + * `hdr` struct properly and have it point to the message payload and + * socket address. + * + * @note + * This function uses `nlmsg_set_src()` to modify the `msg` argument prior to + * invoking the `NL_CB_MSG_OUT` callback to provide the local port number. + * + * @callback This function triggers the `NL_CB_MSG_OUT` callback. + * + * @attention + * Think twice before using this function. It provides a low level access to + * the Netlink socket. Among other limitations, it does not add credentials + * even if enabled or respect the destination address specified in the `msg` + * object. + * + * @see nl_socket_set_local_port() + * @see nl_send_auto() + * @see nl_send_iovec() + * + * @return Number of bytes sent on success or a negative error code. + * + * @lowlevel */ int nl_sendmsg(struct nl_sock *sk, struct nl_msg *msg, struct msghdr *hdr) { struct nl_cb *cb; int ret; + if (sk->s_fd < 0) + return -NLE_BAD_SOCK; + nlmsg_set_src(msg, &sk->s_local); cb = sk->s_cb; @@ -224,13 +273,23 @@ int nl_sendmsg(struct nl_sock *sk, struct nl_msg *msg, struct msghdr *hdr) /** - * Send netlink message. - * @arg sk Netlink socket. - * @arg msg Netlink message to be sent. - * @arg iov iovec to be sent. - * @arg iovlen number of struct iovec to be sent. - * @see nl_sendmsg() - * @return Number of characters sent on success or a negative error code. + * Transmit Netlink message (taking IO vector) + * @arg sk Netlink socket (required) + * @arg msg Netlink message to be sent (required) + * @arg iov IO vector to be sent (required) + * @arg iovlen Number of struct iovec to be sent (required) + * + * This function is identical to nl_send() except that instead of taking a + * `struct nl_msg` object it takes an IO vector. Please see the description + * of `nl_send()`. + * + * @callback This function triggers the `NL_CB_MSG_OUT` callback. + * + * @see nl_send() + * + * @return Number of bytes sent on success or a negative error code. + * + * @lowlevel */ int nl_send_iovec(struct nl_sock *sk, struct nl_msg *msg, struct iovec *iov, unsigned iovlen) { @@ -269,34 +328,86 @@ int nl_send_iovec(struct nl_sock *sk, struct nl_msg *msg, struct iovec *iov, uns return nl_sendmsg(sk, msg, &hdr); } - - /** -* Send netlink message. -* @arg sk Netlink socket. -* @arg msg Netlink message to be sent. -* @see nl_sendmsg() -* @return Number of characters sent on success or a negative error code. + * Transmit Netlink message + * @arg sk Netlink socket (required) + * @arg msg Netlink message (required) + * + * Transmits the Netlink message `msg` over the Netlink socket using the + * `sendmsg()` system call. This function is based on `nl_send_iovec()` but + * takes care of initializing a `struct iovec` based on the `msg` object. + * + * The message is addressed to the peer as specified in the socket by either + * the nl_socket_set_peer_port() or nl_socket_set_peer_groups() function. + * The peer address can be overwritten by specifying an address in the `msg` + * object using nlmsg_set_dst(). + * + * If present in the `msg`, credentials set by the nlmsg_set_creds() function + * are added to the control buffer of the message. + * + * @par Overwriting Capability: + * Calls to this function can be overwritten by providing an alternative using + * the nl_cb_overwrite_send() function. + * + * @callback This function triggers the `NL_CB_MSG_OUT` callback. + * + * @attention + * Unlike `nl_send_auto()`, this function does *not* finalize the message in + * terms of automatically adding needed flags or filling out port numbers. + * + * @see nl_send_auto() + * @see nl_send_iovec() + * @see nl_socket_set_peer_port() + * @see nl_socket_set_peer_groups() + * @see nlmsg_set_dst() + * @see nlmsg_set_creds() + * @see nl_cb_overwrite_send() + * + * @return Number of bytes sent on success or a negative error code. */ int nl_send(struct nl_sock *sk, struct nl_msg *msg) { - struct iovec iov = { - .iov_base = (void *) nlmsg_hdr(msg), - .iov_len = nlmsg_hdr(msg)->nlmsg_len, - }; + struct nl_cb *cb = sk->s_cb; - return nl_send_iovec(sk, msg, &iov, 1); + if (cb->cb_send_ow) + return cb->cb_send_ow(sk, msg); + else { + struct iovec iov = { + .iov_base = (void *) nlmsg_hdr(msg), + .iov_len = nlmsg_hdr(msg)->nlmsg_len, + }; + + return nl_send_iovec(sk, msg, &iov, 1); + } } +/** + * Finalize Netlink message + * @arg sk Netlink socket (required) + * @arg msg Netlink message (required) + * + * This function finalizes a Netlink message by completing the message with + * desirable flags and values depending on the socket configuration. + * + * - If not yet filled out, the source address of the message (`nlmsg_pid`) + * will be set to the local port number of the socket. + * - If not yet specified, the next available sequence number is assigned + * to the message (`nlmsg_seq`). + * - If not yet specified, the protocol field of the message will be set to + * the protocol field of the socket. + * - The `NLM_F_REQUEST` Netlink message flag will be set. + * - The `NLM_F_ACK` flag will be set if Auto-ACK mode is enabled on the + * socket. + */ void nl_complete_msg(struct nl_sock *sk, struct nl_msg *msg) { struct nlmsghdr *nlh; nlh = nlmsg_hdr(msg); - if (nlh->nlmsg_pid == 0) + if (nlh->nlmsg_pid == NL_AUTO_PORT) nlh->nlmsg_pid = sk->s_local.nl_pid; - if (nlh->nlmsg_seq == 0) + if (nlh->nlmsg_seq == NL_AUTO_SEQ) nlh->nlmsg_seq = sk->s_seq_next++; if (msg->nm_protocol == -1) @@ -308,56 +419,84 @@ void nl_complete_msg(struct nl_sock *sk, struct nl_msg *msg) nlh->nlmsg_flags |= NLM_F_ACK; } -void nl_auto_complete(struct nl_sock *sk, struct nl_msg *msg) -{ - nl_complete_msg(sk, msg); -} - /** - * Automatically complete and send a netlink message - * @arg sk Netlink socket. - * @arg msg Netlink message to be sent. + * Finalize and transmit Netlink message + * @arg sk Netlink socket (required) + * @arg msg Netlink message (required) * - * This function takes a netlink message and passes it on to - * nl_auto_complete() for completion. + * Finalizes the message by passing it to `nl_complete_msg()` and transmits it + * by passing it to `nl_send()`. * - * Checks the netlink message \c nlh for completness and extends it - * as required before sending it out. Checked fields include pid, - * sequence nr, and flags. + * @callback This function triggers the `NL_CB_MSG_OUT` callback. * + * @see nl_complete_msg() * @see nl_send() - * @return Number of characters sent or a negative error code. + * + * @return Number of bytes sent or a negative error code. */ int nl_send_auto(struct nl_sock *sk, struct nl_msg *msg) { - struct nl_cb *cb = sk->s_cb; - nl_complete_msg(sk, msg); - if (cb->cb_send_ow) - return cb->cb_send_ow(sk, msg); - else - return nl_send(sk, msg); -} - -int nl_send_auto_complete(struct nl_sock *sk, struct nl_msg *msg) -{ - return nl_send_auto(sk, msg); + return nl_send(sk, msg); } /** - * Send simple netlink message using nl_send_auto_complete() - * @arg sk Netlink socket. - * @arg type Netlink message type. - * @arg flags Netlink message flags. - * @arg buf Data buffer. - * @arg size Size of data buffer. + * Finalize and transmit Netlink message and wait for ACK or error message + * @arg sk Netlink socket (required) + * @arg msg Netlink message (required) * - * Builds a netlink message with the specified type and flags and - * appends the specified data as payload to the message. + * Passes the `msg` to `nl_send_auto()` to finalize and transmit it. Frees the + * message and waits (sleeps) for the ACK or error message to be received. + * + * @attention + * Disabling Auto-ACK (nl_socket_disable_auto_ack()) will cause this function + * to return immediately after transmitting the message. However, the peer may + * still be returning an error message in response to the request. It is the + * responsibility of the caller to handle such messages. + * + * @callback This function triggers the `NL_CB_MSG_OUT` callback. + * + * @attention + * This function frees the `msg` object after transmitting it by calling + * `nlmsg_free()`. + * + * @see nl_send_auto(). + * @see nl_wait_for_ack() + * + * @return 0 on success or a negative error code. + */ +int nl_send_sync(struct nl_sock *sk, struct nl_msg *msg) +{ + int err; + + err = nl_send_auto(sk, msg); + nlmsg_free(msg); + if (err < 0) + return err; + + return wait_for_ack(sk); +} + +/** + * Construct and transmit a Netlink message + * @arg sk Netlink socket (required) + * @arg type Netlink message type (required) + * @arg flags Netlink message flags (optional) + * @arg buf Data buffer (optional) + * @arg size Size of data buffer (optional) + * + * Allocates a new Netlink message based on `type` and `flags`. If `buf` + * points to payload of length `size` that payload will be appended to the + * message. + * + * Sends out the message using `nl_send_auto()` and frees the message + * afterwards. + * + * @see nl_send_auto() * - * @see nl_send_auto_complete() * @return Number of characters sent on success or a negative error code. + * @retval -NLE_NOMEM Unable to allocate Netlink message */ int nl_send_simple(struct nl_sock *sk, int type, int flags, void *buf, size_t size) @@ -374,9 +513,8 @@ int nl_send_simple(struct nl_sock *sk, int type, int flags, void *buf, if (err < 0) goto errout; } - - err = nl_send_auto_complete(sk, msg); + err = nl_send_auto(sk, msg); errout: nlmsg_free(msg); @@ -392,27 +530,54 @@ errout: /** * Receive data from netlink socket - * @arg sk Netlink socket. - * @arg nla Destination pointer for peer's netlink address. - * @arg buf Destination pointer for message content. - * @arg creds Destination pointer for credentials. + * @arg sk Netlink socket (required) + * @arg nla Netlink socket structure to hold address of peer (required) + * @arg buf Destination pointer for message content (required) + * @arg creds Destination pointer for credentials (optional) * - * Receives a netlink message, allocates a buffer in \c *buf and - * stores the message content. The peer's netlink address is stored - * in \c *nla. The caller is responsible for freeing the buffer allocated - * in \c *buf if a positive value is returned. Interrupted system calls - * are handled by repeating the read. The input buffer size is determined - * by peeking before the actual read is done. + * Receives data from a connected netlink socket using recvmsg() and returns + * the number of bytes read. The read data is stored in a newly allocated + * buffer that is assigned to \c *buf. The peer's netlink address will be + * stored in \c *nla. * - * A non-blocking sockets causes the function to return immediately with - * a return value of 0 if no data is available. + * This function blocks until data is available to be read unless the socket + * has been put into non-blocking mode using nl_socket_set_nonblocking() in + * which case this function will return immediately with a return value of 0. * - * @return Number of octets read, 0 on EOF or a negative error code. + * The buffer size used when reading from the netlink socket and thus limiting + * the maximum size of a netlink message that can be read defaults to the size + * of a memory page (getpagesize()). The buffer size can be modified on a per + * socket level using the function nl_socket_set_msg_buf_size(). + * + * If message peeking is enabled using nl_socket_enable_msg_peek() the size of + * the message to be read will be determined using the MSG_PEEK flag prior to + * performing the actual read. This leads to an additional recvmsg() call for + * every read operation which has performance implications and is not + * recommended for high throughput protocols. + * + * An eventual interruption of the recvmsg() system call is automatically + * handled by retrying the operation. + * + * If receiving of credentials has been enabled using the function + * nl_socket_set_passcred(), this function will allocate a new struct ucred + * filled with the received credentials and assign it to \c *creds. The caller + * is responsible for freeing the buffer. + * + * @note The caller is responsible to free the returned data buffer and if + * enabled, the credentials buffer. + * + * @see nl_socket_set_nonblocking() + * @see nl_socket_set_msg_buf_size() + * @see nl_socket_enable_msg_peek() + * @see nl_socket_set_passcred() + * + * @return Number of bytes read, 0 on EOF, 0 on no data event (non-blocking + * mode), or a negative error code. */ int nl_recv(struct nl_sock *sk, struct sockaddr_nl *nla, unsigned char **buf, struct ucred **creds) { - int n; + ssize_t n; int flags = 0; static int page_size = 0; struct iovec iov; @@ -421,89 +586,132 @@ int nl_recv(struct nl_sock *sk, struct sockaddr_nl *nla, .msg_namelen = sizeof(struct sockaddr_nl), .msg_iov = &iov, .msg_iovlen = 1, - .msg_control = NULL, - .msg_controllen = 0, - .msg_flags = 0, }; - struct cmsghdr *cmsg; + struct ucred* tmpcreds = NULL; + int retval = 0; - memset(nla, 0, sizeof(*nla)); + if (!buf || !nla) + return -NLE_INVAL; if (sk->s_flags & NL_MSG_PEEK) - flags |= MSG_PEEK; + flags |= MSG_PEEK | MSG_TRUNC; if (page_size == 0) page_size = getpagesize(); - iov.iov_len = page_size; - iov.iov_base = *buf = malloc(iov.iov_len); + iov.iov_len = sk->s_bufsize ? : page_size; + iov.iov_base = malloc(iov.iov_len); - if (sk->s_flags & NL_SOCK_PASSCRED) { + if (!iov.iov_base) { + retval = -NLE_NOMEM; + goto abort; + } + + if (creds && (sk->s_flags & NL_SOCK_PASSCRED)) { msg.msg_controllen = CMSG_SPACE(sizeof(struct ucred)); - msg.msg_control = calloc(1, msg.msg_controllen); + msg.msg_control = malloc(msg.msg_controllen); + if (!msg.msg_control) { + retval = -NLE_NOMEM; + goto abort; + } } retry: n = recvmsg(sk->s_fd, &msg, flags); - if (!n) + if (!n) { + retval = 0; goto abort; - else if (n < 0) { + } + if (n < 0) { if (errno == EINTR) { NL_DBG(3, "recvmsg() returned EINTR, retrying\n"); goto retry; - } else if (errno == EAGAIN) { - NL_DBG(3, "recvmsg() returned EAGAIN, aborting\n"); - goto abort; - } else { - free(msg.msg_control); - free(*buf); - return -nl_syserr2nlerr(errno); } + if (errno == EAGAIN || errno == EWOULDBLOCK) { + NL_DBG(3, "recvmsg() returned EAGAIN||EWOULDBLOCK, aborting\n"); + retval = 0; + goto abort; + } + retval = -nl_syserr2nlerr(errno); + goto abort; } - if (iov.iov_len < n || - msg.msg_flags & MSG_TRUNC) { - /* Provided buffer is not long enough, enlarge it - * and try again. */ - iov.iov_len *= 2; - iov.iov_base = *buf = realloc(*buf, iov.iov_len); - goto retry; - } else if (msg.msg_flags & MSG_CTRUNC) { + if (msg.msg_flags & MSG_CTRUNC) { + void *tmp; msg.msg_controllen *= 2; - msg.msg_control = realloc(msg.msg_control, msg.msg_controllen); + tmp = realloc(msg.msg_control, msg.msg_controllen); + if (!tmp) { + retval = -NLE_NOMEM; + goto abort; + } + msg.msg_control = tmp; goto retry; - } else if (flags != 0) { + } + + if (iov.iov_len < n || (msg.msg_flags & MSG_TRUNC)) { + void *tmp; + /* Provided buffer is not long enough, enlarge it + * to size of n (which should be total length of the message) + * and try again. */ + iov.iov_len = n; + tmp = realloc(iov.iov_base, iov.iov_len); + if (!tmp) { + retval = -NLE_NOMEM; + goto abort; + } + iov.iov_base = tmp; + flags = 0; + goto retry; + } + + if (flags != 0) { /* Buffer is big enough, do the actual reading */ flags = 0; goto retry; } if (msg.msg_namelen != sizeof(struct sockaddr_nl)) { - free(msg.msg_control); - free(*buf); - return -NLE_NOADDR; + retval = -NLE_NOADDR; + goto abort; } - for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { - if (cmsg->cmsg_level == SOL_SOCKET && - cmsg->cmsg_type == SCM_CREDENTIALS) { - if (creds) { - *creds = calloc(1, sizeof(struct ucred)); - memcpy(*creds, CMSG_DATA(cmsg), sizeof(struct ucred)); + if (creds && (sk->s_flags & NL_SOCK_PASSCRED)) { + struct cmsghdr *cmsg; + + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { + if (cmsg->cmsg_level != SOL_SOCKET) + continue; + if (cmsg->cmsg_type != SCM_CREDENTIALS) + continue; + tmpcreds = malloc(sizeof(*tmpcreds)); + if (!tmpcreds) { + retval = -NLE_NOMEM; + goto abort; } + memcpy(tmpcreds, CMSG_DATA(cmsg), sizeof(*tmpcreds)); break; } } - free(msg.msg_control); - return n; - + retval = n; abort: free(msg.msg_control); - free(*buf); - return 0; + + if (retval <= 0) { + free(iov.iov_base); + iov.iov_base = NULL; + free(tmpcreds); + tmpcreds = NULL; + } else + *buf = iov.iov_base; + + if (creds) + *creds = tmpcreds; + + return retval; } +/** @cond SKIP */ #define NL_CB_CALL(cb, type, msg) \ do { \ err = nl_cb_call(cb, type, msg); \ @@ -519,12 +727,19 @@ do { \ goto out; \ } \ } while (0) +/** @endcond */ static int recvmsgs(struct nl_sock *sk, struct nl_cb *cb) { - int n, err = 0, multipart = 0; + int n, err = 0, multipart = 0, interrupted = 0, nrecv = 0; unsigned char *buf = NULL; struct nlmsghdr *hdr; + + /* + nla is passed on to not only to nl_recv() but may also be passed + to a function pointer provided by the caller which may or may not + initialize the variable. Thomas Graf. + */ struct sockaddr_nl nla = {0}; struct nl_msg *msg = NULL; struct ucred *creds = NULL; @@ -543,7 +758,7 @@ continue_reading: hdr = (struct nlmsghdr *) buf; while (nlmsg_ok(hdr, n)) { - NL_DBG(3, "recgmsgs(%p): Processing valid message...\n", sk); + NL_DBG(3, "recvmsgs(%p): Processing valid message...\n", sk); nlmsg_free(msg); msg = nlmsg_convert(hdr); @@ -557,6 +772,8 @@ continue_reading: if (creds) nlmsg_set_creds(msg, creds); + nrecv++; + /* Raw callback is the first, it gives the most control * to the user and he can do his very own parsing. */ if (cb->cb_set[NL_CB_MSG_IN]) @@ -594,6 +811,19 @@ continue_reading: if (hdr->nlmsg_flags & NLM_F_MULTI) multipart = 1; + + if (hdr->nlmsg_flags & NLM_F_DUMP_INTR) { + if (cb->cb_set[NL_CB_DUMP_INTR]) + NL_CB_CALL(cb, NL_CB_DUMP_INTR, msg); + else { + /* + * We have to continue reading to clear + * all messages until a NLMSG_DONE is + * received and report the inconsistency. + */ + interrupted = 1; + } + } /* Other side wishes to see an ack for this message */ if (hdr->nlmsg_flags & NLM_F_ACK) { @@ -701,9 +931,35 @@ out: free(buf); free(creds); + if (interrupted) + err = -NLE_DUMP_INTR; + + if (!err) + err = nrecv; + return err; } +/** + * Receive a set of messages from a netlink socket and report parsed messages + * @arg sk Netlink socket. + * @arg cb set of callbacks to control behaviour. + * + * This function is identical to nl_recvmsgs() to the point that it will + * return the number of parsed messages instead of 0 on success. + * + * @see nl_recvmsgs() + * + * @return Number of received messages or a negative error code from nl_recv(). + */ +int nl_recvmsgs_report(struct nl_sock *sk, struct nl_cb *cb) +{ + if (cb->cb_recvmsgs_ow) + return cb->cb_recvmsgs_ow(sk, cb); + else + return recvmsgs(sk, cb); +} + /** * Receive a set of messages from a netlink socket. * @arg sk Netlink socket. @@ -717,14 +973,18 @@ out: * A non-blocking sockets causes the function to return immediately if * no data is available. * + * @see nl_recvmsgs_report() + * * @return 0 on success or a negative error code from nl_recv(). */ int nl_recvmsgs(struct nl_sock *sk, struct nl_cb *cb) { - if (cb->cb_recvmsgs_ow) - return cb->cb_recvmsgs_ow(sk, cb); - else - return recvmsgs(sk, cb); + int err; + + if ((err = nl_recvmsgs_report(sk, cb)) > 0) + err = 0; + + return err; } /** @@ -768,6 +1028,102 @@ int nl_wait_for_ack(struct nl_sock *sk) return err; } +/** @cond SKIP */ +struct pickup_param +{ + int (*parser)(struct nl_cache_ops *, struct sockaddr_nl *, + struct nlmsghdr *, struct nl_parser_param *); + struct nl_object *result; +}; + +static int __store_answer(struct nl_object *obj, struct nl_parser_param *p) +{ + struct pickup_param *pp = p->pp_arg; + /* + * the parser will put() the object at the end, expecting the cache + * to take the reference. + */ + nl_object_get(obj); + pp->result = obj; + + return 0; +} + +static int __pickup_answer(struct nl_msg *msg, void *arg) +{ + struct pickup_param *pp = arg; + struct nl_parser_param parse_arg = { + .pp_cb = __store_answer, + .pp_arg = pp, + }; + + return pp->parser(NULL, &msg->nm_src, msg->nm_nlh, &parse_arg); +} + +/** @endcond */ + +/** + * Pickup netlink answer, parse is and return object + * @arg sk Netlink socket + * @arg parser Parser function to parse answer + * @arg result Result pointer to return parsed object + * + * @return 0 on success or a negative error code. + */ +int nl_pickup(struct nl_sock *sk, + int (*parser)(struct nl_cache_ops *, struct sockaddr_nl *, + struct nlmsghdr *, struct nl_parser_param *), + struct nl_object **result) +{ + struct nl_cb *cb; + int err; + struct pickup_param pp = { + .parser = parser, + }; + + cb = nl_cb_clone(sk->s_cb); + if (cb == NULL) + return -NLE_NOMEM; + + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, __pickup_answer, &pp); + + err = nl_recvmsgs(sk, cb); + if (err < 0) + goto errout; + + *result = pp.result; +errout: + nl_cb_put(cb); + + return err; +} + +/** @} */ + +/** + * @name Deprecated + * @{ + */ + +/** + * @deprecated Please use nl_complete_msg() + */ +void nl_auto_complete(struct nl_sock *sk, struct nl_msg *msg) +{ + nl_complete_msg(sk, msg); +} + +/** + * @deprecated Please use nl_send_auto() + */ +int nl_send_auto_complete(struct nl_sock *sk, struct nl_msg *msg) +{ + return nl_send_auto(sk, msg); +} + + +/** @} */ + /** @} */ /** @} */ diff --git a/lib/object.c b/lib/object.c index d881ac9..17b98f6 100644 --- a/lib/object.c +++ b/lib/object.c @@ -6,16 +6,28 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2008 Thomas Graf + * Copyright (c) 2003-2012 Thomas Graf */ /** - * @ingroup cache - * @defgroup object Object + * @ingroup core_types + * @defgroup object Object (Cacheable) + * + * Generic object data type, for inheritance purposes to implement cacheable + * data types. + * + * Related sections in the development guide: + * * @{ + * + * Header + * ------ + * ~~~~{.c} + * #include + * ~~~~ */ -#include +#include #include #include #include @@ -63,19 +75,23 @@ struct nl_object *nl_object_alloc(struct nl_object_ops *ops) } /** - * Allocate a new object of kind specified by the name + * Allocate new object of kind specified by the name * @arg kind name of object type - * @return The new object or nULL + * @arg result Result pointer + * + * @return 0 on success or a negative error code. */ int nl_object_alloc_name(const char *kind, struct nl_object **result) { struct nl_cache_ops *ops; - ops = nl_cache_ops_lookup(kind); + ops = nl_cache_ops_lookup_safe(kind); if (!ops) return -NLE_OPNOTSUPP; - if (!(*result = nl_object_alloc(ops->co_obj_ops))) + *result = nl_object_alloc(ops->co_obj_ops); + nl_cache_ops_put(ops); + if (!*result) return -NLE_NOMEM; return 0; @@ -124,6 +140,23 @@ struct nl_object *nl_object_clone(struct nl_object *obj) return new; } +/** + * Merge a cacheable object + * @arg dst object to be merged into + * @arg src new object to be merged into dst + * + * @return 0 or a negative error code. + */ +int nl_object_update(struct nl_object *dst, struct nl_object *src) +{ + struct nl_object_ops *ops = obj_ops(dst); + + if (ops->oo_update) + return ops->oo_update(dst, src); + + return -NLE_OPNOTSUPP; +} + /** * Free a cacheable object * @arg obj object to free @@ -245,9 +278,22 @@ int nl_object_is_marked(struct nl_object *obj) */ void nl_object_dump(struct nl_object *obj, struct nl_dump_params *params) { + if (params->dp_buf) + memset(params->dp_buf, 0, params->dp_buflen); + dump_from_ops(obj, params); } +void nl_object_dump_buf(struct nl_object *obj, char *buf, size_t len) +{ + struct nl_dump_params dp = { + .dp_buf = buf, + .dp_buflen = len, + }; + + return nl_object_dump(obj, &dp); +} + /** * Check if the identifiers of two objects are identical * @arg a an object @@ -258,14 +304,22 @@ void nl_object_dump(struct nl_object *obj, struct nl_dump_params *params) int nl_object_identical(struct nl_object *a, struct nl_object *b) { struct nl_object_ops *ops = obj_ops(a); - int req_attrs; + uint32_t req_attrs; /* Both objects must be of same type */ if (ops != obj_ops(b)) return 0; - req_attrs = ops->oo_id_attrs; - if (req_attrs == ~0) + if (ops->oo_id_attrs_get) { + int req_attrs_a = ops->oo_id_attrs_get(a); + int req_attrs_b = ops->oo_id_attrs_get(b); + if (req_attrs_a != req_attrs_b) + return 0; + req_attrs = req_attrs_a; + } else { + req_attrs = ops->oo_id_attrs; + } + if (req_attrs == 0xFFFFFFFF) req_attrs = a->ce_mask & b->ce_mask; /* Both objects must provide all required attributes to uniquely @@ -361,6 +415,27 @@ char *nl_object_attr_list(struct nl_object *obj, char *buf, size_t len) return nl_object_attrs2str(obj, obj->ce_mask, buf, len); } +/** + * Generate object hash key + * @arg obj the object + * @arg hashkey destination buffer to be used for key stream + * @arg hashtbl_sz hash table size + * + * @return hash key in destination buffer + */ +void nl_object_keygen(struct nl_object *obj, uint32_t *hashkey, + uint32_t hashtbl_sz) +{ + struct nl_object_ops *ops = obj_ops(obj); + + if (ops->oo_keygen) + ops->oo_keygen(obj, hashkey, hashtbl_sz); + else + *hashkey = 0; + + return; +} + /** @} */ /** @@ -368,16 +443,91 @@ char *nl_object_attr_list(struct nl_object *obj, char *buf, size_t len) * @{ */ +/** + * Return number of references held + * @arg obj object + * + * @return The number of references held to this object + */ int nl_object_get_refcnt(struct nl_object *obj) { return obj->ce_refcnt; } +/** + * Return cache the object is associated with + * @arg obj object + * + * @note The returned pointer is not protected with a reference counter, + * it is your responsibility. + * + * @return Pointer to cache or NULL if not associated with a cache. + */ struct nl_cache *nl_object_get_cache(struct nl_object *obj) { return obj->ce_cache; } +/** + * Return the object's type + * @arg obj object + * + * FIXME: link to list of object types + * + * @return Name of the object type + */ +const char *nl_object_get_type(const struct nl_object *obj) +{ + if (!obj->ce_ops) + BUG(); + + return obj->ce_ops->oo_name; +} + +/** + * Return the netlink message type the object was derived from + * @arg obj object + * + * @return Netlink message type or 0. + */ +int nl_object_get_msgtype(const struct nl_object *obj) +{ + return obj->ce_msgtype; +} + +/** + * Return object operations structure + * @arg obj object + * + * @return Pointer to the object operations structure + */ +struct nl_object_ops *nl_object_get_ops(const struct nl_object *obj) +{ + return obj->ce_ops; +} + +/** + * Return object id attribute mask + * @arg obj object + * + * @return object id attribute mask + */ +uint32_t nl_object_get_id_attrs(struct nl_object *obj) +{ + struct nl_object_ops *ops = obj_ops(obj); + uint32_t id_attrs; + + if (!ops) + return 0; + + if (ops->oo_id_attrs_get) + id_attrs = ops->oo_id_attrs_get(obj); + else + id_attrs = ops->oo_id_attrs; + + return id_attrs; +} + /** @} */ /** @} */ diff --git a/lib/route/addr.c b/lib/route/addr.c index b367883..71fca94 100644 --- a/lib/route/addr.c +++ b/lib/route/addr.c @@ -6,7 +6,7 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2008 Thomas Graf + * Copyright (c) 2003-2012 Thomas Graf * Copyright (c) 2003-2006 Baruch Even , * Mediatrix Telecom, inc. */ @@ -106,7 +106,7 @@ * @{ */ -#include +#include #include #include #include @@ -151,6 +151,7 @@ static void addr_free_data(struct nl_object *obj) nl_addr_put(addr->a_bcast); nl_addr_put(addr->a_multicast); nl_addr_put(addr->a_anycast); + rtnl_link_put(addr->a_link); } static int addr_clone(struct nl_object *_dst, struct nl_object *_src) @@ -158,6 +159,11 @@ static int addr_clone(struct nl_object *_dst, struct nl_object *_src) struct rtnl_addr *dst = nl_object_priv(_dst); struct rtnl_addr *src = nl_object_priv(_src); + if (src->a_link) { + nl_object_get(OBJ_CAST(src->a_link)); + dst->a_link = src->a_link; + } + if (src->a_peer) if (!(dst->a_peer = nl_addr_clone(src->a_peer))) return -NLE_NOMEM; @@ -193,7 +199,9 @@ static int addr_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, struct rtnl_addr *addr; struct ifaddrmsg *ifa; struct nlattr *tb[IFA_MAX+1]; - int err, peer_prefix = 0, family; + int err, family; + struct nl_cache *link_cache; + struct nl_addr *plen_addr = NULL; addr = rtnl_addr_alloc(); if (!addr) @@ -220,6 +228,7 @@ static int addr_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, addr->ce_mask |= ADDR_ATTR_LABEL; } + /* IPv6 only */ if (tb[IFA_CACHEINFO]) { struct ifa_cacheinfo *ca; @@ -236,6 +245,7 @@ static int addr_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, if (!addr->a_local) goto errout_nomem; addr->ce_mask |= ADDR_ATTR_LOCAL; + plen_addr = addr->a_local; } if (tb[IFA_ADDRESS]) { @@ -255,13 +265,15 @@ static int addr_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, } else { addr->a_peer = a; addr->ce_mask |= ADDR_ATTR_PEER; - peer_prefix = 1; } + + plen_addr = a; } - nl_addr_set_prefixlen(peer_prefix ? addr->a_peer : addr->a_local, - addr->a_prefixlen); + if (plen_addr) + nl_addr_set_prefixlen(plen_addr, addr->a_prefixlen); + /* IPv4 only */ if (tb[IFA_BROADCAST]) { addr->a_bcast = nl_addr_alloc_attr(tb[IFA_BROADCAST], family); if (!addr->a_bcast) @@ -270,6 +282,7 @@ static int addr_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, addr->ce_mask |= ADDR_ATTR_BROADCAST; } + /* IPv6 only */ if (tb[IFA_MULTICAST]) { addr->a_multicast = nl_addr_alloc_attr(tb[IFA_MULTICAST], family); @@ -279,6 +292,7 @@ static int addr_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, addr->ce_mask |= ADDR_ATTR_MULTICAST; } + /* IPv6 only */ if (tb[IFA_ANYCAST]) { addr->a_anycast = nl_addr_alloc_attr(tb[IFA_ANYCAST], family); @@ -288,6 +302,17 @@ static int addr_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, addr->ce_mask |= ADDR_ATTR_ANYCAST; } + if ((link_cache = __nl_cache_mngt_require("route/link"))) { + struct rtnl_link *link; + + if ((link = rtnl_link_get(link_cache, addr->a_ifindex))) { + rtnl_addr_set_link(addr, link); + + /* rtnl_addr_set_link incs refcnt */ + rtnl_link_put(link); + } + } + err = pp->pp_cb((struct nl_object *) addr, pp); errout: rtnl_addr_put(addr); @@ -310,7 +335,7 @@ static void addr_dump_line(struct nl_object *obj, struct nl_dump_params *p) struct nl_cache *link_cache; char buf[128]; - link_cache = nl_cache_mngt_require("route/link"); + link_cache = nl_cache_mngt_require_safe("route/link"); if (addr->ce_mask & ADDR_ATTR_LOCAL) nl_dump_line(p, "%s", @@ -339,6 +364,9 @@ static void addr_dump_line(struct nl_object *obj, struct nl_dump_params *p) nl_dump(p, " <%s>", buf); nl_dump(p, "\n"); + + if (link_cache) + nl_cache_put(link_cache); } static void addr_dump_details(struct nl_object *obj, struct nl_dump_params *p) @@ -478,6 +506,42 @@ int rtnl_addr_alloc_cache(struct nl_sock *sk, struct nl_cache **result) return nl_cache_alloc_and_fill(&rtnl_addr_ops, sk, result); } +/** + * Search address in cache + * @arg cache Address cache + * @arg ifindex Interface index of address + * @arg addr Local address part + * + * Searches address cache previously allocated with rtnl_addr_alloc_cache() + * for an address with a matching local address. + * + * The reference counter is incremented before returning the address, therefore + * the reference must be given back with rtnl_addr_put() after usage. + * + * @return Address object or NULL if no match was found. + */ +struct rtnl_addr *rtnl_addr_get(struct nl_cache *cache, int ifindex, + struct nl_addr *addr) +{ + struct rtnl_addr *a; + + if (cache->c_ops != &rtnl_addr_ops) + return NULL; + + nl_list_for_each_entry(a, &cache->c_items, ce_list) { + if (ifindex && a->a_ifindex != ifindex) + continue; + + if (a->ce_mask & ADDR_ATTR_LOCAL && + !nl_addr_cmp(a->a_local, addr)) { + nl_object_get((struct nl_object *) a); + return a; + } + } + + return NULL; +} + /** @} */ static int build_addr_msg(struct rtnl_addr *tmpl, int cmd, int flags, @@ -571,7 +635,7 @@ nla_put_failure: int rtnl_addr_build_add_request(struct rtnl_addr *addr, int flags, struct nl_msg **result) { - int required = ADDR_ATTR_IFINDEX | ADDR_ATTR_FAMILY | + uint32_t required = ADDR_ATTR_IFINDEX | ADDR_ATTR_FAMILY | ADDR_ATTR_PREFIXLEN | ADDR_ATTR_LOCAL; if ((addr->ce_mask & required) != required) @@ -644,7 +708,7 @@ int rtnl_addr_add(struct nl_sock *sk, struct rtnl_addr *addr, int flags) int rtnl_addr_build_delete_request(struct rtnl_addr *addr, int flags, struct nl_msg **result) { - int required = ADDR_ATTR_IFINDEX | ADDR_ATTR_FAMILY; + uint32_t required = ADDR_ATTR_IFINDEX | ADDR_ATTR_FAMILY; if ((addr->ce_mask & required) != required) return -NLE_MISSING_ATTR; @@ -719,6 +783,29 @@ int rtnl_addr_get_ifindex(struct rtnl_addr *addr) return addr->a_ifindex; } +void rtnl_addr_set_link(struct rtnl_addr *addr, struct rtnl_link *link) +{ + rtnl_link_put(addr->a_link); + + if (!link) + return; + + nl_object_get(OBJ_CAST(link)); + addr->a_link = link; + addr->a_ifindex = link->l_index; + addr->ce_mask |= ADDR_ATTR_IFINDEX; +} + +struct rtnl_link *rtnl_addr_get_link(struct rtnl_addr *addr) +{ + if (addr->a_link) { + nl_object_get(OBJ_CAST(addr->a_link)); + return addr->a_link; + } + + return NULL; +} + void rtnl_addr_set_family(struct rtnl_addr *addr, int family) { addr->a_family = family; @@ -730,10 +817,39 @@ int rtnl_addr_get_family(struct rtnl_addr *addr) return addr->a_family; } -void rtnl_addr_set_prefixlen(struct rtnl_addr *addr, int prefix) +/** + * Set the prefix length / netmask + * @arg addr Address + * @arg prefixlen Length of prefix (netmask) + * + * Modifies the length of the prefix. If the address object contains a peer + * address the prefix length will apply to it, otherwise the prefix length + * will apply to the local address of the address. + * + * If the address object contains a peer or local address the corresponding + * `struct nl_addr` will be updated with the new prefix length. + * + * @note Specifying a length of 0 will remove the prefix length alltogether. + * + * @see rtnl_addr_get_prefixlen() + */ +void rtnl_addr_set_prefixlen(struct rtnl_addr *addr, int prefixlen) { - addr->a_prefixlen = prefix; - addr->ce_mask |= ADDR_ATTR_PREFIXLEN; + addr->a_prefixlen = prefixlen; + + if (prefixlen) + addr->ce_mask |= ADDR_ATTR_PREFIXLEN; + else + addr->ce_mask &= ~ADDR_ATTR_PREFIXLEN; + + /* + * The prefix length always applies to the peer address if + * a peer address is present. + */ + if (addr->a_peer) + nl_addr_set_prefixlen(addr->a_peer, prefixlen); + else if (addr->a_local) + nl_addr_set_prefixlen(addr->a_local, prefixlen); } int rtnl_addr_get_prefixlen(struct rtnl_addr *addr) @@ -774,17 +890,25 @@ unsigned int rtnl_addr_get_flags(struct rtnl_addr *addr) static inline int __assign_addr(struct rtnl_addr *addr, struct nl_addr **pos, struct nl_addr *new, int flag) { - if (addr->ce_mask & ADDR_ATTR_FAMILY) { - if (new->a_family != addr->a_family) - return -NLE_AF_MISMATCH; - } else - addr->a_family = new->a_family; + if (new) { + if (addr->ce_mask & ADDR_ATTR_FAMILY) { + if (new->a_family != addr->a_family) + return -NLE_AF_MISMATCH; + } else + addr->a_family = new->a_family; - if (*pos) - nl_addr_put(*pos); + if (*pos) + nl_addr_put(*pos); - *pos = nl_addr_get(new); - addr->ce_mask |= (flag | ADDR_ATTR_FAMILY); + *pos = nl_addr_get(new); + addr->ce_mask |= (flag | ADDR_ATTR_FAMILY); + } else { + if (*pos) + nl_addr_put(*pos); + + *pos = NULL; + addr->ce_mask &= ~flag; + } return 0; } @@ -793,14 +917,18 @@ int rtnl_addr_set_local(struct rtnl_addr *addr, struct nl_addr *local) { int err; + /* Prohibit local address with prefix length if peer address is present */ + if ((addr->ce_mask & ADDR_ATTR_PEER) && local && + nl_addr_get_prefixlen(local)) + return -NLE_INVAL; + err = __assign_addr(addr, &addr->a_local, local, ADDR_ATTR_LOCAL); if (err < 0) return err; - if (!(addr->ce_mask & ADDR_ATTR_PEER)) { - addr->a_prefixlen = nl_addr_get_prefixlen(addr->a_local); - addr->ce_mask |= ADDR_ATTR_PREFIXLEN; - } + /* Never overwrite the prefix length if a peer address is present */ + if (!(addr->ce_mask & ADDR_ATTR_PEER)) + rtnl_addr_set_prefixlen(addr, local ? nl_addr_get_prefixlen(local) : 0); return 0; } @@ -812,10 +940,16 @@ struct nl_addr *rtnl_addr_get_local(struct rtnl_addr *addr) int rtnl_addr_set_peer(struct rtnl_addr *addr, struct nl_addr *peer) { - return __assign_addr(addr, &addr->a_peer, peer, ADDR_ATTR_PEER); + int err; - addr->a_prefixlen = nl_addr_get_prefixlen(addr->a_peer); - addr->ce_mask |= ADDR_ATTR_PREFIXLEN; + if (peer && peer->a_family != AF_INET) + return -NLE_AF_NOSUPPORT; + + err = __assign_addr(addr, &addr->a_peer, peer, ADDR_ATTR_PEER); + if (err < 0) + return err; + + rtnl_addr_set_prefixlen(addr, peer ? nl_addr_get_prefixlen(peer) : 0); return 0; } @@ -827,6 +961,9 @@ struct nl_addr *rtnl_addr_get_peer(struct rtnl_addr *addr) int rtnl_addr_set_broadcast(struct rtnl_addr *addr, struct nl_addr *bcast) { + if (bcast && bcast->a_family != AF_INET) + return -NLE_AF_NOSUPPORT; + return __assign_addr(addr, &addr->a_bcast, bcast, ADDR_ATTR_BROADCAST); } @@ -837,6 +974,9 @@ struct nl_addr *rtnl_addr_get_broadcast(struct rtnl_addr *addr) int rtnl_addr_set_multicast(struct rtnl_addr *addr, struct nl_addr *multicast) { + if (multicast && multicast->a_family != AF_INET6) + return -NLE_AF_NOSUPPORT; + return __assign_addr(addr, &addr->a_multicast, multicast, ADDR_ATTR_MULTICAST); } @@ -848,6 +988,9 @@ struct nl_addr *rtnl_addr_get_multicast(struct rtnl_addr *addr) int rtnl_addr_set_anycast(struct rtnl_addr *addr, struct nl_addr *anycast) { + if (anycast && anycast->a_family != AF_INET6) + return -NLE_AF_NOSUPPORT; + return __assign_addr(addr, &addr->a_anycast, anycast, ADDR_ATTR_ANYCAST); } diff --git a/lib/route/class.c b/lib/route/class.c index 47356e2..050f42a 100644 --- a/lib/route/class.c +++ b/lib/route/class.c @@ -1,24 +1,24 @@ /* - * lib/route/class.c Queueing Classes + * lib/route/class.c Traffic Classes * * 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 version 2.1 * of the License. * - * Copyright (c) 2003-2010 Thomas Graf + * Copyright (c) 2003-2013 Thomas Graf */ /** * @ingroup tc - * @defgroup class Queueing Classes + * @defgroup class Traffic Classes * @{ */ -#include -#include +#include +#include #include -#include +#include #include #include #include @@ -68,126 +68,6 @@ static int class_request_update(struct nl_cache *cache, struct nl_sock *sk) sizeof(tchdr)); } -/** - * @name Addition/Modification - * @{ - */ - -static int class_build(struct rtnl_class *class, int type, int flags, - struct nl_msg **result) -{ - return rtnl_tc_msg_build(TC_CAST(class), type, flags, result); -} - -/** - * Build a netlink message to add a new class - * @arg class class to add - * @arg flags additional netlink message flags - * @arg result Pointer to store resulting message. - * - * Builds a new netlink message requesting an addition of a class. - * The netlink message header isn't fully equipped with all relevant - * fields and must be sent out via nl_send_auto_complete() or - * supplemented as needed. - * - * Common message flags - * - NLM_F_REPLACE - replace possibly existing classes - * - * @return 0 on success or a negative error code. - */ -int rtnl_class_build_add_request(struct rtnl_class *class, int flags, - struct nl_msg **result) -{ - return class_build(class, RTM_NEWTCLASS, NLM_F_CREATE | flags, result); -} - -/** - * Add a new class - * @arg sk Netlink socket. - * @arg class class to delete - * @arg flags additional netlink message flags - * - * Builds a netlink message by calling rtnl_qdisc_build_add_request(), - * sends the request to the kernel and waits for the next ACK to be - * received and thus blocks until the request has been processed. - * - * Common message flags - * - NLM_F_REPLACE - replace possibly existing classes - * - * @return 0 on success or a negative error code - */ -int rtnl_class_add(struct nl_sock *sk, struct rtnl_class *class, int flags) -{ - struct nl_msg *msg; - int err; - - if ((err = rtnl_class_build_add_request(class, flags, &msg)) < 0) - return err; - - err = nl_send_auto_complete(sk, msg); - nlmsg_free(msg); - if (err < 0) - return err; - - return wait_for_ack(sk); -} - -int rtnl_class_build_delete_request(struct rtnl_class *class, - struct nl_msg **result) -{ - struct nl_msg *msg; - struct tcmsg tchdr; - int required = TCA_ATTR_IFINDEX | TCA_ATTR_PARENT; - - if ((class->ce_mask & required) != required) - BUG(); - - msg = nlmsg_alloc_simple(RTM_DELTCLASS, 0); - if (!msg) - return -NLE_NOMEM; - - tchdr.tcm_family = AF_UNSPEC; - tchdr.tcm_handle = class->c_handle; - tchdr.tcm_parent = class->c_parent; - tchdr.tcm_ifindex = class->c_ifindex; - if (nlmsg_append(msg, &tchdr, sizeof(tchdr), NLMSG_ALIGNTO) < 0) { - nlmsg_free(msg); - return -NLE_MSGSIZE; - } - - *result = msg; - return 0; -} - -/** - * Delete a class - * @arg sk Netlink socket. - * @arg class class to delete - * - * Builds a netlink message by calling rtnl_class_build_delete_request(), - * sends the request to the kernel and waits for the ACK to be - * received and thus blocks until the request has been processed. - * - * @return 0 on success or a negative error code - */ -int rtnl_class_delete(struct nl_sock *sk, struct rtnl_class *class) -{ - struct nl_msg *msg; - int err; - - if ((err = rtnl_class_build_delete_request(class, &msg)) < 0) - return err; - - err = nl_send_auto_complete(sk, msg); - nlmsg_free(msg); - if (err < 0) - return err; - - return wait_for_ack(sk); -} - -/** @} */ - /** * @name Allocation/Freeing * @{ @@ -211,17 +91,188 @@ void rtnl_class_put(struct rtnl_class *class) /** @} */ + +/** + * @name Addition/Modification/Deletion + * @{ + */ + +static int class_build(struct rtnl_class *class, int type, int flags, + struct nl_msg **result) +{ + uint32_t needed = TCA_ATTR_PARENT | TCA_ATTR_HANDLE; + + if ((class->ce_mask & needed) == needed && + TC_H_MAJ(class->c_parent) && TC_H_MAJ(class->c_handle) && + TC_H_MAJ(class->c_parent) != TC_H_MAJ(class->c_handle)) { + APPBUG("TC_H_MAJ(parent) must match TC_H_MAJ(handle)"); + return -NLE_INVAL; + } + + return rtnl_tc_msg_build(TC_CAST(class), type, flags, result); +} + +/** + * Build a netlink message requesting the addition of a traffic class + * @arg class Traffic class to add + * @arg flags Additional netlink message flags + * @arg result Pointer to store resulting netlink message + * + * The behaviour of this function is identical to rtnl_class_add() with + * the exception that it will not send the message but return it int the + * provided return pointer instead. + * + * @see rtnl_class_add() + * + * @return 0 on success or a negative error code. + */ +int rtnl_class_build_add_request(struct rtnl_class *class, int flags, + struct nl_msg **result) +{ + return class_build(class, RTM_NEWTCLASS, flags, result); +} + +/** + * Add/Update traffic class + * @arg sk Netlink socket + * @arg class Traffic class to add + * @arg flags Additional netlink message flags + * + * Builds a \c RTM_NEWTCLASS netlink message requesting the addition + * of a new traffic class and sends the message to the kernel. The + * configuration of the traffic class is derived from the attributes + * of the specified traffic class. + * + * The following flags may be specified: + * - \c NLM_F_CREATE: Create traffic class if it does not exist, + * otherwise -NLE_OBJ_NOTFOUND is returned. + * - \c NLM_F_EXCL: Return -NLE_EXISTS if a traffic class with + * matching handle exists already. + * + * Existing traffic classes with matching handles will be updated, + * unless the flag \c NLM_F_EXCL is specified. If no matching traffic + * class exists, it will be created if the flag \c NLM_F_CREATE is set, + * otherwise the error -NLE_OBJ_NOTFOUND is returned. + * + * If the parent qdisc does not support classes, the error + * \c NLE_OPNOTSUPP is returned. + * + * After sending, the function will wait for the ACK or an eventual + * error message to be received and will therefore block until the + * operation has been completed. + * + * @note Disabling auto-ack (nl_socket_disable_auto_ack()) will cause + * this function to return immediately after sending. In this case, + * it is the responsibility of the caller to handle any error + * messages returned. + * + * @return 0 on success or a negative error code. + */ +int rtnl_class_add(struct nl_sock *sk, struct rtnl_class *class, int flags) +{ + struct nl_msg *msg; + int err; + + if ((err = rtnl_class_build_add_request(class, flags, &msg)) < 0) + return err; + + return nl_send_sync(sk, msg); +} + +/** + * Build netlink message requesting the deletion of a traffic class + * @arg class Traffic class to delete + * @arg result Pointer to store resulting netlink message + * + * The behaviour of this function is identical to rtnl_class_delete() with + * the exception that it will not send the message but return it in the + * provided return pointer instead. + * + * @see rtnl_class_delete() + * + * @return 0 on success or a negative error code. + */ +int rtnl_class_build_delete_request(struct rtnl_class *class, struct nl_msg **result) +{ + struct nl_msg *msg; + struct tcmsg tchdr; + uint32_t required = TCA_ATTR_IFINDEX | TCA_ATTR_HANDLE; + + if ((class->ce_mask & required) != required) { + APPBUG("ifindex and handle must be specified"); + return -NLE_MISSING_ATTR; + } + + if (!(msg = nlmsg_alloc_simple(RTM_DELTCLASS, 0))) + return -NLE_NOMEM; + + memset(&tchdr, 0, sizeof(tchdr)); + tchdr.tcm_family = AF_UNSPEC; + tchdr.tcm_ifindex = class->c_ifindex; + tchdr.tcm_handle = class->c_handle; + + if (class->ce_mask & TCA_ATTR_PARENT) + tchdr.tcm_parent = class->c_parent; + + if (nlmsg_append(msg, &tchdr, sizeof(tchdr), NLMSG_ALIGNTO) < 0) { + nlmsg_free(msg); + return -NLE_MSGSIZE; + } + + *result = msg; + return 0; +} + +/** + * Delete traffic class + * @arg sk Netlink socket + * @arg class Traffic class to delete + * + * Builds a \c RTM_DELTCLASS netlink message requesting the deletion + * of a traffic class and sends the message to the kernel. + * + * The message is constructed out of the following attributes: + * - \c ifindex and \c handle (required) + * - \c parent (optional, must match if provided) + * + * All other class attributes including all class type specific + * attributes are ignored. + * + * After sending, the function will wait for the ACK or an eventual + * error message to be received and will therefore block until the + * operation has been completed. + * + * @note Disabling auto-ack (nl_socket_disable_auto_ack()) will cause + * this function to return immediately after sending. In this case, + * it is the responsibility of the caller to handle any error + * messages returned. + * + * @return 0 on success or a negative error code. + */ +int rtnl_class_delete(struct nl_sock *sk, struct rtnl_class *class) +{ + struct nl_msg *msg; + int err; + + if ((err = rtnl_class_build_delete_request(class, &msg)) < 0) + return err; + + return nl_send_sync(sk, msg); +} + +/** @} */ + /** * @name Leaf Qdisc * @{ */ /** - * Lookup the leaf qdisc of a class - * @arg class the parent class - * @arg cache a qdisc cache including at laest all qdiscs of the - * interface the specified class is attached to - * @return The qdisc from the cache or NULL if the class has no leaf qdisc + * Lookup the leaf qdisc of a traffic class + * @arg class the parent traffic class + * @arg cache a qdisc cache allocated using rtnl_qdisc_alloc_cache() + * + * @return Matching Qdisc or NULL if the traffic class has no leaf qdisc */ struct rtnl_qdisc *rtnl_class_leaf_qdisc(struct rtnl_class *class, struct nl_cache *cache) @@ -241,19 +292,93 @@ struct rtnl_qdisc *rtnl_class_leaf_qdisc(struct rtnl_class *class, /** @} */ +/** + * @name Cache Related Functions + * @{ + */ /** - * @name Iterators + * Allocate a cache and fill it with all configured traffic classes + * @arg sk Netlink socket + * @arg ifindex Interface index of the network device + * @arg result Pointer to store the created cache + * + * Allocates a new traffic class cache and fills it with a list of all + * configured traffic classes on a specific network device. Release the + * cache with nl_cache_free(). + * + * @return 0 on success or a negative error code. + */ +int rtnl_class_alloc_cache(struct nl_sock *sk, int ifindex, + struct nl_cache **result) +{ + struct nl_cache * cache; + int err; + + if (!ifindex) { + APPBUG("ifindex must be specified"); + return -NLE_INVAL; + } + + if (!(cache = nl_cache_alloc(&rtnl_class_ops))) + return -NLE_NOMEM; + + cache->c_iarg1 = ifindex; + + if (sk && (err = nl_cache_refill(sk, cache)) < 0) { + nl_cache_free(cache); + return err; + } + + *result = cache; + return 0; +} + +/** + * Search traffic class by interface index and handle + * @arg cache Traffic class cache + * @arg ifindex Interface index + * @arg handle ID of traffic class + * + * Searches a traffic class cache previously allocated with + * rtnl_class_alloc_cache() and searches for a traffi class matching + * the interface index and handle. + * + * The reference counter is incremented before returning the traffic + * class, therefore the reference must be given back with rtnl_class_put() + * after usage. + * + * @return Traffic class or NULL if no match was found. + */ +struct rtnl_class *rtnl_class_get(struct nl_cache *cache, int ifindex, + uint32_t handle) +{ + struct rtnl_class *class; + + if (cache->c_ops != &rtnl_class_ops) + return NULL; + + nl_list_for_each_entry(class, &cache->c_items, ce_list) { + if (class->c_handle == handle && class->c_ifindex == ifindex) { + nl_object_get((struct nl_object *) class); + return class; + } + } + return NULL; +} + +/** @} */ + +/** + * @name Deprecated Functions * @{ */ /** * Call a callback for each child of a class - * @arg class the parent class - * @arg cache a class cache including all classes of the interface - * the specified class is attached to - * @arg cb callback function - * @arg arg argument to be passed to callback function + * + * @deprecated Use of this function is deprecated, it does not allow + * to handle the out of memory situation that can occur. */ void rtnl_class_foreach_child(struct rtnl_class *class, struct nl_cache *cache, void (*cb)(struct nl_object *, void *), void *arg) @@ -274,11 +399,9 @@ void rtnl_class_foreach_child(struct rtnl_class *class, struct nl_cache *cache, /** * Call a callback for each classifier attached to the class - * @arg class the parent class - * @arg cache a filter cache including at least all the filters - * attached to the specified class - * @arg cb callback function - * @arg arg argument to be passed to callback function + * + * @deprecated Use of this function is deprecated, it does not allow + * to handle the out of memory situation that can occur. */ void rtnl_class_foreach_cls(struct rtnl_class *class, struct nl_cache *cache, void (*cb)(struct nl_object *, void *), void *arg) @@ -298,70 +421,6 @@ void rtnl_class_foreach_cls(struct rtnl_class *class, struct nl_cache *cache, /** @} */ - -/** - * @name Cache Management - * @{ - */ - -/** - * Build a class cache including all classes attached to the specified interface - * @arg sk Netlink socket. - * @arg ifindex interface index of the link the classes are - * attached to. - * - * Allocates a new cache, initializes it properly and updates it to - * include all classes attached to the specified interface. - * - * @return The cache or NULL if an error has occured. - */ -int rtnl_class_alloc_cache(struct nl_sock *sk, int ifindex, - struct nl_cache **result) -{ - struct nl_cache * cache; - int err; - - cache = nl_cache_alloc(&rtnl_class_ops); - if (!cache) - return -NLE_NOMEM; - - cache->c_iarg1 = ifindex; - - if (sk && (err = nl_cache_refill(sk, cache)) < 0) { - nl_cache_free(cache); - return err; - } - - *result = cache; - return 0; -} - -/** - * Look up class by its handle in the provided cache - * @arg cache class cache - * @arg ifindex interface the class is attached to - * @arg handle class handle - * @return pointer to class inside the cache or NULL if no match was found. - */ -struct rtnl_class *rtnl_class_get(struct nl_cache *cache, int ifindex, - uint32_t handle) -{ - struct rtnl_class *class; - - if (cache->c_ops != &rtnl_class_ops) - return NULL; - - nl_list_for_each_entry(class, &cache->c_items, ce_list) { - if (class->c_handle == handle && class->c_ifindex == ifindex) { - nl_object_get((struct nl_object *) class); - return class; - } - } - return NULL; -} - -/** @} */ - static struct rtnl_tc_type_ops class_ops = { .tt_type = RTNL_TC_TYPE_CLASS, .tt_dump_prefix = "class", diff --git a/lib/route/classid.c b/lib/route/classid.c index 35cafe6..f2d3a01 100644 --- a/lib/route/classid.c +++ b/lib/route/classid.c @@ -6,7 +6,7 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2010 Thomas Graf + * Copyright (c) 2010-2013 Thomas Graf */ /** @@ -15,8 +15,8 @@ * @{ */ -#include -#include +#include +#include #include #include #include @@ -41,7 +41,7 @@ static int compare_id(const void *pa, const void *pb) if (ma->classid < mb->classid) return -1; - + if (ma->classid > mb->classid) return 1; @@ -106,7 +106,7 @@ static char *name_lookup(const uint32_t classid) * @return The destination buffer or the type encoded in hexidecimal * form if no match was found. */ -char * rtnl_tc_handle2str(uint32_t handle, char *buf, size_t len) +char *rtnl_tc_handle2str(uint32_t handle, char *buf, size_t len) { if (TC_H_ROOT == handle) snprintf(buf, len, "root"); @@ -120,11 +120,11 @@ char * rtnl_tc_handle2str(uint32_t handle, char *buf, size_t len) if ((name = name_lookup(handle))) snprintf(buf, len, "%s", name); else if (0 == TC_H_MAJ(handle)) - snprintf(buf, len, ":%02x", TC_H_MIN(handle)); + snprintf(buf, len, ":%x", TC_H_MIN(handle)); else if (0 == TC_H_MIN(handle)) - snprintf(buf, len, "%02x:", TC_H_MAJ(handle) >> 16); + snprintf(buf, len, "%x:", TC_H_MAJ(handle) >> 16); else - snprintf(buf, len, "%02x:%02x", + snprintf(buf, len, "%x:%x", TC_H_MAJ(handle) >> 16, TC_H_MIN(handle)); } @@ -154,7 +154,8 @@ char * rtnl_tc_handle2str(uint32_t handle, char *buf, size_t len) int rtnl_tc_str2handle(const char *str, uint32_t *res) { char *colon, *end; - uint32_t h, err; + uint32_t h; + int err; if (!strcasecmp(str, "root")) { *res = TC_H_ROOT; @@ -166,6 +167,11 @@ int rtnl_tc_str2handle(const char *str, uint32_t *res) return 0; } + if (!strcasecmp(str, "ingress")) { + *res = TC_H_INGRESS; + return 0; + } + h = strtoul(str, &colon, 16); /* MAJ is not a number */ @@ -217,7 +223,7 @@ not_a_number: } else { /* XXXX:YYYY */ uint32_t l; - + update: l = strtoul(colon+1, &end, 16); @@ -296,7 +302,7 @@ static int classid_map_add(uint32_t classid, const char *name) /** * (Re-)read classid file - * + * * Rereads the contents of the classid file (typically found at the location * /etc/libnl/classid) and refreshes the classid maps. * @@ -305,12 +311,13 @@ static int classid_map_add(uint32_t classid, const char *name) int rtnl_tc_read_classid_file(void) { static time_t last_read; - struct stat st = {0}; + struct stat st; char buf[256], *path; FILE *fd; int err; - asprintf(&path, "%s/classid", SYSCONFDIR); + if (build_sysconf_path(&path, "classid") < 0) + return -NLE_NOMEM; /* if stat fails, just (re-)read the file */ if (stat(path, &st) == 0) { @@ -392,7 +399,7 @@ int rtnl_classid_generate(const char *name, uint32_t *result, uint32_t parent) NL_DBG(2, "Generated new classid %#x\n", classid); - if (asprintf(&path, "%s/classid", SYSCONFDIR) < 0) + if (build_sysconf_path(&path, "classid") < 0) return -NLE_NOMEM; if (!(fd = fopen(path, "a"))) { @@ -408,7 +415,7 @@ int rtnl_classid_generate(const char *name, uint32_t *result, uint32_t parent) fclose(fd); if ((err = classid_map_add(classid, name)) < 0) { - /* + /* * Error adding classid map, re-read classid file is best * option here. It is likely to fail as well but better * than nothing, entry was added to the file already anyway. @@ -434,7 +441,16 @@ static void __init classid_init(void) nl_init_list_head(&tbl_name[i]); if ((err = rtnl_tc_read_classid_file()) < 0) - fprintf(stderr, "Failed to read classid file: %s\n", nl_geterror(err)); + NL_DBG(1, "Failed to read classid file: %s\n", nl_geterror(err)); } +static void free_map(void *map) { + free(((struct classid_map *)map)->name); + free(map); +}; + +static void __exit classid_exit(void) +{ + tdestroy(id_root, free_map); +} /** @} */ diff --git a/lib/route/cls.c b/lib/route/cls.c index a041baa..7a809bb 100644 --- a/lib/route/cls.c +++ b/lib/route/cls.c @@ -6,29 +6,20 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2009 Thomas Graf + * Copyright (c) 2003-2013 Thomas Graf */ /** * @ingroup tc * @defgroup cls Classifiers - * - * @par Classifier Identification - * - protocol - * - priority - * - parent - * - interface - * - kind - * - handle - * * @{ */ -#include -#include +#include +#include #include #include -#include +#include #include #include @@ -46,6 +37,12 @@ static int cls_build(struct rtnl_cls *cls, int type, int flags, { int err, prio, proto; struct tcmsg *tchdr; + uint32_t required = TCA_ATTR_IFINDEX; + + if ((cls->ce_mask & required) != required) { + APPBUG("ifindex must be specified"); + return -NLE_MISSING_ATTR; + } err = rtnl_tc_msg_build(TC_CAST(cls), type, flags, result); if (err < 0) @@ -119,42 +116,70 @@ uint16_t rtnl_cls_get_protocol(struct rtnl_cls *cls) /** - * @name Classifier Addition/Modification/Deletion + * @name Addition/Modification/Deletion * @{ */ /** - * Build a netlink message to add a new classifier - * @arg cls classifier to add - * @arg flags additional netlink message flags - * @arg result Pointer to store resulting message. + * Build a netlink message requesting the addition of a classifier + * @arg cls Classifier to add + * @arg flags Additional netlink message flags + * @arg result Pointer to store resulting netlink message * - * Builds a new netlink message requesting an addition of a classifier - * The netlink message header isn't fully equipped with all relevant - * fields and must be sent out via nl_send_auto_complete() or - * supplemented as needed. \a classifier must contain the attributes of - * the new classifier set via \c rtnl_cls_set_* functions. \a opts - * may point to the clsasifier specific options. + * The behaviour of this function is identical to rtnl_cls_add() with + * the exception that it will not send the message but return it int the + * provided return pointer instead. + * + * @see rtnl_cls_add() * * @return 0 on success or a negative error code. */ int rtnl_cls_build_add_request(struct rtnl_cls *cls, int flags, struct nl_msg **result) { - return cls_build(cls, RTM_NEWTFILTER, NLM_F_CREATE | flags, result); + if (!(flags & NLM_F_CREATE) && !(cls->ce_mask & CLS_ATTR_PRIO)) { + APPBUG("prio must be specified if not a new classifier"); + return -NLE_MISSING_ATTR; + } + + return cls_build(cls, RTM_NEWTFILTER, flags, result); } /** - * Add a new classifier - * @arg sk Netlink socket. - * @arg cls classifier to add - * @arg flags additional netlink message flags + * Add/Update classifier + * @arg sk Netlink socket + * @arg cls Classifier to add/update + * @arg flags Additional netlink message flags * - * Builds a netlink message by calling rtnl_cls_build_add_request(), - * sends the request to the kernel and waits for the next ACK to be - * received and thus blocks until the request has been processed. + * Builds a \c RTM_NEWTFILTER netlink message requesting the addition + * of a new classifier and sends the message to the kernel. The + * configuration of the classifier is derived from the attributes of + * the specified traffic class. * - * @return 0 on sucess or a negative error if an error occured. + * The following flags may be specified: + * - \c NLM_F_CREATE: Create classifier if it does not exist, + * otherwise -NLE_OBJ_NOTFOUND is returned. + * - \c NLM_F_EXCL: Return -NLE_EXISTS if a classifier with + * matching handle exists already. + * + * Existing classifiers with matching handles will be updated, unless + * the flag \c NLM_F_EXCL is specified. If no matching classifier + * exists, it will be created if the flag \c NLM_F_CREATE is set, + * otherwise the error -NLE_OBJ_NOTFOUND is returned. + * + * If the parent qdisc does not support classes, the error + * \c NLE_OPNOTSUPP is returned. + * + * After sending, the function will wait for the ACK or an eventual + * error message to be received and will therefore block until the + * operation has been completed. + * + * @note Disabling auto-ack (nl_socket_disable_auto_ack()) will cause + * this function to return immediately after sending. In this case, + * it is the responsibility of the caller to handle any error + * messages returned. + * + * @return 0 on success or a negative error code. */ int rtnl_cls_add(struct nl_sock *sk, struct rtnl_cls *cls, int flags) { @@ -163,13 +188,8 @@ int rtnl_cls_add(struct nl_sock *sk, struct rtnl_cls *cls, int flags) if ((err = rtnl_cls_build_add_request(cls, flags, &msg)) < 0) return err; - - err = nl_send_auto_complete(sk, msg); - nlmsg_free(msg); - if (err < 0) - return err; - return nl_wait_for_ack(sk); + return nl_send_sync(sk, msg); } /** @@ -211,45 +231,66 @@ int rtnl_cls_change(struct nl_sock *sk, struct rtnl_cls *cls, int flags) if ((err = rtnl_cls_build_change_request(cls, flags, &msg)) < 0) return err; - err = nl_send_auto_complete(sk, msg); - nlmsg_free(msg); - if (err < 0) - return err; - - return nl_wait_for_ack(sk); + return nl_send_sync(sk, msg); } /** - * Build a netlink request message to delete a classifier - * @arg cls classifier to delete - * @arg flags additional netlink message flags - * @arg result Pointer to store resulting message. + * Build netlink message requesting the deletion of a classifier + * @arg cls Classifier to delete + * @arg flags Additional netlink message flags + * @arg result Pointer to store resulting netlink message * - * Builds a new netlink message requesting a deletion of a classifier. - * The netlink message header isn't fully equipped with all relevant - * fields and must thus be sent out via nl_send_auto_complete() - * or supplemented as needed. + * The behaviour of this function is identical to rtnl_cls_delete() with + * the exception that it will not send the message but return it in the + * provided return pointer instead. + * + * @see rtnl_cls_delete() * * @return 0 on success or a negative error code. */ int rtnl_cls_build_delete_request(struct rtnl_cls *cls, int flags, struct nl_msg **result) { + uint32_t required = CLS_ATTR_PRIO; + + if ((cls->ce_mask & required) != required) { + APPBUG("prio must be specified"); + return -NLE_MISSING_ATTR; + } + return cls_build(cls, RTM_DELTFILTER, flags, result); } - /** - * Delete a classifier - * @arg sk Netlink socket. - * @arg cls classifier to delete - * @arg flags additional netlink message flags + * Delete classifier + * @arg sk Netlink socket + * @arg cls Classifier to delete + * @arg flags Additional netlink message flags * - * Builds a netlink message by calling rtnl_cls_build_delete_request(), - * sends the request to the kernel and waits for the next ACK to be - * received and thus blocks until the request has been processed. + * Builds a \c RTM_DELTFILTER netlink message requesting the deletion + * of a classifier and sends the message to the kernel. * - * @return 0 on sucess or a negative error if an error occured. + * The message is constructed out of the following attributes: + * - \c ifindex (required) + * - \c prio (required) + * - \c protocol (required) + * - \c handle (required) + * - \c parent (optional, if not specified parent equals root-qdisc) + * - \c kind (optional, must match if provided) + * + * All other classifier attributes including all class type specific + * attributes are ignored. + * + * After sending, the function will wait for the ACK or an eventual + * error message to be received and will therefore block until the + * operation has been completed. + * + * @note Disabling auto-ack (nl_socket_disable_auto_ack()) will cause + * this function to return immediately after sending. In this case, + * it is the responsibility of the caller to handle any error + * messages returned. + * + * @return 0 on success or a negative error code. */ int rtnl_cls_delete(struct nl_sock *sk, struct rtnl_cls *cls, int flags) { @@ -259,35 +300,28 @@ int rtnl_cls_delete(struct nl_sock *sk, struct rtnl_cls *cls, int flags) if ((err = rtnl_cls_build_delete_request(cls, flags, &msg)) < 0) return err; - err = nl_send_auto_complete(sk, msg); - nlmsg_free(msg); - if (err < 0) - return err; - - return nl_wait_for_ack(sk); + return nl_send_sync(sk, msg); } /** @} */ /** - * @name Cache Management + * @name Cache Related Functions * @{ */ /** - * Build a classifier cache including all classifiers attached to the - * specified class/qdisc on eht specified interface. - * @arg sk Netlink socket. - * @arg ifindex interface index of the link the classes are - * attached to. - * @arg parent parent qdisc/class - * @arg result Pointer to store resulting cache. + * Allocate a cache and fill it with all configured classifiers + * @arg sk Netlink socket + * @arg ifindex Interface index of the network device + * @arg parent Parent qdisc/traffic class class + * @arg result Pointer to store the created cache * - * Allocates a new cache, initializes it properly and updates it to - * include all classes attached to the specified interface. + * Allocates a new classifier cache and fills it with a list of all + * configured classifier attached to the specified parent qdisc/traffic + * class on the specified network device. Release the cache with + * nl_cache_free(). * - * @note The caller is responsible for destroying and freeing the - * cache after using it. * @return 0 on success or a negative error code. */ int rtnl_cls_alloc_cache(struct nl_sock *sk, int ifindex, uint32_t parent, struct nl_cache **result) diff --git a/lib/route/cls/basic.c b/lib/route/cls/basic.c index 9d7710a..fb1c382 100644 --- a/lib/route/cls/basic.c +++ b/lib/route/cls/basic.c @@ -6,7 +6,7 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2008-2011 Thomas Graf + * Copyright (c) 2008-2013 Thomas Graf */ /** @@ -22,10 +22,10 @@ * @{ */ -#include -#include +#include +#include #include -#include +#include #include #include #include diff --git a/lib/route/cls/cgroup.c b/lib/route/cls/cgroup.c index 230863b..c5b7561 100644 --- a/lib/route/cls/cgroup.c +++ b/lib/route/cls/cgroup.c @@ -6,7 +6,7 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2009-2011 Thomas Graf + * Copyright (c) 2009-2013 Thomas Graf */ /** @@ -16,12 +16,12 @@ * @{ */ -#include -#include +#include +#include #include #include #include -#include +#include #include #include #include diff --git a/lib/route/cls/ematch.c b/lib/route/cls/ematch.c index 8c9c3dd..89b5067 100644 --- a/lib/route/cls/ematch.c +++ b/lib/route/cls/ematch.c @@ -6,7 +6,7 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2008-2010 Thomas Graf + * Copyright (c) 2008-2013 Thomas Graf */ /** @@ -16,8 +16,8 @@ * @{ */ -#include -#include +#include +#include #include #include #include diff --git a/lib/route/cls/ematch/cmp.c b/lib/route/cls/ematch/cmp.c index 2a1070a..2997cdb 100644 --- a/lib/route/cls/ematch/cmp.c +++ b/lib/route/cls/ematch/cmp.c @@ -6,7 +6,7 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2008-2010 Thomas Graf + * Copyright (c) 2008-2013 Thomas Graf */ /** @@ -16,8 +16,8 @@ * @{ */ -#include -#include +#include +#include #include #include #include diff --git a/lib/route/cls/ematch/container.c b/lib/route/cls/ematch/container.c index ddbdce0..813391a 100644 --- a/lib/route/cls/ematch/container.c +++ b/lib/route/cls/ematch/container.c @@ -6,16 +6,22 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2008-2010 Thomas Graf + * Copyright (c) 2008-2013 Thomas Graf */ -#include -#include +#include +#include #include #include -static int container_parse(struct rtnl_ematch *e, void *data, size_t len) +static int container_parse(struct rtnl_ematch *e, void *data, size_t len __attribute__((unused))) { + /* + The kernel may provide more than 4 bytes of data in the future and we want + older libnl versions to be ok with that. We want interfaces to be growable + so we only ever enforce a minimum data length and copy as much as we are + aware of. Thomas Graf. + */ memcpy(e->e_data, data, sizeof(uint32_t)); return 0; diff --git a/lib/route/cls/ematch/meta.c b/lib/route/cls/ematch/meta.c index 11f87d6..44f11b9 100644 --- a/lib/route/cls/ematch/meta.c +++ b/lib/route/cls/ematch/meta.c @@ -6,7 +6,7 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2010 Thomas Graf + * Copyright (c) 2010-2013 Thomas Graf */ /** @@ -16,8 +16,8 @@ * @{ */ -#include -#include +#include +#include #include #include #include diff --git a/lib/route/cls/ematch/nbyte.c b/lib/route/cls/ematch/nbyte.c index 25a9866..8852489 100644 --- a/lib/route/cls/ematch/nbyte.c +++ b/lib/route/cls/ematch/nbyte.c @@ -6,7 +6,7 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2010 Thomas Graf + * Copyright (c) 2010-2013 Thomas Graf */ /** @@ -16,8 +16,8 @@ * @{ */ -#include -#include +#include +#include #include #include #include diff --git a/lib/route/cls/ematch/text.c b/lib/route/cls/ematch/text.c index 9d0241e..e8cdcae 100644 --- a/lib/route/cls/ematch/text.c +++ b/lib/route/cls/ematch/text.c @@ -6,7 +6,7 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2010 Thomas Graf + * Copyright (c) 2010-2013 Thomas Graf */ /** @@ -16,8 +16,8 @@ * @{ */ -#include -#include +#include +#include #include #include #include diff --git a/lib/route/cls/ematch_grammar.l b/lib/route/cls/ematch_grammar.l index 07e7e8c..96ef1a0 100644 --- a/lib/route/cls/ematch_grammar.l +++ b/lib/route/cls/ematch_grammar.l @@ -6,12 +6,12 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2010 Thomas Graf + * Copyright (c) 2010-2013 Thomas Graf */ %{ - #include - #include + #include + #include #include #include #include diff --git a/lib/route/cls/ematch_syntax.y b/lib/route/cls/ematch_syntax.y index 61c91d1..da21039 100644 --- a/lib/route/cls/ematch_syntax.y +++ b/lib/route/cls/ematch_syntax.y @@ -6,12 +6,12 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2010 Thomas Graf + * Copyright (c) 2010-2013 Thomas Graf */ %{ -#include -#include +#include +#include #include #include #include @@ -53,7 +53,9 @@ extern int ematch_lex(YYSTYPE *, void *); static void yyerror(void *scanner, char **errp, struct nl_list_head *root, const char *msg) { if (msg) - asprintf(errp, "%s", msg); + *errp = strdup(msg); + else + *errp = NULL; } %} @@ -186,7 +188,7 @@ ematch: struct rtnl_ematch *e; if (!(e = rtnl_ematch_alloc())) { - asprintf(errp, "Unable to allocate ematch object"); + *errp = strdup("Unable to allocate ematch object"); YYABORT; } @@ -201,7 +203,7 @@ ematch: struct rtnl_ematch *e; if (!(e = rtnl_ematch_alloc())) { - asprintf(errp, "Unable to allocate ematch object"); + *errp = strdup("Unable to allocate ematch object"); YYABORT; } @@ -219,7 +221,7 @@ ematch: struct rtnl_ematch *e; if (!(e = rtnl_ematch_alloc())) { - asprintf(errp, "Unable to allocate ematch object"); + *errp = strdup("Unable to allocate ematch object"); YYABORT; } @@ -246,7 +248,7 @@ ematch: struct rtnl_ematch *e; if (!(e = rtnl_ematch_alloc())) { - asprintf(errp, "Unable to allocate ematch object"); + *errp = strdup("Unable to allocate ematch object"); YYABORT; } @@ -265,7 +267,7 @@ ematch: struct rtnl_ematch *e; if (!(e = rtnl_ematch_alloc())) { - asprintf(errp, "Unable to allocate ematch object"); + *errp = strdup("Unable to allocate ematch object"); YYABORT; } @@ -417,7 +419,8 @@ pattern: memcpy($$.data, nl_addr_get_binary_addr(addr), $$.len); nl_addr_put(addr); } else { - asprintf(errp, "invalid pattern \"%s\"", $1); + if (asprintf(errp, "invalid pattern \"%s\"", $1) == -1) + *errp = NULL; YYABORT; } } @@ -433,7 +436,8 @@ pktloc: struct rtnl_pktloc *loc; if (rtnl_pktloc_lookup($1, &loc) < 0) { - asprintf(errp, "Packet location \"%s\" not found", $1); + if (asprintf(errp, "Packet location \"%s\" not found", $1) == -1) + *errp = NULL; YYABORT; } @@ -445,12 +449,12 @@ pktloc: struct rtnl_pktloc *loc; if ($5 && (!$1 || $1 > TCF_EM_ALIGN_U32)) { - asprintf(errp, "mask only allowed for alignments u8|u16|u32"); + *errp = strdup("mask only allowed for alignments u8|u16|u32"); YYABORT; } if (!(loc = rtnl_pktloc_alloc())) { - asprintf(errp, "Unable to allocate packet location object"); + *errp = strdup("Unable to allocate packet location object"); YYABORT; } diff --git a/lib/route/cls/fw.c b/lib/route/cls/fw.c index 11c9c67..b569d4f 100644 --- a/lib/route/cls/fw.c +++ b/lib/route/cls/fw.c @@ -6,7 +6,7 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2011 Thomas Graf + * Copyright (c) 2003-2013 Thomas Graf * Copyright (c) 2006 Petr Gotthard * Copyright (c) 2006 Siemens AG Oesterreich */ @@ -18,10 +18,10 @@ * @{ */ -#include -#include +#include +#include #include -#include +#include #include #include @@ -30,12 +30,14 @@ #define FW_ATTR_ACTION 0x002 #define FW_ATTR_POLICE 0x004 #define FW_ATTR_INDEV 0x008 +#define FW_ATTR_MASK 0x010 /** @endcond */ static struct nla_policy fw_policy[TCA_FW_MAX+1] = { [TCA_FW_CLASSID] = { .type = NLA_U32 }, [TCA_FW_INDEV] = { .type = NLA_STRING, .maxlen = IFNAMSIZ }, + [TCA_FW_MASK] = { .type = NLA_U32 }, }; static int fw_msg_parser(struct rtnl_tc *tc, void *data) @@ -72,6 +74,11 @@ static int fw_msg_parser(struct rtnl_tc *tc, void *data) f->cf_mask |= FW_ATTR_INDEV; } + if (tb[TCA_FW_MASK]) { + f->cf_fwmask = nla_get_u32(tb[TCA_FW_MASK]); + f->cf_mask |= FW_ATTR_MASK; + } + return 0; } @@ -101,12 +108,18 @@ static void fw_dump_line(struct rtnl_tc *tc, void *data, { struct rtnl_fw *f = data; - if (f && f->cf_mask & FW_ATTR_CLASSID) { + if (!f) + return; + + if (f->cf_mask & FW_ATTR_CLASSID) { char buf[32]; nl_dump(p, " target %s", rtnl_tc_handle2str(f->cf_classid, buf, sizeof(buf))); } + + if (f->cf_mask & FW_ATTR_MASK) + nl_dump(p, " mask 0x%x", f->cf_fwmask); } static void fw_dump_details(struct rtnl_tc *tc, void *data, @@ -137,6 +150,9 @@ static int fw_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg) if (f->cf_mask & FW_ATTR_INDEV) NLA_PUT_STRING(msg, TCA_FW_INDEV, f->cf_indev); + if (f->cf_mask & FW_ATTR_MASK) + NLA_PUT_U32(msg, TCA_FW_MASK, f->cf_fwmask); + return 0; nla_put_failure: @@ -161,6 +177,19 @@ int rtnl_fw_set_classid(struct rtnl_cls *cls, uint32_t classid) return 0; } +int rtnl_fw_set_mask(struct rtnl_cls *cls, uint32_t mask) +{ + struct rtnl_fw *f; + + if (!(f = rtnl_tc_data(TC_CAST(cls)))) + return -NLE_NOMEM; + + f->cf_fwmask = mask; + f->cf_mask |= FW_ATTR_MASK; + + return 0; +} + /** @} */ static struct rtnl_tc_ops fw_ops = { diff --git a/lib/route/cls/police.c b/lib/route/cls/police.c index d7ab26b..1f5d284 100644 --- a/lib/route/cls/police.c +++ b/lib/route/cls/police.c @@ -6,14 +6,14 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2006 Thomas Graf + * Copyright (c) 2003-2013 Thomas Graf */ -#include -#include +#include +#include #include #include -#include +#include #include #include diff --git a/lib/route/cls/u32.c b/lib/route/cls/u32.c index 2016460..c468ba7 100644 --- a/lib/route/cls/u32.c +++ b/lib/route/cls/u32.c @@ -6,7 +6,7 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2011 Thomas Graf + * Copyright (c) 2003-2013 Thomas Graf * Copyright (c) 2005-2006 Petr Gotthard * Copyright (c) 2005-2006 Siemens AG Oesterreich */ @@ -18,12 +18,12 @@ * @{ */ -#include -#include +#include +#include #include #include #include -#include +#include #include #include @@ -116,7 +116,7 @@ static int u32_msg_parser(struct rtnl_tc *tc, void *data) if (tb[TCA_U32_PCNT]) { struct tc_u32_sel *sel; - int pcnt_size; + size_t pcnt_size; if (!tb[TCA_U32_SEL]) { err = -NLE_MISSING_ATTR; @@ -306,7 +306,7 @@ static void u32_dump_stats(struct rtnl_tc *tc, void *data, if (u->cu_mask & U32_ATTR_PCNT) { struct tc_u32_pcnt *pc = u->cu_pcnt->d_data; nl_dump(p, "\n"); - nl_dump_line(p, " hit %8llu count %8llu\n", + nl_dump_line(p, " hit %8" PRIu64 " count %8" PRIu64 "\n", pc->rhit, pc->rcnt); } } @@ -374,6 +374,91 @@ int rtnl_u32_set_classid(struct rtnl_cls *cls, uint32_t classid) return 0; } +int rtnl_u32_set_divisor(struct rtnl_cls *cls, uint32_t divisor) +{ + struct rtnl_u32 *u; + + if (!(u = (struct rtnl_u32 *) rtnl_tc_data(TC_CAST(cls)))) + return -NLE_NOMEM; + + u->cu_divisor = divisor; + u->cu_mask |= U32_ATTR_DIVISOR; + return 0; +} + +int rtnl_u32_set_link(struct rtnl_cls *cls, uint32_t link) +{ + struct rtnl_u32 *u; + + if (!(u = (struct rtnl_u32 *) rtnl_tc_data(TC_CAST(cls)))) + return -NLE_NOMEM; + + u->cu_link = link; + u->cu_mask |= U32_ATTR_LINK; + return 0; +} + +int rtnl_u32_set_hashtable(struct rtnl_cls *cls, uint32_t ht) +{ + struct rtnl_u32 *u; + + if (!(u = (struct rtnl_u32 *) rtnl_tc_data(TC_CAST(cls)))) + return -NLE_NOMEM; + + u->cu_hash = ht; + u->cu_mask |= U32_ATTR_HASH; + return 0; +} + +int rtnl_u32_set_hashmask(struct rtnl_cls *cls, uint32_t hashmask, uint32_t offset) +{ + struct rtnl_u32 *u; + struct tc_u32_sel *sel; + int err; + + hashmask = htonl(hashmask); + + if (!(u = (struct rtnl_u32 *) rtnl_tc_data(TC_CAST(cls)))) + return -NLE_NOMEM; + + sel = u32_selector_alloc(u); + if (!sel) + return -NLE_NOMEM; + + err = nl_data_append(u->cu_selector, NULL, sizeof(struct tc_u32_key)); + if(err < 0) + return err; + + sel = u32_selector(u); + + sel->hmask = hashmask; + sel->hoff = offset; + return 0; +} + +int rtnl_u32_set_cls_terminal(struct rtnl_cls *cls) +{ + struct rtnl_u32 *u; + struct tc_u32_sel *sel; + int err; + + if (!(u = (struct rtnl_u32 *) rtnl_tc_data(TC_CAST(cls)))) + return -NLE_NOMEM; + + sel = u32_selector_alloc(u); + if (!sel) + return -NLE_NOMEM; + + err = nl_data_append(u->cu_selector, NULL, sizeof(struct tc_u32_key)); + if(err < 0) + return err; + + sel = u32_selector(u); + + sel->flags |= TC_U32_TERMINAL; + return 0; +} + /** @} */ /** diff --git a/lib/route/link.c b/lib/route/link.c index f9039a1..77dd7f1 100644 --- a/lib/route/link.c +++ b/lib/route/link.c @@ -6,178 +6,57 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2010 Thomas Graf + * Copyright (c) 2003-2012 Thomas Graf */ /** * @ingroup rtnl * @defgroup link Links (Interfaces) - * @brief * - * @par Link Identification - * A link can be identified by either its interface index or by its - * name. The kernel favours the interface index but falls back to the - * interface name if the interface index is lesser-than 0 for kernels - * >= 2.6.11. Therefore you can request changes without mapping a - * interface name to the corresponding index first. - * - * @par Changeable Attributes - * @anchor link_changeable - * - Link layer address - * - Link layer broadcast address - * - device mapping (ifmap) (>= 2.6.9) - * - MTU (>= 2.6.9) - * - Transmission queue length (>= 2.6.9) - * - Weight (>= 2.6.9) - * - Link name (only via access through interface index) (>= 2.6.9) - * - Flags (>= 2.6.9) - * - IFF_DEBUG - * - IFF_NOTRAILERS - * - IFF_NOARP - * - IFF_DYNAMIC - * - IFF_MULTICAST - * - IFF_PORTSEL - * - IFF_AUTOMEDIA - * - IFF_UP - * - IFF_PROMISC - * - IFF_ALLMULTI - * - * @par Link Flags (linux/if.h) - * @anchor link_flags - * @code - * IFF_UP Status of link (up|down) - * IFF_BROADCAST Indicates this link allows broadcasting - * IFF_MULTICAST Indicates this link allows multicasting - * IFF_ALLMULTI Indicates this link is doing multicast routing - * IFF_DEBUG Tell the driver to do debugging (currently unused) - * IFF_LOOPBACK This is the loopback link - * IFF_POINTOPOINT Point-to-point link - * IFF_NOARP Link is unable to perform ARP - * IFF_PROMISC Status of promiscious mode flag - * IFF_MASTER Used by teql - * IFF_SLAVE Used by teql - * IFF_PORTSEL Indicates this link allows port selection - * IFF_AUTOMEDIA Indicates this link selects port automatically - * IFF_DYNAMIC Indicates the address of this link is dynamic - * IFF_RUNNING Link is running and carrier is ok. - * IFF_NOTRAILERS Unused, BSD compat. - * @endcode - * - * @par Notes on IFF_PROMISC and IFF_ALLMULTI flags - * Although you can query the status of IFF_PROMISC and IFF_ALLMULTI - * they do not represent the actual state in the kernel but rather - * whether the flag has been enabled/disabled by userspace. The link - * may be in promiscious mode even if IFF_PROMISC is not set in a link - * dump request response because promiscity might be needed by the driver - * for a period of time. - * - * @note The unit of the transmission queue length depends on the - * link type, a common unit is \a packets. - * - * @par 1) Retrieving information about available links - * @code - * // The first step is to retrieve a list of all available interfaces within - * // the kernel and put them into a cache. - * struct nl_cache *cache = rtnl_link_alloc_cache(sk); - * - * // In a second step, a specific link may be looked up by either interface - * // index or interface name. - * struct rtnl_link *link = rtnl_link_get_by_name(cache, "lo"); - * - * // rtnl_link_get_by_name() is the short version for translating the - * // interface name to an interface index first like this: - * int ifindex = rtnl_link_name2i(cache, "lo"); - * struct rtnl_link *link = rtnl_link_get(cache, ifindex); - * - * // After successful usage, the object must be given back to the cache - * rtnl_link_put(link); - * @endcode - * - * @par 2) Changing link attributes - * @code - * // In order to change any attributes of an existing link, we must allocate - * // a new link to hold the change requests: - * struct rtnl_link *request = rtnl_link_alloc(); - * - * // Now we can go on and specify the attributes we want to change: - * rtnl_link_set_weight(request, 300); - * rtnl_link_set_mtu(request, 1360); - * - * // We can also shut an interface down administratively - * rtnl_link_unset_flags(request, rtnl_link_str2flags("up")); - * - * // Actually, we should know which link to change, so let's look it up - * struct rtnl_link *old = rtnl_link_get(cache, "eth0"); - * - * // Two ways exist to commit this change request, the first one is to - * // build the required netlink message and send it out in one single - * // step: - * rtnl_link_change(sk, old, request, 0); - * - * // An alternative way is to build the netlink message and send it - * // out yourself using nl_send_auto_complete() - * struct nl_msg *msg = rtnl_link_build_change_request(old, request); - * nl_send_auto_complete(sk, nlmsg_hdr(msg)); - * nlmsg_free(msg); - * - * // Don't forget to give back the link object ;-> - * rtnl_link_put(old); - * @endcode - * - * @par 3) Link Type Specific Attributes - * @code - * // Some link types offer additional parameters and statistics specific - * // to their type. F.e. a VLAN link can be configured like this: - * // - * // Allocate a new link and set the info type to "vlan". This is required - * // to prepare the link to hold vlan specific attributes. - * struct rtnl_link *request = rtnl_link_alloc(); - * rtnl_link_set_info_type(request, "vlan"); - * - * // Now vlan specific attributes can be set: - * rtnl_link_vlan_set_id(request, 10); - * rtnl_link_vlan_set_ingress_map(request, 2, 8); - * - * // Of course the attributes can also be read, check the info type - * // to make sure you are using the right access functions: - * char *type = rtnl_link_get_info_type(link); - * if (!strcmp(type, "vlan")) - * int id = rtnl_link_vlan_get_id(link); - * @endcode + * @details + * @route_doc{route_link, Link Documentation} * @{ */ -#include +#include #include #include #include #include +#include #include #include -#include +#include /** @cond SKIP */ -#define LINK_ATTR_MTU 0x0001 -#define LINK_ATTR_LINK 0x0002 -#define LINK_ATTR_TXQLEN 0x0004 -#define LINK_ATTR_WEIGHT 0x0008 -#define LINK_ATTR_MASTER 0x0010 -#define LINK_ATTR_QDISC 0x0020 -#define LINK_ATTR_MAP 0x0040 -#define LINK_ATTR_ADDR 0x0080 -#define LINK_ATTR_BRD 0x0100 -#define LINK_ATTR_FLAGS 0x0200 -#define LINK_ATTR_IFNAME 0x0400 -#define LINK_ATTR_IFINDEX 0x0800 -#define LINK_ATTR_FAMILY 0x1000 -#define LINK_ATTR_ARPTYPE 0x2000 -#define LINK_ATTR_STATS 0x4000 -#define LINK_ATTR_CHANGE 0x8000 -#define LINK_ATTR_OPERSTATE 0x10000 -#define LINK_ATTR_LINKMODE 0x20000 -#define LINK_ATTR_LINKINFO 0x40000 -#define LINK_ATTR_IFALIAS 0x80000 -#define LINK_ATTR_NUM_VF 0x100000 +#define LINK_ATTR_MTU (1 << 0) +#define LINK_ATTR_LINK (1 << 1) +#define LINK_ATTR_TXQLEN (1 << 2) +#define LINK_ATTR_WEIGHT (1 << 3) +#define LINK_ATTR_MASTER (1 << 4) +#define LINK_ATTR_QDISC (1 << 5) +#define LINK_ATTR_MAP (1 << 6) +#define LINK_ATTR_ADDR (1 << 7) +#define LINK_ATTR_BRD (1 << 8) +#define LINK_ATTR_FLAGS (1 << 9) +#define LINK_ATTR_IFNAME (1 << 10) +#define LINK_ATTR_IFINDEX (1 << 11) +#define LINK_ATTR_FAMILY (1 << 12) +#define LINK_ATTR_ARPTYPE (1 << 13) +#define LINK_ATTR_STATS (1 << 14) +#define LINK_ATTR_CHANGE (1 << 15) +#define LINK_ATTR_OPERSTATE (1 << 16) +#define LINK_ATTR_LINKMODE (1 << 17) +#define LINK_ATTR_LINKINFO (1 << 18) +#define LINK_ATTR_IFALIAS (1 << 19) +#define LINK_ATTR_NUM_VF (1 << 20) +#define LINK_ATTR_PROMISCUITY (1 << 21) +#define LINK_ATTR_NUM_TX_QUEUES (1 << 22) +#define LINK_ATTR_NUM_RX_QUEUES (1 << 23) +#define LINK_ATTR_GROUP (1 << 24) +#define LINK_ATTR_CARRIER (1 << 25) +#define LINK_ATTR_PROTINFO (1 << 26) +#define LINK_ATTR_AF_SPEC (1 << 27) static struct nl_cache_ops rtnl_link_ops; static struct nl_object_ops link_obj_ops; @@ -193,8 +72,10 @@ static struct rtnl_link_af_ops *af_lookup_and_alloc(struct rtnl_link *link, if (!af_ops) return NULL; - if (!(data = rtnl_link_af_alloc(link, af_ops))) + if (!(data = rtnl_link_af_alloc(link, af_ops))) { + rtnl_link_af_ops_put(af_ops); return NULL; + } return af_ops; } @@ -216,7 +97,7 @@ static int af_clone(struct rtnl_link *link, struct rtnl_link_af_ops *ops, struct rtnl_link *dst = arg; if (ops->ao_clone && - !(dst->l_af_data[ops->ao_family] = ops->ao_clone(link, data))) + !(dst->l_af_data[ops->ao_family] = ops->ao_clone(dst, data))) return -NLE_NOMEM; return 0; @@ -290,7 +171,11 @@ static int do_foreach_af(struct rtnl_link *link, if (!(ops = rtnl_link_af_ops_lookup(i))) BUG(); - if ((err = cb(link, ops, link->l_af_data[i], arg)) < 0) + err = cb(link, ops, link->l_af_data[i], arg); + + rtnl_link_af_ops_put(ops); + + if (err < 0) return err; } } @@ -303,7 +188,8 @@ static void release_link_info(struct rtnl_link *link) struct rtnl_link_info_ops *io = link->l_info_ops; if (io != NULL) { - io->io_free(link); + if (io->io_free) + io->io_free(link); rtnl_link_info_ops_put(io); link->l_info_ops = NULL; } @@ -319,10 +205,14 @@ static void link_free_data(struct nl_object *c) if ((io = link->l_info_ops) != NULL) release_link_info(link); + /* proto info af reference */ + rtnl_link_af_ops_put(link->l_af_ops); + nl_addr_put(link->l_addr); nl_addr_put(link->l_bcast); free(link->l_ifalias); + free(link->l_info_kind); do_foreach_af(link, af_free, NULL); } @@ -346,6 +236,10 @@ static int link_clone(struct nl_object *_dst, struct nl_object *_src) if (!(dst->l_ifalias = strdup(src->l_ifalias))) return -NLE_NOMEM; + if (src->l_info_kind) + if (!(dst->l_info_kind = strdup(src->l_info_kind))) + return -NLE_NOMEM; + if (src->l_info_ops && src->l_info_ops->io_clone) { err = src->l_info_ops->io_clone(dst, src); if (err < 0) @@ -359,24 +253,29 @@ static int link_clone(struct nl_object *_dst, struct nl_object *_src) } static struct nla_policy link_policy[IFLA_MAX+1] = { - [IFLA_IFNAME] = { .type = NLA_STRING, - .maxlen = IFNAMSIZ }, - [IFLA_MTU] = { .type = NLA_U32 }, - [IFLA_TXQLEN] = { .type = NLA_U32 }, - [IFLA_LINK] = { .type = NLA_U32 }, - [IFLA_WEIGHT] = { .type = NLA_U32 }, - [IFLA_MASTER] = { .type = NLA_U32 }, - [IFLA_OPERSTATE]= { .type = NLA_U8 }, - [IFLA_LINKMODE] = { .type = NLA_U8 }, - [IFLA_LINKINFO] = { .type = NLA_NESTED }, - [IFLA_QDISC] = { .type = NLA_STRING, - .maxlen = IFQDISCSIZ }, - [IFLA_STATS] = { .minlen = sizeof(struct rtnl_link_stats) }, - [IFLA_STATS64] = { .minlen = sizeof(struct rtnl_link_stats64) }, - [IFLA_MAP] = { .minlen = sizeof(struct rtnl_link_ifmap) }, - [IFLA_IFALIAS] = { .type = NLA_STRING, .maxlen = IFALIASZ }, - [IFLA_NUM_VF] = { .type = NLA_U32 }, - [IFLA_AF_SPEC] = { .type = NLA_NESTED }, + [IFLA_IFNAME] = { .type = NLA_STRING, + .maxlen = IFNAMSIZ }, + [IFLA_MTU] = { .type = NLA_U32 }, + [IFLA_TXQLEN] = { .type = NLA_U32 }, + [IFLA_LINK] = { .type = NLA_U32 }, + [IFLA_WEIGHT] = { .type = NLA_U32 }, + [IFLA_MASTER] = { .type = NLA_U32 }, + [IFLA_OPERSTATE] = { .type = NLA_U8 }, + [IFLA_LINKMODE] = { .type = NLA_U8 }, + [IFLA_LINKINFO] = { .type = NLA_NESTED }, + [IFLA_QDISC] = { .type = NLA_STRING, + .maxlen = IFQDISCSIZ }, + [IFLA_STATS] = { .minlen = sizeof(struct rtnl_link_stats) }, + [IFLA_STATS64] = { .minlen = sizeof(struct rtnl_link_stats64)}, + [IFLA_MAP] = { .minlen = sizeof(struct rtnl_link_ifmap) }, + [IFLA_IFALIAS] = { .type = NLA_STRING, .maxlen = IFALIASZ }, + [IFLA_NUM_VF] = { .type = NLA_U32 }, + [IFLA_AF_SPEC] = { .type = NLA_NESTED }, + [IFLA_PROMISCUITY] = { .type = NLA_U32 }, + [IFLA_NUM_TX_QUEUES] = { .type = NLA_U32 }, + [IFLA_NUM_RX_QUEUES] = { .type = NLA_U32 }, + [IFLA_GROUP] = { .type = NLA_U32 }, + [IFLA_CARRIER] = { .type = NLA_U8 }, }; static struct nla_policy link_info_policy[IFLA_INFO_MAX+1] = { @@ -393,6 +292,9 @@ static int link_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, struct nlattr *tb[IFLA_MAX+1]; struct rtnl_link_af_ops *af_ops = NULL; int err, family; + struct nla_policy real_link_policy[IFLA_MAX+1]; + + memcpy(&real_link_policy, link_policy, sizeof(link_policy)); link = rtnl_link_alloc(); if (link == NULL) { @@ -417,13 +319,15 @@ static int link_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, if ((af_ops = af_lookup_and_alloc(link, family))) { if (af_ops->ao_protinfo_policy) { - memcpy(&link_policy[IFLA_PROTINFO], + memcpy(&real_link_policy[IFLA_PROTINFO], af_ops->ao_protinfo_policy, sizeof(struct nla_policy)); } + + link->l_af_ops = af_ops; } - err = nlmsg_parse(n, sizeof(*ifi), tb, IFLA_MAX, link_policy); + err = nlmsg_parse(n, sizeof(*ifi), tb, IFLA_MAX, real_link_policy); if (err < 0) goto errout; @@ -469,34 +373,44 @@ static int link_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, } if (tb[IFLA_STATS64]) { - struct rtnl_link_stats64 *st = nla_data(tb[IFLA_STATS64]); + /* + * This structure contains 64bit parameters, and per the + * documentation in lib/attr.c, must not be accessed + * directly (because of alignment to 4 instead of 8). + * Therefore, copy the data to the stack and access it from + * there, where it will be aligned to 8. + */ + struct rtnl_link_stats64 st; + + nla_memcpy(&st, tb[IFLA_STATS64], + sizeof(struct rtnl_link_stats64)); - link->l_stats[RTNL_LINK_RX_PACKETS] = st->rx_packets; - link->l_stats[RTNL_LINK_TX_PACKETS] = st->tx_packets; - link->l_stats[RTNL_LINK_RX_BYTES] = st->rx_bytes; - link->l_stats[RTNL_LINK_TX_BYTES] = st->tx_bytes; - link->l_stats[RTNL_LINK_RX_ERRORS] = st->rx_errors; - link->l_stats[RTNL_LINK_TX_ERRORS] = st->tx_errors; - link->l_stats[RTNL_LINK_RX_DROPPED] = st->rx_dropped; - link->l_stats[RTNL_LINK_TX_DROPPED] = st->tx_dropped; - link->l_stats[RTNL_LINK_MULTICAST] = st->multicast; - link->l_stats[RTNL_LINK_COLLISIONS] = st->collisions; + link->l_stats[RTNL_LINK_RX_PACKETS] = st.rx_packets; + link->l_stats[RTNL_LINK_TX_PACKETS] = st.tx_packets; + link->l_stats[RTNL_LINK_RX_BYTES] = st.rx_bytes; + link->l_stats[RTNL_LINK_TX_BYTES] = st.tx_bytes; + link->l_stats[RTNL_LINK_RX_ERRORS] = st.rx_errors; + link->l_stats[RTNL_LINK_TX_ERRORS] = st.tx_errors; + link->l_stats[RTNL_LINK_RX_DROPPED] = st.rx_dropped; + link->l_stats[RTNL_LINK_TX_DROPPED] = st.tx_dropped; + link->l_stats[RTNL_LINK_MULTICAST] = st.multicast; + link->l_stats[RTNL_LINK_COLLISIONS] = st.collisions; - link->l_stats[RTNL_LINK_RX_LEN_ERR] = st->rx_length_errors; - link->l_stats[RTNL_LINK_RX_OVER_ERR] = st->rx_over_errors; - link->l_stats[RTNL_LINK_RX_CRC_ERR] = st->rx_crc_errors; - link->l_stats[RTNL_LINK_RX_FRAME_ERR] = st->rx_frame_errors; - link->l_stats[RTNL_LINK_RX_FIFO_ERR] = st->rx_fifo_errors; - link->l_stats[RTNL_LINK_RX_MISSED_ERR] = st->rx_missed_errors; + link->l_stats[RTNL_LINK_RX_LEN_ERR] = st.rx_length_errors; + link->l_stats[RTNL_LINK_RX_OVER_ERR] = st.rx_over_errors; + link->l_stats[RTNL_LINK_RX_CRC_ERR] = st.rx_crc_errors; + link->l_stats[RTNL_LINK_RX_FRAME_ERR] = st.rx_frame_errors; + link->l_stats[RTNL_LINK_RX_FIFO_ERR] = st.rx_fifo_errors; + link->l_stats[RTNL_LINK_RX_MISSED_ERR] = st.rx_missed_errors; - link->l_stats[RTNL_LINK_TX_ABORT_ERR] = st->tx_aborted_errors; - link->l_stats[RTNL_LINK_TX_CARRIER_ERR] = st->tx_carrier_errors; - link->l_stats[RTNL_LINK_TX_FIFO_ERR] = st->tx_fifo_errors; - link->l_stats[RTNL_LINK_TX_HBEAT_ERR] = st->tx_heartbeat_errors; - link->l_stats[RTNL_LINK_TX_WIN_ERR] = st->tx_window_errors; + link->l_stats[RTNL_LINK_TX_ABORT_ERR] = st.tx_aborted_errors; + link->l_stats[RTNL_LINK_TX_CARRIER_ERR] = st.tx_carrier_errors; + link->l_stats[RTNL_LINK_TX_FIFO_ERR] = st.tx_fifo_errors; + link->l_stats[RTNL_LINK_TX_HBEAT_ERR] = st.tx_heartbeat_errors; + link->l_stats[RTNL_LINK_TX_WIN_ERR] = st.tx_window_errors; - link->l_stats[RTNL_LINK_RX_COMPRESSED] = st->rx_compressed; - link->l_stats[RTNL_LINK_TX_COMPRESSED] = st->tx_compressed; + link->l_stats[RTNL_LINK_RX_COMPRESSED] = st.rx_compressed; + link->l_stats[RTNL_LINK_TX_COMPRESSED] = st.tx_compressed; link->ce_mask |= LINK_ATTR_STATS; } @@ -560,6 +474,11 @@ static int link_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, link->ce_mask |= LINK_ATTR_MASTER; } + if (tb[IFLA_CARRIER]) { + link->l_carrier = nla_get_u8(tb[IFLA_CARRIER]); + link->ce_mask |= LINK_ATTR_CARRIER; + } + if (tb[IFLA_OPERSTATE]) { link->l_operstate = nla_get_u8(tb[IFLA_OPERSTATE]); link->ce_mask |= LINK_ATTR_OPERSTATE; @@ -592,23 +511,34 @@ static int link_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, if (err < 0) goto errout; - if (li[IFLA_INFO_KIND] && - (li[IFLA_INFO_DATA] || li[IFLA_INFO_XSTATS])) { + if (li[IFLA_INFO_KIND]) { struct rtnl_link_info_ops *ops; char *kind; - kind = nla_get_string(li[IFLA_INFO_KIND]); + kind = nla_strdup(li[IFLA_INFO_KIND]); + if (kind == NULL) { + err = -NLE_NOMEM; + goto errout; + } + link->l_info_kind = kind; + link->ce_mask |= LINK_ATTR_LINKINFO; + ops = rtnl_link_info_ops_lookup(kind); - if (ops != NULL) { - link->l_info_ops = ops; - err = ops->io_parse(link, li[IFLA_INFO_DATA], - li[IFLA_INFO_XSTATS]); - if (err < 0) - goto errout; - } else { - /* XXX: Warn about unparsed info? */ + link->l_info_ops = ops; + + if (ops) { + if (ops->io_parse && + (li[IFLA_INFO_DATA] || li[IFLA_INFO_XSTATS])) { + err = ops->io_parse(link, li[IFLA_INFO_DATA], + li[IFLA_INFO_XSTATS]); + if (err < 0) + goto errout; + } else { + /* XXX: Warn about unparsed info? */ + } } } + link->ce_mask |= LINK_ATTR_LINKINFO; } if (tb[IFLA_PROTINFO] && af_ops && af_ops->ao_parse_protinfo) { @@ -616,6 +546,7 @@ static int link_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, link->l_af_data[link->l_family]); if (err < 0) goto errout; + link->ce_mask |= LINK_ATTR_PROTINFO; } if (tb[IFLA_AF_SPEC]) { @@ -628,14 +559,32 @@ static int link_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, char *af_data = link->l_af_data[nla_type(af_attr)]; err = af_ops->ao_parse_af(link, af_attr, af_data); - - rtnl_link_af_ops_put(af_ops); - if (err < 0) goto errout; } } + link->ce_mask |= LINK_ATTR_AF_SPEC; + } + + if (tb[IFLA_PROMISCUITY]) { + link->l_promiscuity = nla_get_u32(tb[IFLA_PROMISCUITY]); + link->ce_mask |= LINK_ATTR_PROMISCUITY; + } + + if (tb[IFLA_NUM_TX_QUEUES]) { + link->l_num_tx_queues = nla_get_u32(tb[IFLA_NUM_TX_QUEUES]); + link->ce_mask |= LINK_ATTR_NUM_TX_QUEUES; + } + + if (tb[IFLA_NUM_RX_QUEUES]) { + link->l_num_rx_queues = nla_get_u32(tb[IFLA_NUM_RX_QUEUES]); + link->ce_mask |= LINK_ATTR_NUM_RX_QUEUES; + } + + if (tb[IFLA_GROUP]) { + link->l_group = nla_get_u32(tb[IFLA_GROUP]); + link->ce_mask |= LINK_ATTR_GROUP; } err = pp->pp_cb((struct nl_object *) link, pp); @@ -655,7 +604,7 @@ static int link_request_update(struct nl_cache *cache, struct nl_sock *sk) static void link_dump_line(struct nl_object *obj, struct nl_dump_params *p) { char buf[128]; - struct nl_cache *cache = dp_cache(obj); + struct nl_cache *cache = obj->ce_cache; struct rtnl_link *link = (struct rtnl_link *) obj; nl_dump_line(p, "%s %s ", link->l_name, @@ -682,6 +631,9 @@ static void link_dump_line(struct nl_object *obj, struct nl_dump_params *p) rtnl_link_put(ll); } + if (link->ce_mask & LINK_ATTR_GROUP) + nl_dump(p, "group %u ", link->l_group); + if (link->l_info_ops && link->l_info_ops->io_dump[NL_DUMP_LINE]) link->l_info_ops->io_dump[NL_DUMP_LINE](link, p); @@ -709,6 +661,8 @@ static void link_dump_details(struct nl_object *obj, struct nl_dump_params *p) if (link->ce_mask & LINK_ATTR_IFINDEX) nl_dump(p, "index %u ", link->l_index); + if (link->ce_mask & LINK_ATTR_PROMISCUITY && link->l_promiscuity > 0) + nl_dump(p, "promisc-mode (%u users) ", link->l_promiscuity); nl_dump(p, "\n"); @@ -717,6 +671,12 @@ static void link_dump_details(struct nl_object *obj, struct nl_dump_params *p) nl_dump_line(p, " "); + if (link->ce_mask & LINK_ATTR_NUM_TX_QUEUES) + nl_dump(p, "txq %u ", link->l_num_tx_queues); + + if (link->ce_mask & LINK_ATTR_NUM_RX_QUEUES) + nl_dump(p, "rxq %u ", link->l_num_rx_queues); + if (link->ce_mask & LINK_ATTR_BRD) nl_dump(p, "brd %s ", nl_addr2str(link->l_bcast, buf, sizeof(buf))); @@ -730,9 +690,14 @@ static void link_dump_details(struct nl_object *obj, struct nl_dump_params *p) if (link->ce_mask & LINK_ATTR_NUM_VF) nl_dump(p, "num-vf %u ", link->l_num_vf); - nl_dump(p, "mode %s\n", + nl_dump(p, "mode %s ", rtnl_link_mode2str(link->l_linkmode, buf, sizeof(buf))); + nl_dump(p, "carrier %s", + rtnl_link_carrier2str(link->l_carrier, buf, sizeof(buf))); + + nl_dump(p, "\n"); + if (link->l_info_ops && link->l_info_ops->io_dump[NL_DUMP_DETAILS]) link->l_info_ops->io_dump[NL_DUMP_DETAILS](link, p); @@ -752,7 +717,7 @@ static void link_dump_stats(struct nl_object *obj, struct nl_dump_params *p) res = nl_cancel_down_bytes(link->l_stats[RTNL_LINK_RX_BYTES], &unit); - strcpy(fmt, " RX %X.2f %s %10llu %10llu %10llu %10llu %10llu\n"); + strcpy(fmt, " RX %X.2f %s %10" PRIu64 " %10" PRIu64 " %10" PRIu64 " %10" PRIu64 " %10" PRIu64 "\n"); fmt[9] = *unit == 'B' ? '9' : '7'; nl_dump_line(p, fmt, res, unit, @@ -764,7 +729,7 @@ static void link_dump_stats(struct nl_object *obj, struct nl_dump_params *p) res = nl_cancel_down_bytes(link->l_stats[RTNL_LINK_TX_BYTES], &unit); - strcpy(fmt, " TX %X.2f %s %10llu %10llu %10llu %10llu %10llu\n"); + strcpy(fmt, " TX %X.2f %s %10" PRIu64 " %10" PRIu64 " %10" PRIu64 " %10" PRIu64 " %10" PRIu64 "\n"); fmt[9] = *unit == 'B' ? '9' : '7'; nl_dump_line(p, fmt, res, unit, @@ -850,6 +815,29 @@ static int link_handle_event(struct nl_object *a, struct rtnl_link_event_cb *cb) } #endif + +static void link_keygen(struct nl_object *obj, uint32_t *hashkey, + uint32_t table_sz) +{ + struct rtnl_link *link = (struct rtnl_link *) obj; + unsigned int lkey_sz; + struct link_hash_key { + uint32_t l_index; + uint32_t l_family; + } __attribute__((packed)) lkey; + + lkey_sz = sizeof(lkey); + lkey.l_index = link->l_index; + lkey.l_family = link->l_family; + + *hashkey = nl_hash(&lkey, lkey_sz, 0) % table_sz; + + NL_DBG(5, "link %p key (dev %d fam %d) keysz %d, hash 0x%x\n", + link, lkey.l_index, lkey.l_family, lkey_sz, *hashkey); + + return; +} + static int link_compare(struct nl_object *_a, struct nl_object *_b, uint32_t attrs, int flags) { @@ -874,6 +862,10 @@ static int link_compare(struct nl_object *_a, struct nl_object *_b, diff |= LINK_DIFF(BRD, nl_addr_cmp(a->l_bcast, b->l_bcast)); diff |= LINK_DIFF(IFALIAS, strcmp(a->l_ifalias, b->l_ifalias)); diff |= LINK_DIFF(NUM_VF, a->l_num_vf != b->l_num_vf); + diff |= LINK_DIFF(PROMISCUITY, a->l_promiscuity != b->l_promiscuity); + diff |= LINK_DIFF(NUM_TX_QUEUES,a->l_num_tx_queues != b->l_num_tx_queues); + diff |= LINK_DIFF(NUM_RX_QUEUES,a->l_num_rx_queues != b->l_num_rx_queues); + diff |= LINK_DIFF(GROUP, a->l_group != b->l_group); if (flags & LOOSE_COMPARISON) diff |= LINK_DIFF(FLAGS, @@ -881,9 +873,22 @@ static int link_compare(struct nl_object *_a, struct nl_object *_b, else diff |= LINK_DIFF(FLAGS, a->l_flags != b->l_flags); -#undef LINK_DIFF + /* + * Compare LINK_ATTR_PROTINFO af_data + */ + if (a->l_family == b->l_family) { + if (rtnl_link_af_data_compare(a, b, a->l_family) != 0) + goto protinfo_mismatch; + } +out: return diff; + +protinfo_mismatch: + diff |= LINK_DIFF(PROTINFO, 1); + goto out; + +#undef LINK_DIFF } static const struct trans_tbl link_attrs[] = { @@ -907,6 +912,11 @@ static const struct trans_tbl link_attrs[] = { __ADD(LINK_ATTR_LINKMODE, linkmode) __ADD(LINK_ATTR_IFALIAS, ifalias) __ADD(LINK_ATTR_NUM_VF, num_vf) + __ADD(LINK_ATTR_PROMISCUITY, promiscuity) + __ADD(LINK_ATTR_NUM_TX_QUEUES, num_tx_queues) + __ADD(LINK_ATTR_NUM_RX_QUEUES, num_rx_queues) + __ADD(LINK_ATTR_GROUP, group) + __ADD(LINK_ATTR_CARRIER, carrier) }; static char *link_attrs2str(int attrs, char *buf, size_t len) @@ -916,24 +926,7 @@ static char *link_attrs2str(int attrs, char *buf, size_t len) } /** - * @name Allocation/Freeing - * @{ - */ - -struct rtnl_link *rtnl_link_alloc(void) -{ - return (struct rtnl_link *) nl_object_alloc(&link_obj_ops); -} - -void rtnl_link_put(struct rtnl_link *link) -{ - nl_object_put((struct nl_object *) link); -} - -/** @} */ - -/** - * @name Cache Management + * @name Get / List * @{ */ @@ -944,9 +937,21 @@ void rtnl_link_put(struct rtnl_link *link) * @arg family Link address family or AF_UNSPEC * @arg result Pointer to store resulting cache. * - * Allocates a new link cache, initializes it properly and updates it - * to include all links currently configured in the kernel. + * Allocates and initializes a new link cache. A netlink message is sent to + * the kernel requesting a full dump of all configured links. The returned + * messages are parsed and filled into the cache. If the operation succeeds + * the resulting cache will a link object for each link configured in the + * kernel. * + * If \c family is set to an address family other than \c AF_UNSPEC the + * contents of the cache can be limited to a specific address family. + * Currently the following address families are supported: + * - AF_BRIDGE + * - AF_INET6 + * + * @route_doc{link_list, Get List of Links} + * @see rtnl_link_get() + * @see rtnl_link_get_by_name() * @return 0 on success or a negative error code. */ int rtnl_link_alloc_cache(struct nl_sock *sk, int family, struct nl_cache **result) @@ -970,14 +975,19 @@ int rtnl_link_alloc_cache(struct nl_sock *sk, int family, struct nl_cache **resu } /** - * Look up link by interface index in the provided cache - * @arg cache link cache - * @arg ifindex link interface index + * Lookup link in cache by interface index + * @arg cache Link cache + * @arg ifindex Interface index * - * The caller owns a reference on the returned object and - * must give the object back via rtnl_link_put(). + * Searches through the provided cache looking for a link with matching + * interface index. * - * @return pointer to link inside the cache or NULL if no match was found. + * @attention The reference counter of the returned link object will be + * incremented. Use rtnl_link_put() to release the reference. + * + * @route_doc{link_list, Get List of Links} + * @see rtnl_link_get_by_name() + * @return Link object or NULL if no match was found. */ struct rtnl_link *rtnl_link_get(struct nl_cache *cache, int ifindex) { @@ -997,14 +1007,19 @@ struct rtnl_link *rtnl_link_get(struct nl_cache *cache, int ifindex) } /** - * Look up link by link name in the provided cache - * @arg cache link cache - * @arg name link name + * Lookup link in cache by link name + * @arg cache Link cache + * @arg name Name of link * - * The caller owns a reference on the returned object and - * must give the object back via rtnl_link_put(). + * Searches through the provided cache looking for a link with matching + * link name * - * @return pointer to link inside the cache or NULL if no match was found. + * @attention The reference counter of the returned link object will be + * incremented. Use rtnl_link_put() to release the reference. + * + * @route_doc{link_list, Get List of Links} + * @see rtnl_link_get() + * @return Link object or NULL if no match was found. */ struct rtnl_link *rtnl_link_get_by_name(struct nl_cache *cache, const char *name) @@ -1024,103 +1039,44 @@ struct rtnl_link *rtnl_link_get_by_name(struct nl_cache *cache, return NULL; } -/** @} */ - /** - * @name Link Modifications - * @{ - */ - -/** - * Builds a netlink change request message to change link attributes - * @arg old link to be changed - * @arg tmpl template with requested changes - * @arg flags additional netlink message flags + * Construct RTM_GETLINK netlink message + * @arg ifindex Interface index + * @arg name Name of link + * @arg result Pointer to store resulting netlink message * - * Builds a new netlink message requesting a change of link attributes. - * The netlink message header isn't fully equipped with all relevant - * fields and must be sent out via nl_send_auto_complete() or - * supplemented as needed. - * \a old must point to a link currently configured in the kernel - * and \a tmpl must contain the attributes to be changed set via - * \c rtnl_link_set_* functions. + * The behaviour of this function is identical to rtnl_link_get_kernel() + * with the exception that it will not send the message but return it in + * the provided return pointer instead. * - * @return New netlink message - * @note Not all attributes can be changed, see - * \ref link_changeable "Changeable Attributes" for more details. + * @see rtnl_link_get_kernel() + * + * @return 0 on success or a negative error code. */ -int rtnl_link_build_change_request(struct rtnl_link *old, - struct rtnl_link *tmpl, int flags, - struct nl_msg **result) +int rtnl_link_build_get_request(int ifindex, const char *name, + struct nl_msg **result) { + struct ifinfomsg ifi; struct nl_msg *msg; - struct nlattr *af_spec; - struct ifinfomsg ifi = { - .ifi_family = old->l_family, - .ifi_index = old->l_index, - }; - if (tmpl->ce_mask & LINK_ATTR_FLAGS) { - ifi.ifi_flags = old->l_flags & ~tmpl->l_flag_mask; - ifi.ifi_flags |= tmpl->l_flags; + if (ifindex <= 0 && !name) { + APPBUG("ifindex or name must be specified"); + return -NLE_MISSING_ATTR; } - msg = nlmsg_alloc_simple(RTM_SETLINK, flags); - if (!msg) + memset(&ifi, 0, sizeof(ifi)); + + if (!(msg = nlmsg_alloc_simple(RTM_GETLINK, 0))) return -NLE_NOMEM; + if (ifindex > 0) + ifi.ifi_index = ifindex; + if (nlmsg_append(msg, &ifi, sizeof(ifi), NLMSG_ALIGNTO) < 0) goto nla_put_failure; - if (tmpl->ce_mask & LINK_ATTR_ADDR) - NLA_PUT_ADDR(msg, IFLA_ADDRESS, tmpl->l_addr); - - if (tmpl->ce_mask & LINK_ATTR_BRD) - NLA_PUT_ADDR(msg, IFLA_BROADCAST, tmpl->l_bcast); - - if (tmpl->ce_mask & LINK_ATTR_MTU) - NLA_PUT_U32(msg, IFLA_MTU, tmpl->l_mtu); - - if (tmpl->ce_mask & LINK_ATTR_TXQLEN) - NLA_PUT_U32(msg, IFLA_TXQLEN, tmpl->l_txqlen); - - if (tmpl->ce_mask & LINK_ATTR_WEIGHT) - NLA_PUT_U32(msg, IFLA_WEIGHT, tmpl->l_weight); - - if (tmpl->ce_mask & LINK_ATTR_IFNAME) - NLA_PUT_STRING(msg, IFLA_IFNAME, tmpl->l_name); - - if (tmpl->ce_mask & LINK_ATTR_OPERSTATE) - NLA_PUT_U8(msg, IFLA_OPERSTATE, tmpl->l_operstate); - - if (tmpl->ce_mask & LINK_ATTR_LINKMODE) - NLA_PUT_U8(msg, IFLA_LINKMODE, tmpl->l_linkmode); - - if (tmpl->ce_mask & LINK_ATTR_IFALIAS) - NLA_PUT_STRING(msg, IFLA_IFALIAS, tmpl->l_ifalias); - - if ((tmpl->ce_mask & LINK_ATTR_LINKINFO) && tmpl->l_info_ops && - tmpl->l_info_ops->io_put_attrs) { - struct nlattr *info; - - if (!(info = nla_nest_start(msg, IFLA_LINKINFO))) - goto nla_put_failure; - - NLA_PUT_STRING(msg, IFLA_INFO_KIND, tmpl->l_info_ops->io_name); - - if (tmpl->l_info_ops->io_put_attrs(msg, tmpl) < 0) - goto nla_put_failure; - - nla_nest_end(msg, info); - } - - if (!(af_spec = nla_nest_start(msg, IFLA_AF_SPEC))) - goto nla_put_failure; - - if (do_foreach_af(tmpl, af_fill, msg) < 0) - goto nla_put_failure; - - nla_nest_end(msg, af_spec); + if (name) + NLA_PUT_STRING(msg, IFLA_IFNAME, name); *result = msg; return 0; @@ -1131,55 +1087,62 @@ nla_put_failure: } /** - * Change link attributes - * @arg sk Netlink socket. - * @arg old link to be changed - * @arg tmpl template with requested changes - * @arg flags additional netlink message flags + * Get a link object directly from kernel + * @arg sk Netlink socket + * @arg ifindex Interface index + * @arg name Name of link + * @arg result Pointer to store resulting link object * - * Builds a new netlink message by calling rtnl_link_build_change_request(), - * sends the request to the kernel and waits for the next ACK to be - * received, i.e. blocks until the request has been processed. + * This function builds a \c RTM_GETLINK netlink message to request + * a specific link directly from the kernel. The returned answer is + * parsed into a struct rtnl_link object and returned via the result + * pointer or -NLE_OBJ_NOTFOUND is returned if no matching link was + * found. * - * @return 0 on success or a negative error code - * @note Not all attributes can be changed, see - * \ref link_changeable "Changeable Attributes" for more details. + * @route_doc{link_direct_lookup, Lookup Single Link (Direct Lookup)} + * @return 0 on success or a negative error code. */ -int rtnl_link_change(struct nl_sock *sk, struct rtnl_link *old, - struct rtnl_link *tmpl, int flags) +int rtnl_link_get_kernel(struct nl_sock *sk, int ifindex, const char *name, + struct rtnl_link **result) { - struct nl_msg *msg; + struct nl_msg *msg = NULL; + struct nl_object *obj; int err; - - if ((err = rtnl_link_build_change_request(old, tmpl, flags, &msg)) < 0) + + if ((err = rtnl_link_build_get_request(ifindex, name, &msg)) < 0) return err; - - err = nl_send_auto_complete(sk, msg); + + err = nl_send_auto(sk, msg); nlmsg_free(msg); if (err < 0) return err; - return wait_for_ack(sk); + if ((err = nl_pickup(sk, link_msg_parser, &obj)) < 0) + return err; + + /* We have used link_msg_parser(), object is definitely a link */ + *result = (struct rtnl_link *) obj; + + /* If an object has been returned, we also need to wait for the ACK */ + if (err == 0 && obj) + wait_for_ack(sk); + + return 0; } -/** @} */ - /** - * @name Name <-> Index Translations - * @{ - */ - -/** - * Translate an interface index to the corresponding link name - * @arg cache link cache - * @arg ifindex link interface index - * @arg dst destination buffer - * @arg len length of destination buffer + * Translate interface index to corresponding link name + * @arg cache Link cache + * @arg ifindex Interface index + * @arg dst String to store name + * @arg len Length of destination string * * Translates the specified interface index to the corresponding - * link name and stores the name in the destination buffer. + * link name and stores the name in the destination string. * - * @return link name or NULL if no match was found. + * @route_doc{link_translate_ifindex, Translating interface index to link name} + * @see rtnl_link_name2i() + * @return Name of link or NULL if no match was found. */ char * rtnl_link_i2name(struct nl_cache *cache, int ifindex, char *dst, size_t len) @@ -1196,11 +1159,13 @@ char * rtnl_link_i2name(struct nl_cache *cache, int ifindex, char *dst, } /** - * Translate a link name to the corresponding interface index - * @arg cache link cache - * @arg name link name + * Translate link name to corresponding interface index + * @arg cache Link cache + * @arg name Name of link * - * @return interface index or 0 if no match was found. + * @route_doc{link_translate_ifindex, Translating interface index to link name} + * @see rtnl_link_i2name() + * @return Interface index or 0 if no match was found. */ int rtnl_link_name2i(struct nl_cache *cache, const char *name) { @@ -1218,8 +1183,1215 @@ int rtnl_link_name2i(struct nl_cache *cache, const char *name) /** @} */ +static int build_link_msg(int cmd, struct ifinfomsg *hdr, + struct rtnl_link *link, int flags, struct nl_msg **result) +{ + struct nl_msg *msg; + struct nlattr *af_spec; + + msg = nlmsg_alloc_simple(cmd, flags); + if (!msg) + return -NLE_NOMEM; + + if (nlmsg_append(msg, hdr, sizeof(*hdr), NLMSG_ALIGNTO) < 0) + goto nla_put_failure; + + if (link->ce_mask & LINK_ATTR_ADDR) + NLA_PUT_ADDR(msg, IFLA_ADDRESS, link->l_addr); + + if (link->ce_mask & LINK_ATTR_BRD) + NLA_PUT_ADDR(msg, IFLA_BROADCAST, link->l_bcast); + + if (link->ce_mask & LINK_ATTR_MTU) + NLA_PUT_U32(msg, IFLA_MTU, link->l_mtu); + + if (link->ce_mask & LINK_ATTR_TXQLEN) + NLA_PUT_U32(msg, IFLA_TXQLEN, link->l_txqlen); + + if (link->ce_mask & LINK_ATTR_WEIGHT) + NLA_PUT_U32(msg, IFLA_WEIGHT, link->l_weight); + + if (link->ce_mask & LINK_ATTR_IFNAME) + NLA_PUT_STRING(msg, IFLA_IFNAME, link->l_name); + + if (link->ce_mask & LINK_ATTR_OPERSTATE) + NLA_PUT_U8(msg, IFLA_OPERSTATE, link->l_operstate); + + if (link->ce_mask & LINK_ATTR_CARRIER) + NLA_PUT_U8(msg, IFLA_CARRIER, link->l_carrier); + + if (link->ce_mask & LINK_ATTR_LINKMODE) + NLA_PUT_U8(msg, IFLA_LINKMODE, link->l_linkmode); + + if (link->ce_mask & LINK_ATTR_IFALIAS) + NLA_PUT_STRING(msg, IFLA_IFALIAS, link->l_ifalias); + + if (link->ce_mask & LINK_ATTR_LINK) + NLA_PUT_U32(msg, IFLA_LINK, link->l_link); + + if (link->ce_mask & LINK_ATTR_MASTER) + NLA_PUT_U32(msg, IFLA_MASTER, link->l_master); + + if (link->ce_mask & LINK_ATTR_NUM_TX_QUEUES) + NLA_PUT_U32(msg, IFLA_NUM_TX_QUEUES, link->l_num_tx_queues); + + if (link->ce_mask & LINK_ATTR_NUM_RX_QUEUES) + NLA_PUT_U32(msg, IFLA_NUM_RX_QUEUES, link->l_num_rx_queues); + + if (link->ce_mask & LINK_ATTR_GROUP) + NLA_PUT_U32(msg, IFLA_GROUP, link->l_group); + + if (link->ce_mask & LINK_ATTR_LINKINFO) { + struct nlattr *info; + + if (!(info = nla_nest_start(msg, IFLA_LINKINFO))) + goto nla_put_failure; + + NLA_PUT_STRING(msg, IFLA_INFO_KIND, link->l_info_kind); + + if (link->l_info_ops) { + if (link->l_info_ops->io_put_attrs && + link->l_info_ops->io_put_attrs(msg, link) < 0) + goto nla_put_failure; + } + + nla_nest_end(msg, info); + } + + if (!(af_spec = nla_nest_start(msg, IFLA_AF_SPEC))) + goto nla_put_failure; + + if (do_foreach_af(link, af_fill, msg) < 0) + goto nla_put_failure; + + nla_nest_end(msg, af_spec); + + *result = msg; + return 0; + +nla_put_failure: + nlmsg_free(msg); + return -NLE_MSGSIZE; +} + /** - * @name Link Flags Translations + * @name Add / Modify + * @{ + */ + +/** + * Build a netlink message requesting the addition of new virtual link + * @arg link new link to add + * @arg flags additional netlink message flags + * @arg result pointer to store resulting netlink message + * + * The behaviour of this function is identical to rtnl_link_add() with + * the exception that it will not send the message but return it in the + * provided return pointer instead. + * + * @see rtnl_link_add() + * + * @note This operation is not supported on all kernel versions. + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_build_add_request(struct rtnl_link *link, int flags, + struct nl_msg **result) +{ + struct ifinfomsg ifi = { + .ifi_family = link->l_family, + .ifi_index = link->l_index, + .ifi_flags = link->l_flags, + }; + + return build_link_msg(RTM_NEWLINK, &ifi, link, flags, result); +} + +/** + * Add virtual link + * @arg sk netlink socket. + * @arg link new link to add + * @arg flags additional netlink message flags + * + * Builds a \c RTM_NEWLINK netlink message requesting the addition of + * a new virtual link. + * + * After sending, the function will wait for the ACK or an eventual + * error message to be received and will therefore block until the + * operation has been completed. + * + * @copydoc auto_ack_warning + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_add(struct nl_sock *sk, struct rtnl_link *link, int flags) +{ + struct nl_msg *msg; + int err; + + err = rtnl_link_build_add_request(link, flags, &msg); + if (err < 0) + return err; + + return nl_send_sync(sk, msg); +} + +/** + * Build a netlink message requesting the modification of link + * @arg orig original link to change + * @arg changes link containing the changes to be made + * @arg flags additional netlink message flags + * @arg result pointer to store resulting netlink message + * + * The behaviour of this function is identical to rtnl_link_change() with + * the exception that it will not send the message but return it in the + * provided return pointer instead. + * + * @see rtnl_link_change() + * + * @note The resulting message will have message type set to RTM_NEWLINK + * which may not work with older kernels. You may have to modify it + * to RTM_SETLINK (does not allow changing link info attributes) to + * have the change request work with older kernels. + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_build_change_request(struct rtnl_link *orig, + struct rtnl_link *changes, int flags, + struct nl_msg **result) +{ + struct ifinfomsg ifi = { + .ifi_family = orig->l_family, + .ifi_index = orig->l_index, + }; + int err; + + if (changes->ce_mask & LINK_ATTR_FLAGS) { + ifi.ifi_flags = orig->l_flags & ~changes->l_flag_mask; + ifi.ifi_flags |= changes->l_flags; + } + + if (changes->l_family && changes->l_family != orig->l_family) { + APPBUG("link change: family is immutable"); + return -NLE_IMMUTABLE; + } + + /* Avoid unnecessary name change requests */ + if (orig->ce_mask & LINK_ATTR_IFINDEX && + orig->ce_mask & LINK_ATTR_IFNAME && + changes->ce_mask & LINK_ATTR_IFNAME && + !strcmp(orig->l_name, changes->l_name)) + changes->ce_mask &= ~LINK_ATTR_IFNAME; + + if ((err = build_link_msg(RTM_NEWLINK, &ifi, changes, flags, result)) < 0) + goto errout; + + return 0; + +errout: + return err; +} + +/** + * Change link + * @arg sk netlink socket. + * @arg orig original link to be changed + * @arg changes link containing the changes to be made + * @arg flags additional netlink message flags + * + * Builds a \c RTM_NEWLINK netlink message requesting the change of + * a network link. If -EOPNOTSUPP is returned by the kernel, the + * message type will be changed to \c RTM_SETLINK and the message is + * resent to work around older kernel versions. + * + * The link to be changed is looked up based on the interface index + * supplied in the \p orig link. Optionaly the link name is used but + * only if no interface index is provided, otherwise providing an + * link name will result in the link name being changed. + * + * If no matching link exists, the function will return + * -NLE_OBJ_NOTFOUND. + * + * After sending, the function will wait for the ACK or an eventual + * error message to be received and will therefore block until the + * operation has been completed. + * + * @copydoc auto_ack_warning + * + * @note The link name can only be changed if the link has been put + * in opertional down state. (~IF_UP) + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_change(struct nl_sock *sk, struct rtnl_link *orig, + struct rtnl_link *changes, int flags) +{ + struct nl_msg *msg; + int err; + + err = rtnl_link_build_change_request(orig, changes, flags, &msg); + if (err < 0) + return err; + +retry: + err = nl_send_auto_complete(sk, msg); + if (err < 0) + goto errout; + + err = wait_for_ack(sk); + if (err == -NLE_OPNOTSUPP && msg->nm_nlh->nlmsg_type == RTM_NEWLINK) { + msg->nm_nlh->nlmsg_type = RTM_SETLINK; + goto retry; + } + +errout: + nlmsg_free(msg); + return err; +} + +/** @} */ + +/** + * @name Delete + * @{ + */ + +/** + * Build a netlink message requesting the deletion of a link + * @arg link Link to delete + * @arg result Pointer to store resulting netlink message + * + * The behaviour of this function is identical to rtnl_link_delete() with + * the exception that it will not send the message but return it in the + * provided return pointer instead. + * + * @see rtnl_link_delete() + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_build_delete_request(const struct rtnl_link *link, + struct nl_msg **result) +{ + struct nl_msg *msg; + struct ifinfomsg ifi = { + .ifi_index = link->l_index, + }; + + if (!(link->ce_mask & (LINK_ATTR_IFINDEX | LINK_ATTR_IFNAME))) { + APPBUG("ifindex or name must be specified"); + return -NLE_MISSING_ATTR; + } + + if (!(msg = nlmsg_alloc_simple(RTM_DELLINK, 0))) + return -NLE_NOMEM; + + if (nlmsg_append(msg, &ifi, sizeof(ifi), NLMSG_ALIGNTO) < 0) + goto nla_put_failure; + + if (link->ce_mask & LINK_ATTR_IFNAME) + NLA_PUT_STRING(msg, IFLA_IFNAME, link->l_name); + + *result = msg; + return 0; + +nla_put_failure: + nlmsg_free(msg); + return -NLE_MSGSIZE; +} + +/** + * Delete link + * @arg sk Netlink socket + * @arg link Link to delete + * + * Builds a \c RTM_DELLINK netlink message requesting the deletion of + * a network link which has been previously added to the kernel and + * sends the message to the kernel. + * + * If no matching link exists, the function will return + * -NLE_OBJ_NOTFOUND. + * + * After sending, the function will wait for the ACK or an eventual + * error message to be received and will therefore block until the + * operation has been completed. + * + * @copydoc auto_ack_warning + * + * @note Only virtual links such as dummy interface or vlan interfaces + * can be deleted. It is not possible to delete physical interfaces + * such as ethernet interfaces or the loopback device. + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_delete(struct nl_sock *sk, const struct rtnl_link *link) +{ + struct nl_msg *msg; + int err; + + if ((err = rtnl_link_build_delete_request(link, &msg)) < 0) + return err; + + return nl_send_sync(sk, msg); +} + +/** @} */ + +/** + * @name Link Object + * @{ + */ + +/** + * Allocate link object + * + * @see rtnl_link_put() + * @return New link object or NULL if allocation failed + */ +struct rtnl_link *rtnl_link_alloc(void) +{ + return (struct rtnl_link *) nl_object_alloc(&link_obj_ops); +} + +/** + * Return a link object reference + * @arg link Link object + */ +void rtnl_link_put(struct rtnl_link *link) +{ + nl_object_put((struct nl_object *) link); +} + +/** + * Set name of link object + * @arg link Link object + * @arg name New name + * + * @note To change the name of a link in the kernel, set the interface + * index to the link you wish to change, modify the link name using + * this function and pass the link object to rtnl_link_change() or + * rtnl_link_add(). + * + * @route_doc{link_attr_name, Link Name} + * @see rtnl_link_get_name() + * @see rtnl_link_set_ifindex() + */ +void rtnl_link_set_name(struct rtnl_link *link, const char *name) +{ + strncpy(link->l_name, name, sizeof(link->l_name) - 1); + link->ce_mask |= LINK_ATTR_IFNAME; +} + +/** + * Return name of link object + * @arg link Link object + * + * @route_doc{link_attr_name, Link Name} + * @see rtnl_link_set_name() + * @return Link name or NULL if name is not specified + */ +char *rtnl_link_get_name(struct rtnl_link *link) +{ + return link->ce_mask & LINK_ATTR_IFNAME ? link->l_name : NULL; +} + +/** + * Set the group identifier of a link object + * @arg link Link object + * @arg group Group identifier + */ +void rtnl_link_set_group(struct rtnl_link *link, uint32_t group) +{ + link->l_group = group; + link->ce_mask |= LINK_ATTR_GROUP; +} + +/** + * Return the group identifier of link object + * @arg link Link object + * + * @return Group identifier or 0 if not set. + */ +uint32_t rtnl_link_get_group(struct rtnl_link *link) +{ + return link->l_group; +} + +static inline void __assign_addr(struct rtnl_link *link, struct nl_addr **pos, + struct nl_addr *new, int flag) +{ + if (*pos) + nl_addr_put(*pos); + + nl_addr_get(new); + *pos = new; + + link->ce_mask |= flag; +} + +/** + * Set link layer address of link object + * @arg link Link object + * @arg addr New link layer address + * + * The function increments the reference counter of the address object + * and overwrites any existing link layer address previously assigned. + * + * @route_doc{link_attr_address, Link layer address} + * @see rtnl_link_get_addr() + */ +void rtnl_link_set_addr(struct rtnl_link *link, struct nl_addr *addr) +{ + __assign_addr(link, &link->l_addr, addr, LINK_ATTR_ADDR); +} + +/** + * Return link layer address of link object + * @arg link Link object + * + * @copydoc pointer_lifetime_warning + * @route_doc{link_attr_address, Link Layer Address} + * @see rtnl_link_set_addr() + * @return Link layer address or NULL if not set. + */ +struct nl_addr *rtnl_link_get_addr(struct rtnl_link *link) +{ + return link->ce_mask & LINK_ATTR_ADDR ? link->l_addr : NULL; +} + +/** + * Set link layer broadcast address of link object + * @arg link Link object + * @arg addr New broadcast address + * + * The function increments the reference counter of the address object + * and overwrites any existing link layer broadcast address previously + * assigned. + * + * @route_doc{link_attr_broadcast, Link Layer Broadcast Address} + * @see rtnl_link_get_broadcast() + */ +void rtnl_link_set_broadcast(struct rtnl_link *link, struct nl_addr *addr) +{ + __assign_addr(link, &link->l_bcast, addr, LINK_ATTR_BRD); +} + +/** + * Return link layer broadcast address of link object + * @arg link Link object + * + * @copydoc pointer_lifetime_warning + * @route_doc{link_attr_address, Link Layer Address} + * @see rtnl_link_set_broadcast() + * @return Link layer address or NULL if not set. + */ +struct nl_addr *rtnl_link_get_broadcast(struct rtnl_link *link) +{ + return link->ce_mask & LINK_ATTR_BRD ? link->l_bcast : NULL; +} + +/** + * Set flags of link object + * @arg link Link object + * @arg flags Flags + * + * @see rtnl_link_get_flags() + * @see rtnl_link_unset_flags() + */ +void rtnl_link_set_flags(struct rtnl_link *link, unsigned int flags) +{ + link->l_flag_mask |= flags; + link->l_flags |= flags; + link->ce_mask |= LINK_ATTR_FLAGS; +} + +/** + * Unset flags of link object + * @arg link Link object + * @arg flags Flags + * + * @see rtnl_link_set_flags() + * @see rtnl_link_get_flags() + */ +void rtnl_link_unset_flags(struct rtnl_link *link, unsigned int flags) +{ + link->l_flag_mask |= flags; + link->l_flags &= ~flags; + link->ce_mask |= LINK_ATTR_FLAGS; +} + +/** + * Return flags of link object + * @arg link Link object + * + * @route_doc{link_attr_flags, Link Flags} + * @see rtnl_link_set_flags() + * @see rtnl_link_unset_flags() + * @return Link flags or 0 if none have been set. + */ +unsigned int rtnl_link_get_flags(struct rtnl_link *link) +{ + return link->l_flags; +} + +/** + * Set address family of link object + * + * @see rtnl_link_get_family() + */ +void rtnl_link_set_family(struct rtnl_link *link, int family) +{ + link->l_family = family; + link->ce_mask |= LINK_ATTR_FAMILY; + + if (link->l_af_ops) + af_free(link, link->l_af_ops, + link->l_af_data[link->l_af_ops->ao_family], NULL); + + link->l_af_ops = af_lookup_and_alloc(link, family); +} + +/** + * Return address family of link object + * @arg link Link object + * + * @see rtnl_link_set_family() + * @return Address family or \c AF_UNSPEC if not specified. + */ +int rtnl_link_get_family(struct rtnl_link *link) +{ + return link->ce_mask & LINK_ATTR_FAMILY ? link->l_family : AF_UNSPEC; +} + +/** + * Set hardware type of link object + * @arg link Link object + * @arg arptype New hardware type \c (ARPHRD_*) + * + * @route_doc{link_attr_arptype, Hardware Type} + * @copydoc read_only_attribute + * @see rtnl_link_get_arptype() + */ +void rtnl_link_set_arptype(struct rtnl_link *link, unsigned int arptype) +{ + link->l_arptype = arptype; + link->ce_mask |= LINK_ATTR_ARPTYPE; +} + +/** + * Get hardware type of link object + * @arg link Link object + * + * @route_doc{link_attr_arptype, Hardware Type} + * @see rtnl_link_set_arptype() + * @return Hardware type \c (ARPHRD_ETHER *) or \c ARPHRD_VOID + */ +unsigned int rtnl_link_get_arptype(struct rtnl_link *link) +{ + if (link->ce_mask & LINK_ATTR_ARPTYPE) + return link->l_arptype; + else + return ARPHRD_VOID; +} + +/** + * Set interface index of link object + * @arg link Link object + * @arg ifindex Interface index + * + * @route_doc{link_attr_ifindex, Interface Index} + * @see rtnl_link_get_ifindex() + */ +void rtnl_link_set_ifindex(struct rtnl_link *link, int ifindex) +{ + link->l_index = ifindex; + link->ce_mask |= LINK_ATTR_IFINDEX; +} + + +/** + * Return interface index of link object + * @arg link Link object + * + * @route_doc{link_attr_ifindex, Interface Index} + * @see rtnl_link_set_ifindex() + * @return Interface index or 0 if not set. + */ +int rtnl_link_get_ifindex(struct rtnl_link *link) +{ + return link->l_index; +} + +/** + * Set Maximum Transmission Unit of link object + * @arg link Link object + * @arg mtu New MTU value in number of bytes + * + * @route_doc{link_attr_mtu, Maximum Transmission Unit} + * @see rtnl_link_get_mtu() + */ +void rtnl_link_set_mtu(struct rtnl_link *link, unsigned int mtu) +{ + link->l_mtu = mtu; + link->ce_mask |= LINK_ATTR_MTU; +} + +/** + * Return maximum transmission unit of link object + * @arg link Link object + * + * @route_doc{link_attr_mtu, Maximum Transmission Unit} + * @see rtnl_link_set_mtu() + * @return MTU in bytes or 0 if not set + */ +unsigned int rtnl_link_get_mtu(struct rtnl_link *link) +{ + return link->l_mtu; +} + +/** + * Set transmission queue length + * @arg link Link object + * @arg txqlen New queue length + * + * The unit is dependant on the link type. The most common units is number + * of packets. + * + * @route_doc{link_attr_txqlen, Transmission Queue Length} + */ +void rtnl_link_set_txqlen(struct rtnl_link *link, unsigned int txqlen) +{ + link->l_txqlen = txqlen; + link->ce_mask |= LINK_ATTR_TXQLEN; +} + +/** + * Return transmission queue length + * @arg link Link object + * + * The unit is dependant on the link type. The most common units is number + * of packets. + * + * @route_doc{link_attr_txqlen, Transmission Queue Length} + * @return queue length or 0 if not specified. + */ +unsigned int rtnl_link_get_txqlen(struct rtnl_link *link) +{ + return link->ce_mask & LINK_ATTR_TXQLEN ? link->l_txqlen : 0; +} + +void rtnl_link_set_link(struct rtnl_link *link, int ifindex) +{ + link->l_link = ifindex; + link->ce_mask |= LINK_ATTR_LINK; +} + +int rtnl_link_get_link(struct rtnl_link *link) +{ + return link->l_link; +} + +/** + * Set master link of link object + * @arg link Link object + * @arg ifindex Interface index of master link + * + * @see rtnl_link_get_master() + */ +void rtnl_link_set_master(struct rtnl_link *link, int ifindex) +{ + link->l_master = ifindex; + link->ce_mask |= LINK_ATTR_MASTER; +} + +/** + * Return master link of link object + * @arg link Link object + * + * @see rtnl_link_set_master() + * @return Interface index of master link or 0 if not specified + */ +int rtnl_link_get_master(struct rtnl_link *link) +{ + return link->l_master; +} + +/** + * Set carrier of link object + * @arg link Link object + * @arg status New carrier status + * + * @see rtnl_link_get_carrier() + */ +void rtnl_link_set_carrier(struct rtnl_link *link, uint8_t status) +{ + link->l_carrier = status; + link->ce_mask |= LINK_ATTR_CARRIER; +} + +/** + * Return carrier status of link object + * @arg link Link object + * + * @see rtnl_link_set_master() + * @return Carrier state. + */ +uint8_t rtnl_link_get_carrier(struct rtnl_link *link) +{ + return link->l_carrier; +} + +/** + * Set operational status of link object + * @arg link Link object + * @arg status New opertional status + * + * @route_doc{link_attr_operstate, Operational Status}} + * @see rtnl_link_get_operstate() + */ +void rtnl_link_set_operstate(struct rtnl_link *link, uint8_t status) +{ + link->l_operstate = status; + link->ce_mask |= LINK_ATTR_OPERSTATE; +} + +/** + * Return operational status of link object + * @arg link Link object + * + * @route_doc{link_attr_operstate, Operational Status} + * @see rtnl_link_set_operstate() + * @return Opertional state or \c IF_OPER_UNKNOWN + */ +uint8_t rtnl_link_get_operstate(struct rtnl_link *link) +{ + return link->l_operstate; +} + +/** + * Set link mode of link object + * @arg link Link object + * @arg mode New link mode + * + * @route_doc{link_attr_mode, Mode} + * @see rtnl_link_get_linkmode() + */ +void rtnl_link_set_linkmode(struct rtnl_link *link, uint8_t mode) +{ + link->l_linkmode = mode; + link->ce_mask |= LINK_ATTR_LINKMODE; +} + +/** + * Return link mode of link object + * @arg link Link object + * + * @route_doc{link_attr_mode, Mode} + * @see rtnl_link_get_linkmode() + * @return Link mode or \c IF_LINK_MODE_DEFAULT + */ +uint8_t rtnl_link_get_linkmode(struct rtnl_link *link) +{ + return link->l_linkmode; +} + +/** + * Return alias name of link object (SNMP IfAlias) + * @arg link Link object + * + * @route_doc{link_attr_alias, Alias} + * @see rtnl_link_set_ifalias() + * @return Alias name or NULL if not set. + */ +const char *rtnl_link_get_ifalias(struct rtnl_link *link) +{ + return link->l_ifalias; +} + +/** + * Set alias name of link object (SNMP IfAlias) + * @arg link Link object + * @arg alias Alias name or NULL to unset + * + * Sets the alias name of the link to the specified name. The alias + * name can be unset by specyfing NULL as the alias. The name will + * be strdup()ed, so no need to provide a persistent character string. + * + * @route_doc{link_attr_alias, Alias} + * @see rtnl_link_get_ifalias() + */ +void rtnl_link_set_ifalias(struct rtnl_link *link, const char *alias) +{ + free(link->l_ifalias); + link->ce_mask &= ~LINK_ATTR_IFALIAS; + + if (alias) { + link->l_ifalias = strdup(alias); + link->ce_mask |= LINK_ATTR_IFALIAS; + } +} + +/** + * Set queueing discipline name of link object + * @arg link Link object + * @arg name Name of queueing discipline + * + * @copydoc read_only_attribute + * + * For more information on how to modify the qdisc of a link, see section + * @ref_route{route_tc, Traffic Control}. + * + * @route_doc{link_attr_qdisc, Queueing Discipline Name} + * @see rtnl_link_get_qdisc() + */ +void rtnl_link_set_qdisc(struct rtnl_link *link, const char *name) +{ + strncpy(link->l_qdisc, name, sizeof(link->l_qdisc) - 1); + link->ce_mask |= LINK_ATTR_QDISC; +} + +/** + * Return name of queueing discipline of link object + * @arg link Link object + * + * @route_doc{link_attr_qdisc, Queueing Discipline Name} + * @see rtnl_link_set_qdisc() + * @return Name of qdisc or NULL if not specified. + */ +char *rtnl_link_get_qdisc(struct rtnl_link *link) +{ + return link->ce_mask & LINK_ATTR_QDISC ? link->l_qdisc : NULL; +} + + +/** + * Return number of PCI virtual functions of link object + * @arg link Link object + * @arg num_vf Pointer to store number of VFs + * + * @return 0 on success or -NLE_OPNOTSUPP if not available + */ +int rtnl_link_get_num_vf(struct rtnl_link *link, uint32_t *num_vf) +{ + if (link->ce_mask & LINK_ATTR_NUM_VF) { + *num_vf = link->l_num_vf; + return 0; + } else + return -NLE_OPNOTSUPP; +} + +/** + * Return value of link statistics counter + * @arg link Link object + * @arg id Identifier of statistical counter + * + * @return Value of counter or 0 if not specified. + */ +uint64_t rtnl_link_get_stat(struct rtnl_link *link, rtnl_link_stat_id_t id) +{ + if (id > RTNL_LINK_STATS_MAX) + return 0; + + return link->l_stats[id]; +} + +/** + * Set value of link statistics counter + * @arg link Link object + * @arg id Identifier of statistical counter + * @arg value New value + * + * \note Changing the value of a statistical counter will not change the + * value in the kernel. + * + * @return 0 on success or a negative error code + */ +int rtnl_link_set_stat(struct rtnl_link *link, rtnl_link_stat_id_t id, + const uint64_t value) +{ + if (id > RTNL_LINK_STATS_MAX) + return -NLE_INVAL; + + link->l_stats[id] = value; + + return 0; +} + +/** + * Set type of link object + * @arg link Link object + * @arg type Name of link type + * + * Looks up the link type module and prepares the link to store type + * specific attributes. If a type has been assigned already it will + * be released with all link type specific attributes lost. + * + * @route_doc{link_modules, Link Modules} + * @return 0 on success or a negative errror code. + */ +int rtnl_link_set_type(struct rtnl_link *link, const char *type) +{ + struct rtnl_link_info_ops *io; + int err; + char *kind; + + free(link->l_info_kind); + link->ce_mask &= ~LINK_ATTR_LINKINFO; + if (link->l_info_ops) + release_link_info(link); + + if (!type) + return 0; + + kind = strdup(type); + if (!kind) + return -NLE_NOMEM; + + io = rtnl_link_info_ops_lookup(type); + if (io) { + if (io->io_alloc && (err = io->io_alloc(link)) < 0) + goto errout; + + link->l_info_ops = io; + } + + link->l_info_kind = kind; + link->ce_mask |= LINK_ATTR_LINKINFO; + + return 0; + +errout: + free(kind); + return err; +} + +/** + * Return type of link + * @arg link Link object + * + * @route_doc{link_modules, Link Modules} + * @return Name of link type or NULL if not specified. + */ +char *rtnl_link_get_type(struct rtnl_link *link) +{ + return link->l_info_kind; +} + +/** + * Set link promiscuity count + * @arg link Link object + * @arg count New promiscuity count + * + * @copydoc read_only_attribute + * + * @see rtnl_link_get_promiscuity() + */ +void rtnl_link_set_promiscuity(struct rtnl_link *link, uint32_t count) +{ + link->l_promiscuity = count; + link->ce_mask |= LINK_ATTR_PROMISCUITY; +} + +/** + * Return link promiscuity count + * @arg link Link object + * + * @see rtnl_link_set_promiscuity() + * @return Link promiscuity count or 0 + */ +uint32_t rtnl_link_get_promiscuity(struct rtnl_link *link) +{ + return link->l_promiscuity; +} + +/** + * Set number of TX queues + * @arg link Link object + * @arg nqueues Number of queues + * + * Sets the number of TX queues of the link object. The value is considered + * by the kernel when creating network devices that can be created via + * netlink. The value will be passed on to alloc_netdev_mqs() + * + * Therefore use of rtnl_link_set_num_tx_queues() only makes sense in + * combination with rtnl_link_add() or if the link object is used as a filter. + * + * @see rtnl_link_get_num_tx_queues() + */ +void rtnl_link_set_num_tx_queues(struct rtnl_link *link, uint32_t nqueues) +{ + link->l_num_tx_queues = nqueues; + link->ce_mask |= LINK_ATTR_NUM_TX_QUEUES; +} + +/** + * Return number of TX queues + * @arg link Link object + * + * @return Number of TX queues or 0 + */ +uint32_t rtnl_link_get_num_tx_queues(struct rtnl_link *link) +{ + return link->l_num_tx_queues; +} + +/** + * Set number of RX queues + * @arg link Link object + * @arg nqueues Number of queues + * + * Sets the number of RX queues of the link object. The value is considered + * by the kernel when creating network devices that can be created via + * netlink. The value will be passed on to alloc_netdev_mqs() + * + * Therefore use of rtnl_link_set_num_rx_queues() only makes sense in + * combination with rtnl_link_add() or if the link object is used as a filter. + * + * @see rtnl_link_get_num_rx_queues() + */ +void rtnl_link_set_num_rx_queues(struct rtnl_link *link, uint32_t nqueues) +{ + link->l_num_rx_queues = nqueues; + link->ce_mask |= LINK_ATTR_NUM_RX_QUEUES; +} + +/** + * Return number of RX queues + * @arg link Link object + * + * @return Number of RX queues or 0 + */ +uint32_t rtnl_link_get_num_rx_queues(struct rtnl_link *link) +{ + return link->l_num_rx_queues; +} + +/** @} */ + +/** + * @name Master/Slave + * @{ + */ + +/** + * Enslave slave link to master link + * @arg sock netlink socket + * @arg master ifindex of master link + * @arg slave ifindex of slave link + * + * This function is identical to rtnl_link_enslave() except that + * it takes interface indices instead of rtnl_link objects. + * + * @see rtnl_link_enslave() + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_enslave_ifindex(struct nl_sock *sock, int master, int slave) +{ + struct rtnl_link *link; + int err; + + if (!(link = rtnl_link_alloc())) + return -NLE_NOMEM; + + rtnl_link_set_ifindex(link, slave); + rtnl_link_set_master(link, master); + + if ((err = rtnl_link_change(sock, link, link, 0)) < 0) + goto errout; + + rtnl_link_put(link); + + /* + * Due to the kernel not signaling whether this opertion is + * supported or not, we will retrieve the attribute to see if the + * request was successful. If the master assigned remains unchanged + * we will return NLE_OPNOTSUPP to allow performing backwards + * compatibility of some sort. + */ + if ((err = rtnl_link_get_kernel(sock, slave, NULL, &link)) < 0) + return err; + + if (rtnl_link_get_master(link) != master) + err = -NLE_OPNOTSUPP; + +errout: + rtnl_link_put(link); + + return err; +} + +/** + * Enslave slave link to master link + * @arg sock netlink socket + * @arg master master link + * @arg slave slave link + * + * Constructs a RTM_NEWLINK or RTM_SETLINK message adding the slave to + * the master and sends the request via the specified netlink socket. + * + * @note The feature of enslaving/releasing via netlink has only been added + * recently to the kernel (Feb 2011). Also, the kernel does not signal + * if the operation is not supported. Therefore this function will + * verify if the master assignment has changed and will return + * -NLE_OPNOTSUPP if it did not. + * + * @see rtnl_link_enslave_ifindex() + * @see rtnl_link_release() + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_enslave(struct nl_sock *sock, struct rtnl_link *master, + struct rtnl_link *slave) +{ + return rtnl_link_enslave_ifindex(sock, rtnl_link_get_ifindex(master), + rtnl_link_get_ifindex(slave)); +} + +/** + * Release slave link from its master + * @arg sock netlink socket + * @arg slave slave link + * + * This function is identical to rtnl_link_release() except that + * it takes an interface index instead of a rtnl_link object. + * + * @see rtnl_link_release() + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_release_ifindex(struct nl_sock *sock, int slave) +{ + return rtnl_link_enslave_ifindex(sock, 0, slave); +} + +/** + * Release slave link from its master + * @arg sock netlink socket + * @arg slave slave link + * + * Constructs a RTM_NEWLINK or RTM_SETLINK message releasing the slave from + * its master and sends the request via the specified netlink socket. + * + * @note The feature of enslaving/releasing via netlink has only been added + * recently to the kernel (Feb 2011). Also, the kernel does not signal + * if the operation is not supported. Therefore this function will + * verify if the master assignment has changed and will return + * -NLE_OPNOTSUPP if it did not. + * + * @see rtnl_link_release_ifindex() + * @see rtnl_link_enslave() + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_release(struct nl_sock *sock, struct rtnl_link *slave) +{ + return rtnl_link_release_ifindex(sock, rtnl_link_get_ifindex(slave)); +} + +/** @} */ + +/** + * @name Utilities * @{ */ @@ -1245,7 +2417,7 @@ static const struct trans_tbl link_flags[] = { __ADD(IFF_ECHO, echo) }; -char * rtnl_link_flags2str(int flags, char *buf, size_t len) +char *rtnl_link_flags2str(int flags, char *buf, size_t len) { return __flags2str(flags, buf, len, link_flags, ARRAY_SIZE(link_flags)); @@ -1256,13 +2428,6 @@ int rtnl_link_str2flags(const char *name) return __str2flags(name, link_flags, ARRAY_SIZE(link_flags)); } -/** @} */ - -/** - * @name Link Statistics Translations - * @{ - */ - static const struct trans_tbl link_stats[] = { __ADD(RTNL_LINK_RX_PACKETS, rx_packets) __ADD(RTNL_LINK_TX_PACKETS, tx_packets) @@ -1333,13 +2498,6 @@ int rtnl_link_str2stat(const char *name) return __str2type(name, link_stats, ARRAY_SIZE(link_stats)); } -/** @} */ - -/** - * @name Link Operstate Translations - * @{ - */ - static const struct trans_tbl link_operstates[] = { __ADD(IF_OPER_UNKNOWN, unknown) __ADD(IF_OPER_NOTPRESENT, notpresent) @@ -1350,7 +2508,7 @@ static const struct trans_tbl link_operstates[] = { __ADD(IF_OPER_UP, up) }; -char *rtnl_link_operstate2str(int st, char *buf, size_t len) +char *rtnl_link_operstate2str(uint8_t st, char *buf, size_t len) { return __type2str(st, buf, len, link_operstates, ARRAY_SIZE(link_operstates)); @@ -1362,19 +2520,17 @@ int rtnl_link_str2operstate(const char *name) ARRAY_SIZE(link_operstates)); } -/** @} */ - -/** - * @name Link Mode Translations - * @{ - */ - static const struct trans_tbl link_modes[] = { __ADD(IF_LINK_MODE_DEFAULT, default) __ADD(IF_LINK_MODE_DORMANT, dormant) }; -char *rtnl_link_mode2str(int st, char *buf, size_t len) +static const struct trans_tbl carrier_states[] = { + __ADD(IF_CARRIER_DOWN, down) + __ADD(IF_CARRIER_UP, up) +}; + +char *rtnl_link_mode2str(uint8_t st, char *buf, size_t len) { return __type2str(st, buf, len, link_modes, ARRAY_SIZE(link_modes)); } @@ -1384,342 +2540,54 @@ int rtnl_link_str2mode(const char *name) return __str2type(name, link_modes, ARRAY_SIZE(link_modes)); } +char *rtnl_link_carrier2str(uint8_t st, char *buf, size_t len) +{ + return __type2str(st, buf, len, carrier_states, + ARRAY_SIZE(carrier_states)); +} + +int rtnl_link_str2carrier(const char *name) +{ + return __str2type(name, carrier_states, ARRAY_SIZE(carrier_states)); +} + /** @} */ /** - * @name Attributes - * @{ + * @name Deprecated Functions */ -void rtnl_link_set_qdisc(struct rtnl_link *link, const char *qdisc) +/** + * @deprecated Use of this function is deprecated, use rtnl_link_set_type() + */ +int rtnl_link_set_info_type(struct rtnl_link *link, const char *type) { - strncpy(link->l_qdisc, qdisc, sizeof(link->l_qdisc) - 1); - link->ce_mask |= LINK_ATTR_QDISC; + return rtnl_link_set_type(link, type); } -char *rtnl_link_get_qdisc(struct rtnl_link *link) +/** + * @deprecated Use of this function is deprecated, use rtnl_link_get_type() + */ +char *rtnl_link_get_info_type(struct rtnl_link *link) { - if (link->ce_mask & LINK_ATTR_QDISC) - return link->l_qdisc; - else - return NULL; -} - -void rtnl_link_set_name(struct rtnl_link *link, const char *name) -{ - strncpy(link->l_name, name, sizeof(link->l_name) - 1); - link->ce_mask |= LINK_ATTR_IFNAME; -} - -char *rtnl_link_get_name(struct rtnl_link *link) -{ - if (link->ce_mask & LINK_ATTR_IFNAME) - return link->l_name; - else - return NULL; -} - -static inline void __assign_addr(struct rtnl_link *link, struct nl_addr **pos, - struct nl_addr *new, int flag) -{ - if (*pos) - nl_addr_put(*pos); - - nl_addr_get(new); - *pos = new; - - link->ce_mask |= flag; -} - -void rtnl_link_set_addr(struct rtnl_link *link, struct nl_addr *addr) -{ - __assign_addr(link, &link->l_addr, addr, LINK_ATTR_ADDR); -} - -struct nl_addr *rtnl_link_get_addr(struct rtnl_link *link) -{ - if (link->ce_mask & LINK_ATTR_ADDR) - return link->l_addr; - else - return NULL; -} - -void rtnl_link_set_broadcast(struct rtnl_link *link, struct nl_addr *brd) -{ - __assign_addr(link, &link->l_bcast, brd, LINK_ATTR_BRD); -} - -struct nl_addr *rtnl_link_get_broadcast(struct rtnl_link *link) -{ - if (link->ce_mask & LINK_ATTR_BRD) - return link->l_bcast; - else - return NULL; -} - -void rtnl_link_set_flags(struct rtnl_link *link, unsigned int flags) -{ - link->l_flag_mask |= flags; - link->l_flags |= flags; - link->ce_mask |= LINK_ATTR_FLAGS; -} - -void rtnl_link_unset_flags(struct rtnl_link *link, unsigned int flags) -{ - link->l_flag_mask |= flags; - link->l_flags &= ~flags; - link->ce_mask |= LINK_ATTR_FLAGS; -} - -unsigned int rtnl_link_get_flags(struct rtnl_link *link) -{ - return link->l_flags; -} - -void rtnl_link_set_family(struct rtnl_link *link, int family) -{ - link->l_family = family; - link->ce_mask |= LINK_ATTR_FAMILY; -} - -int rtnl_link_get_family(struct rtnl_link *link) -{ - if (link->ce_mask & LINK_ATTR_FAMILY) - return link->l_family; - else - return AF_UNSPEC; -} - -void rtnl_link_set_arptype(struct rtnl_link *link, unsigned int arptype) -{ - link->l_arptype = arptype; -} - -unsigned int rtnl_link_get_arptype(struct rtnl_link *link) -{ - return link->l_arptype; -} - -void rtnl_link_set_ifindex(struct rtnl_link *link, int ifindex) -{ - link->l_index = ifindex; - link->ce_mask |= LINK_ATTR_IFINDEX; -} - -int rtnl_link_get_ifindex(struct rtnl_link *link) -{ - return link->l_index; -} - -void rtnl_link_set_mtu(struct rtnl_link *link, unsigned int mtu) -{ - link->l_mtu = mtu; - link->ce_mask |= LINK_ATTR_MTU; -} - -unsigned int rtnl_link_get_mtu(struct rtnl_link *link) -{ - if (link->ce_mask & LINK_ATTR_MTU) - return link->l_mtu; - else - return 0; -} - -void rtnl_link_set_txqlen(struct rtnl_link *link, unsigned int txqlen) -{ - link->l_txqlen = txqlen; - link->ce_mask |= LINK_ATTR_TXQLEN; -} - -unsigned int rtnl_link_get_txqlen(struct rtnl_link *link) -{ - if (link->ce_mask & LINK_ATTR_TXQLEN) - return link->l_txqlen; - else - return UINT_MAX; + return rtnl_link_get_type(link); } +/** + * @deprecated The weight attribute is unused and obsoleted in all recent kernels + */ void rtnl_link_set_weight(struct rtnl_link *link, unsigned int weight) { link->l_weight = weight; link->ce_mask |= LINK_ATTR_WEIGHT; } +/** + * @deprecated The weight attribute is unused and obsoleted in all recent kernels + */ unsigned int rtnl_link_get_weight(struct rtnl_link *link) { - if (link->ce_mask & LINK_ATTR_WEIGHT) - return link->l_weight; - else - return UINT_MAX; -} - -void rtnl_link_set_link(struct rtnl_link *link, int ifindex) -{ - link->l_link = ifindex; - link->ce_mask |= LINK_ATTR_LINK; -} - -int rtnl_link_get_link(struct rtnl_link *link) -{ - return link->l_link; -} - -void rtnl_link_set_master(struct rtnl_link *link, int ifindex) -{ - link->l_master = ifindex; - link->ce_mask |= LINK_ATTR_MASTER; -} - -int rtnl_link_get_master(struct rtnl_link *link) -{ - return link->l_master; -} - -void rtnl_link_set_operstate(struct rtnl_link *link, uint8_t operstate) -{ - link->l_operstate = operstate; - link->ce_mask |= LINK_ATTR_OPERSTATE; -} - -uint8_t rtnl_link_get_operstate(struct rtnl_link *link) -{ - if (link->ce_mask & LINK_ATTR_OPERSTATE) - return link->l_operstate; - else - return IF_OPER_UNKNOWN; -} - -void rtnl_link_set_linkmode(struct rtnl_link *link, uint8_t linkmode) -{ - link->l_linkmode = linkmode; - link->ce_mask |= LINK_ATTR_LINKMODE; -} - -uint8_t rtnl_link_get_linkmode(struct rtnl_link *link) -{ - if (link->ce_mask & LINK_ATTR_LINKMODE) - return link->l_linkmode; - else - return IF_LINK_MODE_DEFAULT; -} - -/** - * Return alias name of link (SNMP IfAlias) - * @arg link Link object - * - * @return Alias name or NULL if not set. - */ -const char *rtnl_link_get_ifalias(struct rtnl_link *link) -{ - return link->l_ifalias; -} - -/** - * Set alias name of link (SNMP IfAlias) - * @arg link Link object - * @arg alias Alias name or NULL to unset - * - * Sets the alias name of the link to the specified name. The alias - * name can be unset by specyfing NULL as the alias. The name will - * be strdup()ed, so no need to provide a persistent character string. - */ -void rtnl_link_set_ifalias(struct rtnl_link *link, const char *alias) -{ - free(link->l_ifalias); - link->ce_mask &= ~LINK_ATTR_IFALIAS; - - if (alias) { - link->l_ifalias = strdup(alias); - link->ce_mask |= LINK_ATTR_IFALIAS; - } -} - -/** - * Retrieve number of PCI VFs of link - * @arg link Link object - * @arg num_vf Pointer to store number of VFs - * - * @return 0 if value is available or -NLE_OPNOTSUPP if not. - */ -int rtnl_link_get_num_vf(struct rtnl_link *link, uint32_t *num_vf) -{ - if (link->ce_mask & LINK_ATTR_NUM_VF) { - *num_vf = link->l_num_vf; - return 0; - } else - return -NLE_OPNOTSUPP; -} - -uint64_t rtnl_link_get_stat(struct rtnl_link *link, int id) -{ - if (id < 0 || id > RTNL_LINK_STATS_MAX) - return 0; - - return link->l_stats[id]; -} - -/** - * Set value of a link statistics counter - * @arg link Link object - * @arg id Counter ID - * @arg value New value - * - * @return 0 on success or a negative error code - */ -int rtnl_link_set_stat(struct rtnl_link *link, const unsigned int id, - const uint64_t value) -{ - if (id > RTNL_LINK_STATS_MAX) - return -NLE_INVAL; - - link->l_stats[id] = value; - - return 0; -} - -/** - * Specify the info type of a link - * @arg link link object - * @arg type info type - * - * Looks up the info type and prepares the link to store info type - * specific attributes. If an info type has been assigned already - * it will be released with all changes lost. - * - * @return 0 on success or a negative errror code. - */ -int rtnl_link_set_info_type(struct rtnl_link *link, const char *type) -{ - struct rtnl_link_info_ops *io; - int err; - - if ((io = rtnl_link_info_ops_lookup(type)) == NULL) - return -NLE_OPNOTSUPP; - - if (link->l_info_ops) - release_link_info(link); - - if ((err = io->io_alloc(link)) < 0) - return err; - - link->l_info_ops = io; - - return 0; -} - -/** - * Return info type of a link - * @arg link link object - * - * @note The returned pointer is only valid as long as the link exists - * @return Info type name or NULL if unknown. - */ -char *rtnl_link_get_info_type(struct rtnl_link *link) -{ - if (link->l_info_ops) - return link->l_info_ops->io_name; - else - return NULL; + return link->l_weight; } /** @} */ @@ -1735,12 +2603,14 @@ static struct nl_object_ops link_obj_ops = { [NL_DUMP_STATS] = link_dump_stats, }, .oo_compare = link_compare, + .oo_keygen = link_keygen, .oo_attrs2str = link_attrs2str, - .oo_id_attrs = LINK_ATTR_IFINDEX, + .oo_id_attrs = LINK_ATTR_IFINDEX | LINK_ATTR_FAMILY, }; static struct nl_af_group link_groups[] = { { AF_UNSPEC, RTNLGRP_LINK }, + { AF_BRIDGE, RTNLGRP_LINK }, { END_OF_GROUP_LIST }, }; diff --git a/lib/route/link/api.c b/lib/route/link/api.c index f7907a7..352bb83 100644 --- a/lib/route/link/api.c +++ b/lib/route/link/api.c @@ -39,14 +39,17 @@ * @{ */ -#include +#include #include #include #include -#include +#include static NL_LIST_HEAD(info_ops); +/* lock protecting info_ops and af_ops */ +static NL_RW_LOCK(info_lock); + static struct rtnl_link_info_ops *__rtnl_link_info_ops_lookup(const char *name) { struct rtnl_link_info_ops *ops; @@ -75,8 +78,10 @@ struct rtnl_link_info_ops *rtnl_link_info_ops_lookup(const char *name) { struct rtnl_link_info_ops *ops; + nl_write_lock(&info_lock); if ((ops = __rtnl_link_info_ops_lookup(name))) ops->io_refcnt++; + nl_write_unlock(&info_lock); return ops; } @@ -105,17 +110,24 @@ void rtnl_link_info_ops_put(struct rtnl_link_info_ops *ops) */ int rtnl_link_register_info(struct rtnl_link_info_ops *ops) { + int err = 0; + if (ops->io_name == NULL) return -NLE_INVAL; - if (__rtnl_link_info_ops_lookup(ops->io_name)) - return -NLE_EXIST; + nl_write_lock(&info_lock); + if (__rtnl_link_info_ops_lookup(ops->io_name)) { + err = -NLE_EXIST; + goto errout; + } NL_DBG(1, "Registered link info operations %s\n", ops->io_name); nl_list_add_tail(&ops->io_list, &info_ops); +errout: + nl_write_unlock(&info_lock); - return 0; + return err; } /** @@ -134,22 +146,30 @@ int rtnl_link_register_info(struct rtnl_link_info_ops *ops) int rtnl_link_unregister_info(struct rtnl_link_info_ops *ops) { struct rtnl_link_info_ops *t; + int err = -NLE_OPNOTSUPP; + + nl_write_lock(&info_lock); nl_list_for_each_entry(t, &info_ops, io_list) { if (t == ops) { - if (t->io_refcnt > 0) - return -NLE_BUSY; + if (t->io_refcnt > 0) { + err = -NLE_BUSY; + goto errout; + } nl_list_del(&t->io_list); NL_DBG(1, "Unregistered link info operations %s\n", ops->io_name); - - return 0; + err = 0; + goto errout; } } - return -NLE_OPNOTSUPP; +errout: + nl_write_unlock(&info_lock); + + return err; } /** @} */ @@ -174,8 +194,10 @@ struct rtnl_link_af_ops *rtnl_link_af_ops_lookup(const unsigned int family) if (family == AF_UNSPEC || family >= AF_MAX) return NULL; + nl_write_lock(&info_lock); if (af_ops[family]) af_ops[family]->ao_refcnt++; + nl_write_unlock(&info_lock); return af_ops[family]; } @@ -262,11 +284,16 @@ void *rtnl_link_af_data(const struct rtnl_link *link, */ int rtnl_link_af_register(struct rtnl_link_af_ops *ops) { + int err = 0; + if (ops->ao_family == AF_UNSPEC || ops->ao_family >= AF_MAX) return -NLE_INVAL; - if (af_ops[ops->ao_family]) - return -NLE_EXIST; + nl_write_lock(&info_lock); + if (af_ops[ops->ao_family]) { + err = -NLE_EXIST; + goto errout; + } ops->ao_refcnt = 0; af_ops[ops->ao_family] = ops; @@ -274,7 +301,10 @@ int rtnl_link_af_register(struct rtnl_link_af_ops *ops) NL_DBG(1, "Registered link address family operations %u\n", ops->ao_family); - return 0; +errout: + nl_write_unlock(&info_lock); + + return err; } /** @@ -293,21 +323,72 @@ int rtnl_link_af_register(struct rtnl_link_af_ops *ops) */ int rtnl_link_af_unregister(struct rtnl_link_af_ops *ops) { + int err = -NLE_INVAL; + if (!ops) - return -NLE_INVAL; + goto errout; - if (!af_ops[ops->ao_family]) - return -NLE_OBJ_NOTFOUND; + nl_write_lock(&info_lock); + if (!af_ops[ops->ao_family]) { + err = -NLE_OBJ_NOTFOUND; + goto errout; + } - if (ops->ao_refcnt > 0) - return -NLE_BUSY; + if (ops->ao_refcnt > 0) { + err = -NLE_BUSY; + goto errout; + } af_ops[ops->ao_family] = NULL; NL_DBG(1, "Unregistered link address family operations %u\n", ops->ao_family); - return 0; +errout: + nl_write_lock(&info_lock); + + return err; +} + +/** + * Compare af data for a link address family + * @arg a Link object a + * @arg b Link object b + * @arg family af data family + * + * This function will compare af_data between two links + * a and b of family given by arg family + * + * @return 0 if address family specific data matches or is not present + * or != 0 if it mismatches. + */ +int rtnl_link_af_data_compare(struct rtnl_link *a, struct rtnl_link *b, + int family) +{ + struct rtnl_link_af_ops *af_ops; + int ret = 0; + + if (!a->l_af_data[family] && !b->l_af_data[family]) + return 0; + + if (!a->l_af_data[family] || !b->l_af_data[family]) + return ~0; + + af_ops = rtnl_link_af_ops_lookup(family); + if (!af_ops) + return ~0; + + if (af_ops->ao_compare == NULL) { + ret = ~0; + goto out; + } + + ret = af_ops->ao_compare(a, b, family, ~0, 0); + +out: + rtnl_link_af_ops_put(af_ops); + + return ret; } /** @} */ diff --git a/lib/route/link/bonding.c b/lib/route/link/bonding.c new file mode 100644 index 0000000..f4c520b --- /dev/null +++ b/lib/route/link/bonding.c @@ -0,0 +1,227 @@ +/* + * lib/route/link/bonding.c Bonding Link Module + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2011-2013 Thomas Graf + */ + +/** + * @ingroup link + * @defgroup bonding Bonding + * + * @details + * \b Link Type Name: "bond" + * + * @route_doc{link_bonding, Bonding Documentation} + * @{ + */ + +#include +#include +#include + +/** + * Allocate link object of type bond + * + * @return Allocated link object or NULL. + */ +struct rtnl_link *rtnl_link_bond_alloc(void) +{ + struct rtnl_link *link; + int err; + + if (!(link = rtnl_link_alloc())) + return NULL; + + if ((err = rtnl_link_set_type(link, "bond")) < 0) { + rtnl_link_put(link); + return NULL; + } + + return link; +} + +/** + * Create a new kernel bonding device + * @arg sock netlink socket + * @arg name name of bonding device or NULL + * @arg opts bonding options (currently unused) + * + * Creates a new bonding device in the kernel. If no name is + * provided, the kernel will automatically pick a name of the + * form "type%d" (e.g. bond0, vlan1, etc.) + * + * The \a opts argument is currently unused. In the future, it + * may be used to carry additional bonding options to be set + * when creating the bonding device. + * + * @note When letting the kernel assign a name, it will become + * difficult to retrieve the interface afterwards because + * you have to guess the name the kernel has chosen. It is + * therefore not recommended to not provide a device name. + * + * @see rtnl_link_bond_enslave() + * @see rtnl_link_bond_release() + * + * @return 0 on success or a negative error code + */ +int rtnl_link_bond_add(struct nl_sock *sock, const char *name, + struct rtnl_link *opts) +{ + struct rtnl_link *link; + int err; + + if (!(link = rtnl_link_bond_alloc())) + return -NLE_NOMEM; + + if (!name && opts) + name = rtnl_link_get_name(opts); + + if (name) + rtnl_link_set_name(link, name); + + err = rtnl_link_add(sock, link, NLM_F_CREATE); + + rtnl_link_put(link); + + return err; +} + +/** + * Add a link to a bond (enslave) + * @arg sock netlink socket + * @arg master ifindex of bonding master + * @arg slave ifindex of slave link to add to bond + * + * This function is identical to rtnl_link_bond_enslave() except that + * it takes interface indices instead of rtnl_link objcets. + * + * @see rtnl_link_bond_enslave() + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_bond_enslave_ifindex(struct nl_sock *sock, int master, + int slave) +{ + struct rtnl_link *link; + int err; + + if (!(link = rtnl_link_bond_alloc())) + return -NLE_NOMEM; + + rtnl_link_set_ifindex(link, slave); + rtnl_link_set_master(link, master); + + if ((err = rtnl_link_change(sock, link, link, 0)) < 0) + goto errout; + + rtnl_link_put(link); + + /* + * Due to the kernel not signaling whether this opertion is + * supported or not, we will retrieve the attribute to see if the + * request was successful. If the master assigned remains unchanged + * we will return NLE_OPNOTSUPP to allow performing backwards + * compatibility of some sort. + */ + if ((err = rtnl_link_get_kernel(sock, slave, NULL, &link)) < 0) + return err; + + if (rtnl_link_get_master(link) != master) + err = -NLE_OPNOTSUPP; + +errout: + rtnl_link_put(link); + + return err; +} + +/** + * Add a link to a bond (enslave) + * @arg sock netlink socket + * @arg master bonding master + * @arg slave slave link to add to bond + * + * Constructs a RTM_NEWLINK or RTM_SETLINK message adding the slave to + * the master and sends the request via the specified netlink socket. + * + * @note The feature of enslaving/releasing via netlink has only been added + * recently to the kernel (Feb 2011). Also, the kernel does not signal + * if the operation is not supported. Therefore this function will + * verify if the master assignment has changed and will return + * -NLE_OPNOTSUPP if it did not. + * + * @see rtnl_link_bond_enslave_ifindex() + * @see rtnl_link_bond_release() + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_bond_enslave(struct nl_sock *sock, struct rtnl_link *master, + struct rtnl_link *slave) +{ + return rtnl_link_bond_enslave_ifindex(sock, + rtnl_link_get_ifindex(master), + rtnl_link_get_ifindex(slave)); +} + +/** + * Release a link from a bond + * @arg sock netlink socket + * @arg slave slave link to be released + * + * This function is identical to rtnl_link_bond_release() except that + * it takes an interface index instead of a rtnl_link object. + * + * @see rtnl_link_bond_release() + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_bond_release_ifindex(struct nl_sock *sock, int slave) +{ + return rtnl_link_bond_enslave_ifindex(sock, 0, slave); +} + +/** + * Release a link from a bond + * @arg sock netlink socket + * @arg slave slave link to be released + * + * Constructs a RTM_NEWLINK or RTM_SETLINK message releasing the slave from + * its master and sends the request via the specified netlink socket. + * + * @note The feature of enslaving/releasing via netlink has only been added + * recently to the kernel (Feb 2011). Also, the kernel does not signal + * if the operation is not supported. Therefore this function will + * verify if the master assignment has changed and will return + * -NLE_OPNOTSUPP if it did not. + * + * @see rtnl_link_bond_release_ifindex() + * @see rtnl_link_bond_enslave() + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_bond_release(struct nl_sock *sock, struct rtnl_link *slave) +{ + return rtnl_link_bond_release_ifindex(sock, + rtnl_link_get_ifindex(slave)); +} + +static struct rtnl_link_info_ops bonding_info_ops = { + .io_name = "bond", +}; + +static void __init bonding_init(void) +{ + rtnl_link_register_info(&bonding_info_ops); +} + +static void __exit bonding_exit(void) +{ + rtnl_link_unregister_info(&bonding_info_ops); +} + +/** @} */ diff --git a/lib/route/link/bridge.c b/lib/route/link/bridge.c index 32fd38f..eb02b22 100644 --- a/lib/route/link/bridge.c +++ b/lib/route/link/bridge.c @@ -1,25 +1,62 @@ /* - * lib/route/link/bridge.c AF_BRIDGE link oeprations + * lib/route/link/bridge.c AF_BRIDGE link support * * 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 version 2.1 * of the License. * - * Copyright (c) 2010 Thomas Graf + * Copyright (c) 2010-2013 Thomas Graf */ -#include +/** + * @ingroup link + * @defgroup bridge Bridging + * + * @details + * @{ + */ + +#include #include #include #include -#include +#include +#include +#include + +/** @cond SKIP */ +#define BRIDGE_ATTR_PORT_STATE (1 << 0) +#define BRIDGE_ATTR_PRIORITY (1 << 1) +#define BRIDGE_ATTR_COST (1 << 2) +#define BRIDGE_ATTR_FLAGS (1 << 3) + +#define PRIV_FLAG_NEW_ATTRS (1 << 0) struct bridge_data { uint8_t b_port_state; + uint8_t b_priv_flags; /* internal flags */ + uint16_t b_priority; + uint32_t b_cost; + uint32_t b_flags; + uint32_t b_flags_mask; + uint32_t ce_mask; /* HACK to support attr macros */ }; +static struct rtnl_link_af_ops bridge_ops; + +#define IS_BRIDGE_LINK_ASSERT(link) \ + if (!rtnl_link_is_bridge(link)) { \ + APPBUG("A function was expecting a link object of type bridge."); \ + return -NLE_OPNOTSUPP; \ + } + +static inline struct bridge_data *bridge_data(struct rtnl_link *link) +{ + return rtnl_link_af_data(link, &bridge_ops); +} + static void *bridge_alloc(struct rtnl_link *link) { return calloc(1, sizeof(struct bridge_data)); @@ -40,12 +77,66 @@ static void bridge_free(struct rtnl_link *link, void *data) free(data); } +static struct nla_policy br_attrs_policy[IFLA_BRPORT_MAX+1] = { + [IFLA_BRPORT_STATE] = { .type = NLA_U8 }, + [IFLA_BRPORT_PRIORITY] = { .type = NLA_U16 }, + [IFLA_BRPORT_COST] = { .type = NLA_U32 }, + [IFLA_BRPORT_MODE] = { .type = NLA_U8 }, + [IFLA_BRPORT_GUARD] = { .type = NLA_U8 }, + [IFLA_BRPORT_PROTECT] = { .type = NLA_U8 }, + [IFLA_BRPORT_FAST_LEAVE] = { .type = NLA_U8 }, +}; + +static void check_flag(struct rtnl_link *link, struct nlattr *attrs[], + int type, int flag) +{ + if (attrs[type] && nla_get_u8(attrs[type])) + rtnl_link_bridge_set_flags(link, flag); +} + static int bridge_parse_protinfo(struct rtnl_link *link, struct nlattr *attr, void *data) { struct bridge_data *bd = data; + struct nlattr *br_attrs[IFLA_BRPORT_MAX+1]; + int err; - bd->b_port_state = nla_get_u8(attr); + /* Backwards compatibility */ + if (!nla_is_nested(attr)) { + if (nla_len(attr) < 1) + return -NLE_RANGE; + + bd->b_port_state = nla_get_u8(attr); + bd->ce_mask |= BRIDGE_ATTR_PORT_STATE; + + return 0; + } + + if ((err = nla_parse_nested(br_attrs, IFLA_BRPORT_MAX, attr, + br_attrs_policy)) < 0) + return err; + + bd->b_priv_flags |= PRIV_FLAG_NEW_ATTRS; + + if (br_attrs[IFLA_BRPORT_STATE]) { + bd->b_port_state = nla_get_u8(br_attrs[IFLA_BRPORT_STATE]); + bd->ce_mask |= BRIDGE_ATTR_PORT_STATE; + } + + if (br_attrs[IFLA_BRPORT_PRIORITY]) { + bd->b_priority = nla_get_u16(br_attrs[IFLA_BRPORT_PRIORITY]); + bd->ce_mask |= BRIDGE_ATTR_PRIORITY; + } + + if (br_attrs[IFLA_BRPORT_COST]) { + bd->b_cost = nla_get_u32(br_attrs[IFLA_BRPORT_COST]); + bd->ce_mask |= BRIDGE_ATTR_COST; + } + + check_flag(link, br_attrs, IFLA_BRPORT_MODE, RTNL_BRIDGE_HAIRPIN_MODE); + check_flag(link, br_attrs, IFLA_BRPORT_GUARD, RTNL_BRIDGE_BPDU_GUARD); + check_flag(link, br_attrs, IFLA_BRPORT_PROTECT, RTNL_BRIDGE_ROOT_BLOCK); + check_flag(link, br_attrs, IFLA_BRPORT_FAST_LEAVE, RTNL_BRIDGE_FAST_LEAVE); return 0; } @@ -55,12 +146,310 @@ static void bridge_dump_details(struct rtnl_link *link, { struct bridge_data *bd = data; - nl_dump(p, "port-state %u ", bd->b_port_state); + nl_dump_line(p, " bridge: "); + + if (bd->ce_mask & BRIDGE_ATTR_PORT_STATE) + nl_dump(p, "port-state %u ", bd->b_port_state); + + if (bd->ce_mask & BRIDGE_ATTR_PRIORITY) + nl_dump(p, "prio %u ", bd->b_priority); + + if (bd->ce_mask & BRIDGE_ATTR_COST) + nl_dump(p, "cost %u ", bd->b_cost); + + nl_dump(p, "\n"); } -static const struct nla_policy protinfo_policy = { - .type = NLA_U8, -}; +static int bridge_compare(struct rtnl_link *_a, struct rtnl_link *_b, + int family, uint32_t attrs, int flags) +{ + struct bridge_data *a = bridge_data(_a); + struct bridge_data *b = bridge_data(_b); + int diff = 0; + +#define BRIDGE_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, BRIDGE_ATTR_##ATTR, a, b, EXPR) + diff |= BRIDGE_DIFF(PORT_STATE, a->b_port_state != b->b_port_state); + diff |= BRIDGE_DIFF(PRIORITY, a->b_priority != b->b_priority); + diff |= BRIDGE_DIFF(COST, a->b_cost != b->b_cost); + + if (flags & LOOSE_COMPARISON) + diff |= BRIDGE_DIFF(FLAGS, + (a->b_flags ^ b->b_flags) & b->b_flags_mask); + else + diff |= BRIDGE_DIFF(FLAGS, a->b_flags != b->b_flags); +#undef BRIDGE_DIFF + + return diff; +} +/** @endcond */ + +/** + * Allocate link object of type bridge + * + * @return Allocated link object or NULL. + */ +struct rtnl_link *rtnl_link_bridge_alloc(void) +{ + struct rtnl_link *link; + int err; + + if (!(link = rtnl_link_alloc())) + return NULL; + + if ((err = rtnl_link_set_type(link, "bridge")) < 0) { + rtnl_link_put(link); + return NULL; + } + + return link; +} + +/** + * Check if a link is a bridge + * @arg link Link object + * + * @return 1 if the link is a bridge, 0 otherwise. + */ +int rtnl_link_is_bridge(struct rtnl_link *link) +{ + return link->l_family == AF_BRIDGE && + link->l_af_ops == &bridge_ops; +} + +/** + * Check if bridge has extended information + * @arg link Link object of type bridge + * + * Checks if the bridge object has been constructed based on + * information that is only available in newer kernels. This + * affectes the following functions: + * - rtnl_link_bridge_get_cost() + * - rtnl_link_bridge_get_priority() + * - rtnl_link_bridge_get_flags() + * + * @return 1 if extended information is available, otherwise 0 is returned. + */ +int rtnl_link_bridge_has_ext_info(struct rtnl_link *link) +{ + struct bridge_data *bd; + + if (!rtnl_link_is_bridge(link)) + return 0; + + bd = bridge_data(link); + return !!(bd->b_priv_flags & PRIV_FLAG_NEW_ATTRS); +} + +/** + * Set Spanning Tree Protocol (STP) port state + * @arg link Link object of type bridge + * @arg state New STP port state + * + * The value of state must be one of the following: + * - BR_STATE_DISABLED + * - BR_STATE_LISTENING + * - BR_STATE_LEARNING + * - BR_STATE_FORWARDING + * - BR_STATE_BLOCKING + * + * @see rtnl_link_bridge_get_port_state() + * + * @return 0 on success or a negative error code. + * @retval -NLE_OPNOTSUPP Link is not a bridge + * @retval -NLE_INVAL Invalid state value (0..BR_STATE_BLOCKING) + */ +int rtnl_link_bridge_set_port_state(struct rtnl_link *link, uint8_t state) +{ + struct bridge_data *bd = bridge_data(link); + + IS_BRIDGE_LINK_ASSERT(link); + + if (state > BR_STATE_BLOCKING) + return -NLE_INVAL; + + bd->b_port_state = state; + bd->ce_mask |= BRIDGE_ATTR_PORT_STATE; + + return 0; +} + +/** + * Get Spanning Tree Protocol (STP) port state + * @arg link Link object of type bridge + * + * @see rtnl_link_bridge_set_port_state() + * + * @return The STP port state or a negative error code. + * @retval -NLE_OPNOTSUPP Link is not a bridge + */ +int rtnl_link_bridge_get_port_state(struct rtnl_link *link) +{ + struct bridge_data *bd = bridge_data(link); + + IS_BRIDGE_LINK_ASSERT(link); + + return bd->b_port_state; +} + +/** + * Set priority + * @arg link Link object of type bridge + * @arg prio Bridge priority + * + * @see rtnl_link_bridge_get_priority() + * + * @return 0 on success or a negative error code. + * @retval -NLE_OPNOTSUPP Link is not a bridge + */ +int rtnl_link_bridge_set_priority(struct rtnl_link *link, uint16_t prio) +{ + struct bridge_data *bd = bridge_data(link); + + IS_BRIDGE_LINK_ASSERT(link); + + bd->b_priority = prio; + bd->ce_mask |= BRIDGE_ATTR_PRIORITY; + + return 0; +} + +/** + * Get priority + * @arg link Link object of type bridge + * + * @see rtnl_link_bridge_set_priority() + * + * @return 0 on success or a negative error code. + * @retval -NLE_OPNOTSUPP Link is not a bridge + */ +int rtnl_link_bridge_get_priority(struct rtnl_link *link) +{ + struct bridge_data *bd = bridge_data(link); + + IS_BRIDGE_LINK_ASSERT(link); + + return bd->b_priority; +} + +/** + * Set Spanning Tree Protocol (STP) path cost + * @arg link Link object of type bridge + * @arg cost New STP path cost value + * + * @see rtnl_link_bridge_get_cost() + * + * @return The bridge priority or a negative error code. + * @retval -NLE_OPNOTSUPP Link is not a bridge + */ +int rtnl_link_bridge_set_cost(struct rtnl_link *link, uint32_t cost) +{ + struct bridge_data *bd = bridge_data(link); + + IS_BRIDGE_LINK_ASSERT(link); + + bd->b_cost = cost; + bd->ce_mask |= BRIDGE_ATTR_COST; + + return 0; +} + +/** + * Get Spanning Tree Protocol (STP) path cost + * @arg link Link object of type bridge + * @arg cost Pointer to store STP cost value + * + * @see rtnl_link_bridge_set_cost() + * + * @return 0 on success or a negative error code. + * @retval -NLE_OPNOTSUPP Link is not a bridge + * @retval -NLE_INVAL `cost` is not a valid pointer + */ +int rtnl_link_bridge_get_cost(struct rtnl_link *link, uint32_t *cost) +{ + struct bridge_data *bd = bridge_data(link); + + IS_BRIDGE_LINK_ASSERT(link); + + if (!cost) + return -NLE_INVAL; + + *cost = bd->b_cost; + + return 0; +} + +/** + * Unset flags + * @arg link Link object of type bridge + * @arg flags Bridging flags to unset + * + * @see rtnl_link_bridge_set_flags() + * @see rtnl_link_bridge_get_flags() + * + * @return 0 on success or a negative error code. + * @retval -NLE_OPNOTSUPP Link is not a bridge + */ +int rtnl_link_bridge_unset_flags(struct rtnl_link *link, unsigned int flags) +{ + struct bridge_data *bd = bridge_data(link); + + IS_BRIDGE_LINK_ASSERT(link); + + bd->b_flags_mask |= flags; + bd->b_flags &= ~flags; + bd->ce_mask |= BRIDGE_ATTR_FLAGS; + + return 0; +} + +/** + * Set flags + * @arg link Link object of type bridge + * @arg flags Bridging flags to set + * + * Valid flags are: + * - RTNL_BRIDGE_HAIRPIN_MODE + * - RTNL_BRIDGE_BPDU_GUARD + * - RTNL_BRIDGE_ROOT_BLOCK + * - RTNL_BRIDGE_FAST_LEAVE + * + * @see rtnl_link_bridge_unset_flags() + * @see rtnl_link_bridge_get_flags() + * + * @return 0 on success or a negative error code. + * @retval -NLE_OPNOTSUPP Link is not a bridge + */ +int rtnl_link_bridge_set_flags(struct rtnl_link *link, unsigned int flags) +{ + struct bridge_data *bd = bridge_data(link); + + IS_BRIDGE_LINK_ASSERT(link); + + bd->b_flags_mask |= flags; + bd->b_flags |= flags; + bd->ce_mask |= BRIDGE_ATTR_FLAGS; + + return 0; +} + +/** + * Get flags + * @arg link Link object of type bridge + * + * @see rtnl_link_bridge_set_flags() + * @see rtnl_link_bridge_unset_flags() + * + * @return Flags or a negative error code. + * @retval -NLE_OPNOTSUPP Link is not a bridge + */ +int rtnl_link_bridge_get_flags(struct rtnl_link *link) +{ + struct bridge_data *bd = bridge_data(link); + + IS_BRIDGE_LINK_ASSERT(link); + + return bd->b_flags; +} static struct rtnl_link_af_ops bridge_ops = { .ao_family = AF_BRIDGE, @@ -69,7 +458,7 @@ static struct rtnl_link_af_ops bridge_ops = { .ao_free = &bridge_free, .ao_parse_protinfo = &bridge_parse_protinfo, .ao_dump[NL_DUMP_DETAILS] = &bridge_dump_details, - .ao_protinfo_policy = &protinfo_policy, + .ao_compare = &bridge_compare, }; static void __init bridge_init(void) @@ -81,3 +470,5 @@ static void __exit bridge_exit(void) { rtnl_link_af_unregister(&bridge_ops); } + +/** @} */ diff --git a/lib/route/link/can.c b/lib/route/link/can.c new file mode 100644 index 0000000..60da42d --- /dev/null +++ b/lib/route/link/can.c @@ -0,0 +1,784 @@ +/* + * lib/route/link/can.c CAN Link Info + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2012 Benedikt Spranger + */ + +/** + * @ingroup link + * @defgroup can CAN + * Controller Area Network link module + * + * @details + * \b Link Type Name: "can" + * + * @route_doc{link_can, CAN Documentation} + * + * @{ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/** @cond SKIP */ +#define CAN_HAS_BITTIMING (1<<0) +#define CAN_HAS_BITTIMING_CONST (1<<1) +#define CAN_HAS_CLOCK (1<<2) +#define CAN_HAS_STATE (1<<3) +#define CAN_HAS_CTRLMODE (1<<4) +#define CAN_HAS_RESTART_MS (1<<5) +#define CAN_HAS_RESTART (1<<6) +#define CAN_HAS_BERR_COUNTER (1<<7) + +struct can_info { + uint32_t ci_state; + uint32_t ci_restart; + uint32_t ci_restart_ms; + struct can_ctrlmode ci_ctrlmode; + struct can_bittiming ci_bittiming; + struct can_bittiming_const ci_bittiming_const; + struct can_clock ci_clock; + struct can_berr_counter ci_berr_counter; + uint32_t ci_mask; +}; + +/** @endcond */ + +static struct nla_policy can_policy[IFLA_CAN_MAX + 1] = { + [IFLA_CAN_STATE] = { .type = NLA_U32 }, + [IFLA_CAN_CTRLMODE] = { .minlen = sizeof(struct can_ctrlmode) }, + [IFLA_CAN_RESTART_MS] = { .type = NLA_U32 }, + [IFLA_CAN_RESTART] = { .type = NLA_U32 }, + [IFLA_CAN_BITTIMING] = { .minlen = sizeof(struct can_bittiming) }, + [IFLA_CAN_BITTIMING_CONST] + = { .minlen = sizeof(struct can_bittiming_const) }, + [IFLA_CAN_CLOCK] = { .minlen = sizeof(struct can_clock) }, + [IFLA_CAN_BERR_COUNTER] = { .minlen = sizeof(struct can_berr_counter) }, +}; + +static int can_alloc(struct rtnl_link *link) +{ + struct can_info *ci; + + ci = calloc(1, sizeof(*ci)); + if (!ci) + return -NLE_NOMEM; + + link->l_info = ci; + + return 0; +} + +static int can_parse(struct rtnl_link *link, struct nlattr *data, + struct nlattr *xstats) +{ + struct nlattr *tb[IFLA_CAN_MAX+1]; + struct can_info *ci; + int err; + + NL_DBG(3, "Parsing CAN link info"); + + if ((err = nla_parse_nested(tb, IFLA_CAN_MAX, data, can_policy)) < 0) + goto errout; + + if ((err = can_alloc(link)) < 0) + goto errout; + + ci = link->l_info; + + if (tb[IFLA_CAN_STATE]) { + ci->ci_state = nla_get_u32(tb[IFLA_CAN_STATE]); + ci->ci_mask |= CAN_HAS_STATE; + } + + if (tb[IFLA_CAN_RESTART]) { + ci->ci_restart = nla_get_u32(tb[IFLA_CAN_RESTART]); + ci->ci_mask |= CAN_HAS_RESTART; + } + + if (tb[IFLA_CAN_RESTART_MS]) { + ci->ci_restart_ms = nla_get_u32(tb[IFLA_CAN_RESTART_MS]); + ci->ci_mask |= CAN_HAS_RESTART_MS; + } + + if (tb[IFLA_CAN_CTRLMODE]) { + nla_memcpy(&ci->ci_ctrlmode, tb[IFLA_CAN_CTRLMODE], + sizeof(ci->ci_ctrlmode)); + ci->ci_mask |= CAN_HAS_CTRLMODE; + } + + if (tb[IFLA_CAN_BITTIMING]) { + nla_memcpy(&ci->ci_bittiming, tb[IFLA_CAN_BITTIMING], + sizeof(ci->ci_bittiming)); + ci->ci_mask |= CAN_HAS_BITTIMING; + } + + if (tb[IFLA_CAN_BITTIMING_CONST]) { + nla_memcpy(&ci->ci_bittiming_const, + tb[IFLA_CAN_BITTIMING_CONST], + sizeof(ci->ci_bittiming_const)); + ci->ci_mask |= CAN_HAS_BITTIMING_CONST; + } + + if (tb[IFLA_CAN_CLOCK]) { + nla_memcpy(&ci->ci_clock, tb[IFLA_CAN_CLOCK], + sizeof(ci->ci_clock)); + ci->ci_mask |= CAN_HAS_CLOCK; + } + + if (tb[IFLA_CAN_BERR_COUNTER]) { + nla_memcpy(&ci->ci_berr_counter, tb[IFLA_CAN_BERR_COUNTER], + sizeof(ci->ci_berr_counter)); + ci->ci_mask |= CAN_HAS_BERR_COUNTER; + } + + err = 0; +errout: + return err; +} + +static void can_free(struct rtnl_link *link) +{ + struct can_info *ci = link->l_info; + + free(ci); + link->l_info = NULL; +} + +static char *print_can_state (uint32_t state) +{ + char *text; + + switch (state) + { + case CAN_STATE_ERROR_ACTIVE: + text = "error active"; + break; + case CAN_STATE_ERROR_WARNING: + text = "error warning"; + break; + case CAN_STATE_ERROR_PASSIVE: + text = "error passive"; + break; + case CAN_STATE_BUS_OFF: + text = "bus off"; + break; + case CAN_STATE_STOPPED: + text = "stopped"; + break; + case CAN_STATE_SLEEPING: + text = "sleeping"; + break; + default: + text = "unknown state"; + } + + return text; +} + +static void can_dump_line(struct rtnl_link *link, struct nl_dump_params *p) +{ + struct can_info *ci = link->l_info; + char buf [64]; + + rtnl_link_can_ctrlmode2str(ci->ci_ctrlmode.flags, buf, sizeof(buf)); + nl_dump(p, "bitrate %d %s <%s>", + ci->ci_bittiming.bitrate, print_can_state(ci->ci_state), buf); +} + +static void can_dump_details(struct rtnl_link *link, struct nl_dump_params *p) +{ + struct can_info *ci = link->l_info; + char buf [64]; + + rtnl_link_can_ctrlmode2str(ci->ci_ctrlmode.flags, buf, sizeof(buf)); + nl_dump(p, " bitrate %d %s <%s>", + ci->ci_bittiming.bitrate, print_can_state(ci->ci_state), buf); + + if (ci->ci_mask & CAN_HAS_RESTART) { + if (ci->ci_restart) + nl_dump_line(p," restarting\n"); + } + + if (ci->ci_mask & CAN_HAS_RESTART_MS) { + nl_dump_line(p," restart interval %d ms\n", + ci->ci_restart_ms); + } + + if (ci->ci_mask & CAN_HAS_BITTIMING) { + nl_dump_line(p," sample point %f %%\n", + ((float) ci->ci_bittiming.sample_point)/10); + nl_dump_line(p," time quanta %d ns\n", + ci->ci_bittiming.tq); + nl_dump_line(p," propagation segment %d tq\n", + ci->ci_bittiming.prop_seg); + nl_dump_line(p," phase buffer segment1 %d tq\n", + ci->ci_bittiming.phase_seg1); + nl_dump_line(p," phase buffer segment2 %d tq\n", + ci->ci_bittiming.phase_seg2); + nl_dump_line(p," synchronisation jump width %d tq\n", + ci->ci_bittiming.sjw); + nl_dump_line(p," bitrate prescaler %d\n", + ci->ci_bittiming.brp); + } + + if (ci->ci_mask & CAN_HAS_BITTIMING_CONST) { + nl_dump_line(p," minimum tsig1 %d tq\n", + ci->ci_bittiming_const.tseg1_min); + nl_dump_line(p," maximum tsig1 %d tq\n", + ci->ci_bittiming_const.tseg1_max); + nl_dump_line(p," minimum tsig2 %d tq\n", + ci->ci_bittiming_const.tseg2_min); + nl_dump_line(p," maximum tsig2 %d tq\n", + ci->ci_bittiming_const.tseg2_max); + nl_dump_line(p," maximum sjw %d tq\n", + ci->ci_bittiming_const.sjw_max); + nl_dump_line(p," minimum brp %d\n", + ci->ci_bittiming_const.brp_min); + nl_dump_line(p," maximum brp %d\n", + ci->ci_bittiming_const.brp_max); + nl_dump_line(p," brp increment %d\n", + ci->ci_bittiming_const.brp_inc); + } + + if (ci->ci_mask & CAN_HAS_CLOCK) { + nl_dump_line(p," base freq %d Hz\n", ci->ci_clock); + + } + + if (ci->ci_mask & CAN_HAS_BERR_COUNTER) { + nl_dump_line(p," bus error RX %d\n", + ci->ci_berr_counter.rxerr); + nl_dump_line(p," bus error TX %d\n", + ci->ci_berr_counter.txerr); + } + + return; +} + +static int can_clone(struct rtnl_link *dst, struct rtnl_link *src) +{ + struct can_info *cdst, *csrc = src->l_info; + int ret; + + dst->l_info = NULL; + ret = rtnl_link_set_type(dst, "can"); + if (ret < 0) + return ret; + + cdst = malloc(sizeof(*cdst)); + if (!cdst) + return -NLE_NOMEM; + + *cdst = *csrc; + dst->l_info = cdst; + + return 0; +} + +static int can_put_attrs(struct nl_msg *msg, struct rtnl_link *link) +{ + struct can_info *ci = link->l_info; + struct nlattr *data; + + data = nla_nest_start(msg, IFLA_INFO_DATA); + if (!data) + return -NLE_MSGSIZE; + + if (ci->ci_mask & CAN_HAS_RESTART) + NLA_PUT_U32(msg, CAN_HAS_RESTART, ci->ci_restart); + + if (ci->ci_mask & CAN_HAS_RESTART_MS) + NLA_PUT_U32(msg, CAN_HAS_RESTART_MS, ci->ci_restart_ms); + + if (ci->ci_mask & CAN_HAS_CTRLMODE) + NLA_PUT(msg, CAN_HAS_CTRLMODE, sizeof(ci->ci_ctrlmode), + &ci->ci_ctrlmode); + + if (ci->ci_mask & CAN_HAS_BITTIMING) + NLA_PUT(msg, CAN_HAS_BITTIMING, sizeof(ci->ci_bittiming), + &ci->ci_bittiming); + + if (ci->ci_mask & CAN_HAS_BITTIMING_CONST) + NLA_PUT(msg, CAN_HAS_BITTIMING_CONST, + sizeof(ci->ci_bittiming_const), + &ci->ci_bittiming_const); + + if (ci->ci_mask & CAN_HAS_CLOCK) + NLA_PUT(msg, CAN_HAS_CLOCK, sizeof(ci->ci_clock), + &ci->ci_clock); + + nla_nest_end(msg, data); + +nla_put_failure: + + return 0; +} + +static struct rtnl_link_info_ops can_info_ops = { + .io_name = "can", + .io_alloc = can_alloc, + .io_parse = can_parse, + .io_dump = { + [NL_DUMP_LINE] = can_dump_line, + [NL_DUMP_DETAILS] = can_dump_details, + }, + .io_clone = can_clone, + .io_put_attrs = can_put_attrs, + .io_free = can_free, +}; + +/** @cond SKIP */ +#define IS_CAN_LINK_ASSERT(link) \ + if ((link)->l_info_ops != &can_info_ops) { \ + APPBUG("Link is not a CAN link. set type \"can\" first."); \ + return -NLE_OPNOTSUPP; \ + } +/** @endcond */ + +/** + * @name CAN Object + * @{ + */ + +/** + * Check if link is a CAN link + * @arg link Link object + * + * @return True if link is a CAN link, otherwise false is returned. + */ +int rtnl_link_is_can(struct rtnl_link *link) +{ + return link->l_info_ops && !strcmp(link->l_info_ops->io_name, "can"); +} + +/** + * Restart CAN device + * @arg link Link object + * + * @return 0 on success or a negative error code + */ +int rtnl_link_can_restart(struct rtnl_link *link) +{ + struct can_info *ci = link->l_info; + + IS_CAN_LINK_ASSERT(link); + + ci->ci_restart = 1; + ci->ci_restart |= CAN_HAS_RESTART; + + return 0; +} + +/** + * Get CAN base frequency + * @arg link Link object + * @arg freq frequency in Hz + * + * @return 0 on success or a negative error code + */ +int rtnl_link_can_freq(struct rtnl_link *link, uint32_t *freq) +{ + struct can_info *ci = link->l_info; + + IS_CAN_LINK_ASSERT(link); + if (!freq) + return -NLE_INVAL; + + if (ci->ci_mask & CAN_HAS_CLOCK) + *freq = ci->ci_clock.freq; + else + return -NLE_AGAIN; + + return 0; +} + +/** + * Get CAN state + * @arg link Link object + * @arg state CAN bus state + * @return 0 on success or a negative error code + */ +int rtnl_link_can_state(struct rtnl_link *link, uint32_t *state) +{ + struct can_info *ci = link->l_info; + + IS_CAN_LINK_ASSERT(link); + if (!state) + return -NLE_INVAL; + + *state = ci->ci_state; + + return 0; +} + +/** + * Get CAN RX bus error count + * @arg link Link object + * + * @return RX bus error count on success or a negative error code + */ +int rtnl_link_can_berr_rx(struct rtnl_link *link) +{ + struct can_info *ci = link->l_info; + + IS_CAN_LINK_ASSERT(link); + + if (ci->ci_mask & CAN_HAS_BERR_COUNTER) + return ci->ci_berr_counter.rxerr; + else + return -NLE_AGAIN; +} + +/** + * Get CAN TX bus error count + * @arg link Link object + * + * @return TX bus error count on success or a negative error code + */ +int rtnl_link_can_berr_tx(struct rtnl_link *link) +{ + struct can_info *ci = link->l_info; + + IS_CAN_LINK_ASSERT(link); + + if (ci->ci_mask & CAN_HAS_BERR_COUNTER) + return ci->ci_berr_counter.txerr; + else + return -NLE_AGAIN; +} + +/** + * Get CAN bus error count + * @arg link Link object + * @arg berr Bus error count + * + * @return 0 on success or a negative error code + */ +int rtnl_link_can_berr(struct rtnl_link *link, struct can_berr_counter *berr) +{ + struct can_info *ci = link->l_info; + + IS_CAN_LINK_ASSERT(link); + if (!berr) + return -NLE_INVAL; + + if (ci->ci_mask & CAN_HAS_BERR_COUNTER) + *berr = ci->ci_berr_counter; + else + return -NLE_AGAIN; + + return 0; +} + +/** + * Get CAN harware-dependent bit-timing constant + * @arg link Link object + * @arg bt_const Bit-timing constant + * + * @return 0 on success or a negative error code + */ +int rtnl_link_can_get_bt_const(struct rtnl_link *link, + struct can_bittiming_const *bt_const) +{ + struct can_info *ci = link->l_info; + + IS_CAN_LINK_ASSERT(link); + if (!bt_const) + return -NLE_INVAL; + + if (ci->ci_mask & CAN_HAS_BITTIMING_CONST) + *bt_const = ci->ci_bittiming_const; + else + return -NLE_AGAIN; + + return 0; +} + +/** + * Get CAN device bit-timing + * @arg link Link object + * @arg bit_timing CAN bit-timing + * + * @return 0 on success or a negative error code + */ +int rtnl_link_can_get_bittiming(struct rtnl_link *link, + struct can_bittiming *bit_timing) +{ + struct can_info *ci = link->l_info; + + IS_CAN_LINK_ASSERT(link); + if (!bit_timing) + return -NLE_INVAL; + + if (ci->ci_mask & CAN_HAS_BITTIMING) + *bit_timing = ci->ci_bittiming; + else + return -NLE_AGAIN; + + return 0; +} + +/** + * Set CAN device bit-timing + * @arg link Link object + * @arg bit_timing CAN bit-timing + * + * @return 0 on success or a negative error code + */ +int rtnl_link_can_set_bittiming(struct rtnl_link *link, + struct can_bittiming *bit_timing) +{ + struct can_info *ci = link->l_info; + + IS_CAN_LINK_ASSERT(link); + if (!bit_timing) + return -NLE_INVAL; + + ci->ci_bittiming = *bit_timing; + ci->ci_mask |= CAN_HAS_BITTIMING; + + return 0; +} + +/** + * Get CAN device bit-timing + * @arg link Link object + * @arg bitrate CAN bitrate + * + * @return 0 on success or a negative error code + */ +int rtnl_link_can_get_bitrate(struct rtnl_link *link, uint32_t *bitrate) +{ + struct can_info *ci = link->l_info; + + IS_CAN_LINK_ASSERT(link); + if (!bitrate) + return -NLE_INVAL; + + if (ci->ci_mask & CAN_HAS_BITTIMING) + *bitrate = ci->ci_bittiming.bitrate; + else + return -NLE_AGAIN; + + return 0; +} + +/** + * Set CAN device bit-rate + * @arg link Link object + * @arg bitrate CAN bitrate + * + * @return 0 on success or a negative error code + */ +int rtnl_link_can_set_bitrate(struct rtnl_link *link, uint32_t bitrate) +{ + struct can_info *ci = link->l_info; + + IS_CAN_LINK_ASSERT(link); + + ci->ci_bittiming.bitrate = bitrate; + ci->ci_mask |= CAN_HAS_BITTIMING; + + return 0; +} + +/** + * Get CAN device sample point + * @arg link Link object + * @arg sp CAN sample point + * + * @return 0 on success or a negative error code + */ +int rtnl_link_can_get_sample_point(struct rtnl_link *link, uint32_t *sp) +{ + struct can_info *ci = link->l_info; + + IS_CAN_LINK_ASSERT(link); + if (!sp) + return -NLE_INVAL; + + if (ci->ci_mask & CAN_HAS_BITTIMING) + *sp = ci->ci_bittiming.sample_point; + else + return -NLE_AGAIN; + + return 0; +} + +/** + * Set CAN device sample point + * @arg link Link object + * @arg sp CAN sample point + * + * @return 0 on success or a negative error code + */ +int rtnl_link_can_set_sample_point(struct rtnl_link *link, uint32_t sp) +{ + struct can_info *ci = link->l_info; + + IS_CAN_LINK_ASSERT(link); + + ci->ci_bittiming.sample_point = sp; + ci->ci_mask |= CAN_HAS_BITTIMING; + + return 0; +} + +/** + * Get CAN device restart intervall + * @arg link Link object + * @arg interval Restart intervall in ms + * + * @return 0 on success or a negative error code + */ +int rtnl_link_can_get_restart_ms(struct rtnl_link *link, uint32_t *interval) +{ + struct can_info *ci = link->l_info; + + IS_CAN_LINK_ASSERT(link); + if (!interval) + return -NLE_INVAL; + + if (ci->ci_mask & CAN_HAS_RESTART_MS) + *interval = ci->ci_restart_ms; + else + return -NLE_AGAIN; + + return 0; +} + +/** + * Set CAN device restart intervall + * @arg link Link object + * @arg interval Restart intervall in ms + * + * @return 0 on success or a negative error code + */ +int rtnl_link_can_set_restart_ms(struct rtnl_link *link, uint32_t interval) +{ + struct can_info *ci = link->l_info; + + IS_CAN_LINK_ASSERT(link); + + ci->ci_restart_ms = interval; + ci->ci_mask |= CAN_HAS_RESTART_MS; + + return 0; +} + +/** + * Get CAN control mode + * @arg link Link object + * @arg ctrlmode CAN control mode + * + * @return 0 on success or a negative error code + */ +int rtnl_link_can_get_ctrlmode(struct rtnl_link *link, uint32_t *ctrlmode) +{ + struct can_info *ci = link->l_info; + + IS_CAN_LINK_ASSERT(link); + if (!ctrlmode) + return -NLE_INVAL; + + if (ci->ci_mask & CAN_HAS_CTRLMODE) + *ctrlmode = ci->ci_ctrlmode.flags; + else + return -NLE_AGAIN; + + return 0; +} + +/** + * Set a CAN Control Mode + * @arg link Link object + * @arg ctrlmode CAN control mode + * + * @return 0 on success or a negative error code + */ +int rtnl_link_can_set_ctrlmode(struct rtnl_link *link, uint32_t ctrlmode) +{ + struct can_info *ci = link->l_info; + + IS_CAN_LINK_ASSERT(link); + + ci->ci_ctrlmode.flags |= ctrlmode; + ci->ci_ctrlmode.mask |= ctrlmode; + ci->ci_mask |= CAN_HAS_CTRLMODE; + + return 0; +} + +/** + * Unset a CAN Control Mode + * @arg link Link object + * @arg ctrlmode CAN control mode + * + * @return 0 on success or a negative error code + */ +int rtnl_link_can_unset_ctrlmode(struct rtnl_link *link, uint32_t ctrlmode) +{ + struct can_info *ci = link->l_info; + + IS_CAN_LINK_ASSERT(link); + + ci->ci_ctrlmode.flags &= ~ctrlmode; + ci->ci_ctrlmode.mask |= ctrlmode; + ci->ci_mask |= CAN_HAS_CTRLMODE; + + return 0; +} + +/** @} */ + +/** + * @name Control Mode Translation + * @{ + */ + +static const struct trans_tbl can_ctrlmode[] = { + __ADD(CAN_CTRLMODE_LOOPBACK, loopback) + __ADD(CAN_CTRLMODE_LISTENONLY, listen-only) + __ADD(CAN_CTRLMODE_3_SAMPLES, triple-sampling) + __ADD(CAN_CTRLMODE_ONE_SHOT, one-shot) + __ADD(CAN_CTRLMODE_BERR_REPORTING, berr-reporting) +}; + +char *rtnl_link_can_ctrlmode2str(int ctrlmode, char *buf, size_t len) +{ + return __flags2str(ctrlmode, buf, len, can_ctrlmode, + ARRAY_SIZE(can_ctrlmode)); +} + +int rtnl_link_can_str2ctrlmode(const char *name) +{ + return __str2flags(name, can_ctrlmode, ARRAY_SIZE(can_ctrlmode)); +} + +/** @} */ + +static void __init can_init(void) +{ + rtnl_link_register_info(&can_info_ops); +} + +static void __exit can_exit(void) +{ + rtnl_link_unregister_info(&can_info_ops); +} + +/** @} */ diff --git a/lib/route/link/dummy.c b/lib/route/link/dummy.c new file mode 100644 index 0000000..1fd9f5a --- /dev/null +++ b/lib/route/link/dummy.c @@ -0,0 +1,40 @@ +/* + * lib/route/link/dummy.c Dummy Interfaces + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2011 Thomas Graf + */ + +/** + * @ingroup link + * @defgroup dummy Dummy + * + * @details + * \b Link Type Name: "dummy" + * + * @{ + */ + +#include +#include +#include + +static struct rtnl_link_info_ops dummy_info_ops = { + .io_name = "dummy", +}; + +static void __init dummy_init(void) +{ + rtnl_link_register_info(&dummy_info_ops); +} + +static void __exit dummy_exit(void) +{ + rtnl_link_unregister_info(&dummy_info_ops); +} + +/** @} */ diff --git a/lib/route/link/inet.c b/lib/route/link/inet.c index 9a82a8b..e2c867d 100644 --- a/lib/route/link/inet.c +++ b/lib/route/link/inet.c @@ -57,11 +57,11 @@ */ -#include +#include #include #include #include -#include +#include /** @cond SKIP */ struct inet_data @@ -166,7 +166,7 @@ const char *rtnl_link_inet_devconf2str(int type, char *buf, size_t len) ARRAY_SIZE(inet_devconf)); } -unsigned int rtnl_link_inet_str2devconf(const char *name) +int rtnl_link_inet_str2devconf(const char *name) { return __str2type(name, inet_devconf, ARRAY_SIZE(inet_devconf)); } diff --git a/lib/route/link/inet6.c b/lib/route/link/inet6.c index 399dd4b..4c627bd 100644 --- a/lib/route/link/inet6.c +++ b/lib/route/link/inet6.c @@ -9,11 +9,11 @@ * Copyright (c) 2010 Thomas Graf */ -#include +#include #include #include #include -#include +#include struct inet6_data { @@ -71,23 +71,33 @@ static int inet6_parse_protinfo(struct rtnl_link *link, struct nlattr *attr, if (tb[IFLA_INET6_CONF]) nla_memcpy(&i6->i6_conf, tb[IFLA_INET6_CONF], sizeof(i6->i6_conf)); - + + /* + * Due to 32bit data alignment, these addresses must be copied to an + * aligned location prior to access. + */ if (tb[IFLA_INET6_STATS]) { - uint64_t *cnt = nla_data(tb[IFLA_INET6_STATS]); + unsigned char *cnt = nla_data(tb[IFLA_INET6_STATS]); + uint64_t stat; int i; - for (i = 1; i < __IPSTATS_MIB_MAX; i++) + for (i = 1; i < __IPSTATS_MIB_MAX; i++) { + memcpy(&stat, &cnt[i * sizeof(stat)], sizeof(stat)); rtnl_link_set_stat(link, RTNL_LINK_IP6_INPKTS + i - 1, - cnt[i]); + stat); + } } if (tb[IFLA_INET6_ICMP6STATS]) { - uint64_t *cnt = nla_data(tb[IFLA_INET6_ICMP6STATS]); + unsigned char *cnt = nla_data(tb[IFLA_INET6_ICMP6STATS]); + uint64_t stat; int i; - for (i = 1; i < __ICMP6_MIB_MAX; i++) + for (i = 1; i < __ICMP6_MIB_MAX; i++) { + memcpy(&stat, &cnt[i * sizeof(stat)], sizeof(stat)); rtnl_link_set_stat(link, RTNL_LINK_ICMP6_INMSGS + i - 1, - cnt[i]); + stat); + } } return 0; @@ -232,78 +242,78 @@ static void inet6_dump_stats(struct rtnl_link *link, nl_dump(p, " IPv6: InPkts InOctets " " InDiscards InDelivers\n"); - nl_dump(p, " %18llu ", link->l_stats[RTNL_LINK_IP6_INPKTS]); + nl_dump(p, " %18" PRIu64 " ", link->l_stats[RTNL_LINK_IP6_INPKTS]); octets = nl_cancel_down_bytes(link->l_stats[RTNL_LINK_IP6_INOCTETS], &octetsUnit); if (octets) nl_dump(p, "%14.2f %3s ", octets, octetsUnit); else - nl_dump(p, "%16llu B ", 0); + nl_dump(p, "%16" PRIu64 " B ", 0); - nl_dump(p, "%18llu %18llu\n", + nl_dump(p, "%18" PRIu64 " %18" PRIu64 "\n", link->l_stats[RTNL_LINK_IP6_INDISCARDS], link->l_stats[RTNL_LINK_IP6_INDELIVERS]); nl_dump(p, " OutPkts OutOctets " " OutDiscards OutForwards\n"); - nl_dump(p, " %18llu ", link->l_stats[RTNL_LINK_IP6_OUTPKTS]); + nl_dump(p, " %18" PRIu64 " ", link->l_stats[RTNL_LINK_IP6_OUTPKTS]); octets = nl_cancel_down_bytes(link->l_stats[RTNL_LINK_IP6_OUTOCTETS], &octetsUnit); if (octets) nl_dump(p, "%14.2f %3s ", octets, octetsUnit); else - nl_dump(p, "%16llu B ", 0); + nl_dump(p, "%16" PRIu64 " B ", 0); - nl_dump(p, "%18llu %18llu\n", + nl_dump(p, "%18" PRIu64 " %18" PRIu64 "\n", link->l_stats[RTNL_LINK_IP6_OUTDISCARDS], link->l_stats[RTNL_LINK_IP6_OUTFORWDATAGRAMS]); nl_dump(p, " InMcastPkts InMcastOctets " " InBcastPkts InBcastOctests\n"); - nl_dump(p, " %18llu ", link->l_stats[RTNL_LINK_IP6_INMCASTPKTS]); + nl_dump(p, " %18" PRIu64 " ", link->l_stats[RTNL_LINK_IP6_INMCASTPKTS]); octets = nl_cancel_down_bytes(link->l_stats[RTNL_LINK_IP6_INMCASTOCTETS], &octetsUnit); if (octets) nl_dump(p, "%14.2f %3s ", octets, octetsUnit); else - nl_dump(p, "%16llu B ", 0); + nl_dump(p, "%16" PRIu64 " B ", 0); - nl_dump(p, "%18llu ", link->l_stats[RTNL_LINK_IP6_INBCASTPKTS]); + nl_dump(p, "%18" PRIu64 " ", link->l_stats[RTNL_LINK_IP6_INBCASTPKTS]); octets = nl_cancel_down_bytes(link->l_stats[RTNL_LINK_IP6_INBCASTOCTETS], &octetsUnit); if (octets) nl_dump(p, "%14.2f %3s\n", octets, octetsUnit); else - nl_dump(p, "%16llu B\n", 0); + nl_dump(p, "%16" PRIu64 " B\n", 0); nl_dump(p, " OutMcastPkts OutMcastOctets " " OutBcastPkts OutBcastOctests\n"); - nl_dump(p, " %18llu ", link->l_stats[RTNL_LINK_IP6_OUTMCASTPKTS]); + nl_dump(p, " %18" PRIu64 " ", link->l_stats[RTNL_LINK_IP6_OUTMCASTPKTS]); octets = nl_cancel_down_bytes(link->l_stats[RTNL_LINK_IP6_OUTMCASTOCTETS], &octetsUnit); if (octets) nl_dump(p, "%14.2f %3s ", octets, octetsUnit); else - nl_dump(p, "%16llu B ", 0); + nl_dump(p, "%16" PRIu64 " B ", 0); - nl_dump(p, "%18llu ", link->l_stats[RTNL_LINK_IP6_OUTBCASTPKTS]); + nl_dump(p, "%18" PRIu64 " ", link->l_stats[RTNL_LINK_IP6_OUTBCASTPKTS]); octets = nl_cancel_down_bytes(link->l_stats[RTNL_LINK_IP6_OUTBCASTOCTETS], &octetsUnit); if (octets) nl_dump(p, "%14.2f %3s\n", octets, octetsUnit); else - nl_dump(p, "%16llu B\n", 0); + nl_dump(p, "%16" PRIu64 " B\n", 0); nl_dump(p, " ReasmOKs ReasmFails " " ReasmReqds ReasmTimeout\n"); - nl_dump(p, " %18llu %18llu %18llu %18llu\n", + nl_dump(p, " %18" PRIu64 " %18" PRIu64 " %18" PRIu64 " %18" PRIu64 "\n", link->l_stats[RTNL_LINK_IP6_REASMOKS], link->l_stats[RTNL_LINK_IP6_REASMFAILS], link->l_stats[RTNL_LINK_IP6_REASMREQDS], @@ -311,14 +321,14 @@ static void inet6_dump_stats(struct rtnl_link *link, nl_dump(p, " FragOKs FragFails " " FragCreates\n"); - nl_dump(p, " %18llu %18llu %18llu\n", + nl_dump(p, " %18" PRIu64 " %18" PRIu64 " %18" PRIu64 "\n", link->l_stats[RTNL_LINK_IP6_FRAGOKS], link->l_stats[RTNL_LINK_IP6_FRAGFAILS], link->l_stats[RTNL_LINK_IP6_FRAGCREATES]); nl_dump(p, " InHdrErrors InTooBigErrors " " InNoRoutes InAddrErrors\n"); - nl_dump(p, " %18llu %18llu %18llu %18llu\n", + nl_dump(p, " %18" PRIu64 " %18" PRIu64 " %18" PRIu64 " %18" PRIu64 "\n", link->l_stats[RTNL_LINK_IP6_INHDRERRORS], link->l_stats[RTNL_LINK_IP6_INTOOBIGERRORS], link->l_stats[RTNL_LINK_IP6_INNOROUTES], @@ -326,14 +336,14 @@ static void inet6_dump_stats(struct rtnl_link *link, nl_dump(p, " InUnknownProtos InTruncatedPkts " " OutNoRoutes\n"); - nl_dump(p, " %18llu %18llu %18llu\n", + nl_dump(p, " %18" PRIu64 " %18" PRIu64 " %18" PRIu64 "\n", link->l_stats[RTNL_LINK_IP6_INUNKNOWNPROTOS], link->l_stats[RTNL_LINK_IP6_INTRUNCATEDPKTS], link->l_stats[RTNL_LINK_IP6_OUTNOROUTES]); nl_dump(p, " ICMPv6: InMsgs InErrors " " OutMsgs OutErrors\n"); - nl_dump(p, " %18llu %18llu %18llu %18llu\n", + nl_dump(p, " %18" PRIu64 " %18" PRIu64 " %18" PRIu64 " %18" PRIu64 "\n", link->l_stats[RTNL_LINK_ICMP6_INMSGS], link->l_stats[RTNL_LINK_ICMP6_INERRORS], link->l_stats[RTNL_LINK_ICMP6_OUTMSGS], diff --git a/lib/route/link/vlan.c b/lib/route/link/vlan.c index cbfdf34..9bbe7d5 100644 --- a/lib/route/link/vlan.c +++ b/lib/route/link/vlan.c @@ -6,24 +6,29 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2010 Thomas Graf + * Copyright (c) 2003-2013 Thomas Graf */ /** * @ingroup link * @defgroup vlan VLAN - * @brief + * Virtual LAN link module + * + * @details + * \b Link Type Name: "vlan" + * + * @route_doc{link_vlan, VLAN Documentation} * * @{ */ -#include +#include #include #include #include #include #include -#include +#include #include #include @@ -45,22 +50,9 @@ struct vlan_info struct vlan_map * vi_egress_qos; uint32_t vi_mask; }; + /** @endcond */ -static const struct trans_tbl vlan_flags[] = { - __ADD(VLAN_FLAG_REORDER_HDR, reorder_hdr) -}; - -char *rtnl_link_vlan_flags2str(int flags, char *buf, size_t len) -{ - return __flags2str(flags, buf, len, vlan_flags, ARRAY_SIZE(vlan_flags)); -} - -int rtnl_link_vlan_str2flags(const char *name) -{ - return __str2flags(name, vlan_flags, ARRAY_SIZE(vlan_flags)); -} - static struct nla_policy vlan_policy[IFLA_VLAN_MAX+1] = { [IFLA_VLAN_ID] = { .type = NLA_U16 }, [IFLA_VLAN_FLAGS] = { .minlen = sizeof(struct ifla_vlan_flags) }, @@ -122,7 +114,7 @@ static int vlan_parse(struct rtnl_link *link, struct nlattr *data, return -NLE_INVAL; map = nla_data(nla); - if (map->from < 0 || map->from > VLAN_PRIO_MAX) { + if (map->from > VLAN_PRIO_MAX) { return -NLE_INVAL; } @@ -189,7 +181,8 @@ static void vlan_dump_line(struct rtnl_link *link, struct nl_dump_params *p) static void vlan_dump_details(struct rtnl_link *link, struct nl_dump_params *p) { struct vlan_info *vi = link->l_info; - int i, printed; + int printed; + uint32_t i; char buf[64]; rtnl_link_vlan_flags2str(vi->vi_flags, buf, sizeof(buf)); @@ -241,7 +234,7 @@ static int vlan_clone(struct rtnl_link *dst, struct rtnl_link *src) int err; dst->l_info = NULL; - if ((err = rtnl_link_set_info_type(dst, "vlan")) < 0) + if ((err = rtnl_link_set_type(dst, "vlan")) < 0) return err; vdst = dst->l_info; @@ -299,7 +292,7 @@ static int vlan_put_attrs(struct nl_msg *msg, struct rtnl_link *link) if (vi->vi_mask & VLAN_HAS_EGRESS_QOS) { struct ifla_vlan_qos_mapping map; struct nlattr *qos; - int i; + uint32_t i; if (!(qos = nla_nest_start(msg, IFLA_VLAN_EGRESS_QOS))) goto nla_put_failure; @@ -334,12 +327,63 @@ static struct rtnl_link_info_ops vlan_info_ops = { .io_free = vlan_free, }; -int rtnl_link_vlan_set_id(struct rtnl_link *link, int id) +/** @cond SKIP */ +#define IS_VLAN_LINK_ASSERT(link) \ + if ((link)->l_info_ops != &vlan_info_ops) { \ + APPBUG("Link is not a vlan link. set type \"vlan\" first."); \ + return -NLE_OPNOTSUPP; \ + } +/** @endcond */ + +/** + * @name VLAN Object + * @{ + */ + +/** + * Allocate link object of type VLAN + * + * @return Allocated link object or NULL. + */ +struct rtnl_link *rtnl_link_vlan_alloc(void) +{ + struct rtnl_link *link; + int err; + + if (!(link = rtnl_link_alloc())) + return NULL; + + if ((err = rtnl_link_set_type(link, "vlan")) < 0) { + rtnl_link_put(link); + return NULL; + } + + return link; +} + +/** + * Check if link is a VLAN link + * @arg link Link object + * + * @return True if link is a VLAN link, otherwise false is returned. + */ +int rtnl_link_is_vlan(struct rtnl_link *link) +{ + return link->l_info_ops && !strcmp(link->l_info_ops->io_name, "vlan"); +} + +/** + * Set VLAN ID + * @arg link Link object + * @arg id VLAN identifier + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vlan_set_id(struct rtnl_link *link, uint16_t id) { struct vlan_info *vi = link->l_info; - if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops) - return -NLE_OPNOTSUPP; + IS_VLAN_LINK_ASSERT(link); vi->vi_vlan_id = id; vi->vi_mask |= VLAN_HAS_ID; @@ -347,12 +391,17 @@ int rtnl_link_vlan_set_id(struct rtnl_link *link, int id) return 0; } +/** + * Get VLAN Id + * @arg link Link object + * + * @return VLAN id, 0 if not set or a negative error code. + */ int rtnl_link_vlan_get_id(struct rtnl_link *link) { struct vlan_info *vi = link->l_info; - if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops) - return -NLE_OPNOTSUPP; + IS_VLAN_LINK_ASSERT(link); if (vi->vi_mask & VLAN_HAS_ID) return vi->vi_vlan_id; @@ -360,12 +409,18 @@ int rtnl_link_vlan_get_id(struct rtnl_link *link) return 0; } +/** + * Set VLAN flags + * @arg link Link object + * @arg flags VLAN flags + * + * @return 0 on success or a negative error code. + */ int rtnl_link_vlan_set_flags(struct rtnl_link *link, unsigned int flags) { struct vlan_info *vi = link->l_info; - if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops) - return -NLE_OPNOTSUPP; + IS_VLAN_LINK_ASSERT(link); vi->vi_flags_mask |= flags; vi->vi_flags |= flags; @@ -374,12 +429,18 @@ int rtnl_link_vlan_set_flags(struct rtnl_link *link, unsigned int flags) return 0; } +/** + * Unset VLAN flags + * @arg link Link object + * @arg flags VLAN flags + * + * @return 0 on success or a negative error code. + */ int rtnl_link_vlan_unset_flags(struct rtnl_link *link, unsigned int flags) { struct vlan_info *vi = link->l_info; - if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops) - return -NLE_OPNOTSUPP; + IS_VLAN_LINK_ASSERT(link); vi->vi_flags_mask |= flags; vi->vi_flags &= ~flags; @@ -388,23 +449,34 @@ int rtnl_link_vlan_unset_flags(struct rtnl_link *link, unsigned int flags) return 0; } -unsigned int rtnl_link_vlan_get_flags(struct rtnl_link *link) +/** + * Get VLAN flags + * @arg link Link object + * + * @return VLAN flags, 0 if none set, or a negative error code. + */ +int rtnl_link_vlan_get_flags(struct rtnl_link *link) { struct vlan_info *vi = link->l_info; - if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops) - return -NLE_OPNOTSUPP; + IS_VLAN_LINK_ASSERT(link); return vi->vi_flags; } +/** @} */ + +/** + * @name Quality of Service + * @{ + */ + int rtnl_link_vlan_set_ingress_map(struct rtnl_link *link, int from, uint32_t to) { struct vlan_info *vi = link->l_info; - if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops) - return -NLE_OPNOTSUPP; + IS_VLAN_LINK_ASSERT(link); if (from < 0 || from > VLAN_PRIO_MAX) return -NLE_INVAL; @@ -478,6 +550,30 @@ struct vlan_map *rtnl_link_vlan_get_egress_map(struct rtnl_link *link, } } +/** @} */ + +static const struct trans_tbl vlan_flags[] = { + __ADD(VLAN_FLAG_REORDER_HDR, reorder_hdr) +}; + +/** + * @name Flag Translation + * @{ + */ + +char *rtnl_link_vlan_flags2str(int flags, char *buf, size_t len) +{ + return __flags2str(flags, buf, len, vlan_flags, ARRAY_SIZE(vlan_flags)); +} + +int rtnl_link_vlan_str2flags(const char *name) +{ + return __str2flags(name, vlan_flags, ARRAY_SIZE(vlan_flags)); +} + +/** @} */ + + static void __init vlan_init(void) { rtnl_link_register_info(&vlan_info_ops); diff --git a/lib/route/neigh.c b/lib/route/neigh.c index aa19faa..288bb85 100644 --- a/lib/route/neigh.c +++ b/lib/route/neigh.c @@ -148,12 +148,14 @@ * @{ */ -#include +#include #include #include +#include #include #include #include +#include /** @cond SKIP */ #define NEIGH_ATTR_FLAGS 0x01 @@ -165,6 +167,7 @@ #define NEIGH_ATTR_FAMILY 0x40 #define NEIGH_ATTR_TYPE 0x80 #define NEIGH_ATTR_PROBES 0x100 +#define NEIGH_ATTR_MASTER 0x200 static struct nl_cache_ops rtnl_neigh_ops; static struct nl_object_ops neigh_obj_ops; @@ -197,6 +200,57 @@ static int neigh_clone(struct nl_object *_dst, struct nl_object *_src) return 0; } +static void neigh_keygen(struct nl_object *obj, uint32_t *hashkey, + uint32_t table_sz) +{ + struct rtnl_neigh *neigh = (struct rtnl_neigh *) obj; + unsigned int nkey_sz; + struct nl_addr *addr = NULL; + struct neigh_hash_key { + uint32_t n_family; + uint32_t n_ifindex; + char n_addr[0]; + } __attribute__((packed)) *nkey; + char buf[INET6_ADDRSTRLEN+5]; + + if (neigh->n_family == AF_BRIDGE) { + if (neigh->n_lladdr) + addr = neigh->n_lladdr; + } else if (neigh->n_dst) { + addr = neigh->n_dst; + } + + nkey_sz = sizeof(*nkey); + if (addr) + nkey_sz += nl_addr_get_len(addr); + + nkey = calloc(1, nkey_sz); + if (!nkey) { + *hashkey = 0; + return; + } + nkey->n_family = neigh->n_family; + if (neigh->n_family == AF_BRIDGE) + nkey->n_ifindex = neigh->n_master; + else + nkey->n_ifindex = neigh->n_ifindex; + if (addr) + memcpy(nkey->n_addr, + nl_addr_get_binary_addr(addr), + nl_addr_get_len(addr)); + + *hashkey = nl_hash(nkey, nkey_sz, 0) % table_sz; + + NL_DBG(5, "neigh %p key (fam %d dev %d addr %s) keysz %d hash 0x%x\n", + neigh, nkey->n_family, nkey->n_ifindex, + nl_addr2str(addr, buf, sizeof(buf)), + nkey_sz, *hashkey); + + free(nkey); + + return; +} + static int neigh_compare(struct nl_object *_a, struct nl_object *_b, uint32_t attrs, int flags) { @@ -211,6 +265,7 @@ static int neigh_compare(struct nl_object *_a, struct nl_object *_b, diff |= NEIGH_DIFF(TYPE, a->n_type != b->n_type); diff |= NEIGH_DIFF(LLADDR, nl_addr_cmp(a->n_lladdr, b->n_lladdr)); diff |= NEIGH_DIFF(DST, nl_addr_cmp(a->n_dst, b->n_dst)); + diff |= NEIGH_DIFF(MASTER, a->n_master != b->n_master); if (flags & LOOSE_COMPARISON) { diff |= NEIGH_DIFF(STATE, @@ -245,6 +300,16 @@ static char *neigh_attrs2str(int attrs, char *buf, size_t len) ARRAY_SIZE(neigh_attrs)); } +static uint32_t neigh_id_attrs_get(struct nl_object *obj) +{ + struct rtnl_neigh *neigh = (struct rtnl_neigh *)obj; + + if (neigh->n_family == AF_BRIDGE) + return (NEIGH_ATTR_LLADDR | NEIGH_ATTR_FAMILY | NEIGH_ATTR_MASTER); + else + return (NEIGH_ATTR_IFINDEX | NEIGH_ATTR_DST | NEIGH_ATTR_FAMILY); +} + static struct nla_policy neigh_policy[NDA_MAX+1] = { [NDA_CACHEINFO] = { .minlen = sizeof(struct nda_cacheinfo) }, [NDA_PROBES] = { .type = NLA_U32 }, @@ -252,6 +317,21 @@ static struct nla_policy neigh_policy[NDA_MAX+1] = { static int neigh_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, struct nlmsghdr *n, struct nl_parser_param *pp) +{ + struct rtnl_neigh *neigh; + int err; + + if ((err = rtnl_neigh_parse(n, &neigh)) < 0) + return err; + + err = pp->pp_cb((struct nl_object *) neigh, pp); + + rtnl_neigh_put(neigh); + return err; +} + + +int rtnl_neigh_parse(struct nlmsghdr *n, struct rtnl_neigh **result) { struct rtnl_neigh *neigh; struct nlattr *tb[NDA_MAX + 1]; @@ -317,7 +397,27 @@ static int neigh_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, neigh->ce_mask |= NEIGH_ATTR_PROBES; } - err = pp->pp_cb((struct nl_object *) neigh, pp); + /* + * Get the bridge index for AF_BRIDGE family entries + */ + if (neigh->n_family == AF_BRIDGE) { + struct nl_cache *lcache = nl_cache_mngt_require_safe("route/link"); + if (lcache ) { + struct rtnl_link *link = rtnl_link_get(lcache, + neigh->n_ifindex); + if (link) { + neigh->n_master = link->l_master; + rtnl_link_put(link); + neigh->ce_mask |= NEIGH_ATTR_MASTER; + } + + nl_cache_put(lcache); + } + } + + *result = neigh; + return 0; + errout: rtnl_neigh_put(neigh); return err; @@ -325,7 +425,9 @@ errout: static int neigh_request_update(struct nl_cache *c, struct nl_sock *h) { - return nl_rtgen_request(h, RTM_GETNEIGH, AF_UNSPEC, NLM_F_DUMP); + int family = c->c_iarg1; + + return nl_rtgen_request(h, RTM_GETNEIGH, family, NLM_F_DUMP); } @@ -336,9 +438,10 @@ static void neigh_dump_line(struct nl_object *a, struct nl_dump_params *p) struct nl_cache *link_cache; char state[128], flags[64]; - link_cache = nl_cache_mngt_require("route/link"); + link_cache = nl_cache_mngt_require_safe("route/link"); - nl_dump_line(p, "%s ", nl_addr2str(n->n_dst, dst, sizeof(dst))); + if (n->n_family != AF_BRIDGE) + nl_dump_line(p, "%s ", nl_addr2str(n->n_dst, dst, sizeof(dst))); if (link_cache) nl_dump(p, "dev %s ", @@ -361,6 +464,9 @@ static void neigh_dump_line(struct nl_object *a, struct nl_dump_params *p) if (state[0] || flags[0]) nl_dump(p, ">"); nl_dump(p, "\n"); + + if (link_cache) + nl_cache_put(link_cache); } static void neigh_dump_details(struct nl_object *a, struct nl_dump_params *p) @@ -408,7 +514,7 @@ void rtnl_neigh_put(struct rtnl_neigh *neigh) /** * Build a neighbour cache including all neighbours currently configured in the kernel. - * @arg sk Netlink socket. + * @arg sock Netlink socket. * @arg result Pointer to store resulting cache. * * Allocates a new neighbour cache, initializes it properly and updates it @@ -426,6 +532,7 @@ int rtnl_neigh_alloc_cache(struct nl_sock *sock, struct nl_cache **result) * @arg cache neighbour cache * @arg ifindex interface index the neighbour is on * @arg dst destination address of the neighbour + * * @return neighbour handle or NULL if no match was found. */ struct rtnl_neigh * rtnl_neigh_get(struct nl_cache *cache, int ifindex, @@ -460,10 +567,16 @@ static int build_neigh_msg(struct rtnl_neigh *tmpl, int cmd, int flags, .ndm_state = NUD_PERMANENT, }; - if (!(tmpl->ce_mask & NEIGH_ATTR_DST)) - return -NLE_MISSING_ATTR; + if (tmpl->n_family != AF_BRIDGE) { + if (!(tmpl->ce_mask & NEIGH_ATTR_DST)) + return -NLE_MISSING_ATTR; + nhdr.ndm_family = nl_addr_get_family(tmpl->n_dst); + } + else + nhdr.ndm_family = AF_BRIDGE; - nhdr.ndm_family = nl_addr_get_family(tmpl->n_dst); + if (tmpl->ce_mask & NEIGH_ATTR_FLAGS) + nhdr.ndm_flags = tmpl->n_flags; if (tmpl->ce_mask & NEIGH_ATTR_STATE) nhdr.ndm_state = tmpl->n_state; @@ -475,7 +588,8 @@ static int build_neigh_msg(struct rtnl_neigh *tmpl, int cmd, int flags, if (nlmsg_append(msg, &nhdr, sizeof(nhdr), NLMSG_ALIGNTO) < 0) goto nla_put_failure; - NLA_PUT_ADDR(msg, NDA_DST, tmpl->n_dst); + if (tmpl->n_family != AF_BRIDGE) + NLA_PUT_ADDR(msg, NDA_DST, tmpl->n_dst); if (tmpl->ce_mask & NEIGH_ATTR_LLADDR) NLA_PUT_ADDR(msg, NDA_LLADDR, tmpl->n_lladdr); @@ -805,12 +919,15 @@ static struct nl_object_ops neigh_obj_ops = { [NL_DUMP_STATS] = neigh_dump_stats, }, .oo_compare = neigh_compare, + .oo_keygen = neigh_keygen, .oo_attrs2str = neigh_attrs2str, .oo_id_attrs = (NEIGH_ATTR_IFINDEX | NEIGH_ATTR_DST | NEIGH_ATTR_FAMILY), + .oo_id_attrs_get = neigh_id_attrs_get }; static struct nl_af_group neigh_groups[] = { { AF_UNSPEC, RTNLGRP_NEIGH }, + { AF_BRIDGE, RTNLGRP_NEIGH }, { END_OF_GROUP_LIST }, }; diff --git a/lib/route/neightbl.c b/lib/route/neightbl.c index 9599faa..f9c9c27 100644 --- a/lib/route/neightbl.c +++ b/lib/route/neightbl.c @@ -16,7 +16,7 @@ * @{ */ -#include +#include #include #include #include @@ -143,7 +143,7 @@ static int neightbl_msg_parser(struct nl_cache_ops *ops, ntbl->nt_family = rtmsg->rtgen_family; if (tb[NDTA_NAME] == NULL) { - return -NLE_MISSING_ATTR; + err = -NLE_MISSING_ATTR; goto errout; } @@ -237,7 +237,7 @@ static void neightbl_dump_line(struct nl_object *arg, struct nl_dump_params *p) if (ntbl->nt_parms.ntp_mask & NEIGHTBLPARM_ATTR_IFINDEX) { struct nl_cache *link_cache; - link_cache = nl_cache_mngt_require("route/link"); + link_cache = nl_cache_mngt_require_safe("route/link"); if (link_cache) { char buf[32]; @@ -245,6 +245,7 @@ static void neightbl_dump_line(struct nl_object *arg, struct nl_dump_params *p) rtnl_link_i2name(link_cache, ntbl->nt_parms.ntp_ifindex, buf, sizeof(buf))); + nl_cache_put(link_cache); } else nl_dump(p, "<%u> ", ntbl->nt_parms.ntp_ifindex); } else @@ -332,21 +333,32 @@ static void neightbl_dump_stats(struct nl_object *arg, struct nl_dump_params *p) if (!(ntbl->ce_mask & NEIGHTBL_ATTR_STATS)) return; - nl_dump_line(p, " lookups %lld hits %lld failed %lld " \ - "allocations %lld destroys %lld\n", + nl_dump_line(p, " " \ + " lookups %" PRIu64 \ + " hits %" PRIu64 \ + " failed %" PRIu64 \ + " allocations %" PRIu64 \ + " destroys %" PRIu64 \ + "\n", ntbl->nt_stats.ndts_lookups, ntbl->nt_stats.ndts_hits, ntbl->nt_stats.ndts_res_failed, ntbl->nt_stats.ndts_allocs, ntbl->nt_stats.ndts_destroys); - nl_dump_line(p, " hash-grows %lld forced-gc-runs %lld " \ - "periodic-gc-runs %lld\n", + nl_dump_line(p, " " \ + " hash-grows %" PRIu64 \ + " forced-gc-runs %" PRIu64 \ + " periodic-gc-runs %" PRIu64 \ + "\n", ntbl->nt_stats.ndts_hash_grows, ntbl->nt_stats.ndts_forced_gc_runs, ntbl->nt_stats.ndts_periodic_gc_runs); - nl_dump_line(p, " rcv-unicast-probes %lld rcv-multicast-probes %lld\n", + nl_dump_line(p, " " \ + " rcv-unicast-probes %" PRIu64 \ + " rcv-multicast-probes %" PRIu64 \ + "\n", ntbl->nt_stats.ndts_rcv_probes_ucast, ntbl->nt_stats.ndts_rcv_probes_mcast); } diff --git a/lib/route/nexthop.c b/lib/route/nexthop.c index 48cbfcf..d3ca499 100644 --- a/lib/route/nexthop.c +++ b/lib/route/nexthop.c @@ -15,7 +15,7 @@ * @{ */ -#include +#include #include #include #include @@ -109,7 +109,7 @@ static void nh_dump_line(struct rtnl_nexthop *nh, struct nl_dump_params *dp) struct nl_cache *link_cache; char buf[128]; - link_cache = nl_cache_mngt_require("route/link"); + link_cache = nl_cache_mngt_require_safe("route/link"); nl_dump(dp, "via"); @@ -128,6 +128,9 @@ static void nh_dump_line(struct rtnl_nexthop *nh, struct nl_dump_params *dp) } nl_dump(dp, " "); + + if (link_cache) + nl_cache_put(link_cache); } static void nh_dump_details(struct rtnl_nexthop *nh, struct nl_dump_params *dp) @@ -135,7 +138,7 @@ static void nh_dump_details(struct rtnl_nexthop *nh, struct nl_dump_params *dp) struct nl_cache *link_cache; char buf[128]; - link_cache = nl_cache_mngt_require("route/link"); + link_cache = nl_cache_mngt_require_safe("route/link"); nl_dump(dp, "nexthop"); @@ -164,6 +167,9 @@ static void nh_dump_details(struct rtnl_nexthop *nh, struct nl_dump_params *dp) if (nh->ce_mask & NH_ATTR_FLAGS) nl_dump(dp, " <%s>", rtnl_route_nh_flags2str(nh->rtnh_flags, buf, sizeof(buf))); + + if (link_cache) + nl_cache_put(link_cache); } void rtnl_route_nh_dump(struct rtnl_nexthop *nh, struct nl_dump_params *dp) @@ -211,6 +217,7 @@ int rtnl_route_nh_get_ifindex(struct rtnl_nexthop *nh) return nh->rtnh_ifindex; } +/* FIXME: Convert to return an int */ void rtnl_route_nh_set_gateway(struct rtnl_nexthop *nh, struct nl_addr *addr) { struct nl_addr *old = nh->rtnh_gateway; diff --git a/lib/route/pktloc.c b/lib/route/pktloc.c index 823a3c7..9fcf4a4 100644 --- a/lib/route/pktloc.c +++ b/lib/route/pktloc.c @@ -2,10 +2,11 @@ * lib/route/pktloc.c Packet Location Aliasing * * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2 of the License. + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. * - * Copyright (c) 2008-2010 Thomas Graf + * Copyright (c) 2008-2013 Thomas Graf */ /** @@ -20,7 +21,7 @@ * library and provides a well defined set of definitions for most common * protocol fields. * - * @subsection pktloc_examples Examples + * @section pktloc_examples Examples * @par Example 1.1 Looking up a packet location * @code * struct rtnl_pktloc *loc; @@ -30,8 +31,8 @@ * @{ */ -#include -#include +#include +#include #include #include #include @@ -45,7 +46,7 @@ static struct nl_list_head pktloc_name_ht[PKTLOC_NAME_HT_SIZ]; /* djb2 */ -unsigned int pktloc_hash(const char *str) +static unsigned int pktloc_hash(const char *str) { unsigned long hash = 5381; int c; @@ -89,12 +90,13 @@ static int read_pktlocs(void) YY_BUFFER_STATE buf = NULL; yyscan_t scanner = NULL; static time_t last_read; - struct stat st = {0}; + struct stat st; char *path; int i, err; FILE *fd; - asprintf(&path, "%s/pktloc", SYSCONFDIR); + if (build_sysconf_path(&path, "pktloc") < 0) + return -NLE_NOMEM; /* if stat fails, just try to read the file */ if (stat(path, &st) == 0) { @@ -151,6 +153,7 @@ errout: /** * Lookup packet location alias * @arg name Name of packet location. + * @arg result Result pointer * * Tries to find a matching packet location alias for the supplied * packet location name. diff --git a/lib/route/pktloc_grammar.l b/lib/route/pktloc_grammar.l index 6b7a933..cbb42b3 100644 --- a/lib/route/pktloc_grammar.l +++ b/lib/route/pktloc_grammar.l @@ -1,6 +1,6 @@ %{ - #include - #include + #include + #include #include #include #include diff --git a/lib/route/pktloc_syntax.y b/lib/route/pktloc_syntax.y index 4a2ce48..25d8710 100644 --- a/lib/route/pktloc_syntax.y +++ b/lib/route/pktloc_syntax.y @@ -1,6 +1,6 @@ %{ -#include -#include +#include +#include #include #include #include diff --git a/lib/route/qdisc.c b/lib/route/qdisc.c index 6ec3a40..250d191 100644 --- a/lib/route/qdisc.c +++ b/lib/route/qdisc.c @@ -12,87 +12,21 @@ /** * @ingroup tc * @defgroup qdisc Queueing Disciplines - * - * @par Qdisc Handles - * In general, qdiscs are identified by the major part of a traffic control - * handle (the upper 16 bits). A few special values exist though: - * - \c TC_H_ROOT: root qdisc (directly attached to the device) - * - \c TC_H_INGRESS: ingress qdisc (directly attached to the device) - * - \c TC_H_UNSPEC: unspecified qdisc (no reference) - * - * @par 1) Adding a Qdisc - * @code - * // Allocate a new empty qdisc to be filled out - * struct rtnl_qdisc *qdisc = rtnl_qdisc_alloc(); - * - * // ... specify the kind of the Qdisc - * rtnl_qdisc_set_kind(qdisc, "pfifo"); - * - * // Specify the device the qdisc should be attached to - * rtnl_qdisc_set_ifindex(qdisc, ifindex); - * - * // ... specify the parent qdisc - * rtnl_qdisc_set_parent(qdisc, TC_H_ROOT); - * - * // Specifying the handle is not required but makes reidentifying easier - * // and may help to avoid adding a qdisc twice. - * rtnl_qdisc_set_handle(qdisc, 0x000A0000); - * - * // Now on to specify the qdisc specific options, see the relevant qdisc - * // modules for documentation, in this example we set the upper limit of - * // the packet fifo qdisc to 64 - * rtnl_qdisc_fifo_set_limit(qdisc, 64); - * - * rtnl_qdisc_add(handle, qdisc, NLM_R_REPLACE); - * - * // Free up the memory - * rtnl_qdisc_put(qdisc); - * @endcode - * - * @par 2) Deleting a Qdisc - * @code - * // Allocate a new empty qdisc to be filled out with the parameters - * // specifying the qdisc to be deleted. Alternatively a fully equiped - * // Qdisc object from a cache can be used. - * struct rtnl_qdisc *qdisc = rtnl_qdisc_alloc(); - * - * // The interface index of the device the qdisc is on and the parent handle - * // are the least required fields to be filled out. - * // Note: Specify TC_H_ROOT or TC_H_INGRESS as parent handle to delete the - * // root respectively root ingress qdisc. - * rtnl_qdisc_set_ifindex(qdisc, ifindex); - * rtnl_qdisc_set_parent(qdisc, parent_handle); - * - * // If required for identification, the handle can be specified as well. - * rtnl_qdisc_set_handle(qdisc, qdisc_handle); - * - * // Not required but maybe helpful as sanity check, the kind of the qdisc - * // can be specified to avoid mistakes. - * rtnl_qdisc_set_kind(qdisc, "pfifo"); - * - * // Finally delete the qdisc with rtnl_qdisc_delete(), alternatively - * // rtnl_qdisc_build_delete_request() can be invoked to generate an - * // appropritate netlink message to send out. - * rtnl_qdisc_delete(handle, qdisc); - * - * // Free up the memory - * rtnl_qdisc_put(qdisc); - * @endcode - * * @{ */ -#include -#include +#include +#include #include #include #include -#include +#include #include #include #include static struct nl_cache_ops rtnl_qdisc_ops; +static struct nl_object_ops qdisc_obj_ops; static int qdisc_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, struct nlmsghdr *n, struct nl_parser_param *pp) @@ -124,286 +58,6 @@ static int qdisc_request_update(struct nl_cache *c, struct nl_sock *sk) sizeof(tchdr)); } -/** - * @name QDisc Addition - * @{ - */ - -static int qdisc_build(struct rtnl_qdisc *qdisc, int type, int flags, - struct nl_msg **result) -{ - return rtnl_tc_msg_build(TC_CAST(qdisc), type, flags, result); - -#if 0 - /* Some qdiscs don't accept properly nested messages (e.g. netem). To - * accomodate for this, they can complete the message themselves. - */ - else if (qops && qops->qo_build_msg) { - err = qops->qo_build_msg(qdisc, *result); - if (err < 0) - goto errout; - } -#endif -} - -/** - * Build a netlink message to add a new qdisc - * @arg qdisc qdisc to add - * @arg flags additional netlink message flags - * @arg result Pointer to store resulting message. - * - * Builds a new netlink message requesting an addition of a qdisc. - * The netlink message header isn't fully equipped with all relevant - * fields and must be sent out via nl_send_auto_complete() or - * supplemented as needed. - * - * Common message flags used: - * - NLM_F_REPLACE - replace a potential existing qdisc - * - * @return 0 on success or a negative error code. - */ -int rtnl_qdisc_build_add_request(struct rtnl_qdisc *qdisc, int flags, - struct nl_msg **result) -{ - return qdisc_build(qdisc, RTM_NEWQDISC, NLM_F_CREATE | flags, result); -} - -/** - * Add a new qdisc - * @arg sk Netlink socket. - * @arg qdisc qdisc to delete - * @arg flags additional netlink message flags - * - * Builds a netlink message by calling rtnl_qdisc_build_add_request(), - * sends the request to the kernel and waits for the ACK to be - * received and thus blocks until the request has been processed. - * - * Common message flags used: - * - NLM_F_REPLACE - replace a potential existing qdisc - * - * @return 0 on success or a negative error code - */ -int rtnl_qdisc_add(struct nl_sock *sk, struct rtnl_qdisc *qdisc, - int flags) -{ - struct nl_msg *msg; - int err; - - if ((err = rtnl_qdisc_build_add_request(qdisc, flags, &msg)) < 0) - return err; - - err = nl_send_auto_complete(sk, msg); - nlmsg_free(msg); - if (err < 0) - return err; - - return wait_for_ack(sk); -} - -/** @} */ - -/** - * @name QDisc Modification - * @{ - */ - -/** - * Build a netlink message to change attributes of a existing qdisc - * @arg qdisc qdisc to change - * @arg new new qdisc attributes - * @arg result Pointer to store resulting message. - * - * Builds a new netlink message requesting an change of qdisc - * attributes. The netlink message header isn't fully equipped - * with all relevant fields and must be sent out via - * nl_send_auto_complete() or supplemented as needed. - * - * @return 0 on success or a negative error code. - */ -int rtnl_qdisc_build_change_request(struct rtnl_qdisc *qdisc, - struct rtnl_qdisc *new, - struct nl_msg **result) -{ - return qdisc_build(qdisc, RTM_NEWQDISC, NLM_F_REPLACE, result); -} - -/** - * Change attributes of a qdisc - * @arg sk Netlink socket. - * @arg qdisc qdisc to change - * @arg new new qdisc attributes - * - * Builds a netlink message by calling rtnl_qdisc_build_change_request(), - * sends the request to the kernel and waits for the ACK to be - * received and thus blocks until the request has been processed. - * - * @return 0 on success or a negative error code - */ -int rtnl_qdisc_change(struct nl_sock *sk, struct rtnl_qdisc *qdisc, - struct rtnl_qdisc *new) -{ - struct nl_msg *msg; - int err; - - if ((err = rtnl_qdisc_build_change_request(qdisc, new, &msg)) < 0) - return err; - - err = nl_send_auto_complete(sk, msg); - nlmsg_free(msg); - if (err < 0) - return err; - - return wait_for_ack(sk); -} - -/** @} */ - -/** - * @name QDisc Deletion - * @{ - */ - -/** - * Build a netlink request message to delete a qdisc - * @arg qdisc qdisc to delete - * @arg result Pointer to store resulting message. - * - * Builds a new netlink message requesting a deletion of a qdisc. - * The netlink message header isn't fully equipped with all relevant - * fields and must thus be sent out via nl_send_auto_complete() - * or supplemented as needed. - * - * @return 0 on success or a negative error code. - */ -int rtnl_qdisc_build_delete_request(struct rtnl_qdisc *qdisc, - struct nl_msg **result) -{ - struct nl_msg *msg; - struct tcmsg tchdr; - int required = TCA_ATTR_IFINDEX | TCA_ATTR_PARENT; - - if ((qdisc->ce_mask & required) != required) - BUG(); - - msg = nlmsg_alloc_simple(RTM_DELQDISC, 0); - if (!msg) - return -NLE_NOMEM; - - tchdr.tcm_family = AF_UNSPEC; - tchdr.tcm_handle = qdisc->q_handle; - tchdr.tcm_parent = qdisc->q_parent; - tchdr.tcm_ifindex = qdisc->q_ifindex; - if (nlmsg_append(msg, &tchdr, sizeof(tchdr), NLMSG_ALIGNTO) < 0) { - nlmsg_free(msg); - return -NLE_MSGSIZE; - } - - *result = msg; - return 0; -} - -/** - * Delete a qdisc - * @arg sk Netlink socket. - * @arg qdisc qdisc to delete - * - * Builds a netlink message by calling rtnl_qdisc_build_delete_request(), - * sends the request to the kernel and waits for the ACK to be - * received and thus blocks until the request has been processed. - * - * @return 0 on success or a negative error code - */ -int rtnl_qdisc_delete(struct nl_sock *sk, struct rtnl_qdisc *qdisc) -{ - struct nl_msg *msg; - int err; - - if ((err = rtnl_qdisc_build_delete_request(qdisc, &msg)) < 0) - return err; - - err = nl_send_auto_complete(sk, msg); - nlmsg_free(msg); - if (err < 0) - return err; - - return wait_for_ack(sk); -} - -/** @} */ - -/** - * @name Qdisc Cache Management - * @{ - */ - -/** - * Build a qdisc cache including all qdiscs currently configured in - * the kernel - * @arg sk Netlink socket. - * @arg result Pointer to store resulting message. - * - * Allocates a new cache, initializes it properly and updates it to - * include all qdiscs currently configured in the kernel. - * - * @return 0 on success or a negative error code. - */ -int rtnl_qdisc_alloc_cache(struct nl_sock *sk, struct nl_cache **result) -{ - return nl_cache_alloc_and_fill(&rtnl_qdisc_ops, sk, result); -} - -/** - * Look up qdisc by its parent in the provided cache - * @arg cache qdisc cache - * @arg ifindex interface the qdisc is attached to - * @arg parent parent handle - * @return pointer to qdisc inside the cache or NULL if no match was found. - */ -struct rtnl_qdisc * rtnl_qdisc_get_by_parent(struct nl_cache *cache, - int ifindex, uint32_t parent) -{ - struct rtnl_qdisc *q; - - if (cache->c_ops != &rtnl_qdisc_ops) - return NULL; - - nl_list_for_each_entry(q, &cache->c_items, ce_list) { - if (q->q_parent == parent && q->q_ifindex == ifindex) { - nl_object_get((struct nl_object *) q); - return q; - } - } - - return NULL; -} - -/** - * Look up qdisc by its handle in the provided cache - * @arg cache qdisc cache - * @arg ifindex interface the qdisc is attached to - * @arg handle qdisc handle - * @return pointer to qdisc inside the cache or NULL if no match was found. - */ -struct rtnl_qdisc * rtnl_qdisc_get(struct nl_cache *cache, - int ifindex, uint32_t handle) -{ - struct rtnl_qdisc *q; - - if (cache->c_ops != &rtnl_qdisc_ops) - return NULL; - - nl_list_for_each_entry(q, &cache->c_items, ce_list) { - if (q->q_handle == handle && q->q_ifindex == ifindex) { - nl_object_get((struct nl_object *) q); - return q; - } - } - - return NULL; -} - -/** @} */ - /** * @name Allocation/Freeing * @{ @@ -428,17 +82,370 @@ void rtnl_qdisc_put(struct rtnl_qdisc *qdisc) /** @} */ /** - * @name Iterators + * @name Addition / Modification / Deletion + * @{ + */ + +static int build_qdisc_msg(struct rtnl_qdisc *qdisc, int type, int flags, + struct nl_msg **result) +{ + if (!(qdisc->ce_mask & TCA_ATTR_IFINDEX)) { + APPBUG("ifindex must be specified"); + return -NLE_MISSING_ATTR; + } + + return rtnl_tc_msg_build(TC_CAST(qdisc), type, flags, result); +} + +/** + * Build a netlink message requesting the addition of a qdisc + * @arg qdisc Qdisc to add + * @arg flags Additional netlink message flags + * @arg result Pointer to store resulting netlink message + * + * The behaviour of this function is identical to rtnl_qdisc_add() with + * the exception that it will not send the message but return it int the + * provided return pointer instead. + * + * @see rtnl_qdisc_add() + * + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_build_add_request(struct rtnl_qdisc *qdisc, int flags, + struct nl_msg **result) +{ + if (!(qdisc->ce_mask & (TCA_ATTR_HANDLE | TCA_ATTR_PARENT))) { + APPBUG("handle or parent must be specified"); + return -NLE_MISSING_ATTR; + } + + return build_qdisc_msg(qdisc, RTM_NEWQDISC, flags, result); +} + +/** + * Add qdisc + * @arg sk Netlink socket + * @arg qdisc Qdisc to add + * @arg flags Additional netlink message flags + * + * Builds a \c RTM_NEWQDISC netlink message requesting the addition + * of a new qdisc and sends the message to the kernel. The configuration + * of the qdisc is derived from the attributes of the specified qdisc. + * + * The following flags may be specified: + * - \c NLM_F_CREATE: Create qdisc if it does not exist, otherwise + * -NLE_OBJ_NOTFOUND is returned. + * - \c NLM_F_REPLACE: If another qdisc is already attached to the + * parent, replace it even if the handles mismatch. + * - \c NLM_F_EXCL: Return -NLE_EXISTS if a qdisc with matching + * handle exists already. + * + * Existing qdiscs with matching handles will be updated, unless the + * flag \c NLM_F_EXCL is specified. If their handles do not match, the + * error -NLE_EXISTS is returned unless the flag \c NLM_F_REPLACE is + * specified in which case the existing qdisc is replaced with the new + * one. If no matching qdisc exists, it will be created if the flag + * \c NLM_F_CREATE is set, otherwise the error -NLE_OBJ_NOTFOUND is + * returned. + * + * After sending, the function will wait for the ACK or an eventual + * error message to be received and will therefore block until the + * operation has been completed. + * + * @note Disabling auto-ack (nl_socket_disable_auto_ack()) will cause + * this function to return immediately after sending. In this case, + * it is the responsibility of the caller to handle any error + * messages returned. + * + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_add(struct nl_sock *sk, struct rtnl_qdisc *qdisc, int flags) +{ + struct nl_msg *msg; + int err; + + if ((err = rtnl_qdisc_build_add_request(qdisc, flags, &msg)) < 0) + return err; + + return nl_send_sync(sk, msg); +} + +/** + * Build netlink message requesting the update of a qdisc + * @arg qdisc Qdisc to update + * @arg new Qdisc with updated attributes + * @arg flags Additional netlink message flags + * @arg result Pointer to store resulting netlink message + * + * The behaviour of this function is identical to rtnl_qdisc_update() with + * the exception that it will not send the message but return it in the + * provided return pointer instead. + * + * @see rtnl_qdisc_update() + * + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_build_update_request(struct rtnl_qdisc *qdisc, + struct rtnl_qdisc *new, int flags, + struct nl_msg **result) +{ + if (flags & (NLM_F_CREATE | NLM_F_EXCL)) { + APPBUG("NLM_F_CREATE and NLM_F_EXCL may not be used here, " + "use rtnl_qdisc_add()"); + return -NLE_INVAL; + } + + if (!(qdisc->ce_mask & TCA_ATTR_IFINDEX)) { + APPBUG("ifindex must be specified"); + return -NLE_MISSING_ATTR; + } + + if (!(qdisc->ce_mask & (TCA_ATTR_HANDLE | TCA_ATTR_PARENT))) { + APPBUG("handle or parent must be specified"); + return -NLE_MISSING_ATTR; + } + + rtnl_tc_set_ifindex(TC_CAST(new), qdisc->q_ifindex); + + if (qdisc->ce_mask & TCA_ATTR_HANDLE) + rtnl_tc_set_handle(TC_CAST(new), qdisc->q_handle); + + if (qdisc->ce_mask & TCA_ATTR_PARENT) + rtnl_tc_set_parent(TC_CAST(new), qdisc->q_parent); + + return build_qdisc_msg(new, RTM_NEWQDISC, flags, result); +} + +/** + * Update qdisc + * @arg sk Netlink socket + * @arg qdisc Qdisc to update + * @arg new Qdisc with updated attributes + * @arg flags Additional netlink message flags + * + * Builds a \c RTM_NEWQDISC netlink message requesting the update + * of an existing qdisc and sends the message to the kernel. + * + * This function is a varation of rtnl_qdisc_add() to update qdiscs + * if the qdisc to be updated is available as qdisc object. The + * behaviour is identical to the one of rtnl_qdisc_add except that + * before constructing the message, it copies the \c ifindex, + * \c handle, and \c parent from the original \p qdisc to the \p new + * qdisc. + * + * After sending, the function will wait for the ACK or an eventual + * error message to be received and will therefore block until the + * operation has been completed. + * + * @note Disabling auto-ack (nl_socket_disable_auto_ack()) will cause + * this function to return immediately after sending. In this case, + * it is the responsibility of the caller to handle any error + * messages returned. + * + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_update(struct nl_sock *sk, struct rtnl_qdisc *qdisc, + struct rtnl_qdisc *new, int flags) +{ + struct nl_msg *msg; + int err; + + err = rtnl_qdisc_build_update_request(qdisc, new, flags, &msg); + if (err < 0) + return err; + + return nl_send_sync(sk, msg); +} + +/** + * Build netlink message requesting the deletion of a qdisc + * @arg qdisc Qdisc to delete + * @arg result Pointer to store resulting netlink message + * + * The behaviour of this function is identical to rtnl_qdisc_delete() with + * the exception that it will not send the message but return it in the + * provided return pointer instead. + * + * @see rtnl_qdisc_delete() + * + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_build_delete_request(struct rtnl_qdisc *qdisc, + struct nl_msg **result) +{ + struct nl_msg *msg; + struct tcmsg tchdr; + uint32_t required = TCA_ATTR_IFINDEX | TCA_ATTR_PARENT; + + if ((qdisc->ce_mask & required) != required) { + APPBUG("ifindex and parent must be specified"); + return -NLE_MISSING_ATTR; + } + + if (!(msg = nlmsg_alloc_simple(RTM_DELQDISC, 0))) + return -NLE_NOMEM; + + memset(&tchdr, 0, sizeof(tchdr)); + + tchdr.tcm_family = AF_UNSPEC; + tchdr.tcm_ifindex = qdisc->q_ifindex; + tchdr.tcm_parent = qdisc->q_parent; + + if (qdisc->ce_mask & TCA_ATTR_HANDLE) + tchdr.tcm_handle = qdisc->q_handle; + + if (nlmsg_append(msg, &tchdr, sizeof(tchdr), NLMSG_ALIGNTO) < 0) + goto nla_put_failure; + + if (qdisc->ce_mask & TCA_ATTR_KIND) + NLA_PUT_STRING(msg, TCA_KIND, qdisc->q_kind); + + *result = msg; + return 0; + +nla_put_failure: + nlmsg_free(msg); + return -NLE_MSGSIZE; +} + +/** + * Delete qdisc + * @arg sk Netlink socket + * @arg qdisc Qdisc to add + * + * Builds a \c RTM_NEWQDISC netlink message requesting the deletion + * of a qdisc and sends the message to the kernel. + * + * The message is constructed out of the following attributes: + * - \c ifindex and \c parent + * - \c handle (optional, must match if provided) + * - \c kind (optional, must match if provided) + * + * All other qdisc attributes including all qdisc type specific + * attributes are ignored. + * + * After sending, the function will wait for the ACK or an eventual + * error message to be received and will therefore block until the + * operation has been completed. + * + * @note It is not possible to delete default qdiscs. + * + * @note Disabling auto-ack (nl_socket_disable_auto_ack()) will cause + * this function to return immediately after sending. In this case, + * it is the responsibility of the caller to handle any error + * messages returned. + * + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_delete(struct nl_sock *sk, struct rtnl_qdisc *qdisc) +{ + struct nl_msg *msg; + int err; + + if ((err = rtnl_qdisc_build_delete_request(qdisc, &msg)) < 0) + return err; + + return nl_send_sync(sk, msg); +} + +/** @} */ + +/** + * @name Cache Related Functions * @{ */ /** - * Call a callback for each child class of a qdisc - * @arg qdisc the parent qdisc - * @arg cache a class cache including all classes of the interface - * the specified qdisc is attached to - * @arg cb callback function - * @arg arg argument to be passed to callback function + * Allocate a cache and fill it with all configured qdiscs + * @arg sk Netlink socket + * @arg result Pointer to store the created cache + * + * Allocates a new qdisc cache and fills it with a list of all configured + * qdiscs on all network devices. Release the cache with nl_cache_free(). + * + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_alloc_cache(struct nl_sock *sk, struct nl_cache **result) +{ + return nl_cache_alloc_and_fill(&rtnl_qdisc_ops, sk, result); +} + +/** + * Search qdisc by interface index and parent + * @arg cache Qdisc cache + * @arg ifindex Interface index + * @arg parent Handle of parent qdisc + * + * Searches a qdisc cache previously allocated with rtnl_qdisc_alloc_cache() + * and searches for a qdisc matching the interface index and parent qdisc. + * + * The reference counter is incremented before returning the qdisc, therefore + * the reference must be given back with rtnl_qdisc_put() after usage. + * + * @return pointer to qdisc inside the cache or NULL if no match was found. + */ +struct rtnl_qdisc *rtnl_qdisc_get_by_parent(struct nl_cache *cache, + int ifindex, uint32_t parent) +{ + struct rtnl_qdisc *q; + + if (cache->c_ops != &rtnl_qdisc_ops) + return NULL; + + nl_list_for_each_entry(q, &cache->c_items, ce_list) { + if (q->q_parent == parent && q->q_ifindex == ifindex) { + nl_object_get((struct nl_object *) q); + return q; + } + } + + return NULL; +} + +/** + * Search qdisc by interface index and handle + * @arg cache Qdisc cache + * @arg ifindex Interface index + * @arg handle Handle + * + * Searches a qdisc cache previously allocated with rtnl_qdisc_alloc_cache() + * and searches for a qdisc matching the interface index and handle. + * + * The reference counter is incremented before returning the qdisc, therefore + * the reference must be given back with rtnl_qdisc_put() after usage. + * + * @return Qdisc or NULL if no match was found. + */ +struct rtnl_qdisc *rtnl_qdisc_get(struct nl_cache *cache, int ifindex, + uint32_t handle) +{ + struct rtnl_qdisc *q; + + if (cache->c_ops != &rtnl_qdisc_ops) + return NULL; + + nl_list_for_each_entry(q, &cache->c_items, ce_list) { + if (q->q_handle == handle && q->q_ifindex == ifindex) { + nl_object_get((struct nl_object *) q); + return q; + } + } + + return NULL; +} + +/** @} */ + +/** + * @name Deprecated Functions + * @{ + */ + +/** + * Call a callback for each child class of a qdisc (deprecated) + * + * @deprecated Use of this function is deprecated, it does not allow + * to handle the out of memory situation that can occur. */ void rtnl_qdisc_foreach_child(struct rtnl_qdisc *qdisc, struct nl_cache *cache, void (*cb)(struct nl_object *, void *), void *arg) @@ -459,29 +466,54 @@ void rtnl_qdisc_foreach_child(struct rtnl_qdisc *qdisc, struct nl_cache *cache, } /** - * Call a callback for each filter attached to the qdisc - * @arg qdisc the parent qdisc - * @arg cache a filter cache including at least all the filters - * attached to the specified qdisc - * @arg cb callback function - * @arg arg argument to be passed to callback function + * Call a callback for each filter attached to the qdisc (deprecated) + * + * @deprecated Use of this function is deprecated, it does not allow + * to handle the out of memory situation that can occur. */ void rtnl_qdisc_foreach_cls(struct rtnl_qdisc *qdisc, struct nl_cache *cache, void (*cb)(struct nl_object *, void *), void *arg) { struct rtnl_cls *filter; - filter = rtnl_cls_alloc(); - if (!filter) + if (!(filter = rtnl_cls_alloc())) return; - rtnl_tc_set_ifindex((struct rtnl_tc *) filter, qdisc->q_ifindex); - rtnl_tc_set_parent((struct rtnl_tc *) filter, qdisc->q_parent); + rtnl_tc_set_ifindex(TC_CAST(filter), qdisc->q_ifindex); + rtnl_tc_set_parent(TC_CAST(filter), qdisc->q_parent); - nl_cache_foreach_filter(cache, (struct nl_object *) filter, cb, arg); + nl_cache_foreach_filter(cache, OBJ_CAST(filter), cb, arg); rtnl_cls_put(filter); } +/** + * Build a netlink message requesting the update of a qdisc + * + * @deprecated Use of this function is deprecated in favour of + * rtnl_qdisc_build_update_request() due to the missing + * possibility of specifying additional flags. + */ +int rtnl_qdisc_build_change_request(struct rtnl_qdisc *qdisc, + struct rtnl_qdisc *new, + struct nl_msg **result) +{ + return rtnl_qdisc_build_update_request(qdisc, new, NLM_F_REPLACE, + result); +} + +/** + * Change attributes of a qdisc + * + * @deprecated Use of this function is deprecated in favour of + * rtnl_qdisc_update() due to the missing possibility of + * specifying additional flags. + */ +int rtnl_qdisc_change(struct nl_sock *sk, struct rtnl_qdisc *qdisc, + struct rtnl_qdisc *new) +{ + return rtnl_qdisc_update(sk, qdisc, new, NLM_F_REPLACE); +} + /** @} */ static void qdisc_dump_details(struct rtnl_tc *tc, struct nl_dump_params *p) @@ -514,7 +546,7 @@ static struct nl_cache_ops rtnl_qdisc_ops = { .co_obj_ops = &qdisc_obj_ops, }; -struct nl_object_ops qdisc_obj_ops = { +static struct nl_object_ops qdisc_obj_ops = { .oo_name = "route/qdisc", .oo_size = sizeof(struct rtnl_qdisc), .oo_free_data = rtnl_tc_free_data, diff --git a/lib/route/qdisc/blackhole.c b/lib/route/qdisc/blackhole.c index 06f5380..339cf78 100644 --- a/lib/route/qdisc/blackhole.c +++ b/lib/route/qdisc/blackhole.c @@ -15,9 +15,9 @@ * @{ */ -#include +#include #include -#include +#include static struct rtnl_tc_ops blackhole_ops = { .to_kind = "blackhole", diff --git a/lib/route/qdisc/cbq.c b/lib/route/qdisc/cbq.c index e791a10..95f1761 100644 --- a/lib/route/qdisc/cbq.c +++ b/lib/route/qdisc/cbq.c @@ -9,11 +9,11 @@ * Copyright (c) 2003-2011 Thomas Graf */ -#include -#include +#include +#include #include #include -#include +#include #include #include #include diff --git a/lib/route/qdisc/dsmark.c b/lib/route/qdisc/dsmark.c index b5fd0d6..fd9553d 100644 --- a/lib/route/qdisc/dsmark.c +++ b/lib/route/qdisc/dsmark.c @@ -16,12 +16,12 @@ * @{ */ -#include -#include +#include +#include #include #include #include -#include +#include #include #include diff --git a/lib/route/qdisc/fifo.c b/lib/route/qdisc/fifo.c index e87c79a..d94c007 100644 --- a/lib/route/qdisc/fifo.c +++ b/lib/route/qdisc/fifo.c @@ -29,10 +29,10 @@ * @{ */ -#include -#include +#include +#include #include -#include +#include #include #include #include diff --git a/lib/route/qdisc/htb.c b/lib/route/qdisc/htb.c index 94185de..5a61a4e 100644 --- a/lib/route/qdisc/htb.c +++ b/lib/route/qdisc/htb.c @@ -18,12 +18,12 @@ * @{ */ -#include -#include +#include +#include #include #include #include -#include +#include #include #include #include @@ -39,6 +39,7 @@ #define SCH_HTB_HAS_RBUFFER 0x008 #define SCH_HTB_HAS_CBUFFER 0x010 #define SCH_HTB_HAS_QUANTUM 0x020 +#define SCH_HTB_HAS_LEVEL 0x040 /** @endcond */ static struct nla_policy htb_policy[TCA_HTB_MAX+1] = { @@ -61,6 +62,7 @@ static int htb_qdisc_msg_parser(struct rtnl_tc *tc, void *data) nla_memcpy(&opts, tb[TCA_HTB_INIT], sizeof(opts)); htb->qh_rate2quantum = opts.rate2quantum; htb->qh_defcls = opts.defcls; + htb->qh_direct_pkts = opts.direct_pkts; htb->qh_mask = (SCH_HTB_HAS_RATE2QUANTUM | SCH_HTB_HAS_DEFCLS); } @@ -84,16 +86,20 @@ static int htb_class_msg_parser(struct rtnl_tc *tc, void *data) htb->ch_prio = opts.prio; rtnl_copy_ratespec(&htb->ch_rate, &opts.rate); rtnl_copy_ratespec(&htb->ch_ceil, &opts.ceil); - htb->ch_rbuffer = rtnl_tc_calc_bufsize(opts.buffer, opts.rate.rate); - htb->ch_cbuffer = rtnl_tc_calc_bufsize(opts.cbuffer, opts.ceil.rate); + htb->ch_rbuffer = rtnl_tc_calc_bufsize(nl_ticks2us(opts.buffer), + opts.rate.rate); + htb->ch_cbuffer = rtnl_tc_calc_bufsize(nl_ticks2us(opts.cbuffer), + opts.ceil.rate); htb->ch_quantum = opts.quantum; + htb->ch_level = opts.level; rtnl_tc_set_mpu(tc, htb->ch_rate.rs_mpu); rtnl_tc_set_overhead(tc, htb->ch_rate.rs_overhead); htb->ch_mask = (SCH_HTB_HAS_PRIO | SCH_HTB_HAS_RATE | SCH_HTB_HAS_CEIL | SCH_HTB_HAS_RBUFFER | - SCH_HTB_HAS_CBUFFER | SCH_HTB_HAS_QUANTUM); + SCH_HTB_HAS_CBUFFER | SCH_HTB_HAS_QUANTUM | + SCH_HTB_HAS_LEVEL); } return 0; @@ -111,8 +117,8 @@ static void htb_qdisc_dump_line(struct rtnl_tc *tc, void *data, nl_dump(p, " r2q %u", htb->qh_rate2quantum); if (htb->qh_mask & SCH_HTB_HAS_DEFCLS) { - char buf[32]; - nl_dump(p, " default %s", + char buf[64]; + nl_dump(p, " default-class %s", rtnl_tc_handle2str(htb->qh_defcls, buf, sizeof(buf))); } } @@ -184,10 +190,10 @@ static int htb_qdisc_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg) { struct rtnl_htb_qdisc *htb = data; - struct tc_htb_glob opts = {0}; - - opts.version = TC_HTB_PROTOVER; - opts.rate2quantum = 10; + struct tc_htb_glob opts = { + .version = TC_HTB_PROTOVER, + .rate2quantum = 10, + }; if (htb) { if (htb->qh_mask & SCH_HTB_HAS_RATE2QUANTUM) @@ -211,12 +217,12 @@ static int htb_class_msg_fill(struct rtnl_tc *tc, void *data, if (!htb || !(htb->ch_mask & SCH_HTB_HAS_RATE)) BUG(); + memset(&opts, 0, sizeof(opts)); + /* if not set, zero (0) is used as priority */ if (htb->ch_mask & SCH_HTB_HAS_PRIO) opts.prio = htb->ch_prio; - memset(&opts, 0, sizeof(opts)); - mtu = rtnl_tc_get_mtu(tc); rtnl_tc_build_rate_table(tc, &htb->ch_rate, rtable); @@ -236,16 +242,16 @@ static int htb_class_msg_fill(struct rtnl_tc *tc, void *data, if (htb->ch_mask & SCH_HTB_HAS_RBUFFER) buffer = htb->ch_rbuffer; else - buffer = opts.rate.rate / nl_get_user_hz() + mtu; /* XXX */ + buffer = opts.rate.rate / nl_get_psched_hz() + mtu; /* XXX */ - opts.buffer = rtnl_tc_calc_txtime(buffer, opts.rate.rate); + opts.buffer = nl_us2ticks(rtnl_tc_calc_txtime(buffer, opts.rate.rate)); if (htb->ch_mask & SCH_HTB_HAS_CBUFFER) cbuffer = htb->ch_cbuffer; else - cbuffer = opts.ceil.rate / nl_get_user_hz() + mtu; /* XXX */ + cbuffer = opts.ceil.rate / nl_get_psched_hz() + mtu; /* XXX */ - opts.cbuffer = rtnl_tc_calc_txtime(cbuffer, opts.ceil.rate); + opts.cbuffer = nl_us2ticks(rtnl_tc_calc_txtime(cbuffer, opts.ceil.rate)); if (htb->ch_mask & SCH_HTB_HAS_QUANTUM) opts.quantum = htb->ch_quantum; @@ -260,20 +266,72 @@ nla_put_failure: return -NLE_MSGSIZE; } +static struct rtnl_tc_ops htb_qdisc_ops; +static struct rtnl_tc_ops htb_class_ops; + +static struct rtnl_htb_qdisc *htb_qdisc_data(struct rtnl_qdisc *qdisc) +{ + return rtnl_tc_data_check(TC_CAST(qdisc), &htb_qdisc_ops); +} + +static struct rtnl_htb_class *htb_class_data(struct rtnl_class *class) +{ + return rtnl_tc_data_check(TC_CAST(class), &htb_class_ops); +} + /** * @name Attribute Modifications * @{ */ -void rtnl_htb_set_rate2quantum(struct rtnl_qdisc *qdisc, uint32_t rate2quantum) +/** + * Return rate/quantum ratio of HTB qdisc + * @arg qdisc htb qdisc object + * + * @return rate/quantum ratio or 0 if unspecified + */ +uint32_t rtnl_htb_get_rate2quantum(struct rtnl_qdisc *qdisc) { struct rtnl_htb_qdisc *htb; - if (!(htb = rtnl_tc_data(TC_CAST(qdisc)))) - BUG(); + if ((htb = htb_qdisc_data(qdisc)) && + htb->qh_mask & SCH_HTB_HAS_RATE2QUANTUM) + return htb->qh_rate2quantum; + + return 0; +} + +int rtnl_htb_set_rate2quantum(struct rtnl_qdisc *qdisc, uint32_t rate2quantum) +{ + struct rtnl_htb_qdisc *htb; + + if (!(htb = htb_qdisc_data(qdisc))) + return -NLE_OPNOTSUPP; htb->qh_rate2quantum = rate2quantum; htb->qh_mask |= SCH_HTB_HAS_RATE2QUANTUM; + + return 0; +} + +/** + * Return default class of HTB qdisc + * @arg qdisc htb qdisc object + * + * Returns the classid of the class where all unclassified traffic + * goes to. + * + * @return classid or TC_H_UNSPEC if unspecified. + */ +uint32_t rtnl_htb_get_defcls(struct rtnl_qdisc *qdisc) +{ + struct rtnl_htb_qdisc *htb; + + if ((htb = htb_qdisc_data(qdisc)) && + htb->qh_mask & SCH_HTB_HAS_DEFCLS) + return htb->qh_defcls; + + return TC_H_UNSPEC; } /** @@ -281,70 +339,131 @@ void rtnl_htb_set_rate2quantum(struct rtnl_qdisc *qdisc, uint32_t rate2quantum) * @arg qdisc qdisc to change * @arg defcls new default class */ -void rtnl_htb_set_defcls(struct rtnl_qdisc *qdisc, uint32_t defcls) +int rtnl_htb_set_defcls(struct rtnl_qdisc *qdisc, uint32_t defcls) { struct rtnl_htb_qdisc *htb; - if (!(htb = rtnl_tc_data(TC_CAST(qdisc)))) - BUG(); + if (!(htb = htb_qdisc_data(qdisc))) + return -NLE_OPNOTSUPP; htb->qh_defcls = defcls; htb->qh_mask |= SCH_HTB_HAS_DEFCLS; + + return 0; } -void rtnl_htb_set_prio(struct rtnl_class *class, uint32_t prio) +uint32_t rtnl_htb_get_prio(struct rtnl_class *class) { struct rtnl_htb_class *htb; - if (!(htb = rtnl_tc_data(TC_CAST(class)))) - BUG(); + if ((htb = htb_class_data(class)) && htb->ch_mask & SCH_HTB_HAS_PRIO) + return htb->ch_prio; + + return 0; +} + +int rtnl_htb_set_prio(struct rtnl_class *class, uint32_t prio) +{ + struct rtnl_htb_class *htb; + + if (!(htb = htb_class_data(class))) + return -NLE_OPNOTSUPP; htb->ch_prio = prio; htb->ch_mask |= SCH_HTB_HAS_PRIO; + + return 0; } /** - * Set rate of HTB class. - * @arg class HTB class to be modified. - * @arg rate New rate in bytes per second. + * Return rate of HTB class + * @arg class htb class object + * + * @return Rate in bytes/s or 0 if unspecified. */ -void rtnl_htb_set_rate(struct rtnl_class *class, uint32_t rate) -{ - struct rtnl_htb_class *htb; - - if (!(htb = rtnl_tc_data(TC_CAST(class)))) - BUG(); - - htb->ch_rate.rs_cell_log = UINT8_MAX; /* use default value */ - htb->ch_rate.rs_rate = rate; - htb->ch_mask |= SCH_HTB_HAS_RATE; -} - uint32_t rtnl_htb_get_rate(struct rtnl_class *class) { struct rtnl_htb_class *htb; - if (!(htb = rtnl_tc_data(TC_CAST(class)))) - return 0; + if ((htb = htb_class_data(class)) && htb->ch_mask & SCH_HTB_HAS_RATE) + return htb->ch_rate.rs_rate; - return htb->ch_rate.rs_rate; + return 0; } /** - * Set ceil of HTB class. - * @arg class HTB class to be modified. - * @arg ceil New ceil in bytes per second. + * Set rate of HTB class + * @arg class htb class object + * @arg rate new rate in bytes per second + * + * @return 0 on success or a negative error code. */ -void rtnl_htb_set_ceil(struct rtnl_class *class, uint32_t ceil) +int rtnl_htb_set_rate(struct rtnl_class *class, uint32_t rate) { struct rtnl_htb_class *htb; - if (!(htb = rtnl_tc_data(TC_CAST(class)))) - BUG(); + if (!(htb = htb_class_data(class))) + return -NLE_OPNOTSUPP; + + htb->ch_rate.rs_cell_log = UINT8_MAX; /* use default value */ + htb->ch_rate.rs_rate = rate; + htb->ch_mask |= SCH_HTB_HAS_RATE; + + return 0; +} + +/** + * Return ceil rate of HTB class + * @arg class htb class object + * + * @return Ceil rate in bytes/s or 0 if unspecified + */ +uint32_t rtnl_htb_get_ceil(struct rtnl_class *class) +{ + struct rtnl_htb_class *htb; + + if ((htb = htb_class_data(class)) && htb->ch_mask & SCH_HTB_HAS_CEIL) + return htb->ch_ceil.rs_rate; + + return 0; +} + +/** + * Set ceil rate of HTB class + * @arg class htb class object + * @arg ceil new ceil rate number of bytes per second + * + * @return 0 on success or a negative error code. + */ +int rtnl_htb_set_ceil(struct rtnl_class *class, uint32_t ceil) +{ + struct rtnl_htb_class *htb; + + if (!(htb = htb_class_data(class))) + return -NLE_OPNOTSUPP; htb->ch_ceil.rs_cell_log = UINT8_MAX; /* use default value */ htb->ch_ceil.rs_rate = ceil; htb->ch_mask |= SCH_HTB_HAS_CEIL; + + return 0; +} + +/** + * Return burst buffer size of HTB class + * @arg class htb class object + * + * @return Burst buffer size or 0 if unspecified + */ +uint32_t rtnl_htb_get_rbuffer(struct rtnl_class *class) +{ + struct rtnl_htb_class *htb; + + if ((htb = htb_class_data(class)) && + htb->ch_mask & SCH_HTB_HAS_RBUFFER) + return htb->ch_rbuffer; + + return 0; } /** @@ -352,15 +471,34 @@ void rtnl_htb_set_ceil(struct rtnl_class *class, uint32_t ceil) * @arg class HTB class to be modified. * @arg rbuffer New size in bytes. */ -void rtnl_htb_set_rbuffer(struct rtnl_class *class, uint32_t rbuffer) +int rtnl_htb_set_rbuffer(struct rtnl_class *class, uint32_t rbuffer) { struct rtnl_htb_class *htb; - if (!(htb = rtnl_tc_data(TC_CAST(class)))) - BUG(); + if (!(htb = htb_class_data(class))) + return -NLE_OPNOTSUPP; htb->ch_rbuffer = rbuffer; htb->ch_mask |= SCH_HTB_HAS_RBUFFER; + + return 0; +} + +/** + * Return ceil burst buffer size of HTB class + * @arg class htb class object + * + * @return Ceil burst buffer size or 0 if unspecified + */ +uint32_t rtnl_htb_get_cbuffer(struct rtnl_class *class) +{ + struct rtnl_htb_class *htb; + + if ((htb = htb_class_data(class)) && + htb->ch_mask & SCH_HTB_HAS_CBUFFER) + return htb->ch_cbuffer; + + return 0; } /** @@ -368,31 +506,103 @@ void rtnl_htb_set_rbuffer(struct rtnl_class *class, uint32_t rbuffer) * @arg class HTB class to be modified. * @arg cbuffer New size in bytes. */ -void rtnl_htb_set_cbuffer(struct rtnl_class *class, uint32_t cbuffer) +int rtnl_htb_set_cbuffer(struct rtnl_class *class, uint32_t cbuffer) { struct rtnl_htb_class *htb; - if (!(htb = rtnl_tc_data(TC_CAST(class)))) - BUG(); + if (!(htb = htb_class_data(class))) + return -NLE_OPNOTSUPP; htb->ch_cbuffer = cbuffer; htb->ch_mask |= SCH_HTB_HAS_CBUFFER; + + return 0; } /** - * Set how much bytes to serve from leaf at once of HTB class {use r2q}. - * @arg class HTB class to be modified. - * @arg quantum New size in bytes. + * Return quantum of HTB class + * @arg class htb class object + * + * See XXX[quantum def] + * + * @return Quantum or 0 if unspecified. */ -void rtnl_htb_set_quantum(struct rtnl_class *class, uint32_t quantum) +uint32_t rtnl_htb_get_quantum(struct rtnl_class *class) { struct rtnl_htb_class *htb; - if (!(htb = rtnl_tc_data(TC_CAST(class)))) - BUG(); + if ((htb = htb_class_data(class)) && + htb->ch_mask & SCH_HTB_HAS_QUANTUM) + return htb->ch_quantum; + + return 0; +} + +/** + * Set quantum of HTB class (overwrites value calculated based on r2q) + * @arg class htb class object + * @arg quantum new quantum in number of bytes + * + * See XXX[quantum def] + * + * @return 0 on success or a negative error code. + */ +int rtnl_htb_set_quantum(struct rtnl_class *class, uint32_t quantum) +{ + struct rtnl_htb_class *htb; + + if (!(htb = htb_class_data(class))) + return -NLE_OPNOTSUPP; htb->ch_quantum = quantum; htb->ch_mask |= SCH_HTB_HAS_QUANTUM; + + return 0; +} + +/** + * Return level of HTB class + * @arg class htb class object + * + * Returns the level of the HTB class. Leaf classes are assigned level + * 0, root classes have level (TC_HTB_MAXDEPTH - 1). Interior classes + * have a level of one less than their parent. + * + * @return Level or -NLE_OPNOTSUPP + */ +int rtnl_htb_get_level(struct rtnl_class *class) +{ + struct rtnl_htb_class *htb; + + if ((htb = htb_class_data(class)) && htb->ch_mask & SCH_HTB_HAS_LEVEL) + return htb->ch_level; + + return -NLE_OPNOTSUPP; +} + +/** + * Set level of HTB class + * @arg class htb class object + * @arg level new level of HTB class + * + * Sets the level of a HTB class. Note that changing the level of a HTB + * class does not change the level of its in kernel counterpart. This + * function is provided only to create HTB objects which can be compared + * against or filtered upon. + * + * @return 0 on success or a negative error code. + */ +int rtnl_htb_set_level(struct rtnl_class *class, int level) +{ + struct rtnl_htb_class *htb; + + if (!(htb = htb_class_data(class))) + return -NLE_OPNOTSUPP; + + htb->ch_level = level; + htb->ch_mask |= SCH_HTB_HAS_LEVEL; + + return 0; } /** @} */ diff --git a/lib/route/qdisc/netem.c b/lib/route/qdisc/netem.c index c86af56..06d9fe8 100644 --- a/lib/route/qdisc/netem.c +++ b/lib/route/qdisc/netem.c @@ -18,11 +18,11 @@ * @{ */ -#include -#include +#include +#include #include #include -#include +#include #include #include @@ -145,7 +145,8 @@ static void netem_dump_line(struct rtnl_tc *tc, void *data, nl_dump(p, "limit %d", netem->qnm_limit); } -int netem_msg_fill_raw(struct rtnl_tc *tc, void *data, struct nl_msg *msg) +static int netem_msg_fill_raw(struct rtnl_tc *tc, void *data, + struct nl_msg *msg) { int err = 0; struct tc_netem_qopt opts; @@ -825,8 +826,9 @@ int rtnl_netem_set_delay_distribution(struct rtnl_qdisc *qdisc, const char *dist if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) BUG(); - FILE *f = NULL; - int i, n = 0; + FILE *f; + int n = 0; + size_t i; size_t len = 2048; char *line; char name[NAME_MAX]; @@ -840,9 +842,10 @@ int rtnl_netem_set_delay_distribution(struct rtnl_qdisc *qdisc, const char *dist /* Check several locations for the dist file */ char *test_path[] = { "", "./", "/usr/lib/tc/", "/usr/local/lib/tc/" }; - for (i = 0; i < sizeof(test_path) && f == NULL; i++) { + for (i = 0; i < ARRAY_SIZE(test_path); i++) { snprintf(name, NAME_MAX, "%s%s%s", test_path[i], dist_type, dist_suffix); - f = fopen(name, "r"); + if ((f = fopen(name, "r"))) + break; } if ( f == NULL ) diff --git a/lib/route/qdisc/plug.c b/lib/route/qdisc/plug.c new file mode 100644 index 0000000..9f53637 --- /dev/null +++ b/lib/route/qdisc/plug.c @@ -0,0 +1,177 @@ +/* + * lib/route/qdisc/plug.c PLUG Qdisc + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2012 Shriram Rajagopalan + */ + +/** + * @ingroup qdisc + * @defgroup qdisc_plug Plug/Unplug Traffic (PLUG) + * @brief + * + * Queue traffic until an explicit release command. + * + * There are two ways to use this qdisc: + * 1. A simple "instantaneous" plug/unplug operation, by issuing an alternating + * sequence of TCQ_PLUG_BUFFER & TCQ_PLUG_RELEASE_INDEFINITE commands. + * + * 2. For network output buffering (a.k.a output commit) functionality. + * Output commit property is commonly used by applications using checkpoint + * based fault-tolerance to ensure that the checkpoint from which a system + * is being restored is consistent w.r.t outside world. + * + * Consider for e.g. Remus - a Virtual Machine checkpointing system, + * wherein a VM is checkpointed, say every 50ms. The checkpoint is replicated + * asynchronously to the backup host, while the VM continues executing the + * next epoch speculatively. + * + * The following is a typical sequence of output buffer operations: + * 1.At epoch i, start_buffer(i) + * 2. At end of epoch i (i.e. after 50ms): + * 2.1 Stop VM and take checkpoint(i). + * 2.2 start_buffer(i+1) and Resume VM + * 3. While speculatively executing epoch(i+1), asynchronously replicate + * checkpoint(i) to backup host. + * 4. When checkpoint_ack(i) is received from backup, release_buffer(i) + * Thus, this Qdisc would receive the following sequence of commands: + * TCQ_PLUG_BUFFER (epoch i) + * .. TCQ_PLUG_BUFFER (epoch i+1) + * ....TCQ_PLUG_RELEASE_ONE (epoch i) + * ......TCQ_PLUG_BUFFER (epoch i+2) + * ........ + * + * + * State of the queue, when used for network output buffering: + * + * plug(i+1) plug(i) head + * ------------------+--------------------+----------------> + * | | + * | | + * pkts_current_epoch| pkts_last_epoch |pkts_to_release + * ----------------->|<--------+--------->|+---------------> + * v v + * + * + * @{ + */ + +#include +#include +#include +#include +#include +#include + +static int plug_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg) +{ + struct rtnl_plug *plug = data; + struct tc_plug_qopt opts; + + if (!plug) + return -NLE_INVAL; + + opts.action = plug->action; + opts.limit = plug->limit; + + return nlmsg_append(msg, &opts, sizeof(opts), NL_DONTPAD); +} + +/** + * @name Attribute Modification + * @{ + */ + +/** + * Insert a plug into the qdisc and buffer any incoming + * network traffic. + * @arg qdisc PLUG qdisc to be modified. + */ +int rtnl_qdisc_plug_buffer(struct rtnl_qdisc *qdisc) +{ + struct rtnl_plug *plug; + + if (!(plug = rtnl_tc_data(TC_CAST(qdisc)))) + return -NLE_NOMEM; + + plug->action = TCQ_PLUG_BUFFER; + return 0; +} + +/** + * Unplug the qdisc, releasing packets from queue head + * to the last complete buffer, while new traffic + * continues to be buffered. + * @arg qdisc PLUG qdisc to be modified. + */ +int rtnl_qdisc_plug_release_one(struct rtnl_qdisc *qdisc) +{ + struct rtnl_plug *plug; + + if (!(plug = rtnl_tc_data(TC_CAST(qdisc)))) + return -NLE_NOMEM; + + plug->action = TCQ_PLUG_RELEASE_ONE; + return 0; +} + +/** + * Indefinitely unplug the qdisc, releasing all packets. + * Network traffic will not be buffered until the next + * buffer command is issued. + * @arg qdisc PLUG qdisc to be modified. + */ +int rtnl_qdisc_plug_release_indefinite(struct rtnl_qdisc *qdisc) +{ + struct rtnl_plug *plug; + + if (!(plug = rtnl_tc_data(TC_CAST(qdisc)))) + return -NLE_NOMEM; + + plug->action = TCQ_PLUG_RELEASE_INDEFINITE; + return 0; +} + +/** + * Set limit of PLUG qdisc. + * @arg qdisc PLUG qdisc to be modified. + * @arg limit New limit. + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_plug_set_limit(struct rtnl_qdisc *qdisc, int limit) +{ + struct rtnl_plug *plug; + + if (!(plug = rtnl_tc_data(TC_CAST(qdisc)))) + return -NLE_NOMEM; + + plug->action = TCQ_PLUG_LIMIT; + plug->limit = limit; + + return 0; +} + +/** @} */ + +static struct rtnl_tc_ops plug_ops = { + .to_kind = "plug", + .to_type = RTNL_TC_TYPE_QDISC, + .to_size = sizeof(struct rtnl_plug), + .to_msg_fill = plug_msg_fill, +}; + +static void __init plug_init(void) +{ + rtnl_tc_register(&plug_ops); +} + +static void __exit plug_exit(void) +{ + rtnl_tc_unregister(&plug_ops); +} + +/** @} */ diff --git a/lib/route/qdisc/prio.c b/lib/route/qdisc/prio.c index 2433c61..54a46f0 100644 --- a/lib/route/qdisc/prio.c +++ b/lib/route/qdisc/prio.c @@ -26,11 +26,11 @@ * @{ */ -#include -#include +#include +#include #include #include -#include +#include #include #include diff --git a/lib/route/qdisc/red.c b/lib/route/qdisc/red.c index 0480282..f05626e 100644 --- a/lib/route/qdisc/red.c +++ b/lib/route/qdisc/red.c @@ -16,11 +16,11 @@ * @{ */ -#include -#include +#include +#include #include #include -#include +#include #include #include diff --git a/lib/route/qdisc/sfq.c b/lib/route/qdisc/sfq.c index 207140f..acbb4ef 100644 --- a/lib/route/qdisc/sfq.c +++ b/lib/route/qdisc/sfq.c @@ -23,11 +23,11 @@ * @{ */ -#include -#include +#include +#include #include #include -#include +#include #include #include diff --git a/lib/route/qdisc/tbf.c b/lib/route/qdisc/tbf.c index 8a6c400..eb574d9 100644 --- a/lib/route/qdisc/tbf.c +++ b/lib/route/qdisc/tbf.c @@ -15,12 +15,12 @@ * @{ */ -#include -#include +#include +#include #include #include #include -#include +#include #include #include #include @@ -135,7 +135,7 @@ static int tbf_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg) struct rtnl_tbf *tbf = data; int required = TBF_ATTR_RATE | TBF_ATTR_LIMIT; - if (!(tbf->qt_mask & required) != required) + if ((tbf->qt_mask & required) != required) return -NLE_MISSING_ATTR; memset(&opts, 0, sizeof(opts)); @@ -290,7 +290,7 @@ void rtnl_qdisc_tbf_set_rate(struct rtnl_qdisc *qdisc, int rate, int bucket, tbf->qt_rate.rs_rate = rate; tbf->qt_rate_bucket = bucket; tbf->qt_rate.rs_cell_log = cell_log; - tbf->qt_rate_txtime = rtnl_tc_calc_txtime(bucket, rate); + tbf->qt_rate_txtime = nl_us2ticks(rtnl_tc_calc_txtime(bucket, rate)); tbf->qt_mask |= TBF_ATTR_RATE; } @@ -372,7 +372,7 @@ int rtnl_qdisc_tbf_set_peakrate(struct rtnl_qdisc *qdisc, int rate, int bucket, tbf->qt_peakrate.rs_rate = rate; tbf->qt_peakrate_bucket = bucket; tbf->qt_peakrate.rs_cell_log = cell_log; - tbf->qt_peakrate_txtime = rtnl_tc_calc_txtime(bucket, rate); + tbf->qt_peakrate_txtime = nl_us2ticks(rtnl_tc_calc_txtime(bucket, rate)); tbf->qt_mask |= TBF_ATTR_PEAKRATE; diff --git a/lib/route/route.c b/lib/route/route.c index c85c225..2985187 100644 --- a/lib/route/route.c +++ b/lib/route/route.c @@ -16,7 +16,7 @@ * @{ */ -#include +#include #include #include #include @@ -64,13 +64,18 @@ static int route_request_update(struct nl_cache *c, struct nl_sock *h) * @arg sk Netlink socket. * @arg family Address family of routes to cover or AF_UNSPEC * @arg flags Flags + * @arg result Result pointer * * Allocates a new cache, initializes it properly and updates it to * contain all routes currently configured in the kernel. * + * Valid flags: + * * ROUTE_CACHE_CONTENT - Cache will contain contents of routing cache + * instead of actual routes. + * * @note The caller is responsible for destroying and freeing the * cache after using it. - * @return The cache or NULL if an error has occured. + * @return 0 on success or a negative error code. */ int rtnl_route_alloc_cache(struct nl_sock *sk, int family, int flags, struct nl_cache **result) diff --git a/lib/route/route_obj.c b/lib/route/route_obj.c index 2cbe2ae..795047f 100644 --- a/lib/route/route_obj.c +++ b/lib/route/route_obj.c @@ -30,11 +30,12 @@ * @{ */ -#include +#include #include #include #include #include +#include #include #include #include @@ -70,6 +71,7 @@ static void route_constructor(struct nl_object *c) r->rt_table = RT_TABLE_MAIN; r->rt_protocol = RTPROT_STATIC; r->rt_type = RTN_UNICAST; + r->rt_prio = 0; nl_init_list_head(&r->rt_nexthops); } @@ -110,6 +112,9 @@ static int route_clone(struct nl_object *_dst, struct nl_object *_src) if (!(dst->rt_pref_src = nl_addr_clone(src->rt_pref_src))) return -NLE_NOMEM; + /* Will be inc'ed again while adding the nexthops of the source */ + dst->rt_nr_nh = 0; + nl_init_list_head(&dst->rt_nexthops); nl_list_for_each_entry(nh, &src->rt_nexthops, rtnh_list) { new = rtnl_route_nh_clone(nh); @@ -125,12 +130,9 @@ static int route_clone(struct nl_object *_dst, struct nl_object *_src) static void route_dump_line(struct nl_object *a, struct nl_dump_params *p) { struct rtnl_route *r = (struct rtnl_route *) a; - struct nl_cache *link_cache; int cache = 0, flags; char buf[64]; - link_cache = nl_cache_mngt_require("route/link"); - if (r->rt_flags & RTM_F_CLONED) cache = 1; @@ -206,10 +208,10 @@ static void route_dump_details(struct nl_object *a, struct nl_dump_params *p) { struct rtnl_route *r = (struct rtnl_route *) a; struct nl_cache *link_cache; - char buf[128]; + char buf[256]; int i; - link_cache = nl_cache_mngt_require("route/link"); + link_cache = nl_cache_mngt_require_safe("route/link"); route_dump_line(a, p); nl_dump_line(p, " "); @@ -257,7 +259,7 @@ static void route_dump_details(struct nl_object *a, struct nl_dump_params *p) if ((r->ce_mask & ROUTE_ATTR_CACHEINFO) && r->rt_cacheinfo.rtci_error) { nl_dump_line(p, " cacheinfo error %d (%s)\n", r->rt_cacheinfo.rtci_error, - strerror(-r->rt_cacheinfo.rtci_error)); + strerror_r(-r->rt_cacheinfo.rtci_error, buf, sizeof(buf))); } if (r->ce_mask & ROUTE_ATTR_METRICS) { @@ -270,6 +272,9 @@ static void route_dump_details(struct nl_object *a, struct nl_dump_params *p) r->rt_metrics[i]); nl_dump(p, "]\n"); } + + if (link_cache) + nl_cache_put(link_cache); } static void route_dump_stats(struct nl_object *obj, struct nl_dump_params *p) @@ -289,6 +294,53 @@ static void route_dump_stats(struct nl_object *obj, struct nl_dump_params *p) } } +static void route_keygen(struct nl_object *obj, uint32_t *hashkey, + uint32_t table_sz) +{ + struct rtnl_route *route = (struct rtnl_route *) obj; + unsigned int rkey_sz; + struct nl_addr *addr = NULL; + struct route_hash_key { + uint8_t rt_family; + uint8_t rt_tos; + uint32_t rt_table; + uint32_t rt_prio; + char rt_addr[0]; + } __attribute__((packed)) *rkey; + char buf[INET6_ADDRSTRLEN+5]; + + if (route->rt_dst) + addr = route->rt_dst; + + rkey_sz = sizeof(*rkey); + if (addr) + rkey_sz += nl_addr_get_len(addr); + rkey = calloc(1, rkey_sz); + if (!rkey) { + NL_DBG(2, "Warning: calloc failed for %d bytes...\n", rkey_sz); + *hashkey = 0; + return; + } + rkey->rt_family = route->rt_family; + rkey->rt_tos = route->rt_tos; + rkey->rt_table = route->rt_table; + rkey->rt_prio = route->rt_prio; + if (addr) + memcpy(rkey->rt_addr, nl_addr_get_binary_addr(addr), + nl_addr_get_len(addr)); + + *hashkey = nl_hash(rkey, rkey_sz, 0) % table_sz; + + NL_DBG(5, "route %p key (fam %d tos %d table %d addr %s) keysz %d " + "hash 0x%x\n", route, rkey->rt_family, rkey->rt_tos, + rkey->rt_table, nl_addr2str(addr, buf, sizeof(buf)), + rkey_sz, *hashkey); + + free(rkey); + + return; +} + static int route_compare(struct nl_object *_a, struct nl_object *_b, uint32_t attrs, int flags) { @@ -332,13 +384,13 @@ static int route_compare(struct nl_object *_a, struct nl_object *_b, if (a->rt_metrics_mask & (1 << i) && (!(b->rt_metrics_mask & (1 << i)) || a->rt_metrics[i] != b->rt_metrics[i])) - ROUTE_DIFF(METRICS, 1); + diff |= ROUTE_DIFF(METRICS, 1); } diff |= ROUTE_DIFF(FLAGS, (a->rt_flags ^ b->rt_flags) & b->rt_flag_mask); } else { - if (a->rt_nr_nh != a->rt_nr_nh) + if (a->rt_nr_nh != b->rt_nr_nh) goto nh_mismatch; /* search for a dup in each nh of a */ @@ -346,9 +398,10 @@ static int route_compare(struct nl_object *_a, struct nl_object *_b, found = 0; nl_list_for_each_entry(nh_b, &b->rt_nexthops, rtnh_list) { - if (!rtnl_route_nh_compare(nh_a, nh_b, ~0, 0)) + if (!rtnl_route_nh_compare(nh_a, nh_b, ~0, 0)) { found = 1; break; + } } if (!found) goto nh_mismatch; @@ -360,9 +413,10 @@ static int route_compare(struct nl_object *_a, struct nl_object *_b, found = 0; nl_list_for_each_entry(nh_a, &a->rt_nexthops, rtnh_list) { - if (!rtnl_route_nh_compare(nh_a, nh_b, ~0, 0)) + if (!rtnl_route_nh_compare(nh_a, nh_b, ~0, 0)) { found = 1; break; + } } if (!found) goto nh_mismatch; @@ -390,6 +444,101 @@ nh_mismatch: #undef ROUTE_DIFF } +static int route_update(struct nl_object *old_obj, struct nl_object *new_obj) +{ + struct rtnl_route *new_route = (struct rtnl_route *) new_obj; + struct rtnl_route *old_route = (struct rtnl_route *) old_obj; + struct rtnl_nexthop *new_nh; + char buf[INET6_ADDRSTRLEN+5]; + int action = new_obj->ce_msgtype; + + /* + * ipv6 ECMP route notifications from the kernel come as + * separate notifications, one for every nexthop. This update + * function collapses such route msgs into a single + * route with multiple nexthops. The resulting object looks + * similar to a ipv4 ECMP route + */ + if (new_route->rt_family != AF_INET6 || + new_route->rt_table == RT_TABLE_LOCAL) + return -NLE_OPNOTSUPP; + + /* + * For routes that are already multipath, + * or dont have a nexthop dont do anything + */ + if (rtnl_route_get_nnexthops(new_route) != 1) + return -NLE_OPNOTSUPP; + + /* + * Get the only nexthop entry from the new route. For + * IPv6 we always get a route with a 0th NH + * filled or nothing at all + */ + new_nh = rtnl_route_nexthop_n(new_route, 0); + if (!new_nh || !rtnl_route_nh_get_gateway(new_nh)) + return -NLE_OPNOTSUPP; + + switch(action) { + case RTM_NEWROUTE : { + struct rtnl_nexthop *cloned_nh; + + /* + * Add the nexthop to old route + */ + cloned_nh = rtnl_route_nh_clone(new_nh); + if (!cloned_nh) + return -NLE_NOMEM; + rtnl_route_add_nexthop(old_route, cloned_nh); + + NL_DBG(2, "Route obj %p updated. Added " + "nexthop %p via %s\n", old_route, cloned_nh, + nl_addr2str(cloned_nh->rtnh_gateway, buf, + sizeof(buf))); + } + break; + case RTM_DELROUTE : { + struct rtnl_nexthop *old_nh; + + /* + * Only take care of nexthop deletes and not + * route deletes. So, if there is only one nexthop + * quite likely we did not update it. So dont do + * anything and return + */ + if (rtnl_route_get_nnexthops(old_route) <= 1) + return -NLE_OPNOTSUPP; + + /* + * Find the next hop in old route and delete it + */ + nl_list_for_each_entry(old_nh, &old_route->rt_nexthops, + rtnh_list) { + if (!rtnl_route_nh_compare(old_nh, new_nh, ~0, 0)) { + + rtnl_route_remove_nexthop(old_route, old_nh); + + NL_DBG(2, "Route obj %p updated. Removed " + "nexthop %p via %s\n", old_route, + old_nh, + nl_addr2str(old_nh->rtnh_gateway, buf, + sizeof(buf))); + + rtnl_route_nh_free(old_nh); + break; + } + } + } + break; + default: + NL_DBG(2, "Unknown action associated " + "to object %p during route update\n", new_obj); + return -NLE_OPNOTSUPP; + } + + return NLE_SUCCESS; +} + static const struct trans_tbl route_attrs[] = { __ADD(ROUTE_ATTR_FAMILY, family) __ADD(ROUTE_ATTR_TOS, tos) @@ -687,18 +836,26 @@ void rtnl_route_add_nexthop(struct rtnl_route *route, struct rtnl_nexthop *nh) void rtnl_route_remove_nexthop(struct rtnl_route *route, struct rtnl_nexthop *nh) { - route->rt_nr_nh--; - nl_list_del(&nh->rtnh_list); + if (route->ce_mask & ROUTE_ATTR_MULTIPATH) { + route->rt_nr_nh--; + nl_list_del(&nh->rtnh_list); + } } struct nl_list_head *rtnl_route_get_nexthops(struct rtnl_route *route) { - return &route->rt_nexthops; + if (route->ce_mask & ROUTE_ATTR_MULTIPATH) + return &route->rt_nexthops; + + return NULL; } int rtnl_route_get_nnexthops(struct rtnl_route *route) { - return route->rt_nr_nh; + if (route->ce_mask & ROUTE_ATTR_MULTIPATH) + return route->rt_nr_nh; + + return 0; } void rtnl_route_foreach_nexthop(struct rtnl_route *r, @@ -717,7 +874,7 @@ void rtnl_route_foreach_nexthop(struct rtnl_route *r, struct rtnl_nexthop *rtnl_route_nexthop_n(struct rtnl_route *r, int n) { struct rtnl_nexthop *nh; - int i; + uint32_t i; if (r->ce_mask & ROUTE_ATTR_MULTIPATH && r->rt_nr_nh > n) { i = 0; @@ -872,11 +1029,12 @@ int rtnl_route_parse(struct nlmsghdr *nlh, struct rtnl_route **result) route->rt_scope = rtm->rtm_scope; route->rt_protocol = rtm->rtm_protocol; route->rt_flags = rtm->rtm_flags; + route->rt_prio = 0; route->ce_mask |= ROUTE_ATTR_FAMILY | ROUTE_ATTR_TOS | ROUTE_ATTR_TABLE | ROUTE_ATTR_TYPE | ROUTE_ATTR_SCOPE | ROUTE_ATTR_PROTOCOL | - ROUTE_ATTR_FLAGS; + ROUTE_ATTR_FLAGS | ROUTE_ATTR_PRIO; if (tb[RTA_DST]) { if (!(dst = nl_addr_alloc_attr(tb[RTA_DST], family))) @@ -907,6 +1065,9 @@ int rtnl_route_parse(struct nlmsghdr *nlh, struct rtnl_route **result) nl_addr_put(src); } + if (tb[RTA_TABLE]) + rtnl_route_set_table(route, nla_get_u32(tb[RTA_TABLE])); + if (tb[RTA_IIF]) rtnl_route_set_iif(route, nla_get_u32(tb[RTA_IIF])); @@ -973,6 +1134,7 @@ int rtnl_route_parse(struct nlmsghdr *nlh, struct rtnl_route **result) } if (old_nh) { + rtnl_route_nh_set_flags(old_nh, rtm->rtm_flags & 0xff); if (route->rt_nr_nh == 0) { /* If no nexthops have been provided via RTA_MULTIPATH * we add it as regular nexthop to maintain backwards @@ -1032,10 +1194,15 @@ int rtnl_route_build_msg(struct nl_msg *msg, struct rtnl_route *route) if (route->rt_src) rtmsg.rtm_src_len = nl_addr_get_prefixlen(route->rt_src); - if (rtmsg.rtm_scope == RT_SCOPE_NOWHERE) rtmsg.rtm_scope = rtnl_route_guess_scope(route); + if (rtnl_route_get_nnexthops(route) == 1) { + struct rtnl_nexthop *nh; + nh = rtnl_route_nexthop_n(route, 0); + rtmsg.rtm_flags |= nh->rtnh_flags; + } + if (nlmsg_append(msg, &rtmsg, sizeof(rtmsg), NLMSG_ALIGNTO) < 0) goto nla_put_failure; @@ -1071,7 +1238,17 @@ int rtnl_route_build_msg(struct nl_msg *msg, struct rtnl_route *route) nla_nest_end(msg, metrics); } - if (rtnl_route_get_nnexthops(route) > 0) { + if (rtnl_route_get_nnexthops(route) == 1) { + struct rtnl_nexthop *nh; + + nh = rtnl_route_nexthop_n(route, 0); + if (nh->rtnh_gateway) + NLA_PUT_ADDR(msg, RTA_GATEWAY, nh->rtnh_gateway); + if (nh->rtnh_ifindex) + NLA_PUT_U32(msg, RTA_OIF, nh->rtnh_ifindex); + if (nh->rtnh_realms) + NLA_PUT_U32(msg, RTA_FLOW, nh->rtnh_realms); + } else if (rtnl_route_get_nnexthops(route) > 1) { struct nlattr *multipath; struct rtnl_nexthop *nh; @@ -1122,9 +1299,12 @@ struct nl_object_ops route_obj_ops = { [NL_DUMP_STATS] = route_dump_stats, }, .oo_compare = route_compare, + .oo_keygen = route_keygen, + .oo_update = route_update, .oo_attrs2str = route_attrs2str, .oo_id_attrs = (ROUTE_ATTR_FAMILY | ROUTE_ATTR_TOS | - ROUTE_ATTR_TABLE | ROUTE_ATTR_DST), + ROUTE_ATTR_TABLE | ROUTE_ATTR_DST | + ROUTE_ATTR_PRIO), }; /** @endcond */ diff --git a/lib/route/route_utils.c b/lib/route/route_utils.c index 8b73f2b..a5b3966 100644 --- a/lib/route/route_utils.c +++ b/lib/route/route_utils.c @@ -37,7 +37,7 @@ * @{ */ -#include +#include #include #include #include diff --git a/lib/route/rtnl.c b/lib/route/rtnl.c index 26d1f67..82397e9 100644 --- a/lib/route/rtnl.c +++ b/lib/route/rtnl.c @@ -6,15 +6,15 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2008 Thomas Graf + * Copyright (c) 2003-2012 Thomas Graf */ /** - * @defgroup rtnl Routing Family + * @defgroup rtnl Routing Library (libnl-route) * @{ */ -#include +#include #include #include #include @@ -89,7 +89,7 @@ static const struct trans_tbl scopes[] = { __ADD(254,host) __ADD(253,link) __ADD(200,site) - __ADD(0,universe) + __ADD(0,global) }; char *rtnl_scope2str(int scope, char *buf, size_t size) diff --git a/lib/route/rule.c b/lib/route/rule.c index 36672d3..b2161a2 100644 --- a/lib/route/rule.c +++ b/lib/route/rule.c @@ -16,7 +16,7 @@ * @{ */ -#include +#include #include #include #include @@ -319,7 +319,7 @@ void rtnl_rule_put(struct rtnl_rule *rule) /** * Build a rule cache including all rules currently configured in the kernel. - * @arg sk Netlink socket. + * @arg sock Netlink socket. * @arg family Address family or AF_UNSPEC. * @arg result Pointer to store resulting cache. * @@ -374,18 +374,24 @@ static int build_rule_msg(struct rtnl_rule *tmpl, int cmd, int flags, if (!msg) return -NLE_NOMEM; + if (tmpl->ce_mask & RULE_ATTR_SRC) + frh.src_len = nl_addr_get_prefixlen(tmpl->r_src); + + if (tmpl->ce_mask & RULE_ATTR_DST) + frh.dst_len = nl_addr_get_prefixlen(tmpl->r_dst); + if (nlmsg_append(msg, &frh, sizeof(frh), NLMSG_ALIGNTO) < 0) goto nla_put_failure; - if (tmpl->ce_mask & RULE_ATTR_SRC) { - frh.src_len = nl_addr_get_prefixlen(tmpl->r_src); - NLA_PUT_ADDR(msg, FRA_SRC, tmpl->r_src); - } + /* Additional table attribute replacing the 8bit in the header, was + * required to allow more than 256 tables. */ + NLA_PUT_U32(msg, FRA_TABLE, tmpl->r_table); - if (tmpl->ce_mask & RULE_ATTR_DST) { - frh.dst_len = nl_addr_get_prefixlen(tmpl->r_dst); + if (tmpl->ce_mask & RULE_ATTR_SRC) + NLA_PUT_ADDR(msg, FRA_SRC, tmpl->r_src); + + if (tmpl->ce_mask & RULE_ATTR_DST) NLA_PUT_ADDR(msg, FRA_DST, tmpl->r_dst); - } if (tmpl->ce_mask & RULE_ATTR_IIFNAME) NLA_PUT_STRING(msg, FRA_IIFNAME, tmpl->r_iifname); @@ -421,6 +427,7 @@ nla_put_failure: * Build netlink request message to add a new rule * @arg tmpl template with data of new rule * @arg flags additional netlink message flags + * @arg result Result pointer * * Builds a new netlink message requesting a addition of a new * rule. The netlink message header isn't fully equipped with @@ -428,7 +435,7 @@ nla_put_failure: * or supplemented as needed. \a tmpl must contain the attributes of the new * address set via \c rtnl_rule_set_* functions. * - * @return The netlink message + * @return 0 on success or a negative error code. */ int rtnl_rule_build_add_request(struct rtnl_rule *tmpl, int flags, struct nl_msg **result) @@ -476,6 +483,7 @@ int rtnl_rule_add(struct nl_sock *sk, struct rtnl_rule *tmpl, int flags) * Build a netlink request message to delete a rule * @arg rule rule to delete * @arg flags additional netlink message flags + * @arg result Result pointer * * Builds a new netlink message requesting a deletion of a rule. * The netlink message header isn't fully equipped with all relevant @@ -483,7 +491,7 @@ int rtnl_rule_add(struct nl_sock *sk, struct rtnl_rule *tmpl, int flags) * or supplemented as needed. \a rule must point to an existing * address. * - * @return The netlink message + * @return 0 on success or a negative error code. */ int rtnl_rule_build_delete_request(struct rtnl_rule *rule, int flags, struct nl_msg **result) diff --git a/lib/route/tc.c b/lib/route/tc.c index e4faf92..29954fd 100644 --- a/lib/route/tc.c +++ b/lib/route/tc.c @@ -15,14 +15,14 @@ * @{ */ -#include -#include +#include +#include #include #include #include #include #include -#include +#include /** @cond SKIP */ @@ -60,6 +60,7 @@ static struct nla_policy tc_stats2_policy[TCA_STATS_MAX+1] = { int rtnl_tc_msg_parse(struct nlmsghdr *n, struct rtnl_tc *tc) { + struct nl_cache *link_cache; struct rtnl_tc_ops *ops; struct nlattr *tb[TCA_MAX + 1]; char kind[TCKINDSIZ]; @@ -175,6 +176,17 @@ compat_xstats: return err; } + if ((link_cache = __nl_cache_mngt_require("route/link"))) { + struct rtnl_link *link; + + if ((link = rtnl_link_get(link_cache, tc->tc_ifindex))) { + rtnl_tc_set_link(tc, link); + + /* rtnl_tc_set_link incs refcnt */ + rtnl_link_put(link); + } + } + return 0; } @@ -292,6 +304,32 @@ void rtnl_tc_set_link(struct rtnl_tc *tc, struct rtnl_link *link) tc->ce_mask |= TCA_ATTR_LINK | TCA_ATTR_IFINDEX; } +/** + * Get link of traffic control object + * @arg tc traffic control object + * + * Returns the link of a traffic control object. The link is only + * returned if it has been set before via rtnl_tc_set_link() or + * if a link cache was available while parsing the tc object. This + * function may still return NULL even if an ifindex is assigned to + * the tc object. It will _not_ look up the link by itself. + * + * @note The returned link will have its reference counter incremented. + * It is in the responsibility of the caller to return the + * reference. + * + * @return link object or NULL if not set. + */ +struct rtnl_link *rtnl_tc_get_link(struct rtnl_tc *tc) +{ + if (tc->tc_link) { + nl_object_get(OBJ_CAST(tc->tc_link)); + return tc->tc_link; + } + + return NULL; +} + /** * Set the Maximum Transmission Unit (MTU) of traffic control object * @arg tc traffic control object @@ -318,7 +356,7 @@ void rtnl_tc_set_mtu(struct rtnl_tc *tc, uint32_t mtu) * Returns the MTU of a traffic control object which has been set via: * -# User specified value set via rtnl_tc_set_mtu() * -# Dervied from link set via rtnl_tc_set_link() - * -# Fall back to default: ethernet = 1600 + * -# Fall back to default: ethernet = 1500 */ uint32_t rtnl_tc_get_mtu(struct rtnl_tc *tc) { @@ -327,7 +365,7 @@ uint32_t rtnl_tc_get_mtu(struct rtnl_tc *tc) else if (tc->ce_mask & TCA_ATTR_LINK) return tc->tc_link->l_mtu; else - return 1600; /* default to ethernet */ + return 1500; /* default to ethernet */ } /** @@ -672,7 +710,7 @@ int rtnl_tc_build_rate_table(struct rtnl_tc *tc, struct rtnl_ratespec *spec, for (i = 0; i < RTNL_TC_RTABLE_SIZE; i++) { size = adjust_size((i + 1) << cell_log, spec->rs_mpu, linktype); - dst[i] = rtnl_tc_calc_txtime(size, spec->rs_rate); + dst[i] = nl_us2ticks(rtnl_tc_calc_txtime(size, spec->rs_rate)); } spec->rs_cell_align = -1; @@ -712,10 +750,8 @@ int rtnl_tc_clone(struct nl_object *dstobj, struct nl_object *srcobj) struct rtnl_tc_ops *ops; if (src->tc_link) { - dst->tc_link = (struct rtnl_link *) - nl_object_clone(OBJ_CAST(src->tc_link)); - if (!dst->tc_link) - return -NLE_NOMEM; + nl_object_get(OBJ_CAST(src->tc_link)); + dst->tc_link = src->tc_link; } if (src->tc_opts) { @@ -786,7 +822,7 @@ void rtnl_tc_dump_line(struct nl_object *obj, struct nl_dump_params *p) nl_dump(p, "%s ", tc->tc_kind); - if ((link_cache = nl_cache_mngt_require("route/link"))) { + if ((link_cache = nl_cache_mngt_require_safe("route/link"))) { nl_dump(p, "dev %s ", rtnl_link_i2name(link_cache, tc->tc_ifindex, buf, sizeof(buf))); @@ -801,6 +837,9 @@ void rtnl_tc_dump_line(struct nl_object *obj, struct nl_dump_params *p) tc_dump(tc, NL_DUMP_LINE, p); nl_dump(p, "\n"); + + if (link_cache) + nl_cache_put(link_cache); } void rtnl_tc_dump_details(struct nl_object *obj, struct nl_dump_params *p) @@ -950,6 +989,15 @@ void rtnl_tc_unregister(struct rtnl_tc_ops *ops) nl_list_del(&ops->to_list); } +/** + * Return pointer to private data of traffic control object + * @arg tc traffic control object + * + * Allocates the private traffic control object data section + * as necessary and returns it. + * + * @return Pointer to private tc data or NULL if allocation failed. + */ void *rtnl_tc_data(struct rtnl_tc *tc) { if (!tc->tc_subdata) { @@ -973,6 +1021,36 @@ void *rtnl_tc_data(struct rtnl_tc *tc) return nl_data_get(tc->tc_subdata); } +/** + * Check traffic control object type and return private data section + * @arg tc traffic control object + * @arg ops expected traffic control object operations + * + * Checks whether the traffic control object matches the type + * specified with the traffic control object operations. If the + * type matches, the private tc object data is returned. If type + * mismatches, APPBUG() will print a application bug warning. + * + * @see rtnl_tc_data() + * + * @return Pointer to private tc data or NULL if type mismatches. + */ +void *rtnl_tc_data_check(struct rtnl_tc *tc, struct rtnl_tc_ops *ops) +{ + if (tc->tc_ops != ops) { + char buf[64]; + + snprintf(buf, sizeof(buf), + "tc object %p used in %s context but is of type %s", + tc, ops->to_kind, tc->tc_ops->to_kind); + APPBUG(buf); + + return NULL; + } + + return rtnl_tc_data(tc); +} + void rtnl_tc_type_register(struct rtnl_tc_type_ops *ops) { if (ops->tt_type > RTNL_TC_TYPE_MAX) diff --git a/lib/socket.c b/lib/socket.c index ebdf4e8..d3e636e 100644 --- a/lib/socket.c +++ b/lib/socket.c @@ -6,16 +6,30 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2008 Thomas Graf + * Copyright (c) 2003-2012 Thomas Graf */ /** - * @ingroup core + * @ingroup core_types * @defgroup socket Socket + * + * Representation of a netlink socket + * + * Related sections in the development guide: + * - @core_doc{core_sockets, Netlink Sockets} + * * @{ + * + * Header + * ------ + * ~~~~{.c} + * #include + * ~~~~ */ -#include +#include "defs.h" + +#include #include #include #include @@ -43,12 +57,15 @@ static void __init init_default_cb(void) } static uint32_t used_ports_map[32]; +static NL_RW_LOCK(port_map_lock); static uint32_t generate_local_port(void) { int i, n; uint32_t pid = getpid() & 0x3FFFFF; + nl_write_lock(&port_map_lock); + for (i = 0; i < 32; i++) { if (used_ports_map[i] == 0xFFFFFFFF) continue; @@ -62,11 +79,15 @@ static uint32_t generate_local_port(void) /* PID_MAX_LIMIT is currently at 2^22, leaving 10 bit * to, i.e. 1024 unique ports per application. */ - return pid + (n << 22); + nl_write_unlock(&port_map_lock); + + return pid + (n << 22); } } + nl_write_unlock(&port_map_lock); + /* Out of sockets in our own PID namespace, what to do? FIXME */ return UINT_MAX; } @@ -79,7 +100,10 @@ static void release_local_port(uint32_t port) return; nr = port >> 22; - used_ports_map[nr / 32] &= ~(1 << nr % 32); + + nl_write_lock(&port_map_lock); + used_ports_map[nr / 32] &= ~(1 << (nr % 32)); + nl_write_unlock(&port_map_lock); } /** @@ -256,7 +280,14 @@ void nl_socket_set_local_port(struct nl_sock *sk, uint32_t port) { if (port == 0) { port = generate_local_port(); - sk->s_flags &= ~NL_OWN_PORT; + /* + * Release local port after generation of a new one to be + * able to change local port using nl_socket_set_local_port(, 0) + */ + if (!(sk->s_flags & NL_OWN_PORT)) + release_local_port(sk->s_local.nl_pid); + else + sk->s_flags &= ~NL_OWN_PORT; } else { if (!(sk->s_flags & NL_OWN_PORT)) release_local_port(sk->s_local.nl_pid); @@ -300,13 +331,17 @@ int nl_socket_add_memberships(struct nl_sock *sk, int group, ...) va_start(ap, group); while (group != 0) { - if (group < 0) + if (group < 0) { + va_end(ap); return -NLE_INVAL; + } err = setsockopt(sk->s_fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &group, sizeof(group)); - if (err < 0) + if (err < 0) { + va_end(ap); return -nl_syserr2nlerr(errno); + } group = va_arg(ap, int); } @@ -344,13 +379,17 @@ int nl_socket_drop_memberships(struct nl_sock *sk, int group, ...) va_start(ap, group); while (group != 0) { - if (group < 0) + if (group < 0) { + va_end(ap); return -NLE_INVAL; + } err = setsockopt(sk->s_fd, SOL_NETLINK, NETLINK_DROP_MEMBERSHIP, &group, sizeof(group)); - if (err < 0) + if (err < 0) { + va_end(ap); return -nl_syserr2nlerr(errno); + } group = va_arg(ap, int); } @@ -417,6 +456,15 @@ void nl_socket_set_peer_groups(struct nl_sock *sk, uint32_t groups) * @{ */ +/** + * Return the file descriptor of the backing socket + * @arg sk Netlink socket + * + * Only valid after calling nl_connect() to create and bind the respective + * socket. + * + * @return File descriptor or -1 if not available. + */ int nl_socket_get_fd(const struct nl_sock *sk) { return sk->s_fd; @@ -476,12 +524,12 @@ void nl_socket_set_cb(struct nl_sock *sk, struct nl_cb *cb) } /** - * Modify the callback handler associated to the socket + * Modify the callback handler associated with the socket * @arg sk Netlink socket. * @arg type which type callback to set * @arg kind kind of callback * @arg func callback function - * @arg arg argument to be passwd to callback function + * @arg arg argument to be passed to callback function * * @see nl_cb_set */ @@ -492,6 +540,21 @@ int nl_socket_modify_cb(struct nl_sock *sk, enum nl_cb_type type, return nl_cb_set(sk->s_cb, type, kind, func, arg); } +/** + * Modify the error callback handler associated with the socket + * @arg sk Netlink socket. + * @arg kind kind of callback + * @arg func callback function + * @arg arg argument to be passed to callback function + * + * @see nl_cb_err + */ +int nl_socket_modify_err_cb(struct nl_sock *sk, enum nl_cb_kind kind, + nl_recvmsg_err_cb_t func, void *arg) +{ + return nl_cb_err(sk->s_cb, kind, func, arg); +} + /** @} */ /** @@ -540,6 +603,36 @@ int nl_socket_set_buffer_size(struct nl_sock *sk, int rxbuf, int txbuf) return 0; } +/** + * Set default message buffer size of netlink socket. + * @arg sk Netlink socket. + * @arg bufsize Default message buffer size in bytes. + * + * Sets the default message buffer size to the specified length in bytes. + * The default message buffer size limits the maximum message size the + * socket will be able to receive. It is generally recommneded to specify + * a buffer size no less than the size of a memory page. + * + * @return 0 on success or a negative error code. + */ +int nl_socket_set_msg_buf_size(struct nl_sock *sk, size_t bufsize) +{ + sk->s_bufsize = bufsize; + + return 0; +} + +/** + * Get default message buffer size of netlink socket. + * @arg sk Netlink socket. + * + * @return Size of default message buffer. + */ +size_t nl_socket_get_msg_buf_size(struct nl_sock *sk) +{ + return sk->s_bufsize; +} + /** * Enable/disable credential passing on netlink socket. * @arg sk Netlink socket. diff --git a/lib/utils.c b/lib/utils.c index 8756318..3bfa604 100644 --- a/lib/utils.c +++ b/lib/utils.c @@ -6,25 +6,49 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2008 Thomas Graf + * Copyright (c) 2003-2012 Thomas Graf */ /** * @ingroup core * @defgroup utils Utilities + * + * Collection of helper functions + * * @{ + * + * Header + * ------ + * ~~~~{.c} + * #include + * ~~~~ */ -#include +#include #include #include #include +#include /* exit() */ /** - * Debug level + * Global variable indicating the desired level of debugging output. + * + * Level | Messages Printed + * ----- | --------------------------------------------------------- + * 0 | Debugging output disabled + * 1 | Warnings, important events and notifications + * 2 | More or less important debugging messages + * 3 | Repetitive events causing a flood of debugging messages + * 4 | Even less important messages + * + * If available, the variable will be initialized to the value of the + * environment variable `NLDBG`. The default value is 0 (disabled). + * + * For more information, see section @core_doc{_debugging, Debugging}. */ int nl_debug = 0; +/** @cond SKIP */ struct nl_dump_params nl_debug_dp = { .dp_type = NL_DUMP_DETAILS, }; @@ -60,33 +84,42 @@ int __nl_read_num_str_file(const char *path, int (*cb)(long, const char *)) continue; num = strtol(buf, &end, 0); - if (end == buf) + if (end == buf) { + fclose(fd); return -NLE_INVAL; + } - if (num == LONG_MIN || num == LONG_MAX) + if (num == LONG_MIN || num == LONG_MAX) { + fclose(fd); return -NLE_RANGE; + } while (*end == ' ' || *end == '\t') end++; goodlen = strcspn(end, "#\r\n\t "); - if (goodlen == 0) + if (goodlen == 0) { + fclose(fd); return -NLE_INVAL; + } end[goodlen] = '\0'; err = cb(num, end); - if (err < 0) + if (err < 0) { + fclose(fd); return err; + } } fclose(fd); return 0; } +/** @endcond */ /** - * @name Unit Pretty-Printing + * @name Pretty Printing of Numbers * @{ */ @@ -97,6 +130,7 @@ int __nl_read_num_str_file(const char *path, int (*cb)(long, const char *)) * * Cancels down a byte counter until it reaches a reasonable * unit. The chosen unit is assigned to \a unit. + * This function assume 1024 bytes in one kilobyte * * @return The cancelled down byte counter in the new unit. */ @@ -125,30 +159,57 @@ double nl_cancel_down_bytes(unsigned long long l, char **unit) * @arg l bit counter * @arg unit destination unit pointer * - * Cancels downa bit counter until it reaches a reasonable + * Cancels down bit counter until it reaches a reasonable * unit. The chosen unit is assigned to \a unit. + * This function assume 1000 bits in one kilobit * * @return The cancelled down bit counter in the new unit. */ double nl_cancel_down_bits(unsigned long long l, char **unit) { - if (l >= 1099511627776ULL) { + if (l >= 1000000000000ULL) { *unit = "Tbit"; - return ((double) l) / 1099511627776ULL; - } else if (l >= 1073741824) { - *unit = "Gbit"; - return ((double) l) / 1073741824; - } else if (l >= 1048576) { - *unit = "Mbit"; - return ((double) l) / 1048576; - } else if (l >= 1024) { - *unit = "Kbit"; - return ((double) l) / 1024; - } else { - *unit = "bit"; - return (double) l; + return ((double) l) / 1000000000000ULL; } - + + if (l >= 1000000000) { + *unit = "Gbit"; + return ((double) l) / 1000000000; + } + + if (l >= 1000000) { + *unit = "Mbit"; + return ((double) l) / 1000000; + } + + if (l >= 1000) { + *unit = "Kbit"; + return ((double) l) / 1000; + } + + *unit = "bit"; + return (double) l; +} + +int nl_rate2str(unsigned long long rate, int type, char *buf, size_t len) +{ + char *unit; + double frac; + + switch (type) { + case NL_BYTE_RATE: + frac = nl_cancel_down_bytes(rate, &unit); + break; + + case NL_BIT_RATE: + frac = nl_cancel_down_bits(rate, &unit); + break; + + default: + BUG(); + } + + return snprintf(buf, len, "%.2f%s/s", frac, unit); } /** @@ -193,6 +254,9 @@ double nl_cancel_down_us(uint32_t l, char **unit) * - b,kb/k,m/mb,gb/g for bytes * - bit,kbit/mbit/gbit * + * This function assume 1000 bits in one kilobit and + * 1024 bytes in one kilobyte + * * @return The number of bytes or -1 if the string is unparseable */ long nl_size2int(const char *str) @@ -208,13 +272,13 @@ long nl_size2int(const char *str) else if (!strcasecmp(p, "gb") || !strcasecmp(p, "g")) l *= 1024*1024*1024; else if (!strcasecmp(p, "gbit")) - l *= 1024*1024*1024/8; + l *= 1000000000L/8; else if (!strcasecmp(p, "mb") || !strcasecmp(p, "m")) l *= 1024*1024; else if (!strcasecmp(p, "mbit")) - l *= 1024*1024/8; + l *= 1000000/8; else if (!strcasecmp(p, "kbit")) - l *= 1024/8; + l *= 1000/8; else if (!strcasecmp(p, "bit")) l /= 8; else if (strcasecmp(p, "b") != 0) @@ -260,7 +324,7 @@ static const struct { */ char *nl_size2str(const size_t size, char *buf, const size_t len) { - int i; + size_t i; for (i = 0; i < ARRAY_SIZE(size_units); i++) { if (size >= size_units[i].limit) { @@ -314,12 +378,13 @@ long nl_prob2int(const char *str) * @{ */ -#ifdef USER_HZ -static uint32_t user_hz = USER_HZ; -#else -static uint32_t user_hz = 100; +#ifndef USER_HZ +#define USER_HZ 100 #endif +static uint32_t user_hz = USER_HZ; +static uint32_t psched_hz = USER_HZ; + static double ticks_per_usec = 1.0f; /* Retrieves the configured HZ and ticks/us value in the kernel. @@ -349,6 +414,8 @@ static void __init get_psched_settings(void) if (!got_hz) user_hz = sysconf(_SC_CLK_TCK); + psched_hz = user_hz; + if (getenv("TICKS_PER_USEC")) { double t = strtod(getenv("TICKS_PER_USEC"), NULL); ticks_per_usec = t; @@ -363,14 +430,21 @@ static void __init get_psched_settings(void) strncpy(name, "/proc/net/psched", sizeof(name) - 1); if ((fd = fopen(name, "r"))) { - uint32_t ns_per_usec, ns_per_tick; - /* the file contains 4 hexadecimals, but we just use - the first two of them */ - fscanf(fd, "%08x %08x", &ns_per_usec, &ns_per_tick); + unsigned int ns_per_usec, ns_per_tick, nom, denom; + + if (fscanf(fd, "%08x %08x %08x %08x", + &ns_per_usec, &ns_per_tick, &nom, &denom) != 4) { + NL_DBG(1, "Fatal error: can not read psched settings from \"%s\". " \ + "Try to set TICKS_PER_USEC, PROC_NET_PSCHED or PROC_ROOT " \ + "environment variables\n", name); + exit(1); + } ticks_per_usec = (double) ns_per_usec / (double) ns_per_tick; + if (nom == 1000000) + psched_hz = denom; fclose(fd); } @@ -386,6 +460,13 @@ int nl_get_user_hz(void) return user_hz; } +/** + * Return the value of packet scheduler HZ + */ +int nl_get_psched_hz(void) +{ + return psched_hz; +} /** * Convert micro seconds to ticks @@ -458,10 +539,12 @@ int nl_str2msec(const char *str, uint64_t *result) */ char * nl_msec2str(uint64_t msec, char *buf, size_t len) { - int i, split[5]; - char *units[] = {"d", "h", "m", "s", "msec"}; + uint64_t split[5]; + size_t i; + static const char *units[5] = {"d", "h", "m", "s", "msec"}; + char * const buf_orig = buf; -#define _SPLIT(idx, unit) if ((split[idx] = msec / unit) > 0) msec %= unit +#define _SPLIT(idx, unit) if ((split[idx] = msec / unit)) msec %= unit _SPLIT(0, 86400000); /* days */ _SPLIT(1, 3600000); /* hours */ _SPLIT(2, 60000); /* minutes */ @@ -469,18 +552,17 @@ char * nl_msec2str(uint64_t msec, char *buf, size_t len) #undef _SPLIT split[4] = msec; - memset(buf, 0, len); - - for (i = 0; i < ARRAY_SIZE(split); i++) { - if (split[i] > 0) { - char t[64]; - snprintf(t, sizeof(t), "%s%d%s", - strlen(buf) ? " " : "", split[i], units[i]); - strncat(buf, t, len - strlen(buf) - 1); - } + for (i = 0; i < ARRAY_SIZE(split) && len; i++) { + int l; + if (split[i] == 0) + continue; + l = snprintf(buf, len, "%s%" PRIu64 "%s", + (buf==buf_orig) ? "" : " ", split[i], units[i]); + buf += l; + len -= l; } - return buf; + return buf_orig; } /** @} */ @@ -608,6 +690,9 @@ static const struct trans_tbl llprotos[] = { #ifdef ARPHRD_VOID __ADD(ARPHRD_VOID,void) #endif +#ifdef ARPHRD_NONE + __ADD(ARPHRD_NONE,nohdr) +#endif }; char * nl_llproto2str(int llproto, char *buf, size_t len) @@ -665,6 +750,7 @@ static const struct trans_tbl ether_protos[] = { __ADD(ETH_P_MPLS_UC,mpls_uc) __ADD(ETH_P_MPLS_MC,mpls_mc) __ADD(ETH_P_ATMMPOA,atmmpoa) + __ADD(ETH_P_LINK_CTL,link_ctl) __ADD(ETH_P_ATMFATE,atmfate) __ADD(ETH_P_PAE,pae) __ADD(ETH_P_AOE,aoe) @@ -775,7 +861,7 @@ void nl_new_line(struct nl_dump_params *params) else if (params->dp_buf) strncat(params->dp_buf, " ", params->dp_buflen - - sizeof(params->dp_buf) - 1); + strlen(params->dp_buf) - 1); } } @@ -790,13 +876,15 @@ static void dump_one(struct nl_dump_params *parms, const char *fmt, vfprintf(parms->dp_fd, fmt, args); else if (parms->dp_buf || parms->dp_cb) { char *buf = NULL; - vasprintf(&buf, fmt, args); - if (parms->dp_cb) - parms->dp_cb(parms, buf); - else - strncat(parms->dp_buf, buf, - parms->dp_buflen - strlen(parms->dp_buf) - 1); - free(buf); + if (vasprintf(&buf, fmt, args) >= 0) { + if (parms->dp_cb) + parms->dp_cb(parms, buf); + else + strncat(parms->dp_buf, buf, + parms->dp_buflen - + strlen(parms->dp_buf) - 1); + free(buf); + } } } @@ -859,12 +947,14 @@ void __trans_list_clear(struct nl_list_head *head) free(tl->a); free(tl); } + + nl_init_list_head(head); } char *__type2str(int type, char *buf, size_t len, const struct trans_tbl *tbl, size_t tbl_len) { - int i; + size_t i; for (i = 0; i < tbl_len; i++) { if (tbl[i].i == type) { snprintf(buf, len, "%s", tbl[i].a); @@ -895,7 +985,7 @@ char *__list_type2str(int type, char *buf, size_t len, char *__flags2str(int flags, char *buf, size_t len, const struct trans_tbl *tbl, size_t tbl_len) { - int i; + size_t i; int tmp = flags; memset(buf, 0, len); @@ -916,7 +1006,7 @@ int __str2type(const char *buf, const struct trans_tbl *tbl, size_t tbl_len) { unsigned long l; char *end; - int i; + size_t i; if (*buf == '\0') return -NLE_INVAL; @@ -955,7 +1045,9 @@ int __list_str2type(const char *buf, struct nl_list_head *head) int __str2flags(const char *buf, const struct trans_tbl *tbl, size_t tbl_len) { - int i, flags = 0, len; + int flags = 0; + size_t i; + size_t len; /* ptrdiff_t ? */ char *p = (char *) buf, *t; for (;;) { @@ -965,7 +1057,8 @@ int __str2flags(const char *buf, const struct trans_tbl *tbl, size_t tbl_len) t = strchr(p, ','); len = t ? t - p : strlen(p); for (i = 0; i < tbl_len; i++) - if (!strncasecmp(tbl[i].a, p, len)) + if (len == strlen(tbl[i].a) && + !strncasecmp(tbl[i].a, p, len)) flags |= tbl[i].i; if (!t) diff --git a/lib/version.c b/lib/version.c new file mode 100644 index 0000000..0dcafa0 --- /dev/null +++ b/lib/version.c @@ -0,0 +1,36 @@ +/* + * lib/version.c Run-time version information + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2012 Thomas Graf + */ + +/** + * @ingroup core + * @defgroup utils Utilities + * + * Run-time version information + * + * @{ + */ + + +/** + * @name Run-time version information + * @{ + */ + +#include + +const int nl_ver_num = LIBNL_VER_NUM; +const int nl_ver_maj = LIBNL_VER_MAJ; +const int nl_ver_min = LIBNL_VER_MIN; +const int nl_ver_mic = LIBNL_VER_MIC; + +/** @} */ + +/** @} */ diff --git a/libnl-3.0.pc.in b/libnl-3.0.pc.in index de7954e..b87e3dc 100644 --- a/libnl-3.0.pc.in +++ b/libnl-3.0.pc.in @@ -6,5 +6,5 @@ includedir=@includedir@ Name: libnl Description: Convenience library for netlink sockets Version: @PACKAGE_VERSION@ -Libs: -L${libdir} -lnl -lnl-route -lnl-nf -lnl-genl -lnl-cli -Cflags: -I${includedir} +Libs: -L${libdir} -lnl-@MAJ_VERSION@ +Cflags: -I${includedir}/libnl@MAJ_VERSION@ diff --git a/libnl-cli-3.0.pc.in b/libnl-cli-3.0.pc.in new file mode 100644 index 0000000..d3638ba --- /dev/null +++ b/libnl-cli-3.0.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libnl-cli +Description: Command Line Interface library for netlink sockets +Version: @PACKAGE_VERSION@ +Libs: -L${libdir} -lnl-cli-@MAJ_VERSION@ +Cflags: -I${includedir} +Requires: libnl-3.0 libnl-genl-3.0 libnl-nf-3.0 libnl-route-3.0 diff --git a/libnl-dect-3.0.pc.in b/libnl-dect-3.0.pc.in new file mode 100644 index 0000000..5bab3bf --- /dev/null +++ b/libnl-dect-3.0.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libnl-dect +Description: DECT Netlink Library +Version: @PACKAGE_VERSION@ +Requires: libnl-3.0 +Libs: -L${libdir} -lnl-dect-@MAJ_VERSION@ +Cflags: -I${includedir}/libnl@MAJ_VERSION@ diff --git a/libnl-genl-3.0.pc.in b/libnl-genl-3.0.pc.in new file mode 100644 index 0000000..d6b69b8 --- /dev/null +++ b/libnl-genl-3.0.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libnl-genl +Description: Generic Netlink Library +Version: @PACKAGE_VERSION@ +Requires: libnl-3.0 +Libs: -L${libdir} -lnl-genl-@MAJ_VERSION@ +Cflags: -I${includedir}/libnl@MAJ_VERSION@ diff --git a/libnl-nf-3.0.pc.in b/libnl-nf-3.0.pc.in new file mode 100644 index 0000000..d82e1a6 --- /dev/null +++ b/libnl-nf-3.0.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libnl-nf +Description: Netfilter Netlink Library +Version: @PACKAGE_VERSION@ +Requires: libnl-route-3.0 +Libs: -L${libdir} -lnl-nf-@MAJ_VERSION@ +Cflags: -I${includedir}/libnl@MAJ_VERSION@ diff --git a/libnl-route-3.0.pc.in b/libnl-route-3.0.pc.in new file mode 100644 index 0000000..372a4f4 --- /dev/null +++ b/libnl-route-3.0.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libnl-route +Description: Netlink Routing Family Library +Version: @PACKAGE_VERSION@ +Requires: libnl-3.0 +Libs: -L${libdir} -lnl-route-@MAJ_VERSION@ +Cflags: -I${includedir}/libnl@MAJ_VERSION@ diff --git a/m4/ax_pkg_swig.m4 b/m4/ax_pkg_swig.m4 new file mode 100644 index 0000000..e112f3d --- /dev/null +++ b/m4/ax_pkg_swig.m4 @@ -0,0 +1,135 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_pkg_swig.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_PKG_SWIG([major.minor.micro], [action-if-found], [action-if-not-found]) +# +# DESCRIPTION +# +# This macro searches for a SWIG installation on your system. If found, +# then SWIG is AC_SUBST'd; if not found, then $SWIG is empty. If SWIG is +# found, then SWIG_LIB is set to the SWIG library path, and AC_SUBST'd. +# +# You can use the optional first argument to check if the version of the +# available SWIG is greater than or equal to the value of the argument. It +# should have the format: N[.N[.N]] (N is a number between 0 and 999. Only +# the first N is mandatory.) If the version argument is given (e.g. +# 1.3.17), AX_PKG_SWIG checks that the swig package is this version number +# or higher. +# +# As usual, action-if-found is executed if SWIG is found, otherwise +# action-if-not-found is executed. +# +# In configure.in, use as: +# +# AX_PKG_SWIG(1.3.17, [], [ AC_MSG_ERROR([SWIG is required to build..]) ]) +# AX_SWIG_ENABLE_CXX +# AX_SWIG_MULTI_MODULE_SUPPORT +# AX_SWIG_PYTHON +# +# LICENSE +# +# Copyright (c) 2008 Sebastian Huber +# Copyright (c) 2008 Alan W. Irwin +# Copyright (c) 2008 Rafael Laboissiere +# Copyright (c) 2008 Andrew Collier +# Copyright (c) 2011 Murray Cumming +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 8 + +AC_DEFUN([AX_PKG_SWIG],[ + # Ubuntu has swig 2.0 as /usr/bin/swig2.0 + AC_PATH_PROGS([SWIG],[swig swig2.0]) + if test -z "$SWIG" ; then + m4_ifval([$3],[$3],[:]) + elif test -n "$1" ; then + AC_MSG_CHECKING([SWIG version]) + [swig_version=`$SWIG -version 2>&1 | grep 'SWIG Version' | sed 's/.*\([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\).*/\1/g'`] + AC_MSG_RESULT([$swig_version]) + if test -n "$swig_version" ; then + # Calculate the required version number components + [required=$1] + [required_major=`echo $required | sed 's/[^0-9].*//'`] + if test -z "$required_major" ; then + [required_major=0] + fi + [required=`echo $required | sed 's/[0-9]*[^0-9]//'`] + [required_minor=`echo $required | sed 's/[^0-9].*//'`] + if test -z "$required_minor" ; then + [required_minor=0] + fi + [required=`echo $required | sed 's/[0-9]*[^0-9]//'`] + [required_patch=`echo $required | sed 's/[^0-9].*//'`] + if test -z "$required_patch" ; then + [required_patch=0] + fi + # Calculate the available version number components + [available=$swig_version] + [available_major=`echo $available | sed 's/[^0-9].*//'`] + if test -z "$available_major" ; then + [available_major=0] + fi + [available=`echo $available | sed 's/[0-9]*[^0-9]//'`] + [available_minor=`echo $available | sed 's/[^0-9].*//'`] + if test -z "$available_minor" ; then + [available_minor=0] + fi + [available=`echo $available | sed 's/[0-9]*[^0-9]//'`] + [available_patch=`echo $available | sed 's/[^0-9].*//'`] + if test -z "$available_patch" ; then + [available_patch=0] + fi + # Convert the version tuple into a single number for easier comparison. + # Using base 100 should be safe since SWIG internally uses BCD values + # to encode its version number. + required_swig_vernum=`expr $required_major \* 10000 \ + \+ $required_minor \* 100 \+ $required_patch` + available_swig_vernum=`expr $available_major \* 10000 \ + \+ $available_minor \* 100 \+ $available_patch` + + if test $available_swig_vernum -lt $required_swig_vernum; then + AC_MSG_WARN([SWIG version >= $1 is required. You have $swig_version.]) + SWIG='' + m4_ifval([$3],[$3],[]) + else + AC_MSG_CHECKING([for SWIG library]) + SWIG_LIB=`$SWIG -swiglib` + AC_MSG_RESULT([$SWIG_LIB]) + m4_ifval([$2],[$2],[]) + fi + else + AC_MSG_WARN([cannot determine SWIG version]) + SWIG='' + m4_ifval([$3],[$3],[]) + fi + fi + AC_SUBST([SWIG_LIB]) +]) diff --git a/m4/ax_python.m4 b/m4/ax_python.m4 new file mode 100644 index 0000000..1bc9d8a --- /dev/null +++ b/m4/ax_python.m4 @@ -0,0 +1,97 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_python.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_PYTHON +# +# DESCRIPTION +# +# This macro does a complete Python development environment check. +# +# It recurses through several python versions (from 2.1 to 2.6 in this +# version), looking for an executable. When it finds an executable, it +# looks to find the header files and library. +# +# It sets PYTHON_BIN to the name of the python executable, +# PYTHON_INCLUDE_DIR to the directory holding the header files, and +# PYTHON_LIB to the name of the Python library. +# +# This macro calls AC_SUBST on PYTHON_BIN (via AC_CHECK_PROG), +# PYTHON_INCLUDE_DIR and PYTHON_LIB. +# +# LICENSE +# +# Copyright (c) 2008 Michael Tindal +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 9 + +AC_DEFUN([AX_PYTHON], +[AC_MSG_CHECKING(for python build information) +AC_MSG_RESULT([]) +for python in python2.6 python2.5 python2.4 python2.3 python2.2 python2.1 python; do +AC_CHECK_PROGS(PYTHON_BIN, [$python]) +ax_python_bin=$PYTHON_BIN +if test x$ax_python_bin != x; then + AC_CHECK_LIB($ax_python_bin, main, ax_python_lib=$ax_python_bin, ax_python_lib=no) + AC_CHECK_HEADER([$ax_python_bin/Python.h], + [[ax_python_header=`locate $ax_python_bin/Python.h | sed -e s,/Python.h,,`]], + ax_python_header=no) + if test $ax_python_lib != no; then + if test $ax_python_header != no; then + break; + fi + fi +fi +done +if test x$ax_python_bin = x; then + ax_python_bin=no +fi +if test x$ax_python_header = x; then + ax_python_header=no +fi +if test x$ax_python_lib = x; then + ax_python_lib=no +fi + +AC_MSG_RESULT([ results of the Python check:]) +AC_MSG_RESULT([ Binary: $ax_python_bin]) +AC_MSG_RESULT([ Library: $ax_python_lib]) +AC_MSG_RESULT([ Include Dir: $ax_python_header]) + +if test x$ax_python_header != xno; then + PYTHON_INCLUDE_DIR=$ax_python_header + AC_SUBST(PYTHON_INCLUDE_DIR) +fi +if test x$ax_python_lib != xno; then + PYTHON_LIB=$ax_python_lib + AC_SUBST(PYTHON_LIB) +fi +])dnl diff --git a/m4/ax_python_devel.m4 b/m4/ax_python_devel.m4 new file mode 100644 index 0000000..a62b860 --- /dev/null +++ b/m4/ax_python_devel.m4 @@ -0,0 +1,325 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_python_devel.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_PYTHON_DEVEL([version]) +# +# DESCRIPTION +# +# Note: Defines as a precious variable "PYTHON_VERSION". Don't override it +# in your configure.ac. +# +# This macro checks for Python and tries to get the include path to +# 'Python.h'. It provides the $(PYTHON_CPPFLAGS) and $(PYTHON_LDFLAGS) +# output variables. It also exports $(PYTHON_EXTRA_LIBS) and +# $(PYTHON_EXTRA_LDFLAGS) for embedding Python in your code. +# +# You can search for some particular version of Python by passing a +# parameter to this macro, for example ">= '2.3.1'", or "== '2.4'". Please +# note that you *have* to pass also an operator along with the version to +# match, and pay special attention to the single quotes surrounding the +# version number. Don't use "PYTHON_VERSION" for this: that environment +# variable is declared as precious and thus reserved for the end-user. +# +# This macro should work for all versions of Python >= 2.1.0. As an end +# user, you can disable the check for the python version by setting the +# PYTHON_NOVERSIONCHECK environment variable to something else than the +# empty string. +# +# If you need to use this macro for an older Python version, please +# contact the authors. We're always open for feedback. +# +# LICENSE +# +# Copyright (c) 2009 Sebastian Huber +# Copyright (c) 2009 Alan W. Irwin +# Copyright (c) 2009 Rafael Laboissiere +# Copyright (c) 2009 Andrew Collier +# Copyright (c) 2009 Matteo Settenvini +# Copyright (c) 2009 Horst Knorr +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 8 + +AU_ALIAS([AC_PYTHON_DEVEL], [AX_PYTHON_DEVEL]) +AC_DEFUN([AX_PYTHON_DEVEL],[ + # + # Allow the use of a (user set) custom python version + # + AC_ARG_VAR([PYTHON_VERSION],[The installed Python + version to use, for example '2.3'. This string + will be appended to the Python interpreter + canonical name.]) + + AC_PATH_PROG([PYTHON],[python[$PYTHON_VERSION]]) + if test -z "$PYTHON"; then + AC_MSG_ERROR([Cannot find python$PYTHON_VERSION in your system path]) + PYTHON_VERSION="" + fi + + # + # Check for a version of Python >= 2.1.0 + # + AC_MSG_CHECKING([for a version of Python >= '2.1.0']) + ac_supports_python_ver=`$PYTHON -c "import sys; \ + ver = sys.version.split ()[[0]]; \ + print (ver >= '2.1.0')"` + if test "$ac_supports_python_ver" != "True"; then + if test -z "$PYTHON_NOVERSIONCHECK"; then + AC_MSG_RESULT([no]) + AC_MSG_FAILURE([ +This version of the AC@&t@_PYTHON_DEVEL macro +doesn't work properly with versions of Python before +2.1.0. You may need to re-run configure, setting the +variables PYTHON_CPPFLAGS, PYTHON_LDFLAGS, PYTHON_SITE_PKG, +PYTHON_EXTRA_LIBS and PYTHON_EXTRA_LDFLAGS by hand. +Moreover, to disable this check, set PYTHON_NOVERSIONCHECK +to something else than an empty string. +]) + else + AC_MSG_RESULT([skip at user request]) + fi + else + AC_MSG_RESULT([yes]) + fi + + # + # if the macro parameter ``version'' is set, honour it + # + if test -n "$1"; then + AC_MSG_CHECKING([for a version of Python $1]) + ac_supports_python_ver=`$PYTHON -c "import sys; \ + ver = sys.version.split ()[[0]]; \ + print (ver $1)"` + if test "$ac_supports_python_ver" = "True"; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + AC_MSG_ERROR([this package requires Python $1. +If you have it installed, but it isn't the default Python +interpreter in your system path, please pass the PYTHON_VERSION +variable to configure. See ``configure --help'' for reference. +]) + PYTHON_VERSION="" + fi + fi + + # + # Check if you have distutils, else fail + # + AC_MSG_CHECKING([for the distutils Python package]) + ac_distutils_result=`$PYTHON -c "import distutils" 2>&1` + if test -z "$ac_distutils_result"; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + AC_MSG_ERROR([cannot import Python module "distutils". +Please check your Python installation. The error was: +$ac_distutils_result]) + PYTHON_VERSION="" + fi + + # + # Check for Python include path + # + AC_MSG_CHECKING([for Python include path]) + if test -z "$PYTHON_CPPFLAGS"; then + python_path=`$PYTHON -c "import distutils.sysconfig; \ + print (distutils.sysconfig.get_python_inc ());"` + if test -n "${python_path}"; then + python_path="-I$python_path" + fi + PYTHON_CPPFLAGS=$python_path + fi + AC_MSG_RESULT([$PYTHON_CPPFLAGS]) + AC_SUBST([PYTHON_CPPFLAGS]) + + # + # Check for Python library path + # + AC_MSG_CHECKING([for Python library path]) + if test -z "$PYTHON_LDFLAGS"; then + # (makes two attempts to ensure we've got a version number + # from the interpreter) + ac_python_version=`cat<]], + [[Py_Initialize();]]) + ],[pythonexists=yes],[pythonexists=no]) + AC_LANG_POP([C]) + # turn back to default flags + CPPFLAGS="$ac_save_CPPFLAGS" + LIBS="$ac_save_LIBS" + + AC_MSG_RESULT([$pythonexists]) + + if test ! "x$pythonexists" = "xyes"; then + AC_MSG_FAILURE([ + Could not link test program to Python. Maybe the main Python library has been + installed in some non-standard library path. If so, pass it to configure, + via the LDFLAGS environment variable. + Example: ./configure LDFLAGS="-L/usr/non-standard-path/python/lib" + ============================================================================ + ERROR! + You probably have to install the development version of the Python package + for your distribution. The exact name of this package varies among them. + ============================================================================ + ]) + PYTHON_VERSION="" + fi + + # + # all done! + # +]) diff --git a/m4/ax_swig_python.m4 b/m4/ax_swig_python.m4 new file mode 100644 index 0000000..8fd3df5 --- /dev/null +++ b/m4/ax_swig_python.m4 @@ -0,0 +1,64 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_swig_python.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_SWIG_PYTHON([use-shadow-classes = {no, yes}]) +# +# DESCRIPTION +# +# Checks for Python and provides the $(AX_SWIG_PYTHON_CPPFLAGS), and +# $(AX_SWIG_PYTHON_OPT) output variables. +# +# $(AX_SWIG_PYTHON_OPT) contains all necessary SWIG options to generate +# code for Python. Shadow classes are enabled unless the value of the +# optional first argument is exactly 'no'. If you need multi module +# support (provided by the AX_SWIG_MULTI_MODULE_SUPPORT macro) use +# $(AX_SWIG_PYTHON_LIBS) to link against the appropriate library. It +# contains the SWIG Python runtime library that is needed by the type +# check system for example. +# +# LICENSE +# +# Copyright (c) 2008 Sebastian Huber +# Copyright (c) 2008 Alan W. Irwin +# Copyright (c) 2008 Rafael Laboissiere +# Copyright (c) 2008 Andrew Collier +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 7 + +AU_ALIAS([SWIG_PYTHON], [AX_SWIG_PYTHON]) +AC_DEFUN([AX_SWIG_PYTHON],[ + AC_REQUIRE([AX_PKG_SWIG]) + AC_REQUIRE([AX_PYTHON_DEVEL]) + test "x$1" != "xno" || swig_shadow=" -noproxy" + AC_SUBST([AX_SWIG_PYTHON_OPT],[-python$swig_shadow]) + AC_SUBST([AX_SWIG_PYTHON_CPPFLAGS],[$PYTHON_CPPFLAGS]) +]) diff --git a/man/Makefile.am b/man/Makefile.am index afbef3f..af1277b 100644 --- a/man/Makefile.am +++ b/man/Makefile.am @@ -3,4 +3,5 @@ dist_man8_MANS = \ nl-classid-lookup.8 \ nl-pktloc-lookup.8 \ - nl-qdisc-add.8 nl-qdisc-delete.8 nl-qdisc-list.8 + nl-qdisc-add.8 nl-qdisc-delete.8 nl-qdisc-list.8 \ + genl-ctrl-list.8 diff --git a/man/genl-ctrl-list.8 b/man/genl-ctrl-list.8 new file mode 100644 index 0000000..6132475 --- /dev/null +++ b/man/genl-ctrl-list.8 @@ -0,0 +1,104 @@ +.TH genl\-ctrl-list 8 "20 April 2012" "libnl" +.SH NAME +genl\-ctrl\-list \- List available kernel-side Generic Netlink families +.SH SYNOPSIS +.B genl\-ctrl\-list [-d] + +.SH DESCRIPTION +.PP +Queries the Generic Netlink controller in kernel and prints a list of all +registered Generic Netlink families including the version of the interface +that has been registered. + +.SH OPTIONS +.TP +.BR \-\^h " or " \-\-help +Print help text to console and exit. +.TP +.BR \-\^v " or " \-\-version +Print versioning information to console and exit. +.TP +.BR \-\^d " or " \-\-details +Include additional detailed information for each Generic Netlink +family that is printed. + +The information includes: + +.RS +.TP +.B hdrsize N +The size of the user specific header. + +.TP +.B maxattr N +The maximum Netlink attribute identifier expected by the interface. + +.TP +.B op NAME (ID) +A list of available operations including their name, numeric identifier +and the flags indicating the capabilities of the opertion. + +Available flags: +.RS +.TP +.I admin-perm +Requires administrative privileges + +.TP +.I has-doit +Command can handle request + +.TP +.I has-dump +Command can handle a dump request + +.TP +.I has-policy +Command enforces attribute validation policy +.RE + +.TP +.B grp NAME (ID) +A list of registered multicast groups including name (if available) +and identifier. +.RE + +.RS +.B Example: +.RS +0x0010 nlctrl version 2 +.RS 0 + hdrsize 0 maxattr 7 +.RS 0 + op GETFAMILY (0x03) +.RS 0 + grp notify (0x10) +.RE + + +.SH EXAMPLE +.RS 0 +$ genl-ctrl-list +.RS 0 +0x0010 nlctrl version 2 +.RS 0 +0x0011 NLBL_MGMT version 3 +.RS 0 +0x0012 NLBL_CIPSOv4 version 3 +.RS 0 +0x0013 NLBL_UNLBL version 3 +.RS 0 +0x0014 acpi_event version 1 +.RS 0 +0x0015 thermal_event version 1 +.RS 0 +0x0016 VFS_DQUOT version 1 +.RS 0 +0x0017 TASKSTATS version 1 +.RS 0 +0x0018 NET_DM version 2 + +.SH AUTHOR +.PP +Thomas Graf is the original author and current maintainer of libnl and +libnl tools. Many people have contributed to it since. diff --git a/man/nl-classid-lookup.8 b/man/nl-classid-lookup.8 index d48e9eb..3cf13de 100644 --- a/man/nl-classid-lookup.8 +++ b/man/nl-classid-lookup.8 @@ -1,5 +1,4 @@ .TH nl\-classid\-lookup 8 "19 October 2010" "libnl" -.LO 1 .SH NAME nl\-classid\-lookup - Lookup classid definitions .SH SYNOPSIS diff --git a/man/nl-pktloc-lookup.8 b/man/nl-pktloc-lookup.8 index a583dcc..7a1daa4 100644 --- a/man/nl-pktloc-lookup.8 +++ b/man/nl-pktloc-lookup.8 @@ -1,5 +1,4 @@ .TH nl\-pktloc-lookup 8 "27 October 2010" "libnl" -.LO 1 .SH NAME nl\-pktloc\-lookup - Lookup packet location definitions .SH SYNOPSIS diff --git a/man/nl-qdisc-add.8 b/man/nl-qdisc-add.8 index 7031c39..bc3fb17 100644 --- a/man/nl-qdisc-add.8 +++ b/man/nl-qdisc-add.8 @@ -1,5 +1,4 @@ .TH nl\-qdisc 8 "21 October 2010" "libnl" -.LO 1 .SH NAME nl\-qdisc\-{add|list|delete} - Manage queueing disciplines .SH SYNOPSIS diff --git a/python/.gitignore b/python/.gitignore new file mode 100644 index 0000000..a83b942 --- /dev/null +++ b/python/.gitignore @@ -0,0 +1,4 @@ +build +capi_wrap.c +capi.py +setup.py diff --git a/python/Makefile.am b/python/Makefile.am new file mode 100644 index 0000000..bd5ac11 --- /dev/null +++ b/python/Makefile.am @@ -0,0 +1,3 @@ +# -*- Makefile -*- + +SUBDIRS = netlink diff --git a/python/README b/python/README new file mode 100644 index 0000000..4ccc337 --- /dev/null +++ b/python/README @@ -0,0 +1,12 @@ + +*************************************************************************** + +NOTE: The python wrapper is experimental and may or may not work. + +*************************************************************************** + +For the brave: + + (requires an installed libnl) + - $ python ./setup.py build + - $ sudo python ./setup.py install diff --git a/python/doc/conf.py b/python/doc/conf.py new file mode 100644 index 0000000..74041f0 --- /dev/null +++ b/python/doc/conf.py @@ -0,0 +1,216 @@ +# -*- coding: utf-8 -*- +# +# libnl-python documentation build configuration file, created by +# sphinx-quickstart on Mon May 9 10:58:58 2011. +# +# This file is execfile()d with the current directory set to its containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys, os + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +#sys.path.insert(0, os.path.abspath('.')) + +# -- General configuration ----------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +#needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be extensions +# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +extensions = ['sphinx.ext.todo', 'sphinx.ext.ifconfig', 'sphinx.ext.viewcode'] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'libnl-python' +copyright = u'2011, Thomas Graf ' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '1.0' +# The full version, including alpha/beta/rc tags. +release = '1.0' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +#language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = ['_build'] + +# The reST default role (used for this markup: `text`) to use for all documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + + +# -- Options for HTML output --------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'default' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +#html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_domain_indices = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Output file base name for HTML help builder. +htmlhelp_basename = 'libnl-pythondoc' + + +# -- Options for LaTeX output -------------------------------------------------- + +# The paper size ('letter' or 'a4'). +#latex_paper_size = 'letter' + +# The font size ('10pt', '11pt' or '12pt'). +#latex_font_size = '10pt' + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass [howto/manual]). +latex_documents = [ + ('index', 'libnl-python.tex', u'libnl-python Documentation', + u'Thomas Graf \\textless{}tgraf@suug.ch\\textgreater{}', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Additional stuff for the LaTeX preamble. +#latex_preamble = '' + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- Options for manual page output -------------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('index', 'libnl-python', u'libnl-python Documentation', + [u'Thomas Graf '], 1) +] diff --git a/python/doc/core.rst b/python/doc/core.rst new file mode 100644 index 0000000..012e14c --- /dev/null +++ b/python/doc/core.rst @@ -0,0 +1,215 @@ +******************* +Netlink Core Module +******************* + +.. py:module:: netlink.core + +Examples:: + + import netlink.core as netlink + +=============== +Object +=============== + +.. py:class:: Object + + Base class for all classes representing a cacheable object + + Example:: + obj = netlink.Object("route/link", "link") + + .. py:method:: clone + + Clone the object and return a duplicate (used for COW) + + .. py:method:: dump([params=None]) + + Call the libnl internal dump mechanism to dump the object + according to the parameters specified. + + .. py:method:: apply(attr, val) + + Applies a attribute=value pair and modifies the object accordingly. + Example:: + obj.apply("mtu", 1200) # Sets attribute mtu to 1200 (link obj) + + :raises: KeyError if attribute is unknown + :raises: ImmutableError if attribute is not mutable + + .. py:attribute:: mark + + True if the object is marked, otherwise False. + + .. py:attribute:: shared + + True if the object is used by multiple parties, otherwise False. + + .. py:attribute:: refcnt + + Number of users sharing a reference to the object + :rtype: int + + .. py:attribute:: attrs + + List of attributes + + :rtype: list of strings + +=============== +Cache +=============== + +.. py:class:: Cache + + Base class for all cache implementations. + + A cache is a collection of cacheable objects which is typically used + by netlink protocols which handle any kind of object, e.g. network + links, network addresses, neighbours, ... + + .. py:method:: subset(filter) + + Returns a new cache containing the subset which matches the + provided filter. + + :raises: ValueError if no filter is specified + :rtype: :py:class:`Cache` + + .. py:method:: dump([params=None, filter=None]) + + Calls the libnl internal dump mechanism to dump the cache according + to the parameters and filter specified. + + .. py:method:: clear() + + Remove and possibly destroy all objects in the cache + + .. py:method:: refill([socket=None]) -> :py:class:`Cache` + + Clears and refills the cache with the content which is provided by + the kernel, e.g. for a link cache this would mean refilling the + cache with all configured network links. + + .. py:method:: provide() + + Caches which have been "provided" are made available to other users + (of the same application context) which "require" it. F.e. a link + cache is generally provided to allow others to translate interface + indexes to link names + + + .. py:method:: unprovide() + + No longer make the cache available to others. If the cache has been + handed out already, that reference will still be valid. + +=============== +AbstractAddress +=============== + +.. py:class:: AbstractAddress + + Abstract representation of an address. This class is not to be mistaken + with :py:class:`route.Address` which represents a configured network + address. This class represents the actual address in a family independent + way:: + + addr = netlink.AbstractAddress('127.0.0.1/8') + print addr # => '127.0.0.1/8' + print addr.prefixlen # => '8' + print addr.family # => 'inet' + print len(addr) # => '4' (32bit ipv4 address) + + a = netlink.AbstractAddress('10.0.0.1/24') + b = netlink.AbstractAddress('10.0.0.2/24') + print a == b # => False + + .. py:attribute:: prefixlen + + Length of prefix in number of bits. + + :rtype: int + + .. py:attribute:: family + + The family type of the address. Setting the address family can be + done with a string or a :py:class:`AddressFamily` object. + + :rtype: :py:class:`AddressFamily` + + .. py:attribute:: shared + + True if address is in use by multiple callers, otherwise False + + :rtype: bool + +=============== +AddressFamily +=============== + +.. py:class:: AddressFamily + + Address family representation:: + + af = netlink.AddressFamily('inet6') + # raises: + # - ValueError if family name is not known + # - TypeError if invalid type is specified for family + + print af # => 'inet6' (string representation) + print int(af) # => 10 (numeric representation) + print repr(af) # => AddressFamily('inet6') + +=============== +Exceptions +=============== + +.. py:exception:: NetlinkError + + Generic exception raised by netlink modules. + +.. py:exception:: KernelError + + Raised if an error occured while communicating with the kernel. Contains + the error code returning which is automatically included in the error + message. + +.. py:exception:: ImmutableError + + Raised if an attribute is modified which is marked immutable. + +=============== +Socket +=============== + +.. py:class:: Socket + + Netlink socket. + + Note: It is not required to manually create and connect netlink sockets + when using caches. The caches will automatically lookup or create a + socket as needed. + + .. py:attribute:: local_port + + Local port (address) of netlink socket + + .. py:attribute:: peer_port + + Peer port (remote address) of netlink socket. If set, all messages + will be sent to that peer. + + .. py:method:: connect(proto) + + Connect the netlink socket using the specified netlink protocol:: + sock.connect(netlink.NETLINK_ROUTE) + + .. py:method:: disconnect() + + Disconnect the socket + + .. py:method:: set_bufsize(rx, tx) + + Sets the size of the socket buffer + diff --git a/python/doc/index.rst b/python/doc/index.rst new file mode 100644 index 0000000..8de8ae3 --- /dev/null +++ b/python/doc/index.rst @@ -0,0 +1,24 @@ +.. libnl-python documentation master file, created by + sphinx-quickstart on Mon May 9 10:58:58 2011. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to libnl-python's documentation! +======================================== + +Contents: + +.. toctree:: + :maxdepth: 2 + + core + route + route_addr + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` + diff --git a/python/doc/route.rst b/python/doc/route.rst new file mode 100644 index 0000000..0b8f3f9 --- /dev/null +++ b/python/doc/route.rst @@ -0,0 +1,3 @@ +********************** +Routing +********************** diff --git a/python/doc/route_addr.rst b/python/doc/route_addr.rst new file mode 100644 index 0000000..2cfe139 --- /dev/null +++ b/python/doc/route_addr.rst @@ -0,0 +1,47 @@ +================= +Network Addresses +================= + +The **Address** module provides access to the network address configuration +of the kernel. It provides an interface to fetch all configured addresses, +add new addresses and to delete existing addresses. + +Fetching the list of network addresses is achieved by creating a new +address cache:: + + import netlink.route.address as Address + + addr_cache = Address.AddressCache() + addr_cache.refill() + + for addr in addr_cache: + print addr + +.. py:module:: netlink.route.addr + + +AddressCache +------------ + +.. py:class:: AddressCache + + Represents a cache containing all or a subset of network addresses. + + .. py:method:: lookup(ifindex, local) + + Lookup the address which matches ifindex and local address + + :raises: KeyError if address is not found. + +Address +------- + +.. py:class:: Address + + Representation of a configured network address. + + .. py:attribute:: ifindex + + Interface index + + :rtype: int diff --git a/python/netlink/Makefile.am b/python/netlink/Makefile.am new file mode 100644 index 0000000..0f1045b --- /dev/null +++ b/python/netlink/Makefile.am @@ -0,0 +1,10 @@ +# -*- Makefile -*- + +SUBDIRS = route + +EXTRA_DIST = \ + capi.i \ + fixes.h \ + __init__.py \ + core.py \ + util.py diff --git a/python/netlink/__init__.py b/python/netlink/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/python/netlink/capi.i b/python/netlink/capi.i new file mode 100644 index 0000000..73c4bf3 --- /dev/null +++ b/python/netlink/capi.i @@ -0,0 +1,454 @@ +%module capi +%{ +#include +#include +#include +#include +#include +#include +%} + +%include +%include + +%inline %{ + struct nl_dump_params *alloc_dump_params(void) + { + struct nl_dump_params *dp; + if (!(dp = calloc(1, sizeof(*dp)))) + return NULL; + dp->dp_fd = stdout; + return dp; + } + + void free_dump_params(struct nl_dump_params *dp) + { + free(dp); + } +%}; + +/* */ + +enum nl_dump_type { + NL_DUMP_LINE, /**< Dump object briefly on one line */ + NL_DUMP_DETAILS, /**< Dump all attributes but no statistics */ + NL_DUMP_STATS, /**< Dump all attributes including statistics */ + __NL_DUMP_MAX, +}; + +struct nl_dump_params +{ + /** + * Specifies the type of dump that is requested. + */ + enum nl_dump_type dp_type; + + /** + * Specifies the number of whitespaces to be put in front + * of every new line (indentation). + */ + int dp_prefix; + + /** + * Causes the cache index to be printed for each element. + */ + int dp_print_index; + + /** + * Causes each element to be prefixed with the message type. + */ + int dp_dump_msgtype; + + /** + * A callback invoked for output + * + * Passed arguments are: + * - dumping parameters + * - string to append to the output + */ + void (*dp_cb)(struct nl_dump_params *, char *); + + /** + * A callback invoked for every new line, can be used to + * customize the indentation. + * + * Passed arguments are: + * - dumping parameters + * - line number starting from 0 + */ + void (*dp_nl_cb)(struct nl_dump_params *, int); + + /** + * User data pointer, can be used to pass data to callbacks. + */ + void *dp_data; + + /** + * File descriptor the dumping output should go to + */ + FILE * dp_fd; + + /** + * Alternatively the output may be redirected into a buffer + */ + char * dp_buf; + + /** + * Length of the buffer dp_buf + */ + size_t dp_buflen; + + /** + * PRIVATE + * Set if a dump was performed prior to the actual dump handler. + */ + int dp_pre_dump; + + /** + * PRIVATE + * Owned by the current caller + */ + int dp_ivar; + + unsigned int dp_line; +}; + +/* */ +extern const char *nl_geterror(int); + +/* */ + +extern double nl_cancel_down_bytes(unsigned long long, char **); +extern double nl_cancel_down_bits(unsigned long long, char **); +%cstring_output_maxsize(char *buf, size_t len) +extern int nl_rate2str(unsigned long long rate, int type, char *buf, size_t len); +extern double nl_cancel_down_us(uint32_t, char **); + +extern long nl_size2int(const char *); +%cstring_output_maxsize(char *buf, const size_t len) +extern char *nl_size2str(const size_t, char *buf, const size_t len); +extern long nl_prob2int(const char *); + +extern int nl_get_user_hz(void); +extern uint32_t nl_us2ticks(uint32_t); +extern uint32_t nl_ticks2us(uint32_t); +extern int nl_str2msec(const char *, uint64_t *); + +%cstring_output_maxsize(char *buf, size_t len) +extern char *nl_msec2str(uint64_t, char *buf, size_t len); + +%cstring_output_maxsize(char *buf, size_t len) +extern char *nl_llproto2str(int, char *buf, size_t len); +extern int nl_str2llproto(const char *); + +%cstring_output_maxsize(char *buf, size_t len) +extern char *nl_ether_proto2str(int, char *buf, size_t len); +extern int nl_str2ether_proto(const char *); + +%cstring_output_maxsize(char *buf, size_t len) +extern char *nl_ip_proto2str(int, char *buf, size_t len); +extern int nl_str2ip_proto(const char *); + +extern void nl_new_line(struct nl_dump_params *); +extern void nl_dump(struct nl_dump_params *, const char *, ...); +extern void nl_dump_line(struct nl_dump_params *, const char *, ...); + +/* */ +extern struct nl_dump_params *alloc_dump_params(void); +extern void free_dump_params(struct nl_dump_params *); + +extern int nl_connect(struct nl_sock *, int); +extern void nl_close(struct nl_sock *); + +/* */ +extern struct nl_sock *nl_socket_alloc(void); +extern struct nl_sock *nl_socket_alloc_cb(struct nl_cb *); +extern void nl_socket_free(struct nl_sock *); + +extern uint32_t nl_socket_get_local_port(const struct nl_sock *); +extern void nl_socket_set_local_port(struct nl_sock *, uint32_t); + +extern uint32_t nl_socket_get_peer_port(const struct nl_sock *); +extern void nl_socket_set_peer_port(struct nl_sock *, uint32_t); + +extern uint32_t nl_socket_get_peer_groups(const struct nl_sock *sk); +extern void nl_socket_set_peer_groups(struct nl_sock *sk, uint32_t groups); + +extern int nl_socket_set_buffer_size(struct nl_sock *, int, int); + +/* */ +extern int nlmsg_size(int); +extern int nlmsg_total_size(int); +extern int nlmsg_padlen(int); + +extern void * nlmsg_data(const struct nlmsghdr *); +extern int nlmsg_datalen(const struct nlmsghdr *); +extern void * nlmsg_tail(const struct nlmsghdr *); + +/* attribute access */ +extern struct nlattr * nlmsg_attrdata(const struct nlmsghdr *, int); +extern int nlmsg_attrlen(const struct nlmsghdr *, int); + +/* message parsing */ +extern int nlmsg_valid_hdr(const struct nlmsghdr *, int); +extern int nlmsg_ok(const struct nlmsghdr *, int); +extern struct nlmsghdr * nlmsg_next(struct nlmsghdr *, int *); +extern int nlmsg_parse(struct nlmsghdr *, int, struct nlattr **, + int, struct nla_policy *); +extern struct nlattr * nlmsg_find_attr(struct nlmsghdr *, int, int); +extern int nlmsg_validate(struct nlmsghdr *, int, int, + struct nla_policy *); + +extern struct nl_msg * nlmsg_alloc(void); +extern struct nl_msg * nlmsg_alloc_size(size_t); +extern struct nl_msg * nlmsg_alloc_simple(int, int); +extern void nlmsg_set_default_size(size_t); +extern struct nl_msg * nlmsg_inherit(struct nlmsghdr *); +extern struct nl_msg * nlmsg_convert(struct nlmsghdr *); +extern void * nlmsg_reserve(struct nl_msg *, size_t, int); +extern int nlmsg_append(struct nl_msg *, void *, size_t, int); +extern int nlmsg_expand(struct nl_msg *, size_t); + +extern struct nlmsghdr * nlmsg_put(struct nl_msg *, uint32_t, uint32_t, + int, int, int); +extern struct nlmsghdr * nlmsg_hdr(struct nl_msg *); +extern void nlmsg_get(struct nl_msg *); +extern void nlmsg_free(struct nl_msg *); + +/* attribute modification */ +extern void nlmsg_set_proto(struct nl_msg *, int); +extern int nlmsg_get_proto(struct nl_msg *); +extern size_t nlmsg_get_max_size(struct nl_msg *); +extern void nlmsg_set_src(struct nl_msg *, struct sockaddr_nl *); +extern struct sockaddr_nl *nlmsg_get_src(struct nl_msg *); +extern void nlmsg_set_dst(struct nl_msg *, struct sockaddr_nl *); +extern struct sockaddr_nl *nlmsg_get_dst(struct nl_msg *); +extern void nlmsg_set_creds(struct nl_msg *, struct ucred *); +extern struct ucred * nlmsg_get_creds(struct nl_msg *); + +extern char * nl_nlmsgtype2str(int, char *, size_t); +extern int nl_str2nlmsgtype(const char *); + +extern char * nl_nlmsg_flags2str(int, char *, size_t); + +extern int nl_msg_parse(struct nl_msg *, + void (*cb)(struct nl_object *, void *), + void *); + +extern void nl_msg_dump(struct nl_msg *, FILE *); + +%inline %{ + struct nl_object *cast_obj(void *obj) + { + return (struct nl_object *) obj; + } + + struct nl_object *object_alloc_name(const char *name) + { + struct nl_object *obj; + + if (nl_object_alloc_name(name, &obj) < 0) + return NULL; + + return obj; + } +%}; + +extern struct nl_object *nl_object_alloc(struct nl_object_ops *); +extern void nl_object_free(struct nl_object *); +extern struct nl_object *nl_object_clone(struct nl_object *); +extern void nl_object_get(struct nl_object *); +extern void nl_object_put(struct nl_object *); +extern int nl_object_shared(struct nl_object *); + +%cstring_output_maxsize(char *buf, size_t len) +extern void nl_object_dump_buf(struct nl_object *, char *buf, size_t len); + +extern void nl_object_dump(struct nl_object *, struct nl_dump_params *); + +extern int nl_object_identical(struct nl_object *, struct nl_object *); +extern uint32_t nl_object_diff(struct nl_object *, struct nl_object *); +extern int nl_object_match_filter(struct nl_object *, struct nl_object *); + +%cstring_output_maxsize(char *buf, size_t len) +extern char *nl_object_attrs2str(struct nl_object *, uint32_t, char *buf, size_t len); + +%cstring_output_maxsize(char *buf, size_t len) +extern char *nl_object_attr_list(struct nl_object *, char *buf, size_t len); + +extern void nl_object_mark(struct nl_object *); +extern void nl_object_unmark(struct nl_object *); +extern int nl_object_is_marked(struct nl_object *); + +extern int nl_object_get_refcnt(struct nl_object *); + +/* */ + +typedef void (*change_func_t)(struct nl_cache *, struct nl_object *, int, void *); + +%inline %{ + struct nl_cache *alloc_cache_name(const char *name) + { + struct nl_cache *c; + if (nl_cache_alloc_name(name, &c) < 0) + return NULL; + return c; + } + + struct nl_cache_mngr *alloc_cache_mngr(struct nl_sock *sock, + int protocol, int flags) + { + struct nl_cache_mngr *mngr; + + if (nl_cache_mngr_alloc(sock, protocol, flags, &mngr) < 0) + return NULL; + + return mngr; + } + + struct nl_cache *cache_mngr_add(struct nl_cache_mngr *mngr, + const char *name, change_func_t func, + void *arg) + { + struct nl_cache *cache; + + if (nl_cache_mngr_add(mngr, name, func, arg, &cache) < 0) + return NULL; + + return cache; + } +%} + +/* Access Functions */ +extern int nl_cache_nitems(struct nl_cache *); +extern int nl_cache_nitems_filter(struct nl_cache *, + struct nl_object *); +extern struct nl_cache_ops * nl_cache_get_ops(struct nl_cache *); +extern struct nl_object * nl_cache_get_first(struct nl_cache *); +extern struct nl_object * nl_cache_get_last(struct nl_cache *); +extern struct nl_object * nl_cache_get_next(struct nl_object *); +extern struct nl_object * nl_cache_get_prev(struct nl_object *); + +extern struct nl_cache * nl_cache_alloc(struct nl_cache_ops *); +extern struct nl_cache * nl_cache_subset(struct nl_cache *, + struct nl_object *); +extern void nl_cache_clear(struct nl_cache *); +extern void nl_cache_free(struct nl_cache *); + +/* Cache modification */ +extern int nl_cache_add(struct nl_cache *, + struct nl_object *); +extern int nl_cache_parse_and_add(struct nl_cache *, + struct nl_msg *); +extern void nl_cache_remove(struct nl_object *); +extern int nl_cache_refill(struct nl_sock *, + struct nl_cache *); +extern int nl_cache_pickup(struct nl_sock *, + struct nl_cache *); +extern int nl_cache_resync(struct nl_sock *, + struct nl_cache *, + change_func_t, + void *); +extern int nl_cache_include(struct nl_cache *, + struct nl_object *, + change_func_t, + void *); +extern void nl_cache_set_arg1(struct nl_cache *, int); +extern void nl_cache_set_arg2(struct nl_cache *, int); + +/* General */ +extern int nl_cache_is_empty(struct nl_cache *); +extern struct nl_object * nl_cache_search(struct nl_cache *, + struct nl_object *); +extern void nl_cache_mark_all(struct nl_cache *); + +/* Dumping */ +extern void nl_cache_dump(struct nl_cache *, + struct nl_dump_params *); +extern void nl_cache_dump_filter(struct nl_cache *, + struct nl_dump_params *, + struct nl_object *); + +/* Iterators */ +extern void nl_cache_foreach(struct nl_cache *, + void (*cb)(struct nl_object *, + void *), + void *arg); +extern void nl_cache_foreach_filter(struct nl_cache *, + struct nl_object *, + void (*cb)(struct + nl_object *, + void *), + void *arg); + +/* --- cache management --- */ + +/* Cache type management */ +extern struct nl_cache_ops * nl_cache_ops_lookup(const char *); +extern struct nl_cache_ops * nl_cache_ops_associate(int, int); +extern struct nl_msgtype * nl_msgtype_lookup(struct nl_cache_ops *, int); +extern void nl_cache_ops_foreach(void (*cb)(struct nl_cache_ops *, void *), void *); +extern int nl_cache_mngt_register(struct nl_cache_ops *); +extern int nl_cache_mngt_unregister(struct nl_cache_ops *); + +/* Global cache provisioning/requiring */ +extern void nl_cache_mngt_provide(struct nl_cache *); +extern void nl_cache_mngt_unprovide(struct nl_cache *); +extern struct nl_cache * nl_cache_mngt_require(const char *); + +struct nl_cache_mngr; + +#define NL_AUTO_PROVIDE 1 + +extern int nl_cache_mngr_get_fd(struct nl_cache_mngr *); +extern int nl_cache_mngr_poll(struct nl_cache_mngr *, + int); +extern int nl_cache_mngr_data_ready(struct nl_cache_mngr *); +extern void nl_cache_mngr_free(struct nl_cache_mngr *); + +/* */ +%inline %{ + struct nl_addr *addr_parse(const char *addr, int guess) + { + struct nl_addr *result; + + if (nl_addr_parse(addr, guess, &result) < 0) + return NULL; + + return result; + } +%}; + +extern struct nl_addr *nl_addr_alloc(size_t); +extern struct nl_addr *nl_addr_alloc_attr(struct nlattr *, int); +extern struct nl_addr *nl_addr_build(int, void *, size_t); +extern struct nl_addr *nl_addr_clone(struct nl_addr *); + +extern struct nl_addr *nl_addr_get(struct nl_addr *); +extern void nl_addr_put(struct nl_addr *); +extern int nl_addr_shared(struct nl_addr *); + +extern int nl_addr_cmp(struct nl_addr *, struct nl_addr *); +extern int nl_addr_cmp_prefix(struct nl_addr *, struct nl_addr *); +extern int nl_addr_iszero(struct nl_addr *); +extern int nl_addr_valid(char *, int); +extern int nl_addr_guess_family(struct nl_addr *); +extern int nl_addr_fill_sockaddr(struct nl_addr *, struct sockaddr *, socklen_t *); +extern int nl_addr_info(struct nl_addr *, struct addrinfo **); +extern int nl_addr_resolve(struct nl_addr *addr, char *host, size_t hostlen); + +extern void nl_addr_set_family(struct nl_addr *, int); +extern int nl_addr_get_family(struct nl_addr *); +extern int nl_addr_set_binary_addr(struct nl_addr *, void *, size_t); + +extern void *nl_addr_get_binary_addr(struct nl_addr *); +extern unsigned int nl_addr_get_len(struct nl_addr *); +extern void nl_addr_set_prefixlen(struct nl_addr *, int); +extern unsigned int nl_addr_get_prefixlen(struct nl_addr *); + +%cstring_output_maxsize(char *buf, size_t len) +extern char *nl_af2str(int, char *buf, size_t len); +extern int nl_str2af(const char *); + +%cstring_output_maxsize(char *buf, size_t len) +extern char *nl_addr2str(struct nl_addr *, char *buf, size_t len); diff --git a/python/netlink/core.py b/python/netlink/core.py new file mode 100644 index 0000000..3298e2c --- /dev/null +++ b/python/netlink/core.py @@ -0,0 +1,744 @@ +# +# Netlink interface based on libnl +# +# Copyright (c) 2011 Thomas Graf +# + +"""netlink library based on libnl + +This module provides an interface to netlink sockets + +The module contains the following public classes: + - Socket -- The netlink socket + - Object -- Abstract object (based on struct nl_obect in libnl) used as + base class for all object types which can be put into a Cache + - Cache -- A collection of objects which are derived from the base + class Object. Used for netlink protocols which maintain a list + or tree of objects. + - DumpParams -- + +The following exceptions are defined: + - NetlinkError -- Base exception for all general purpose exceptions raised. + - KernelError -- Raised when the kernel returns an error as response to a + request. + +All other classes or functions in this module are considered implementation +details. +""" +from __future__ import absolute_import + + + +from . import capi +import sys +import socket + +__all__ = [ + 'Message', + 'Socket', + 'DumpParams', + 'Object', + 'Cache', + 'KernelError', + 'NetlinkError', +] + +__version__ = '0.1' + +# netlink protocols +NETLINK_ROUTE = 0 +# NETLINK_UNUSED = 1 +NETLINK_USERSOCK = 2 +NETLINK_FIREWALL = 3 +NETLINK_INET_DIAG = 4 +NETLINK_NFLOG = 5 +NETLINK_XFRM = 6 +NETLINK_SELINUX = 7 +NETLINK_ISCSI = 8 +NETLINK_AUDIT = 9 +NETLINK_FIB_LOOKUP = 10 +NETLINK_CONNECTOR = 11 +NETLINK_NETFILTER = 12 +NETLINK_IP6_FW = 13 +NETLINK_DNRTMSG = 14 +NETLINK_KOBJECT_UEVENT = 15 +NETLINK_GENERIC = 16 +NETLINK_SCSITRANSPORT = 18 +NETLINK_ECRYPTFS = 19 + +NL_DONTPAD = 0 +NL_AUTO_PORT = 0 +NL_AUTO_SEQ = 0 + +NL_DUMP_LINE = 0 +NL_DUMP_DETAILS = 1 +NL_DUMP_STATS = 2 + +NLM_F_REQUEST = 1 +NLM_F_MULTI = 2 +NLM_F_ACK = 4 +NLM_F_ECHO = 8 + +NLM_F_ROOT = 0x100 +NLM_F_MATCH = 0x200 +NLM_F_ATOMIC = 0x400 +NLM_F_DUMP = NLM_F_ROOT | NLM_F_MATCH + +NLM_F_REPLACE = 0x100 +NLM_F_EXCL = 0x200 +NLM_F_CREATE = 0x400 +NLM_F_APPEND = 0x800 + +class NetlinkError(Exception): + def __init__(self, error): + self._error = error + self._msg = capi.nl_geterror(error) + + def __str__(self): + return self._msg + +class KernelError(NetlinkError): + def __str__(self): + return 'Kernel returned: {0}'.format(self._msg) + +class ImmutableError(NetlinkError): + def __init__(self, msg): + self._msg = msg + + def __str__(self): + return 'Immutable attribute: {0}'.format(self._msg) + +class Message(object): + """Netlink message""" + + def __init__(self, size=0): + if size == 0: + self._msg = capi.nlmsg_alloc() + else: + self._msg = capi.nlmsg_alloc_size(size) + + if self._msg is None: + raise Exception('Message allocation returned NULL') + + def __del__(self): + capi.nlmsg_free(self._msg) + + def __len__(self): + return capi.nlmsg_len(nlmsg_hdr(self._msg)) + + @property + def protocol(self): + return capi.nlmsg_get_proto(self._msg) + + @protocol.setter + def protocol(self, value): + capi.nlmsg_set_proto(self._msg, value) + + @property + def maxSize(self): + return capi.nlmsg_get_max_size(self._msg) + + @property + def hdr(self): + return capi.nlmsg_hdr(self._msg) + + @property + def data(self): + return capi.nlmsg_data(self._msg) + + @property + def attrs(self): + return capi.nlmsg_attrdata(self._msg) + + def send(self, sock): + sock.send(self) + +class Socket(object): + """Netlink socket""" + + def __init__(self, cb=None): + if cb is None: + self._sock = capi.nl_socket_alloc() + else: + self._sock = capi.nl_socket_alloc_cb(cb) + + if self._sock is None: + raise Exception('NULL pointer returned while allocating socket') + + def __del__(self): + capi.nl_socket_free(self._sock) + + def __str__(self): + return 'nlsock<{0}>'.format(self.localPort) + + @property + def local_port(self): + return capi.nl_socket_get_local_port(self._sock) + + @local_port.setter + def local_port(self, value): + capi.nl_socket_set_local_port(self._sock, int(value)) + + @property + def peer_port(self): + return capi.nl_socket_get_peer_port(self._sock) + + @peer_port.setter + def peer_port(self, value): + capi.nl_socket_set_peer_port(self._sock, int(value)) + + @property + def peer_groups(self): + return capi.nl_socket_get_peer_groups(self._sock) + + @peer_groups.setter + def peer_groups(self, value): + capi.nl_socket_set_peer_groups(self._sock, value) + + def set_bufsize(self, rx, tx): + capi.nl_socket_set_buffer_size(self._sock, rx, tx) + + def connect(self, proto): + capi.nl_connect(self._sock, proto) + return self + + def disconnect(self): + capi.nl_close(self._sock) + + def sendto(self, buf): + ret = capi.nl_sendto(self._sock, buf, len(buf)) + if ret < 0: + raise Exception('Failed to send') + else: + return ret + +_sockets = {} + +def lookup_socket(protocol): + try: + sock = _sockets[protocol] + except KeyError: + sock = Socket() + sock.connect(protocol) + _sockets[protocol] = sock + + return sock + +class DumpParams(object): + """Dumping parameters""" + + def __init__(self, type_=NL_DUMP_LINE): + self._dp = capi.alloc_dump_params() + if not self._dp: + raise Exception('Unable to allocate struct nl_dump_params') + + self._dp.dp_type = type_ + + def __del__(self): + capi.free_dump_params(self._dp) + + @property + def type(self): + return self._dp.dp_type + + @type.setter + def type(self, value): + self._dp.dp_type = value + + @property + def prefix(self): + return self._dp.dp_prefix + + @prefix.setter + def prefix(self, value): + self._dp.dp_prefix = value + +# underscore this to make sure it is deleted first upon module deletion +_defaultDumpParams = DumpParams(NL_DUMP_LINE) + +class Object(object): + """Cacheable object (base class)""" + + def __init__(self, obj_name, name, obj=None): + self._obj_name = obj_name + self._name = name + self._modules = [] + + if not obj: + obj = capi.object_alloc_name(self._obj_name) + + self._nl_object = obj + + # Create a clone which stores the original state to notice + # modifications + clone_obj = capi.nl_object_clone(self._nl_object) + self._orig = self._obj2type(clone_obj) + + def __del__(self): + if not self._nl_object: + raise ValueError() + + capi.nl_object_put(self._nl_object) + + def __str__(self): + if hasattr(self, 'format'): + return self.format() + else: + return capi.nl_object_dump_buf(self._nl_object, 4096).rstrip() + + def _new_instance(self): + raise NotImplementedError() + + def clone(self): + """Clone object""" + return self._new_instance(capi.nl_object_clone(self._nl_object)) + + def _module_lookup(self, path, constructor=None): + """Lookup object specific module and load it + + Object implementations consisting of multiple types may + offload some type specific code to separate modules which + are loadable on demand, e.g. a VLAN link or a specific + queueing discipline implementation. + + Loads the module `path` and calls the constructor if + supplied or `module`.init() + + The constructor/init function typically assigns a new + object covering the type specific implementation aspects + to the new object, e.g. link.vlan = VLANLink() + """ + try: + __import__(path) + except ImportError: + return + + module = sys.modules[path] + + if constructor: + ret = getattr(module, constructor)(self) + else: + ret = module.init(self) + + if ret: + self._modules.append(ret) + + def _module_brief(self): + ret = '' + + for module in self._modules: + if hasattr(module, 'brief'): + ret += module.brief() + + return ret + + def dump(self, params=None): + """Dump object as human readable text""" + if params is None: + params = _defaultDumpParams + + capi.nl_object_dump(self._nl_object, params._dp) + + + @property + def mark(self): + return bool(capi.nl_object_is_marked(self._nl_object)) + + @mark.setter + def mark(self, value): + if value: + capi.nl_object_mark(self._nl_object) + else: + capi.nl_object_unmark(self._nl_object) + + @property + def shared(self): + return capi.nl_object_shared(self._nl_object) != 0 + + @property + def attrs(self): + attr_list = capi.nl_object_attr_list(self._nl_object, 1024) + return attr_list[0].split() + + @property + def refcnt(self): + return capi.nl_object_get_refcnt(self._nl_object) + + # this method resolves multiple levels of sub types to allow + # accessing properties of subclass/subtypes (e.g. link.vlan.id) + def _resolve(self, attr): + obj = self + l = attr.split('.') + while len(l) > 1: + obj = getattr(obj, l.pop(0)) + return (obj, l.pop(0)) + + def _setattr(self, attr, val): + obj, attr = self._resolve(attr) + return setattr(obj, attr, val) + + def _hasattr(self, attr): + obj, attr = self._resolve(attr) + return hasattr(obj, attr) + +class ObjIterator(object): + def __init__(self, cache, obj): + self._cache = cache + self._nl_object = None + + if not obj: + self._end = 1 + else: + capi.nl_object_get(obj) + self._nl_object = obj + self._first = 1 + self._end = 0 + + def __del__(self): + if self._nl_object: + capi.nl_object_put(self._nl_object) + + def __iter__(self): + return self + + def get_next(self): + return capi.nl_cache_get_next(self._nl_object) + + def next(self): + if self._end: + raise StopIteration() + + if self._first: + ret = self._nl_object + self._first = 0 + else: + ret = self.get_next() + if not ret: + self._end = 1 + raise StopIteration() + + # return ref of previous element and acquire ref of current + # element to have object stay around until we fetched the + # next ptr + capi.nl_object_put(self._nl_object) + capi.nl_object_get(ret) + self._nl_object = ret + + # reference used inside object + capi.nl_object_get(ret) + return self._cache._new_object(ret) + + +class ReverseObjIterator(ObjIterator): + def get_next(self): + return capi.nl_cache_get_prev(self._nl_object) + +class Cache(object): + """Collection of netlink objects""" + def __init__(self): + if self.__class__ is Cache: + raise NotImplementedError() + self.arg1 = None + self.arg2 = None + + def __del__(self): + capi.nl_cache_free(self._nl_cache) + + def __len__(self): + return capi.nl_cache_nitems(self._nl_cache) + + def __iter__(self): + obj = capi.nl_cache_get_first(self._nl_cache) + return ObjIterator(self, obj) + + def __reversed__(self): + obj = capi.nl_cache_get_last(self._nl_cache) + return ReverseObjIterator(self, obj) + + def __contains__(self, item): + obj = capi.nl_cache_search(self._nl_cache, item._nl_object) + if obj is None: + return False + else: + capi.nl_object_put(obj) + return True + + # called by sub classes to allocate type specific caches by name + @staticmethod + def _alloc_cache_name(name): + return capi.alloc_cache_name(name) + + # implemented by sub classes, must return new instasnce of cacheable + # object + @staticmethod + def _new_object(obj): + raise NotImplementedError() + + # implemented by sub classes, must return instance of sub class + def _new_cache(self, cache): + raise NotImplementedError() + + def subset(self, filter_): + """Return new cache containing subset of cache + + Cretes a new cache containing all objects which match the + specified filter. + """ + if not filter_: + raise ValueError() + + c = capi.nl_cache_subset(self._nl_cache, filter_._nl_object) + return self._new_cache(cache=c) + + def dump(self, params=None, filter_=None): + """Dump (print) cache as human readable text""" + if not params: + params = _defaultDumpParams + + if filter_: + filter_ = filter_._nl_object + + capi.nl_cache_dump_filter(self._nl_cache, params._dp, filter_) + + def clear(self): + """Remove all cache entries""" + capi.nl_cache_clear(self._nl_cache) + + # Called by sub classes to set first cache argument + def _set_arg1(self, arg): + self.arg1 = arg + capi.nl_cache_set_arg1(self._nl_cache, arg) + + # Called by sub classes to set second cache argument + def _set_arg2(self, arg): + self.arg2 = arg + capi.nl_cache_set_arg2(self._nl_cache, arg) + + def refill(self, socket=None): + """Clear cache and refill it""" + if socket is None: + socket = lookup_socket(self._protocol) + + capi.nl_cache_refill(socket._sock, self._nl_cache) + return self + + def resync(self, socket=None, cb=None): + """Synchronize cache with content in kernel""" + if socket is None: + socket = lookup_socket(self._protocol) + + capi.nl_cache_resync(socket._sock, self._nl_cache, cb) + + def provide(self): + """Provide this cache to others + + Caches which have been "provided" are made available + to other users (of the same application context) which + "require" it. F.e. a link cache is generally provided + to allow others to translate interface indexes to + link names + """ + + capi.nl_cache_mngt_provide(self._nl_cache) + + def unprovide(self): + """Unprovide this cache + + No longer make the cache available to others. If the cache + has been handed out already, that reference will still + be valid. + """ + capi.nl_cache_mngt_unprovide(self._nl_cache) + +# Cache Manager (Work in Progress) +NL_AUTO_PROVIDE = 1 +class CacheManager(object): + def __init__(self, protocol, flags=None): + + self._sock = Socket() + self._sock.connect(protocol) + + if not flags: + flags = NL_AUTO_PROVIDE + + self._mngr = capi.cache_mngr_alloc(self._sock._sock, protocol, flags) + + def __del__(self): + if self._sock: + self._sock.disconnect() + + if self._mngr: + capi.nl_cache_mngr_free(self._mngr) + + def add(self, name): + capi.cache_mngr_add(self._mngr, name, None, None) + +class AddressFamily(object): + """Address family representation + + af = AddressFamily('inet6') + # raises: + # - ValueError if family name is not known + # - TypeError if invalid type is specified for family + + print af # => 'inet6' (string representation) + print int(af) # => 10 (numeric representation) + print repr(af) # => AddressFamily('inet6') + """ + def __init__(self, family=socket.AF_UNSPEC): + if isinstance(family, str): + family = capi.nl_str2af(family) + if family < 0: + raise ValueError('Unknown family name') + elif not isinstance(family, int): + raise TypeError() + + self._family = family + + def __str__(self): + return capi.nl_af2str(self._family, 32)[0] + + def __int__(self): + return self._family + + def __repr__(self): + return 'AddressFamily({0!r})'.format(str(self)) + + +class AbstractAddress(object): + """Abstract address object + + addr = AbstractAddress('127.0.0.1/8') + print addr # => '127.0.0.1/8' + print addr.prefixlen # => '8' + print addr.family # => 'inet' + print len(addr) # => '4' (32bit ipv4 address) + + a = AbstractAddress('10.0.0.1/24') + b = AbstractAddress('10.0.0.2/24') + print a == b # => False + + + """ + def __init__(self, addr): + self._nl_addr = None + + if isinstance(addr, str): + addr = capi.addr_parse(addr, socket.AF_UNSPEC) + if addr is None: + raise ValueError('Invalid address format') + elif addr: + capi.nl_addr_get(addr) + + self._nl_addr = addr + + def __del__(self): + if self._nl_addr: + capi.nl_addr_put(self._nl_addr) + + def __cmp__(self, other): + if isinstance(other, str): + other = AbstractAddress(other) + + diff = self.prefixlen - other.prefixlen + if diff == 0: + diff = capi.nl_addr_cmp(self._nl_addr, other._nl_addr) + + return diff + + def contains(self, item): + diff = int(self.family) - int(item.family) + if diff: + return False + + if item.prefixlen < self.prefixlen: + return False + + diff = capi.nl_addr_cmp_prefix(self._nl_addr, item._nl_addr) + return diff == 0 + + def __nonzero__(self): + if self._nl_addr: + return not capi.nl_addr_iszero(self._nl_addr) + else: + return False + + def __len__(self): + if self._nl_addr: + return capi.nl_addr_get_len(self._nl_addr) + else: + return 0 + + def __str__(self): + if self._nl_addr: + return capi.nl_addr2str(self._nl_addr, 64)[0] + else: + return 'none' + + @property + def shared(self): + """True if address is shared (multiple users)""" + if self._nl_addr: + return capi.nl_addr_shared(self._nl_addr) != 0 + else: + return False + + @property + def prefixlen(self): + """Length of prefix (number of bits)""" + if self._nl_addr: + return capi.nl_addr_get_prefixlen(self._nl_addr) + else: + return 0 + + @prefixlen.setter + def prefixlen(self, value): + if not self._nl_addr: + raise TypeError() + + capi.nl_addr_set_prefixlen(self._nl_addr, int(value)) + + @property + def family(self): + """Address family""" + f = 0 + if self._nl_addr: + f = capi.nl_addr_get_family(self._nl_addr) + + return AddressFamily(f) + + @family.setter + def family(self, value): + if not self._nl_addr: + raise TypeError() + + if not isinstance(value, AddressFamily): + value = AddressFamily(value) + + capi.nl_addr_set_family(self._nl_addr, int(value)) + + +# keyword: +# type = { int | str } +# immutable = { True | False } +# fmt = func (formatting function) +# title = string + +def nlattr(**kwds): + """netlink object attribute decorator + + decorator used to mark mutable and immutable properties + of netlink objects. All properties marked as such are + regarded to be accessable. + + @property + @netlink.nlattr(type=int) + def my_attr(self): + return self._my_attr + + """ + + def wrap_fn(func): + func.formatinfo = kwds + return func + return wrap_fn diff --git a/python/netlink/fixes.h b/python/netlink/fixes.h new file mode 100644 index 0000000..9a6118b --- /dev/null +++ b/python/netlink/fixes.h @@ -0,0 +1 @@ +#include diff --git a/python/netlink/route/Makefile.am b/python/netlink/route/Makefile.am new file mode 100644 index 0000000..ef714f4 --- /dev/null +++ b/python/netlink/route/Makefile.am @@ -0,0 +1,14 @@ +# -*- Makefile -*- + +EXTRA_DIST = \ + capi.i \ + __init__.py \ + address.py \ + link.py \ + tc.py \ + links/__init__.py \ + links/dummy.py \ + links/inet.py \ + links/vlan.py \ + qdisc/__init__.py \ + qdisc/htb.py diff --git a/python/netlink/route/__init__.py b/python/netlink/route/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/python/netlink/route/address.py b/python/netlink/route/address.py new file mode 100644 index 0000000..cab2a97 --- /dev/null +++ b/python/netlink/route/address.py @@ -0,0 +1,367 @@ +# +# Copyright (c) 2011 Thomas Graf +# + +"""Module providing access to network addresses +""" + +from __future__ import absolute_import + + +__version__ = '1.0' +__all__ = [ + 'AddressCache', + 'Address'] + +import datetime +from .. import core as netlink +from . import capi as capi +from . import link as Link +from .. import util as util + +class AddressCache(netlink.Cache): + """Cache containing network addresses""" + + def __init__(self, cache=None): + if not cache: + cache = self._alloc_cache_name('route/addr') + + self._protocol = netlink.NETLINK_ROUTE + self._nl_cache = cache + + def __getitem__(self, key): + # Using ifindex=0 here implies that the local address itself + # is unique, otherwise the first occurence is returned. + return self.lookup(0, key) + + def lookup(self, ifindex, local): + if type(local) is str: + local = netlink.AbstractAddress(local) + + addr = capi.rtnl_addr_get(self._nl_cache, ifindex, + local._nl_addr) + if addr is None: + raise KeyError() + + return Address._from_capi(addr) + + @staticmethod + def _new_object(obj): + return Address(obj) + + @staticmethod + def _new_cache(cache): + return AddressCache(cache=cache) + +class Address(netlink.Object): + """Network address""" + + def __init__(self, obj=None): + netlink.Object.__init__(self, 'route/addr', 'address', obj) + self._rtnl_addr = self._obj2type(self._nl_object) + + @classmethod + def _from_capi(cls, obj): + return cls(capi.addr2obj(obj)) + + @staticmethod + def _obj2type(obj): + return capi.obj2addr(obj) + + def __cmp__(self, other): + # sort by: + # 1. network link + # 2. address family + # 3. local address (including prefixlen) + diff = self.ifindex - other.ifindex + + if diff == 0: + diff = self.family - other.family + if diff == 0: + diff = capi.nl_addr_cmp(self.local, other.local) + + return diff + + @staticmethod + def _new_instance(obj): + return Address(obj) + + @property + @netlink.nlattr(type=int, immutable=True, fmt=util.num) + def ifindex(self): + """interface index""" + return capi.rtnl_addr_get_ifindex(self._rtnl_addr) + + @ifindex.setter + def ifindex(self, value): + link = Link.resolve(value) + if not link: + raise ValueError() + + self.link = link + + @property + @netlink.nlattr(type=str, fmt=util.string) + def link(self): + link = capi.rtnl_addr_get_link(self._rtnl_addr) + if not link: + return None + + return Link.Link.from_capi(link) + + @link.setter + def link(self, value): + if type(value) is str: + try: + value = Link.resolve(value) + except KeyError: + raise ValueError() + + capi.rtnl_addr_set_link(self._rtnl_addr, value._rtnl_link) + + # ifindex is immutable but we assume that if _orig does not + # have an ifindex specified, it was meant to be given here + if capi.rtnl_addr_get_ifindex(self._orig) == 0: + capi.rtnl_addr_set_ifindex(self._orig, value.ifindex) + + @property + @netlink.nlattr(type=str, fmt=util.string) + def label(self): + """address label""" + return capi.rtnl_addr_get_label(self._rtnl_addr) + + @label.setter + def label(self, value): + capi.rtnl_addr_set_label(self._rtnl_addr, value) + + @property + @netlink.nlattr(type=str, fmt=util.string) + def flags(self): + """Flags + + Setting this property will *Not* reset flags to value you supply in + + Examples: + addr.flags = '+xxx' # add xxx flag + addr.flags = 'xxx' # exactly the same + addr.flags = '-xxx' # remove xxx flag + addr.flags = [ '+xxx', '-yyy' ] # list operation + """ + flags = capi.rtnl_addr_get_flags(self._rtnl_addr) + return capi.rtnl_addr_flags2str(flags, 256)[0].split(',') + + def _set_flag(self, flag): + if flag.startswith('-'): + i = capi.rtnl_addr_str2flags(flag[1:]) + capi.rtnl_addr_unset_flags(self._rtnl_addr, i) + elif flag.startswith('+'): + i = capi.rtnl_addr_str2flags(flag[1:]) + capi.rtnl_addr_set_flags(self._rtnl_addr, i) + else: + i = capi.rtnl_addr_str2flags(flag) + capi.rtnl_addr_set_flags(self._rtnl_addr, i) + + @flags.setter + def flags(self, value): + if type(value) is list: + for flag in value: + self._set_flag(flag) + else: + self._set_flag(value) + + @property + @netlink.nlattr(type=int, immutable=True, fmt=util.num) + def family(self): + """Address family""" + fam = capi.rtnl_addr_get_family(self._rtnl_addr) + return netlink.AddressFamily(fam) + + @family.setter + def family(self, value): + if not isinstance(value, netlink.AddressFamily): + value = netlink.AddressFamily(value) + + capi.rtnl_addr_set_family(self._rtnl_addr, int(value)) + + @property + @netlink.nlattr(type=int, fmt=util.num) + def scope(self): + """Address scope""" + scope = capi.rtnl_addr_get_scope(self._rtnl_addr) + return capi.rtnl_scope2str(scope, 32)[0] + + @scope.setter + def scope(self, value): + if type(value) is str: + value = capi.rtnl_str2scope(value) + capi.rtnl_addr_set_scope(self._rtnl_addr, value) + + @property + @netlink.nlattr(type=str, immutable=True, fmt=util.addr) + def local(self): + """Local address""" + a = capi.rtnl_addr_get_local(self._rtnl_addr) + return netlink.AbstractAddress(a) + + @local.setter + def local(self, value): + a = netlink.AbstractAddress(value) + capi.rtnl_addr_set_local(self._rtnl_addr, a._nl_addr) + + # local is immutable but we assume that if _orig does not + # have a local address specified, it was meant to be given here + if capi.rtnl_addr_get_local(self._orig) is None: + capi.rtnl_addr_set_local(self._orig, a._nl_addr) + + @property + @netlink.nlattr(type=str, fmt=util.addr) + def peer(self): + """Peer address""" + a = capi.rtnl_addr_get_peer(self._rtnl_addr) + return netlink.AbstractAddress(a) + + @peer.setter + def peer(self, value): + a = netlink.AbstractAddress(value) + capi.rtnl_addr_set_peer(self._rtnl_addr, a._nl_addr) + + @property + @netlink.nlattr(type=str, fmt=util.addr) + def broadcast(self): + """Broadcast address""" + a = capi.rtnl_addr_get_broadcast(self._rtnl_addr) + return netlink.AbstractAddress(a) + + @broadcast.setter + def broadcast(self, value): + a = netlink.AbstractAddress(value) + capi.rtnl_addr_set_broadcast(self._rtnl_addr, a._nl_addr) + + @property + @netlink.nlattr(type=str, fmt=util.addr) + def multicast(self): + """multicast address""" + a = capi.rtnl_addr_get_multicast(self._rtnl_addr) + return netlink.AbstractAddress(a) + + @multicast.setter + def multicast(self, value): + try: + a = netlink.AbstractAddress(value) + except ValueError as err: + raise AttributeError('multicast', err) + + capi.rtnl_addr_set_multicast(self._rtnl_addr, a._nl_addr) + + @property + @netlink.nlattr(type=str, fmt=util.addr) + def anycast(self): + """anycast address""" + a = capi.rtnl_addr_get_anycast(self._rtnl_addr) + return netlink.AbstractAddress(a) + + @anycast.setter + def anycast(self, value): + a = netlink.AbstractAddress(value) + capi.rtnl_addr_set_anycast(self._rtnl_addr, a._nl_addr) + + @property + @netlink.nlattr(type=int, immutable=True, fmt=util.num) + def valid_lifetime(self): + """Valid lifetime""" + msecs = capi.rtnl_addr_get_valid_lifetime(self._rtnl_addr) + if msecs == 0xFFFFFFFF: + return None + else: + return datetime.timedelta(seconds=msecs) + + @valid_lifetime.setter + def valid_lifetime(self, value): + capi.rtnl_addr_set_valid_lifetime(self._rtnl_addr, int(value)) + + @property + @netlink.nlattr(type=int, immutable=True, fmt=util.num) + def preferred_lifetime(self): + """Preferred lifetime""" + msecs = capi.rtnl_addr_get_preferred_lifetime(self._rtnl_addr) + if msecs == 0xFFFFFFFF: + return None + else: + return datetime.timedelta(seconds=msecs) + + @preferred_lifetime.setter + def preferred_lifetime(self, value): + capi.rtnl_addr_set_preferred_lifetime(self._rtnl_addr, int(value)) + + @property + @netlink.nlattr(type=int, immutable=True, fmt=util.num) + def create_time(self): + """Creation time""" + hsec = capi.rtnl_addr_get_create_time(self._rtnl_addr) + return datetime.timedelta(milliseconds=10*hsec) + + @property + @netlink.nlattr(type=int, immutable=True, fmt=util.num) + def last_update(self): + """Last update""" + hsec = capi.rtnl_addr_get_last_update_time(self._rtnl_addr) + return datetime.timedelta(milliseconds=10*hsec) + + def add(self, socket=None, flags=None): + if not socket: + socket = netlink.lookup_socket(netlink.NETLINK_ROUTE) + + if not flags: + flags = netlink.NLM_F_CREATE + + ret = capi.rtnl_addr_add(socket._sock, self._rtnl_addr, flags) + if ret < 0: + raise netlink.KernelError(ret) + + def delete(self, socket, flags=0): + """Attempt to delete this address in the kernel""" + ret = capi.rtnl_addr_delete(socket._sock, self._rtnl_addr, flags) + if ret < 0: + raise netlink.KernelError(ret) + + ################################################################### + # private properties + # + # Used for formatting output. USE AT OWN RISK + @property + def _flags(self): + return ','.join(self.flags) + + def format(self, details=False, stats=False, nodev=False, indent=''): + """Return address as formatted text""" + fmt = util.MyFormatter(self, indent) + + buf = fmt.format('{a|local!b}') + + if not nodev: + buf += fmt.format(' {a|ifindex}') + + buf += fmt.format(' {a|scope}') + + if self.label: + buf += fmt.format(' "{a|label}"') + + buf += fmt.format(' <{a|_flags}>') + + if details: + buf += fmt.nl('\t{t|broadcast} {t|multicast}') \ + + fmt.nl('\t{t|peer} {t|anycast}') + + if self.valid_lifetime: + buf += fmt.nl('\t{s|valid-lifetime!k} '\ + '{a|valid_lifetime}') + + if self.preferred_lifetime: + buf += fmt.nl('\t{s|preferred-lifetime!k} '\ + '{a|preferred_lifetime}') + + if stats and (self.create_time or self.last_update): + buf += self.nl('\t{s|created!k} {a|create_time}'\ + ' {s|last-updated!k} {a|last_update}') + + return buf diff --git a/python/netlink/route/capi.i b/python/netlink/route/capi.i new file mode 100644 index 0000000..8ac114b --- /dev/null +++ b/python/netlink/route/capi.i @@ -0,0 +1,396 @@ +%module capi +%{ +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include +%} + +%include +%include + +%inline %{ + struct nl_object *link2obj(struct rtnl_link *link) + { + return OBJ_CAST(link); + } + + struct rtnl_link *obj2link(struct nl_object *obj) + { + return (struct rtnl_link *) obj; + } + + struct rtnl_link *get_from_kernel(struct nl_sock *sk, int ifindex, const char *name) + { + struct rtnl_link *link; + if (rtnl_link_get_kernel(sk, ifindex, name, &link) < 0) + return NULL; + return link; + } + + uint32_t inet_get_conf(struct rtnl_link *link, const unsigned int id) + { + uint32_t result; + + if (rtnl_link_inet_get_conf(link, id, &result) < 0) + return 0; + + return result; + } +%}; + +extern struct nl_object *link2obj(struct rtnl_link *); +extern struct rtnl_link *obj2link(struct nl_object *); + +/* */ + +%cstring_output_maxsize(char *buf, size_t len) +extern char * rtnl_scope2str(int, char *buf, size_t len); +extern int rtnl_str2scope(const char *); + +/* */ + +extern struct rtnl_link *rtnl_link_alloc(void); + +extern struct rtnl_link *rtnl_link_get(struct nl_cache *, int); +extern struct rtnl_link *rtnl_link_get_by_name(struct nl_cache *, const char *); + +extern int rtnl_link_build_add_request(struct rtnl_link *, int, struct nl_msg **); +extern int rtnl_link_add(struct nl_sock *, struct rtnl_link *, int); +extern int rtnl_link_build_change_request(struct rtnl_link *, struct rtnl_link *, int, struct nl_msg **); +extern int rtnl_link_change(struct nl_sock *, struct rtnl_link *, struct rtnl_link *, int); + +extern int rtnl_link_build_delete_request(const struct rtnl_link *, struct nl_msg **); +extern int rtnl_link_delete(struct nl_sock *, const struct rtnl_link *); +extern int rtnl_link_build_get_request(int, const char *, struct nl_msg **); + +extern char *rtnl_link_stat2str(int, char *, size_t); +extern int rtnl_link_str2stat(const char *); + +%cstring_output_maxsize(char *buf, size_t len) +extern char *rtnl_link_flags2str(int, char *buf, size_t len); +extern int rtnl_link_str2flags(const char *); + +%cstring_output_maxsize(char *buf, size_t len) +extern char *rtnl_link_operstate2str(uint8_t, char *buf, size_t len); +extern int rtnl_link_str2operstate(const char *); + +%cstring_output_maxsize(char *buf, size_t len) +extern char *rtnl_link_mode2str(uint8_t, char *buf, size_t len); +extern int rtnl_link_str2mode(const char *); + +extern void rtnl_link_set_qdisc(struct rtnl_link *, const char *); +extern char *rtnl_link_get_qdisc(struct rtnl_link *); + +extern void rtnl_link_set_name(struct rtnl_link *, const char *); +extern char *rtnl_link_get_name(struct rtnl_link *); + +extern void rtnl_link_set_flags(struct rtnl_link *, unsigned int); +extern void rtnl_link_unset_flags(struct rtnl_link *, unsigned int); +extern unsigned int rtnl_link_get_flags(struct rtnl_link *); + +extern void rtnl_link_set_mtu(struct rtnl_link *, unsigned int); +extern unsigned int rtnl_link_get_mtu(struct rtnl_link *); + +extern void rtnl_link_set_txqlen(struct rtnl_link *, unsigned int); +extern unsigned int rtnl_link_get_txqlen(struct rtnl_link *); + +extern void rtnl_link_set_ifindex(struct rtnl_link *, int); +extern int rtnl_link_get_ifindex(struct rtnl_link *); + +extern void rtnl_link_set_family(struct rtnl_link *, int); +extern int rtnl_link_get_family(struct rtnl_link *); + +extern void rtnl_link_set_arptype(struct rtnl_link *, unsigned int); +extern unsigned int rtnl_link_get_arptype(struct rtnl_link *); + +extern void rtnl_link_set_addr(struct rtnl_link *, struct nl_addr *); +extern struct nl_addr *rtnl_link_get_addr(struct rtnl_link *); + +extern void rtnl_link_set_broadcast(struct rtnl_link *, struct nl_addr *); +extern struct nl_addr *rtnl_link_get_broadcast(struct rtnl_link *); + +extern void rtnl_link_set_link(struct rtnl_link *, int); +extern int rtnl_link_get_link(struct rtnl_link *); + +extern void rtnl_link_set_master(struct rtnl_link *, int); +extern int rtnl_link_get_master(struct rtnl_link *); + +extern void rtnl_link_set_operstate(struct rtnl_link *, uint8_t); +extern uint8_t rtnl_link_get_operstate(struct rtnl_link *); + +extern void rtnl_link_set_linkmode(struct rtnl_link *, uint8_t); +extern uint8_t rtnl_link_get_linkmode(struct rtnl_link *); + +extern const char *rtnl_link_get_ifalias(struct rtnl_link *); +extern void rtnl_link_set_ifalias(struct rtnl_link *, const char *); + +extern int rtnl_link_get_num_vf(struct rtnl_link *, uint32_t *); + +extern uint64_t rtnl_link_get_stat(struct rtnl_link *, int); +extern int rtnl_link_set_stat(struct rtnl_link *, const unsigned int, const uint64_t); + +extern int rtnl_link_set_type(struct rtnl_link *, const char *); +extern char *rtnl_link_get_type(struct rtnl_link *); + +/* */ + +struct vlan_map +{ + uint32_t vm_from; + uint32_t vm_to; +}; + +#define VLAN_PRIO_MAX 7 + +%cstring_output_maxsize(char *buf, size_t len) +extern char *rtnl_link_vlan_flags2str(int, char *buf, size_t len); +extern int rtnl_link_vlan_str2flags(const char *); + +extern int rtnl_link_vlan_set_id(struct rtnl_link *, int); +extern int rtnl_link_vlan_get_id(struct rtnl_link *); + +extern int rtnl_link_vlan_set_flags(struct rtnl_link *, unsigned int); +extern int rtnl_link_vlan_unset_flags(struct rtnl_link *, unsigned int); +extern unsigned int rtnl_link_vlan_get_flags(struct rtnl_link *); + +extern int rtnl_link_vlan_set_ingress_map(struct rtnl_link *, int, uint32_t); +extern uint32_t *rtnl_link_vlan_get_ingress_map(struct rtnl_link *); + +extern int rtnl_link_vlan_set_egress_map(struct rtnl_link *, uint32_t, int); +extern struct vlan_map *rtnl_link_vlan_get_egress_map(struct rtnl_link *, int *); + +/* */ +%cstring_output_maxsize(char *buf, size_t len) +extern const char *rtnl_link_inet_devconf2str(int, char *buf, size_t len); +extern unsigned int rtnl_link_inet_str2devconf(const char *); + +extern int rtnl_link_inet_set_conf(struct rtnl_link *, const unsigned int, uint32_t); + +/* */ + +%inline %{ + uint32_t tc_str2handle(const char *name) + { + uint32_t result; + + if (rtnl_tc_str2handle(name, &result) < 0) + return 0; + + return result; + } +%}; + +extern void rtnl_tc_set_ifindex(struct rtnl_tc *, int); +extern int rtnl_tc_get_ifindex(struct rtnl_tc *); +extern void rtnl_tc_set_link(struct rtnl_tc *, struct rtnl_link *); +extern struct rtnl_link *rtnl_tc_get_link(struct rtnl_tc *); +extern void rtnl_tc_set_mtu(struct rtnl_tc *, uint32_t); +extern uint32_t rtnl_tc_get_mtu(struct rtnl_tc *); +extern void rtnl_tc_set_mpu(struct rtnl_tc *, uint32_t); +extern uint32_t rtnl_tc_get_mpu(struct rtnl_tc *); +extern void rtnl_tc_set_overhead(struct rtnl_tc *, uint32_t); +extern uint32_t rtnl_tc_get_overhead(struct rtnl_tc *); +extern void rtnl_tc_set_linktype(struct rtnl_tc *, uint32_t); +extern uint32_t rtnl_tc_get_linktype(struct rtnl_tc *); +extern void rtnl_tc_set_handle(struct rtnl_tc *, uint32_t); +extern uint32_t rtnl_tc_get_handle(struct rtnl_tc *); +extern void rtnl_tc_set_parent(struct rtnl_tc *, uint32_t); +extern uint32_t rtnl_tc_get_parent(struct rtnl_tc *); +extern int rtnl_tc_set_kind(struct rtnl_tc *, const char *); +extern char * rtnl_tc_get_kind(struct rtnl_tc *); +extern uint64_t rtnl_tc_get_stat(struct rtnl_tc *, enum rtnl_tc_stat); + +extern int rtnl_tc_calc_txtime(int, int); +extern int rtnl_tc_calc_bufsize(int, int); +extern int rtnl_tc_calc_cell_log(int); + +extern int rtnl_tc_read_classid_file(void); +%cstring_output_maxsize(char *buf, size_t len) +extern char * rtnl_tc_handle2str(uint32_t, char *buf, size_t len); +extern int rtnl_classid_generate(const char *, uint32_t *, uint32_t); + +/* */ + +%inline %{ + struct nl_object *qdisc2obj(struct rtnl_qdisc *qdisc) + { + return OBJ_CAST(qdisc); + } + + struct rtnl_qdisc *obj2qdisc(struct nl_object *obj) + { + return (struct rtnl_qdisc *) obj; + } + + struct nl_object *class2obj(struct rtnl_class *cl) + { + return OBJ_CAST(cl); + } + + struct rtnl_class *obj2class(struct nl_object *obj) + { + return (struct rtnl_class *) obj; + } + + struct nl_object *cls2obj(struct rtnl_cls *cls) + { + return OBJ_CAST(cls); + } + + struct rtnl_cls *obj2cls(struct nl_object *obj) + { + return (struct rtnl_cls *) obj; + } + + struct rtnl_tc *obj2tc(struct nl_object *obj) + { + return TC_CAST(obj); + } +%}; +extern struct rtnl_qdisc * + rtnl_qdisc_alloc(void); + +extern struct rtnl_qdisc * + rtnl_qdisc_get(struct nl_cache *, int, uint32_t); + +extern struct rtnl_qdisc * + rtnl_qdisc_get_by_parent(struct nl_cache *, int, uint32_t); + +extern int rtnl_qdisc_build_add_request(struct rtnl_qdisc *, int, + struct nl_msg **); +extern int rtnl_qdisc_add(struct nl_sock *, struct rtnl_qdisc *, int); + +extern int rtnl_qdisc_build_update_request(struct rtnl_qdisc *, + struct rtnl_qdisc *, + int, struct nl_msg **); + +extern int rtnl_qdisc_update(struct nl_sock *, struct rtnl_qdisc *, + struct rtnl_qdisc *, int); + +extern int rtnl_qdisc_build_delete_request(struct rtnl_qdisc *, + struct nl_msg **); +extern int rtnl_qdisc_delete(struct nl_sock *, struct rtnl_qdisc *); + +/* */ + +extern struct rtnl_cls *rtnl_cls_alloc(void); +extern void rtnl_cls_put(struct rtnl_cls *); + +extern int rtnl_cls_add(struct nl_sock *, struct rtnl_cls *, int); + +extern int rtnl_cls_delete(struct nl_sock *, struct rtnl_cls *, + int); + +extern void rtnl_cls_set_prio(struct rtnl_cls *, uint16_t); +extern uint16_t rtnl_cls_get_prio(struct rtnl_cls *); + +extern void rtnl_cls_set_protocol(struct rtnl_cls *, uint16_t); +extern uint16_t rtnl_cls_get_protocol(struct rtnl_cls *); + +/* */ + +extern uint32_t rtnl_htb_get_rate2quantum(struct rtnl_qdisc *); +extern int rtnl_htb_set_rate2quantum(struct rtnl_qdisc *, uint32_t); +extern uint32_t rtnl_htb_get_defcls(struct rtnl_qdisc *); +extern int rtnl_htb_set_defcls(struct rtnl_qdisc *, uint32_t); + +extern uint32_t rtnl_htb_get_prio(struct rtnl_class *); +extern int rtnl_htb_set_prio(struct rtnl_class *, uint32_t); +extern uint32_t rtnl_htb_get_rate(struct rtnl_class *); +extern int rtnl_htb_set_rate(struct rtnl_class *, uint32_t); +extern uint32_t rtnl_htb_get_ceil(struct rtnl_class *); +extern int rtnl_htb_set_ceil(struct rtnl_class *, uint32_t); +extern uint32_t rtnl_htb_get_rbuffer(struct rtnl_class *); +extern int rtnl_htb_set_rbuffer(struct rtnl_class *, uint32_t); +extern uint32_t rtnl_htb_get_cbuffer(struct rtnl_class *); +extern int rtnl_htb_set_cbuffer(struct rtnl_class *, uint32_t); +extern uint32_t rtnl_htb_get_quantum(struct rtnl_class *); +extern int rtnl_htb_set_quantum(struct rtnl_class *, uint32_t); +extern int rtnl_htb_get_level(struct rtnl_class *); + +/* */ + +%inline %{ + struct nl_object *addr2obj(struct rtnl_addr *addr) + { + return OBJ_CAST(addr); + } + + struct rtnl_addr *obj2addr(struct nl_object *obj) + { + return (struct rtnl_addr *) obj; + } +%}; + +extern struct rtnl_addr *rtnl_addr_alloc(void); + +extern struct rtnl_addr * + rtnl_addr_get(struct nl_cache *, int, struct nl_addr *); + +extern int rtnl_addr_build_add_request(struct rtnl_addr *, int, + struct nl_msg **); +extern int rtnl_addr_add(struct nl_sock *, struct rtnl_addr *, int); + +extern int rtnl_addr_build_delete_request(struct rtnl_addr *, int, + struct nl_msg **); +extern int rtnl_addr_delete(struct nl_sock *, + struct rtnl_addr *, int); + +%cstring_output_maxsize(char *buf, size_t len) +extern char * rtnl_addr_flags2str(int, char *buf, size_t len); +extern int rtnl_addr_str2flags(const char *); + +extern int rtnl_addr_set_label(struct rtnl_addr *, const char *); +extern char * rtnl_addr_get_label(struct rtnl_addr *); + +extern void rtnl_addr_set_ifindex(struct rtnl_addr *, int); +extern int rtnl_addr_get_ifindex(struct rtnl_addr *); + +extern void rtnl_addr_set_link(struct rtnl_addr *, struct rtnl_link *); +extern struct rtnl_link * + rtnl_addr_get_link(struct rtnl_addr *); +extern void rtnl_addr_set_family(struct rtnl_addr *, int); +extern int rtnl_addr_get_family(struct rtnl_addr *); + +extern void rtnl_addr_set_prefixlen(struct rtnl_addr *, int); +extern int rtnl_addr_get_prefixlen(struct rtnl_addr *); + +extern void rtnl_addr_set_scope(struct rtnl_addr *, int); +extern int rtnl_addr_get_scope(struct rtnl_addr *); + +extern void rtnl_addr_set_flags(struct rtnl_addr *, unsigned int); +extern void rtnl_addr_unset_flags(struct rtnl_addr *, unsigned int); +extern unsigned int rtnl_addr_get_flags(struct rtnl_addr *); + +extern int rtnl_addr_set_local(struct rtnl_addr *, + struct nl_addr *); +extern struct nl_addr *rtnl_addr_get_local(struct rtnl_addr *); + +extern int rtnl_addr_set_peer(struct rtnl_addr *, struct nl_addr *); +extern struct nl_addr *rtnl_addr_get_peer(struct rtnl_addr *); + +extern int rtnl_addr_set_broadcast(struct rtnl_addr *, struct nl_addr *); +extern struct nl_addr *rtnl_addr_get_broadcast(struct rtnl_addr *); + +extern int rtnl_addr_set_multicast(struct rtnl_addr *, struct nl_addr *); +extern struct nl_addr *rtnl_addr_get_multicast(struct rtnl_addr *); + +extern int rtnl_addr_set_anycast(struct rtnl_addr *, struct nl_addr *); +extern struct nl_addr *rtnl_addr_get_anycast(struct rtnl_addr *); + +extern uint32_t rtnl_addr_get_valid_lifetime(struct rtnl_addr *); +extern void rtnl_addr_set_valid_lifetime(struct rtnl_addr *, uint32_t); +extern uint32_t rtnl_addr_get_preferred_lifetime(struct rtnl_addr *); +extern void rtnl_addr_set_preferred_lifetime(struct rtnl_addr *, uint32_t); +extern uint32_t rtnl_addr_get_create_time(struct rtnl_addr *); +extern uint32_t rtnl_addr_get_last_update_time(struct rtnl_addr *); diff --git a/python/netlink/route/link.py b/python/netlink/route/link.py new file mode 100644 index 0000000..36d0e9d --- /dev/null +++ b/python/netlink/route/link.py @@ -0,0 +1,539 @@ +# +# Copyright (c) 2011 Thomas Graf +# + +"""Module providing access to network links + +This module provides an interface to view configured network links, +modify them and to add and delete virtual network links. + +The following is a basic example: + import netlink.core as netlink + import netlink.route.link as link + + sock = netlink.Socket() + sock.connect(netlink.NETLINK_ROUTE) + + cache = link.LinkCache() # create new empty link cache + cache.refill(sock) # fill cache with all configured links + eth0 = cache['eth0'] # lookup link "eth0" + print eth0 # print basic configuration + +The module contains the following public classes: + + - Link -- Represents a network link. Instances can be created directly + via the constructor (empty link objects) or via the refill() + method of a LinkCache. + - LinkCache -- Derived from netlink.Cache, holds any number of + network links (Link instances). Main purpose is to keep + a local list of all network links configured in the + kernel. + +The following public functions exist: + - get_from_kernel(socket, name) + +""" + +from __future__ import absolute_import + +__version__ = '0.1' +__all__ = [ + 'LinkCache', + 'Link', + 'get_from_kernel', +] + +import socket +from .. import core as netlink +from .. import capi as core_capi +from . import capi as capi +from .links import inet as inet +from .. import util as util + +# Link statistics definitions +RX_PACKETS = 0 +TX_PACKETS = 1 +RX_BYTES = 2 +TX_BYTES = 3 +RX_ERRORS = 4 +TX_ERRORS = 5 +RX_DROPPED = 6 +TX_DROPPED = 7 +RX_COMPRESSED = 8 +TX_COMPRESSED = 9 +RX_FIFO_ERR = 10 +TX_FIFO_ERR = 11 +RX_LEN_ERR = 12 +RX_OVER_ERR = 13 +RX_CRC_ERR = 14 +RX_FRAME_ERR = 15 +RX_MISSED_ERR = 16 +TX_ABORT_ERR = 17 +TX_CARRIER_ERR = 18 +TX_HBEAT_ERR = 19 +TX_WIN_ERR = 20 +COLLISIONS = 21 +MULTICAST = 22 +IP6_INPKTS = 23 +IP6_INHDRERRORS = 24 +IP6_INTOOBIGERRORS = 25 +IP6_INNOROUTES = 26 +IP6_INADDRERRORS = 27 +IP6_INUNKNOWNPROTOS = 28 +IP6_INTRUNCATEDPKTS = 29 +IP6_INDISCARDS = 30 +IP6_INDELIVERS = 31 +IP6_OUTFORWDATAGRAMS = 32 +IP6_OUTPKTS = 33 +IP6_OUTDISCARDS = 34 +IP6_OUTNOROUTES = 35 +IP6_REASMTIMEOUT = 36 +IP6_REASMREQDS = 37 +IP6_REASMOKS = 38 +IP6_REASMFAILS = 39 +IP6_FRAGOKS = 40 +IP6_FRAGFAILS = 41 +IP6_FRAGCREATES = 42 +IP6_INMCASTPKTS = 43 +IP6_OUTMCASTPKTS = 44 +IP6_INBCASTPKTS = 45 +IP6_OUTBCASTPKTS = 46 +IP6_INOCTETS = 47 +IP6_OUTOCTETS = 48 +IP6_INMCASTOCTETS = 49 +IP6_OUTMCASTOCTETS = 50 +IP6_INBCASTOCTETS = 51 +IP6_OUTBCASTOCTETS = 52 +ICMP6_INMSGS = 53 +ICMP6_INERRORS = 54 +ICMP6_OUTMSGS = 55 +ICMP6_OUTERRORS = 56 + +class LinkCache(netlink.Cache): + """Cache of network links""" + + def __init__(self, family=socket.AF_UNSPEC, cache=None): + if not cache: + cache = self._alloc_cache_name('route/link') + + self._info_module = None + self._protocol = netlink.NETLINK_ROUTE + self._nl_cache = cache + self._set_arg1(family) + + def __getitem__(self, key): + if type(key) is int: + link = capi.rtnl_link_get(self._nl_cache, key) + else: + link = capi.rtnl_link_get_by_name(self._nl_cache, key) + + if link is None: + raise KeyError() + else: + return Link.from_capi(link) + + @staticmethod + def _new_object(obj): + return Link(obj) + + def _new_cache(self, cache): + return LinkCache(family=self.arg1, cache=cache) + +class Link(netlink.Object): + """Network link""" + + def __init__(self, obj=None): + netlink.Object.__init__(self, 'route/link', 'link', obj) + self._rtnl_link = self._obj2type(self._nl_object) + + if self.type: + self._module_lookup('netlink.route.links.' + self.type) + + self.inet = inet.InetLink(self) + self.af = {'inet' : self.inet } + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, tb): + if exc_type is None: + self.change() + else: + return false + + @classmethod + def from_capi(cls, obj): + return cls(capi.link2obj(obj)) + + @staticmethod + def _obj2type(obj): + return capi.obj2link(obj) + + def __cmp__(self, other): + return self.ifindex - other.ifindex + + @staticmethod + def _new_instance(obj): + if not obj: + raise ValueError() + + return Link(obj) + + @property + @netlink.nlattr(type=int, immutable=True, fmt=util.num) + def ifindex(self): + """interface index""" + return capi.rtnl_link_get_ifindex(self._rtnl_link) + + @ifindex.setter + def ifindex(self, value): + capi.rtnl_link_set_ifindex(self._rtnl_link, int(value)) + + # ifindex is immutable but we assume that if _orig does not + # have an ifindex specified, it was meant to be given here + if capi.rtnl_link_get_ifindex(self._orig) == 0: + capi.rtnl_link_set_ifindex(self._orig, int(value)) + + @property + @netlink.nlattr(type=str, fmt=util.bold) + def name(self): + """Name of link""" + return capi.rtnl_link_get_name(self._rtnl_link) + + @name.setter + def name(self, value): + capi.rtnl_link_set_name(self._rtnl_link, value) + + # name is the secondary identifier, if _orig does not have + # the name specified yet, assume it was meant to be specified + # here. ifindex will always take priority, therefore if ifindex + # is specified as well, this will be ignored automatically. + if capi.rtnl_link_get_name(self._orig) is None: + capi.rtnl_link_set_name(self._orig, value) + + @property + @netlink.nlattr(type=str, fmt=util.string) + def flags(self): + """Flags + Setting this property will *Not* reset flags to value you supply in + Examples: + link.flags = '+xxx' # add xxx flag + link.flags = 'xxx' # exactly the same + link.flags = '-xxx' # remove xxx flag + link.flags = [ '+xxx', '-yyy' ] # list operation + """ + flags = capi.rtnl_link_get_flags(self._rtnl_link) + return capi.rtnl_link_flags2str(flags, 256)[0].split(',') + + def _set_flag(self, flag): + if flag.startswith('-'): + i = capi.rtnl_link_str2flags(flag[1:]) + capi.rtnl_link_unset_flags(self._rtnl_link, i) + elif flag.startswith('+'): + i = capi.rtnl_link_str2flags(flag[1:]) + capi.rtnl_link_set_flags(self._rtnl_link, i) + else: + i = capi.rtnl_link_str2flags(flag) + capi.rtnl_link_set_flags(self._rtnl_link, i) + + @flags.setter + def flags(self, value): + if not (type(value) is str): + for flag in value: + self._set_flag(flag) + else: + self._set_flag(value) + + @property + @netlink.nlattr(type=int, fmt=util.num) + def mtu(self): + """Maximum Transmission Unit""" + return capi.rtnl_link_get_mtu(self._rtnl_link) + + @mtu.setter + def mtu(self, value): + capi.rtnl_link_set_mtu(self._rtnl_link, int(value)) + + @property + @netlink.nlattr(type=int, immutable=True, fmt=util.num) + def family(self): + """Address family""" + return capi.rtnl_link_get_family(self._rtnl_link) + + @family.setter + def family(self, value): + capi.rtnl_link_set_family(self._rtnl_link, value) + + @property + @netlink.nlattr(type=str, fmt=util.addr) + def address(self): + """Hardware address (MAC address)""" + a = capi.rtnl_link_get_addr(self._rtnl_link) + return netlink.AbstractAddress(a) + + @address.setter + def address(self, value): + capi.rtnl_link_set_addr(self._rtnl_link, value._addr) + + @property + @netlink.nlattr(type=str, fmt=util.addr) + def broadcast(self): + """Hardware broadcast address""" + a = capi.rtnl_link_get_broadcast(self._rtnl_link) + return netlink.AbstractAddress(a) + + @broadcast.setter + def broadcast(self, value): + capi.rtnl_link_set_broadcast(self._rtnl_link, value._addr) + + @property + @netlink.nlattr(type=str, immutable=True, fmt=util.string) + def qdisc(self): + """Name of qdisc (cannot be changed)""" + return capi.rtnl_link_get_qdisc(self._rtnl_link) + + @qdisc.setter + def qdisc(self, value): + capi.rtnl_link_set_qdisc(self._rtnl_link, value) + + @property + @netlink.nlattr(type=int, fmt=util.num) + def txqlen(self): + """Length of transmit queue""" + return capi.rtnl_link_get_txqlen(self._rtnl_link) + + @txqlen.setter + def txqlen(self, value): + capi.rtnl_link_set_txqlen(self._rtnl_link, int(value)) + + @property + @netlink.nlattr(type=str, immutable=True, fmt=util.string) + def arptype(self): + """Type of link (cannot be changed)""" + type_ = capi.rtnl_link_get_arptype(self._rtnl_link) + return core_capi.nl_llproto2str(type_, 64)[0] + + @arptype.setter + def arptype(self, value): + i = core_capi.nl_str2llproto(value) + capi.rtnl_link_set_arptype(self._rtnl_link, i) + + @property + @netlink.nlattr(type=str, immutable=True, fmt=util.string, title='state') + def operstate(self): + """Operational status""" + operstate = capi.rtnl_link_get_operstate(self._rtnl_link) + return capi.rtnl_link_operstate2str(operstate, 32)[0] + + @operstate.setter + def operstate(self, value): + i = capi.rtnl_link_str2operstate(value) + capi.rtnl_link_set_operstate(self._rtnl_link, i) + + @property + @netlink.nlattr(type=str, immutable=True, fmt=util.string) + def mode(self): + """Link mode""" + mode = capi.rtnl_link_get_linkmode(self._rtnl_link) + return capi.rtnl_link_mode2str(mode, 32)[0] + + @mode.setter + def mode(self, value): + i = capi.rtnl_link_str2mode(value) + capi.rtnl_link_set_linkmode(self._rtnl_link, i) + + @property + @netlink.nlattr(type=str, fmt=util.string) + def alias(self): + """Interface alias (SNMP)""" + return capi.rtnl_link_get_ifalias(self._rtnl_link) + + @alias.setter + def alias(self, value): + capi.rtnl_link_set_ifalias(self._rtnl_link, value) + + @property + @netlink.nlattr(type=str, fmt=util.string) + def type(self): + """Link type""" + return capi.rtnl_link_get_type(self._rtnl_link) + + @type.setter + def type(self, value): + if capi.rtnl_link_set_type(self._rtnl_link, value) < 0: + raise NameError('unknown info type') + + self._module_lookup('netlink.route.links.' + value) + + def get_stat(self, stat): + """Retrieve statistical information""" + if type(stat) is str: + stat = capi.rtnl_link_str2stat(stat) + if stat < 0: + raise NameError('unknown name of statistic') + + return capi.rtnl_link_get_stat(self._rtnl_link, stat) + + def add(self, sock=None, flags=None): + if not sock: + sock = netlink.lookup_socket(netlink.NETLINK_ROUTE) + + if not flags: + flags = netlink.NLM_F_CREATE + + ret = capi.rtnl_link_add(sock._sock, self._rtnl_link, flags) + if ret < 0: + raise netlink.KernelError(ret) + + def change(self, sock=None, flags=0): + """Commit changes made to the link object""" + if sock is None: + sock = netlink.lookup_socket(netlink.NETLINK_ROUTE) + + if not self._orig: + raise netlink.NetlinkError('Original link not available') + ret = capi.rtnl_link_change(sock._sock, self._orig, self._rtnl_link, flags) + if ret < 0: + raise netlink.KernelError(ret) + + def delete(self, sock=None): + """Attempt to delete this link in the kernel""" + if sock is None: + sock = netlink.lookup_socket(netlink.NETLINK_ROUTE) + + ret = capi.rtnl_link_delete(sock._sock, self._rtnl_link) + if ret < 0: + raise netlink.KernelError(ret) + + ################################################################### + # private properties + # + # Used for formatting output. USE AT OWN RISK + @property + def _state(self): + if 'up' in self.flags: + buf = util.good('up') + if 'lowerup' not in self.flags: + buf += ' ' + util.bad('no-carrier') + else: + buf = util.bad('down') + return buf + + @property + def _brief(self): + return self._module_brief() + self._foreach_af('brief') + + @property + def _flags(self): + ignore = [ + 'up', + 'running', + 'lowerup', + ] + return ','.join([flag for flag in self.flags if flag not in ignore]) + + def _foreach_af(self, name, args=None): + buf = '' + for af in self.af: + try: + func = getattr(self.af[af], name) + s = str(func(args)) + if len(s) > 0: + buf += ' ' + s + except AttributeError: + pass + return buf + + def format(self, details=False, stats=False, indent=''): + """Return link as formatted text""" + fmt = util.MyFormatter(self, indent) + + buf = fmt.format('{a|ifindex} {a|name} {a|arptype} {a|address} '\ + '{a|_state} <{a|_flags}> {a|_brief}') + + if details: + buf += fmt.nl('\t{t|mtu} {t|txqlen} {t|weight} '\ + '{t|qdisc} {t|operstate}') + buf += fmt.nl('\t{t|broadcast} {t|alias}') + + buf += self._foreach_af('details', fmt) + + if stats: + l = [['Packets', RX_PACKETS, TX_PACKETS], + ['Bytes', RX_BYTES, TX_BYTES], + ['Errors', RX_ERRORS, TX_ERRORS], + ['Dropped', RX_DROPPED, TX_DROPPED], + ['Compressed', RX_COMPRESSED, TX_COMPRESSED], + ['FIFO Errors', RX_FIFO_ERR, TX_FIFO_ERR], + ['Length Errors', RX_LEN_ERR, None], + ['Over Errors', RX_OVER_ERR, None], + ['CRC Errors', RX_CRC_ERR, None], + ['Frame Errors', RX_FRAME_ERR, None], + ['Missed Errors', RX_MISSED_ERR, None], + ['Abort Errors', None, TX_ABORT_ERR], + ['Carrier Errors', None, TX_CARRIER_ERR], + ['Heartbeat Errors', None, TX_HBEAT_ERR], + ['Window Errors', None, TX_WIN_ERR], + ['Collisions', None, COLLISIONS], + ['Multicast', None, MULTICAST], + ['', None, None], + ['Ipv6:', None, None], + ['Packets', IP6_INPKTS, IP6_OUTPKTS], + ['Bytes', IP6_INOCTETS, IP6_OUTOCTETS], + ['Discards', IP6_INDISCARDS, IP6_OUTDISCARDS], + ['Multicast Packets', IP6_INMCASTPKTS, IP6_OUTMCASTPKTS], + ['Multicast Bytes', IP6_INMCASTOCTETS, IP6_OUTMCASTOCTETS], + ['Broadcast Packets', IP6_INBCASTPKTS, IP6_OUTBCASTPKTS], + ['Broadcast Bytes', IP6_INBCASTOCTETS, IP6_OUTBCASTOCTETS], + ['Delivers', IP6_INDELIVERS, None], + ['Forwarded', None, IP6_OUTFORWDATAGRAMS], + ['No Routes', IP6_INNOROUTES, IP6_OUTNOROUTES], + ['Header Errors', IP6_INHDRERRORS, None], + ['Too Big Errors', IP6_INTOOBIGERRORS, None], + ['Address Errors', IP6_INADDRERRORS, None], + ['Unknown Protocol', IP6_INUNKNOWNPROTOS, None], + ['Truncated Packets', IP6_INTRUNCATEDPKTS, None], + ['Reasm Timeouts', IP6_REASMTIMEOUT, None], + ['Reasm Requests', IP6_REASMREQDS, None], + ['Reasm Failures', IP6_REASMFAILS, None], + ['Reasm OK', IP6_REASMOKS, None], + ['Frag Created', None, IP6_FRAGCREATES], + ['Frag Failures', None, IP6_FRAGFAILS], + ['Frag OK', None, IP6_FRAGOKS], + ['', None, None], + ['ICMPv6:', None, None], + ['Messages', ICMP6_INMSGS, ICMP6_OUTMSGS], + ['Errors', ICMP6_INERRORS, ICMP6_OUTERRORS]] + + buf += '\n\t%s%s%s%s\n' % (33 * ' ', util.title('RX'), + 15 * ' ', util.title('TX')) + + for row in l: + row[0] = util.kw(row[0]) + row[1] = self.get_stat(row[1]) if row[1] else '' + row[2] = self.get_stat(row[2]) if row[2] else '' + buf += '\t{0[0]:27} {0[1]:>16} {0[2]:>16}\n'.format(row) + + buf += self._foreach_af('stats') + + return buf + +def get(name, sock=None): + """Lookup Link object directly from kernel""" + if not name: + raise ValueError() + + if not sock: + sock = netlink.lookup_socket(netlink.NETLINK_ROUTE) + + link = capi.get_from_kernel(sock._sock, 0, name) + if not link: + return None + + return Link.from_capi(link) + +_link_cache = LinkCache() + +def resolve(name): + _link_cache.refill() + return _link_cache[name] diff --git a/python/netlink/route/links/__init__.py b/python/netlink/route/links/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/python/netlink/route/links/dummy.py b/python/netlink/route/links/dummy.py new file mode 100644 index 0000000..e9491cc --- /dev/null +++ b/python/netlink/route/links/dummy.py @@ -0,0 +1,26 @@ +# +# Copyright (c) 2011 Thomas Graf +# + +"""Dummy + +""" +from __future__ import absolute_import + +__version__ = '1.0' +__all__ = [ + 'init', +] + + +class DummyLink(object): + def __init__(self, link): + self._rtnl_link = link + + @staticmethod + def brief(): + return 'dummy' + +def init(link): + link.dummy = DummyLink(link._rtnl_link) + return link.dummy diff --git a/python/netlink/route/links/inet.py b/python/netlink/route/links/inet.py new file mode 100644 index 0000000..f5f45cb --- /dev/null +++ b/python/netlink/route/links/inet.py @@ -0,0 +1,158 @@ +# +# Copyright (c) 2011 Thomas Graf +# + +"""IPv4 + +""" + +from __future__ import absolute_import + +__all__ = [ + '', +] + +from ... import core as netlink +from .. import capi as capi +from ... import util as util +DEVCONF_FORWARDING = 1 +DEVCONF_MC_FORWARDING = 2 +DEVCONF_PROXY_ARP = 3 +DEVCONF_ACCEPT_REDIRECTS = 4 +DEVCONF_SECURE_REDIRECTS = 5 +DEVCONF_SEND_REDIRECTS = 6 +DEVCONF_SHARED_MEDIA = 7 +DEVCONF_RP_FILTER = 8 +DEVCONF_ACCEPT_SOURCE_ROUTE = 9 +DEVCONF_BOOTP_RELAY = 10 +DEVCONF_LOG_MARTIANS = 11 +DEVCONF_TAG = 12 +DEVCONF_ARPFILTER = 13 +DEVCONF_MEDIUM_ID = 14 +DEVCONF_NOXFRM = 15 +DEVCONF_NOPOLICY = 16 +DEVCONF_FORCE_IGMP_VERSION = 17 +DEVCONF_ARP_ANNOUNCE = 18 +DEVCONF_ARP_IGNORE = 19 +DEVCONF_PROMOTE_SECONDARIES = 20 +DEVCONF_ARP_ACCEPT = 21 +DEVCONF_ARP_NOTIFY = 22 +DEVCONF_ACCEPT_LOCAL = 23 +DEVCONF_SRC_VMARK = 24 +DEVCONF_PROXY_ARP_PVLAN = 25 +DEVCONF_MAX = DEVCONF_PROXY_ARP_PVLAN + +def _resolve(id): + if type(id) is str: + id = capi.rtnl_link_inet_str2devconf(id)[0] + if id < 0: + raise NameError('unknown configuration id') + return id + +class InetLink(object): + def __init__(self, link): + self._link = link + + def details(self, fmt): + buf = fmt.nl('\n\t{0}\n\t'.format(util.title('Configuration:'))) + + for i in range(DEVCONF_FORWARDING, DEVCONF_MAX+1): + if i & 1 and i > 1: + buf += fmt.nl('\t') + txt = util.kw(capi.rtnl_link_inet_devconf2str(i, 32)[0]) + buf += fmt.format('{0:28s} {1:12} ', txt, + self.get_conf(i)) + + + return buf + + def get_conf(self, id): + return capi.inet_get_conf(self._link._rtnl_link, _resolve(id)) + + def set_conf(self, id, value): + return capi.rtnl_link_inet_set_conf(self._link._rtnl_link, + _resolve(id), int(value)) + + @property + @netlink.nlattr(type=bool, fmt=util.boolean) + def forwarding(self): + return bool(self.get_conf(DEVCONF_FORWARDING)) + + @forwarding.setter + def forwarding(self, value): + self.set_conf(DEVCONF_FORWARDING, int(value)) + + + @property + @netlink.nlattr(type=bool, fmt=util.boolean) + def mc_forwarding(self): + return bool(self.get_conf(DEVCONF_MC_FORWARDING)) + + @mc_forwarding.setter + def mc_forwarding(self, value): + self.set_conf(DEVCONF_MC_FORWARDING, int(value)) + + + @property + @netlink.nlattr(type=bool, fmt=util.boolean) + def proxy_arp(self): + return bool(self.get_conf(DEVCONF_PROXY_ARP)) + + @proxy_arp.setter + def proxy_arp(self, value): + self.set_conf(DEVCONF_PROXY_ARP, int(value)) + + @property + @netlink.nlattr(type=bool, fmt=util.boolean) + def accept_redirects(self): + return bool(self.get_conf(DEVCONF_ACCEPT_REDIRECTS)) + + @accept_redirects.setter + def accept_redirects(self, value): + self.set_conf(DEVCONF_ACCEPT_REDIRECTS, int(value)) + + @property + @netlink.nlattr(type=bool, fmt=util.boolean) + def secure_redirects(self): + return bool(self.get_conf(DEVCONF_SECURE_REDIRECTS)) + + @secure_redirects.setter + def secure_redirects(self, value): + self.set_conf(DEVCONF_SECURE_REDIRECTS, int(value)) + + @property + @netlink.nlattr(type=bool, fmt=util.boolean) + def send_redirects(self): + return bool(self.get_conf(DEVCONF_SEND_REDIRECTS)) + + @send_redirects.setter + def send_redirects(self, value): + self.set_conf(DEVCONF_SEND_REDIRECTS, int(value)) + + @property + @netlink.nlattr(type=bool, fmt=util.boolean) + def shared_media(self): + return bool(self.get_conf(DEVCONF_SHARED_MEDIA)) + + @shared_media.setter + def shared_media(self, value): + self.set_conf(DEVCONF_SHARED_MEDIA, int(value)) + +# IPV4_DEVCONF_RP_FILTER, +# IPV4_DEVCONF_ACCEPT_SOURCE_ROUTE, +# IPV4_DEVCONF_BOOTP_RELAY, +# IPV4_DEVCONF_LOG_MARTIANS, +# IPV4_DEVCONF_TAG, +# IPV4_DEVCONF_ARPFILTER, +# IPV4_DEVCONF_MEDIUM_ID, +# IPV4_DEVCONF_NOXFRM, +# IPV4_DEVCONF_NOPOLICY, +# IPV4_DEVCONF_FORCE_IGMP_VERSION, +# IPV4_DEVCONF_ARP_ANNOUNCE, +# IPV4_DEVCONF_ARP_IGNORE, +# IPV4_DEVCONF_PROMOTE_SECONDARIES, +# IPV4_DEVCONF_ARP_ACCEPT, +# IPV4_DEVCONF_ARP_NOTIFY, +# IPV4_DEVCONF_ACCEPT_LOCAL, +# IPV4_DEVCONF_SRC_VMARK, +# IPV4_DEVCONF_PROXY_ARP_PVLAN, diff --git a/python/netlink/route/links/vlan.py b/python/netlink/route/links/vlan.py new file mode 100644 index 0000000..70045d5 --- /dev/null +++ b/python/netlink/route/links/vlan.py @@ -0,0 +1,71 @@ +# +# Copyright (c) 2011 Thomas Graf +# + +"""VLAN network link + +""" + +from __future__ import absolute_import + + +from ... import core as netlink +from .. import capi as capi +class VLANLink(object): + def __init__(self, link): + self._link = link + + @property + @netlink.nlattr(type=int) + def id(self): + """vlan identifier""" + return capi.rtnl_link_vlan_get_id(self._link) + + @id.setter + def id(self, value): + capi.rtnl_link_vlan_set_id(self._link, int(value)) + + @property + @netlink.nlattr(type=str) + def flags(self): + """ VLAN flags + Setting this property will *Not* reset flags to value you supply in + Examples: + link.flags = '+xxx' # add xxx flag + link.flags = 'xxx' # exactly the same + link.flags = '-xxx' # remove xxx flag + link.flags = [ '+xxx', '-yyy' ] # list operation + """ + flags = capi.rtnl_link_vlan_get_flags(self._link) + return capi.rtnl_link_vlan_flags2str(flags, 256)[0].split(',') + + def _set_flag(self, flag): + if flag.startswith('-'): + i = capi.rtnl_link_vlan_str2flags(flag[1:]) + capi.rtnl_link_vlan_unset_flags(self._link, i) + elif flag.startswith('+'): + i = capi.rtnl_link_vlan_str2flags(flag[1:]) + capi.rtnl_link_vlan_set_flags(self._link, i) + else: + i = capi.rtnl_link_vlan_str2flags(flag) + capi.rtnl_link_vlan_set_flags(self._link, i) + + @flags.setter + def flags(self, value): + if type(value) is list: + for flag in value: + self._set_flag(flag) + else: + self._set_flag(value) + + ################################################################### + # TODO: + # - ingress map + # - egress map + + def brief(self): + return 'vlan-id {0}'.format(self.id) + +def init(link): + link.vlan = VLANLink(link._link) + return link.vlan diff --git a/python/netlink/route/qdisc/__init__.py b/python/netlink/route/qdisc/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/python/netlink/route/qdisc/htb.py b/python/netlink/route/qdisc/htb.py new file mode 100644 index 0000000..d051c8d --- /dev/null +++ b/python/netlink/route/qdisc/htb.py @@ -0,0 +1,145 @@ +# +# Copyright (c) 2011 Thomas Graf +# + +"""HTB qdisc + +""" + +from __future__ import absolute_import + +from ... import core as netlink +from ... import util as util +from .. import capi as capi +from .. import tc as tc + +class HTBQdisc(object): + def __init__(self, qdisc): + self._qdisc = qdisc + + @property + @netlink.nlattr(type=int) + def default_class(self): + return tc.Handle(capi.rtnl_htb_get_defcls(self._qdisc._rtnl_qdisc)) + + @default_class.setter + def default_class(self, value): + capi.rtnl_htb_set_defcls(self._qdisc._rtnl_qdisc, int(value)) + + @property + @netlink.nlattr('r2q', type=int) + def r2q(self): + return capi.rtnl_htb_get_rate2quantum(self._qdisc._rtnl_qdisc) + + @r2q.setter + def r2q(self, value): + capi.rtnl_htb_get_rate2quantum(self._qdisc._rtnl_qdisc, + int(value)) + + def brief(self): + fmt = util.MyFormatter(self) + + ret = ' {s|default-class!k} {a|default_class}' + + if self.r2q: + ret += ' {s|r2q!k} {a|r2q}' + + return fmt.format(ret) + +class HTBClass(object): + def __init__(self, cl): + self._class = cl + + @property + @netlink.nlattr(type=str) + def rate(self): + rate = capi.rtnl_htb_get_rate(self._class._rtnl_class) + return util.Rate(rate) + + @rate.setter + def rate(self, value): + capi.rtnl_htb_set_rate(self._class._rtnl_class, int(value)) + + @property + @netlink.nlattr(type=str) + def ceil(self): + ceil = capi.rtnl_htb_get_ceil(self._class._rtnl_class) + return util.Rate(ceil) + + @ceil.setter + def ceil(self, value): + capi.rtnl_htb_set_ceil(self._class._rtnl_class, int(value)) + + @property + @netlink.nlattr(type=str) + def burst(self): + burst = capi.rtnl_htb_get_rbuffer(self._class._rtnl_class) + return util.Size(burst) + + @burst.setter + def burst(self, value): + capi.rtnl_htb_set_rbuffer(self._class._rtnl_class, int(value)) + + @property + @netlink.nlattr(type=str) + def ceil_burst(self): + burst = capi.rtnl_htb_get_cbuffer(self._class._rtnl_class) + return util.Size(burst) + + @ceil_burst.setter + def ceil_burst(self, value): + capi.rtnl_htb_set_cbuffer(self._class._rtnl_class, int(value)) + + @property + @netlink.nlattr(type=int) + def prio(self): + return capi.rtnl_htb_get_prio(self._class._rtnl_class) + + @prio.setter + def prio(self, value): + capi.rtnl_htb_set_prio(self._class._rtnl_class, int(value)) + + @property + @netlink.nlattr(type=int) + def quantum(self): + return capi.rtnl_htb_get_quantum(self._class._rtnl_class) + + @quantum.setter + def quantum(self, value): + capi.rtnl_htb_set_quantum(self._class._rtnl_class, int(value)) + + @property + @netlink.nlattr(type=int) + def level(self): + return capi.rtnl_htb_get_level(self._class._rtnl_class) + + @level.setter + def level(self, value): + capi.rtnl_htb_set_level(self._class._rtnl_class, int(value)) + + def brief(self): + fmt = util.MyFormatter(self) + + ret = ' {t|prio} {t|rate}' + + if self.rate != self.ceil: + ret += ' {s|borrow-up-to!k} {a|ceil}' + + ret += ' {t|burst}' + + return fmt.format(ret) + + def details(self): + fmt = util.MyFormatter(self) + + return fmt.nl('\t{t|level} {t|quantum}') + +def init_qdisc(qdisc): + qdisc.htb = HTBQdisc(qdisc) + return qdisc.htb + +def init_class(cl): + cl.htb = HTBClass(cl) + return cl.htb + +#extern void rtnl_htb_set_quantum(struct rtnl_class *, uint32_t quantum); diff --git a/python/netlink/route/tc.py b/python/netlink/route/tc.py new file mode 100644 index 0000000..0689b71 --- /dev/null +++ b/python/netlink/route/tc.py @@ -0,0 +1,595 @@ +# +# Copyright (c) 2011 Thomas Graf +# +from __future__ import absolute_import + +__all__ = [ + 'TcCache', + 'Tc', + 'QdiscCache', + 'Qdisc', + 'TcClassCache', + 'TcClass', +] + +from .. import core as netlink +from . import capi as capi +from .. import util as util +from . import link as Link + +TC_PACKETS = 0 +TC_BYTES = 1 +TC_RATE_BPS = 2 +TC_RATE_PPS = 3 +TC_QLEN = 4 +TC_BACKLOG = 5 +TC_DROPS = 6 +TC_REQUEUES = 7 +TC_OVERLIMITS = 9 + +TC_H_ROOT = 0xFFFFFFFF +TC_H_INGRESS = 0xFFFFFFF1 + +STAT_PACKETS = 0 +STAT_BYTES = 1 +STAT_RATE_BPS = 2 +STAT_RATE_PPS = 3 +STAT_QLEN = 4 +STAT_BACKLOG = 5 +STAT_DROPS = 6 +STAT_REQUEUES = 7 +STAT_OVERLIMITS = 8 +STAT_MAX = STAT_OVERLIMITS + + +class Handle(object): + """ Traffic control handle + + Representation of a traffic control handle which uniquely identifies + each traffic control object in its link namespace. + + handle = tc.Handle('10:20') + handle = tc.handle('root') + print int(handle) + print str(handle) + """ + def __init__(self, val=None): + if type(val) is str: + val = capi.tc_str2handle(val) + elif not val: + val = 0 + + self._val = int(val) + + def __cmp__(self, other): + if other is None: + other = 0 + + if isinstance(other, Handle): + return int(self) - int(other) + elif isinstance(other, int): + return int(self) - other + else: + raise TypeError() + + def __int__(self): + return self._val + + def __str__(self): + return capi.rtnl_tc_handle2str(self._val, 64)[0] + + def isroot(self): + return self._val == TC_H_ROOT or self._val == TC_H_INGRESS + +class TcCache(netlink.Cache): + """Cache of traffic control object""" + + def __getitem__(self, key): + raise NotImplementedError() + +class Tc(netlink.Object): + def __cmp__(self, other): + diff = self.ifindex - other.ifindex + if diff == 0: + diff = int(self.handle) - int(other.handle) + return diff + + def _tc_module_lookup(self): + self._module_lookup(self._module_path + self.kind, + 'init_' + self._name) + + @property + def root(self): + """True if tc object is a root object""" + return self.parent.isroot() + + @property + def ifindex(self): + """interface index""" + return capi.rtnl_tc_get_ifindex(self._rtnl_tc) + + @ifindex.setter + def ifindex(self, value): + capi.rtnl_tc_set_ifindex(self._rtnl_tc, int(value)) + + @property + def link(self): + link = capi.rtnl_tc_get_link(self._rtnl_tc) + if not link: + return None + + return Link.Link.from_capi(link) + + @link.setter + def link(self, value): + capi.rtnl_tc_set_link(self._rtnl_tc, value._link) + + @property + def mtu(self): + return capi.rtnl_tc_get_mtu(self._rtnl_tc) + + @mtu.setter + def mtu(self, value): + capi.rtnl_tc_set_mtu(self._rtnl_tc, int(value)) + + @property + def mpu(self): + return capi.rtnl_tc_get_mpu(self._rtnl_tc) + + @mpu.setter + def mpu(self, value): + capi.rtnl_tc_set_mpu(self._rtnl_tc, int(value)) + + @property + def overhead(self): + return capi.rtnl_tc_get_overhead(self._rtnl_tc) + + @overhead.setter + def overhead(self, value): + capi.rtnl_tc_set_overhead(self._rtnl_tc, int(value)) + + @property + def linktype(self): + return capi.rtnl_tc_get_linktype(self._rtnl_tc) + + @linktype.setter + def linktype(self, value): + capi.rtnl_tc_set_linktype(self._rtnl_tc, int(value)) + + @property + @netlink.nlattr(fmt=util.handle) + def handle(self): + return Handle(capi.rtnl_tc_get_handle(self._rtnl_tc)) + + @handle.setter + def handle(self, value): + capi.rtnl_tc_set_handle(self._rtnl_tc, int(value)) + + @property + @netlink.nlattr(fmt=util.handle) + def parent(self): + return Handle(capi.rtnl_tc_get_parent(self._rtnl_tc)) + + @parent.setter + def parent(self, value): + capi.rtnl_tc_set_parent(self._rtnl_tc, int(value)) + + @property + @netlink.nlattr(fmt=util.bold) + def kind(self): + return capi.rtnl_tc_get_kind(self._rtnl_tc) + + @kind.setter + def kind(self, value): + capi.rtnl_tc_set_kind(self._rtnl_tc, value) + self._tc_module_lookup() + + def get_stat(self, id): + return capi.rtnl_tc_get_stat(self._rtnl_tc, id) + + @property + def _dev(self): + buf = util.kw('dev') + ' ' + + if self.link: + return buf + util.string(self.link.name) + else: + return buf + util.num(self.ifindex) + + def brief(self, title, nodev=False, noparent=False): + ret = title + ' {a|kind} {a|handle}' + + if not nodev: + ret += ' {a|_dev}' + + if not noparent: + ret += ' {t|parent}' + + return ret + self._module_brief() + + @staticmethod + def details(): + return '{t|mtu} {t|mpu} {t|overhead} {t|linktype}' + + @property + def packets(self): + return self.get_stat(STAT_PACKETS) + + @property + def bytes(self): + return self.get_stat(STAT_BYTES) + + @property + def qlen(self): + return self.get_stat(STAT_QLEN) + + @staticmethod + def stats(fmt): + return fmt.nl('{t|packets} {t|bytes} {t|qlen}') + +class QdiscCache(netlink.Cache): + """Cache of qdiscs""" + + def __init__(self, cache=None): + if not cache: + cache = self._alloc_cache_name('route/qdisc') + + self._protocol = netlink.NETLINK_ROUTE + self._nl_cache = cache + +# def __getitem__(self, key): +# if type(key) is int: +# link = capi.rtnl_link_get(self._this, key) +# elif type(key) is str: +# link = capi.rtnl_link_get_by_name(self._this, key) +# +# if qdisc is None: +# raise KeyError() +# else: +# return Qdisc._from_capi(capi.qdisc2obj(qdisc)) + + @staticmethod + def _new_object(obj): + return Qdisc(obj) + + @staticmethod + def _new_cache(cache): + return QdiscCache(cache=cache) + +class Qdisc(Tc): + """Queueing discipline""" + + def __init__(self, obj=None): + netlink.Object.__init__(self, 'route/qdisc', 'qdisc', obj) + self._module_path = 'netlink.route.qdisc.' + self._rtnl_qdisc = self._obj2type(self._nl_object) + self._rtnl_tc = capi.obj2tc(self._nl_object) + + if self.kind: + self._tc_module_lookup() + + @classmethod + def from_capi(cls, obj): + return cls(capi.qdisc2obj(obj)) + + @staticmethod + def _obj2type(obj): + return capi.obj2qdisc(obj) + + @staticmethod + def _new_instance(obj): + if not obj: + raise ValueError() + + return Qdisc(obj) + + @property + def childs(self): + ret = [] + + if int(self.handle): + ret += get_cls(self.ifindex, parent=self.handle) + + if self.root: + ret += get_class(self.ifindex, parent=TC_H_ROOT) + + ret += get_class(self.ifindex, parent=self.handle) + + return ret + +# def add(self, socket, flags=None): +# if not flags: +# flags = netlink.NLM_F_CREATE +# +# ret = capi.rtnl_link_add(socket._sock, self._link, flags) +# if ret < 0: +# raise netlink.KernelError(ret) +# +# def change(self, socket, flags=0): +# """Commit changes made to the link object""" +# if not self._orig: +# raise NetlinkError('Original link not available') +# ret = capi.rtnl_link_change(socket._sock, self._orig, self._link, flags) +# if ret < 0: +# raise netlink.KernelError(ret) +# +# def delete(self, socket): +# """Attempt to delete this link in the kernel""" +# ret = capi.rtnl_link_delete(socket._sock, self._link) +# if ret < 0: +# raise netlink.KernelError(ret) + + def format(self, details=False, stats=False, nodev=False, + noparent=False, indent=''): + """Return qdisc as formatted text""" + fmt = util.MyFormatter(self, indent) + + buf = fmt.format(self.brief('qdisc', nodev, noparent)) + + if details: + buf += fmt.nl('\t' + self.details()) + + if stats: + buf += self.stats(fmt) + +# if stats: +# l = [['Packets', RX_PACKETS, TX_PACKETS], +# ['Bytes', RX_BYTES, TX_BYTES], +# ['Errors', RX_ERRORS, TX_ERRORS], +# ['Dropped', RX_DROPPED, TX_DROPPED], +# ['Compressed', RX_COMPRESSED, TX_COMPRESSED], +# ['FIFO Errors', RX_FIFO_ERR, TX_FIFO_ERR], +# ['Length Errors', RX_LEN_ERR, None], +# ['Over Errors', RX_OVER_ERR, None], +# ['CRC Errors', RX_CRC_ERR, None], +# ['Frame Errors', RX_FRAME_ERR, None], +# ['Missed Errors', RX_MISSED_ERR, None], +# ['Abort Errors', None, TX_ABORT_ERR], +# ['Carrier Errors', None, TX_CARRIER_ERR], +# ['Heartbeat Errors', None, TX_HBEAT_ERR], +# ['Window Errors', None, TX_WIN_ERR], +# ['Collisions', None, COLLISIONS], +# ['Multicast', None, MULTICAST], +# ['', None, None], +# ['Ipv6:', None, None], +# ['Packets', IP6_INPKTS, IP6_OUTPKTS], +# ['Bytes', IP6_INOCTETS, IP6_OUTOCTETS], +# ['Discards', IP6_INDISCARDS, IP6_OUTDISCARDS], +# ['Multicast Packets', IP6_INMCASTPKTS, IP6_OUTMCASTPKTS], +# ['Multicast Bytes', IP6_INMCASTOCTETS, IP6_OUTMCASTOCTETS], +# ['Broadcast Packets', IP6_INBCASTPKTS, IP6_OUTBCASTPKTS], +# ['Broadcast Bytes', IP6_INBCASTOCTETS, IP6_OUTBCASTOCTETS], +# ['Delivers', IP6_INDELIVERS, None], +# ['Forwarded', None, IP6_OUTFORWDATAGRAMS], +# ['No Routes', IP6_INNOROUTES, IP6_OUTNOROUTES], +# ['Header Errors', IP6_INHDRERRORS, None], +# ['Too Big Errors', IP6_INTOOBIGERRORS, None], +# ['Address Errors', IP6_INADDRERRORS, None], +# ['Unknown Protocol', IP6_INUNKNOWNPROTOS, None], +# ['Truncated Packets', IP6_INTRUNCATEDPKTS, None], +# ['Reasm Timeouts', IP6_REASMTIMEOUT, None], +# ['Reasm Requests', IP6_REASMREQDS, None], +# ['Reasm Failures', IP6_REASMFAILS, None], +# ['Reasm OK', IP6_REASMOKS, None], +# ['Frag Created', None, IP6_FRAGCREATES], +# ['Frag Failures', None, IP6_FRAGFAILS], +# ['Frag OK', None, IP6_FRAGOKS], +# ['', None, None], +# ['ICMPv6:', None, None], +# ['Messages', ICMP6_INMSGS, ICMP6_OUTMSGS], +# ['Errors', ICMP6_INERRORS, ICMP6_OUTERRORS]] +# +# buf += '\n\t%s%s%s%s\n' % (33 * ' ', util.title('RX'), +# 15 * ' ', util.title('TX')) +# +# for row in l: +# row[0] = util.kw(row[0]) +# row[1] = self.get_stat(row[1]) if row[1] else '' +# row[2] = self.get_stat(row[2]) if row[2] else '' +# buf += '\t{0:27} {1:>16} {2:>16}\n'.format(*row) + + return buf + +class TcClassCache(netlink.Cache): + """Cache of traffic classes""" + + def __init__(self, ifindex, cache=None): + if not cache: + cache = self._alloc_cache_name('route/class') + + self._protocol = netlink.NETLINK_ROUTE + self._nl_cache = cache + self._set_arg1(ifindex) + + @staticmethod + def _new_object(obj): + return TcClass(obj) + + def _new_cache(self, cache): + return TcClassCache(self.arg1, cache=cache) + +class TcClass(Tc): + """Traffic Class""" + + def __init__(self, obj=None): + netlink.Object.__init__(self, 'route/class', 'class', obj) + self._module_path = 'netlink.route.qdisc.' + self._rtnl_class = self._obj2type(self._nl_object) + self._rtnl_tc = capi.obj2tc(self._nl_object) + + if self.kind: + self._tc_module_lookup() + + @classmethod + def from_capi(cls, obj): + return cls(capi.class2obj(obj)) + + @staticmethod + def _obj2type(obj): + return capi.obj2class(obj) + + @staticmethod + def _new_instance(obj): + if not obj: + raise ValueError() + + return TcClass(obj) + + @property + def childs(self): + ret = [] + + # classes can have classifiers, child classes and leaf + # qdiscs + ret += get_cls(self.ifindex, parent=self.handle) + ret += get_class(self.ifindex, parent=self.handle) + ret += get_qdisc(self.ifindex, parent=self.handle) + + return ret + + def format(self, details=False, _stats=False, nodev=False, + noparent=False, indent=''): + """Return class as formatted text""" + fmt = util.MyFormatter(self, indent) + + buf = fmt.format(self.brief('class', nodev, noparent)) + + if details: + buf += fmt.nl('\t' + self.details()) + + return buf + +class ClassifierCache(netlink.Cache): + """Cache of traffic classifiers objects""" + + def __init__(self, ifindex, parent, cache=None): + if not cache: + cache = self._alloc_cache_name('route/cls') + + self._protocol = netlink.NETLINK_ROUTE + self._nl_cache = cache + self._set_arg1(ifindex) + self._set_arg2(int(parent)) + + @staticmethod + def _new_object(obj): + return Classifier(obj) + + def _new_cache(self, cache): + return ClassifierCache(self.arg1, self.arg2, cache=cache) + +class Classifier(Tc): + """Classifier""" + + def __init__(self, obj=None): + netlink.Object.__init__(self, 'route/cls', 'cls', obj) + self._module_path = 'netlink.route.cls.' + self._rtnl_cls = self._obj2type(self._nl_object) + self._rtnl_tc = capi.obj2tc(self._nl_object) + + @classmethod + def from_capi(cls, obj): + return cls(capi.cls2obj(obj)) + + @staticmethod + def _obj2type(obj): + return capi.obj2cls(obj) + + @staticmethod + def _new_instance(obj): + if not obj: + raise ValueError() + + return Classifier(obj) + + @property + def priority(self): + return capi.rtnl_cls_get_prio(self._rtnl_cls) + + @priority.setter + def priority(self, value): + capi.rtnl_cls_set_prio(self._rtnl_cls, int(value)) + + @property + def protocol(self): + return capi.rtnl_cls_get_protocol(self._rtnl_cls) + + @protocol.setter + def protocol(self, value): + capi.rtnl_cls_set_protocol(self._rtnl_cls, int(value)) + + @property + def childs(self): + return [] + + def format(self, details=False, _stats=False, nodev=False, + noparent=False, indent=''): + """Return class as formatted text""" + fmt = util.MyFormatter(self, indent) + + buf = fmt.format(self.brief('classifier', nodev, noparent)) + buf += fmt.format(' {t|priority} {t|protocol}') + + if details: + buf += fmt.nl('\t' + self.details()) + + return buf + +_qdisc_cache = QdiscCache() + +def get_qdisc(ifindex, handle=None, parent=None): + l = [] + + _qdisc_cache.refill() + + for qdisc in _qdisc_cache: + if qdisc.ifindex != ifindex: + continue + if (handle is not None) and (qdisc.handle != handle): + continue + if (parent is not None) and (qdisc.parent != parent): + continue + l.append(qdisc) + + return l + +_class_cache = {} + +def get_class(ifindex, parent, handle=None): + l = [] + + try: + cache = _class_cache[ifindex] + except KeyError: + cache = TcClassCache(ifindex) + _class_cache[ifindex] = cache + + cache.refill() + + for cl in cache: + if (parent is not None) and (cl.parent != parent): + continue + if (handle is not None) and (cl.handle != handle): + continue + l.append(cl) + + return l + +_cls_cache = {} + +def get_cls(ifindex, parent, handle=None): + + chain = _cls_cache.get(ifindex, dict()) + + try: + cache = chain[parent] + except KeyError: + cache = ClassifierCache(ifindex, parent) + chain[parent] = cache + + cache.refill() + + if handle is None: + return [ cls for cls in cache ] + + return [ cls for cls in cache if cls.handle == handle ] diff --git a/python/netlink/util.py b/python/netlink/util.py new file mode 100644 index 0000000..2394033 --- /dev/null +++ b/python/netlink/util.py @@ -0,0 +1,174 @@ +# +# Utilities +# +# Copyright (c) 2011 Thomas Graf +# + +"""utility module for netlink + +""" + +from __future__ import absolute_import + +from . import core as netlink +from . import capi as capi +from string import Formatter +import types + +__version__ = '1.0' + +def _color(t, c): + return b'{esc}[{color}m{text}{esc}[0m'.format(esc=b'\x1b', color=c, text=t) + +def black(t): + return _color(t, 30) + +def red(t): + return _color(t, 31) + +def green(t): + return _color(t, 32) + +def yellow(t): + return _color(t, 33) + +def blue(t): + return _color(t, 34) + +def magenta(t): + return _color(t, 35) + +def cyan(t): + return _color(t, 36) + +def white(t): + return _color(t, 37) + +def bold(t): + return _color(t, 1) + +def kw(t): + return yellow(t) + +def num(t): + return str(t) + +def string(t): + return t + +def addr(t): + return str(t) + +def bad(t): + return red(t) + +def good(t): + return green(t) + +def title(t): + return t + +def boolean(t): + return str(t) + +def handle(t): + return str(t) + +class MyFormatter(Formatter): + def __init__(self, obj, indent=''): + self._obj = obj + self._indent = indent + + def _nlattr(self, key): + value = getattr(self._obj.__class__, key) + if not isinstance(value, property): + raise ValueError('Invalid formatting string {0}'.format(key)) + + d = getattr(value.fget, 'formatinfo', dict()) + + # value = value.fget() is exactly the same + value = getattr(self._obj, key) + + if 'fmt' in d: + value = d['fmt'](value) + + title_ = d.get('title', None) + + return title_, str(value) + + def get_value(self, key, args, kwds): + # Let default get_value() handle ints + if not isinstance(key, str): + return Formatter.get_value(self, key, args, kwds) + + # HACK, we allow defining strings via fields to allow + # conversions + if key[:2] == 's|': + return key[2:] + + if key[:2] == 't|': + # title mode ("TITLE ATTR") + include_title = True + elif key[:2] == 'a|': + # plain attribute mode ("ATTR") + include_title = False + else: + # No special field, have default get_value() get it + return Formatter.get_value(self, key, args, kwds) + + key = key[2:] + (title_, value) = self._nlattr(key) + + if include_title: + if not title_: + title_ = key # fall back to key as title + value = '{0} {1}'.format(kw(title_), value) + + return value + + def convert_field(self, value, conversion): + if conversion == 'r': + return repr(value) + elif conversion == 's': + return str(value) + elif conversion == 'k': + return kw(value) + elif conversion == 'b': + return bold(value) + elif conversion is None: + return value + + raise ValueError('Unknown converion specifier {0!s}'.format(conversion)) + + def nl(self, format_string=''): + return '\n' + self._indent + self.format(format_string) + +NL_BYTE_RATE = 0 +NL_BIT_RATE = 1 + +class Rate(object): + def __init__(self, rate, mode=NL_BYTE_RATE): + self._rate = rate + self._mode = mode + + def __str__(self): + return capi.nl_rate2str(self._rate, self._mode, 32)[1] + + def __int__(self): + return self._rate + + def __cmp__(self, other): + return int(self) - int(other) + +class Size(object): + def __init__(self, size): + self._size = size + + def __str__(self): + return capi.nl_size2str(self._size, 32)[0] + + def __int__(self): + return self._size + + def __cmp__(self, other): + return int(self) - int(other) diff --git a/python/setup.py.in b/python/setup.py.in new file mode 100644 index 0000000..7ba90d7 --- /dev/null +++ b/python/setup.py.in @@ -0,0 +1,30 @@ +#!/usr/bin/env python + +from distutils.core import setup, Extension + +opts = ['-O', '-nodefaultctor'] +include = ['@top_builddir@/include'] + +netlink_capi = Extension('netlink/_capi', + sources = ['netlink/capi.i'], + include_dirs = include, + swig_opts = opts, + libraries = ['nl-3'], + ) + +route_capi = Extension('netlink/route/_capi', + sources = ['netlink/route/capi.i'], + include_dirs = include, + swig_opts = opts, + libraries = ['nl-3', 'nl-route-3'], + ) + +setup(name = 'netlink', + version = '1.0', + description = 'Python wrapper for netlink protocols', + author = 'Thomas Graf', + author_email = 'tgraf@suug.ch', + ext_modules = [netlink_capi, route_capi], + packages = ['netlink', 'netlink.route', 'netlink.route.links', + 'netlink.route.qdisc'], + ) diff --git a/src/.gitignore b/src/.gitignore index 448df08..1624901 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -1,5 +1,8 @@ genl-ctrl-list nf-ct-list +nf-exp-list +nf-exp-add +nf-exp-delete nf-log nf-monitor nl-addr-add @@ -46,3 +49,5 @@ dect-cluster-list dect-llme-scan nl-classid-lookup nl-pktloc-lookup +nl-link-enslave +nl-link-release diff --git a/src/Makefile.am b/src/Makefile.am index 3aca2a2..aa6ffe7 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -2,10 +2,18 @@ SUBDIRS = lib -AM_CPPFLAGS = -Wall -I${top_srcdir}/include -I${top_builddir}/include -D_GNU_SOURCE -AM_LDFLAGS = -L${top_builddir}/lib -L${top_builddir}/src/lib -lnl-cli -lnl -lnl-nf -lnl-genl -lnl-route -lnl-dect +AM_CPPFLAGS = -Wall -I${top_srcdir}/include -I${top_builddir}/include -D_GNU_SOURCE -DSYSCONFDIR=\"$(sysconfdir)/libnl\" + +LDADD = \ + ${top_builddir}/src/lib/libnl-cli-3.la \ + ${top_builddir}/lib/libnl-3.la \ + ${top_builddir}/lib/libnl-nf-3.la \ + ${top_builddir}/lib/libnl-genl-3.la \ + ${top_builddir}/lib/libnl-route-3.la \ + $(top_builddir)/lib/libnl-dect-3.la sbin_PROGRAMS = \ + genl-ctrl-list \ nl-qdisc-add nl-qdisc-list nl-qdisc-delete \ nl-class-add nl-class-list nl-class-delete \ nl-cls-add nl-cls-list nl-cls-delete \ @@ -14,8 +22,8 @@ sbin_PROGRAMS = \ nl-link-list noinst_PROGRAMS = \ - genl-ctrl-list \ nf-ct-list nf-log nf-queue nf-monitor \ + nf-exp-list nf-exp-add nf-exp-delete \ nl-addr-add nl-addr-delete nl-addr-list \ nl-link-set nl-link-stats \ nl-link-ifindex2name nl-link-name2ifindex \ @@ -28,6 +36,8 @@ noinst_PROGRAMS = \ nl-fib-lookup \ nl-list-caches nl-list-sockets \ nl-util-addr \ + nl-link-enslave \ + nl-link-release \ dect-transceiver-bind dect-transceiver-list \ dect-cell-add dect-cell-delete dect-cell-list \ dect-cluster-add dect-cluster-delete dect-cluster-list \ @@ -40,6 +50,10 @@ nf_log_SOURCES = nf-log.c nf_queue_SOURCES = nf-queue.c nf_monitor_SOURCES = nf-monitor.c +nf_exp_list_SOURCES = nf-exp-list.c +nf_exp_add_SOURCES = nf-exp-add.c +nf_exp_delete_SOURCES = nf-exp-delete.c + nl_addr_add_SOURCES = nl-addr-add.c nl_addr_delete_SOURCES = nl-addr-delete.c nl_addr_list_SOURCES = nl-addr-list.c diff --git a/src/genl-ctrl-list.c b/src/genl-ctrl-list.c index e25eb2a..0895bcc 100644 --- a/src/genl-ctrl-list.c +++ b/src/genl-ctrl-list.c @@ -1,12 +1,12 @@ /* - * src/genl-ctrl-list.c List Generic Netlink Controller + * src/genl-ctrl-list.c List Generic Netlink Families * * 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 version 2.1 * of the License. * - * Copyright (c) 2003-2009 Thomas Graf + * Copyright (c) 2003-2012 Thomas Graf */ #include @@ -20,10 +20,10 @@ static struct nl_cache *alloc_genl_family_cache(struct nl_sock *sk) static void print_usage(void) { printf( - "Usage: genl-ctrl-list [OPTION]...\n" + "Usage: genl-ctrl-list [--details]\n" "\n" "Options\n" - " -f, --format=TYPE Output format { brief | details | stats }\n" + " -d, --details Include detailed information in the list\n" " -h, --help Show this help\n" " -v, --version Show versioning information\n" ); @@ -46,18 +46,20 @@ int main(int argc, char *argv[]) for (;;) { int c, optidx = 0; static struct option long_opts[] = { + { "details", 0, 0, 'd' }, { "format", 1, 0, 'f' }, { "help", 0, 0, 'h' }, { "version", 0, 0, 'v' }, { 0, 0, 0, 0 } }; - c = getopt_long(argc, argv, "f:hv", long_opts, &optidx); + c = getopt_long(argc, argv, "df:hv", long_opts, &optidx); if (c == -1) break; switch (c) { case 'f': params.dp_type = nl_cli_parse_dumptype(optarg); break; + case 'd': params.dp_type = NL_DUMP_DETAILS; break; case 'h': print_usage(); break; case 'v': nl_cli_print_version(); break; } diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index 3236dbe..6688e7c 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -1,7 +1,10 @@ # -*- Makefile -*- AM_CPPFLAGS = -Wall -I${top_srcdir}/include -I${top_builddir}/include -D_GNU_SOURCE -DPKGLIBDIR=\"$(pkglibdir)\" -DSYSCONFDIR=\"$(sysconfdir)\" -rdynamic -AM_LDFLAGS = -L${top_builddir}/lib -ldl -version-info 3:0:0 +AM_LDFLAGS = \ + -L${top_builddir}/lib \ + -ldl \ + -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) #nobase_pkglib_LTLIBRARIES = cls/basic.la cls/ematch/cmp.la #cls_basic_la_LDFLAGS = -module -version-info 2:0:0 @@ -26,15 +29,15 @@ AM_LDFLAGS = -L${top_builddir}/lib -ldl -version-info 3:0:0 # cls/pktloc_syntax.c cls/pktloc_syntax.h lib_LTLIBRARIES = \ - libnl-cli.la + libnl-cli-3.la -libnl_cli_la_LIBADD = ${top_builddir}/lib/libnl.la \ - ${top_builddir}/lib/libnl-route.la \ - ${top_builddir}/lib/libnl-nf.la \ - ${top_builddir}/lib/libnl-genl.la +libnl_cli_3_la_LIBADD = ${top_builddir}/lib/libnl-3.la \ + ${top_builddir}/lib/libnl-route-3.la \ + ${top_builddir}/lib/libnl-nf-3.la \ + ${top_builddir}/lib/libnl-genl-3.la -libnl_cli_la_SOURCES = \ +libnl_cli_3_la_SOURCES = \ utils.c addr.c ct.c link.c neigh.c rule.c route.c \ - tc.c qdisc.c class.c cls.c + tc.c qdisc.c class.c cls.c exp.c # cls/ematch_syntax.c cls/ematch_grammar.c cls/ematch.c # cls/pktloc_syntax.c cls/pktloc_grammar.c cls/utils.c diff --git a/src/lib/exp.c b/src/lib/exp.c new file mode 100644 index 0000000..a7a74f5 --- /dev/null +++ b/src/lib/exp.c @@ -0,0 +1,165 @@ +/* + * src/lib/exp.c CLI Expectation Helpers + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2008-2009 Thomas Graf + * Copyright (c) 2012 Rich Fought + */ + +/** + * @ingroup cli + * @defgroup cli_exp Expectation Tracking + * + * @{ + */ + +#include +#include + +struct nfnl_exp *nl_cli_exp_alloc(void) +{ + struct nfnl_exp *exp; + + exp = nfnl_exp_alloc(); + if (!exp) + nl_cli_fatal(ENOMEM, "Unable to allocate expectation object"); + + return exp; +} + +struct nl_cache *nl_cli_exp_alloc_cache(struct nl_sock *sk) +{ + return nl_cli_alloc_cache(sk, "expectation", nfnl_exp_alloc_cache); +} + +void nl_cli_exp_parse_family(struct nfnl_exp *exp, char *arg) +{ + int family; + + if ((family = nl_str2af(arg)) == AF_UNSPEC) + nl_cli_fatal(EINVAL, + "Unable to nl_cli_exp_parse family \"%s\": %s", + arg, nl_geterror(NLE_INVAL)); + + nfnl_exp_set_family(exp, family); +} + +void nl_cli_exp_parse_timeout(struct nfnl_exp *exp, char *arg) +{ + uint32_t timeout = nl_cli_parse_u32(arg); + nfnl_exp_set_timeout(exp, timeout); +} + +void nl_cli_exp_parse_id(struct nfnl_exp *exp, char *arg) +{ + uint32_t id = nl_cli_parse_u32(arg); + nfnl_exp_set_id(exp, id); +} + +void nl_cli_exp_parse_helper_name(struct nfnl_exp *exp, char *arg) +{ + nfnl_exp_set_helper_name(exp, arg); +} + +void nl_cli_exp_parse_zone(struct nfnl_exp *exp, char *arg) +{ + uint32_t zone = nl_cli_parse_u32(arg); + nfnl_exp_set_zone(exp, zone); +} + +void nl_cli_exp_parse_flags(struct nfnl_exp *exp, char *arg) +{ + uint32_t flags = nl_cli_parse_u32(arg); + nfnl_exp_set_flags(exp, flags); +} + +void nl_cli_exp_parse_class(struct nfnl_exp *exp, char *arg) +{ + uint32_t class = nl_cli_parse_u32(arg); + nfnl_exp_set_class(exp, class); +} + +void nl_cli_exp_parse_nat_dir(struct nfnl_exp *exp, char *arg) +{ + uint32_t nat_dir = nl_cli_parse_u32(arg); + nfnl_exp_set_nat_dir(exp, nat_dir); +} + +void nl_cli_exp_parse_fn(struct nfnl_exp *exp, char *arg) +{ + nfnl_exp_set_fn(exp, arg); +} + +void nl_cli_exp_parse_src(struct nfnl_exp *exp, int tuple, char *arg) +{ + int err; + struct nl_addr *a = nl_cli_addr_parse(arg, nfnl_exp_get_family(exp)); + if ((err = nfnl_exp_set_src(exp, tuple, a)) < 0) + nl_cli_fatal(err, "Unable to set source address: %s", + nl_geterror(err)); +} + +void nl_cli_exp_parse_dst(struct nfnl_exp *exp, int tuple, char *arg) +{ + int err; + struct nl_addr *a = nl_cli_addr_parse(arg, nfnl_exp_get_family(exp)); + if ((err = nfnl_exp_set_dst(exp, tuple, a)) < 0) + nl_cli_fatal(err, "Unable to set destination address: %s", + nl_geterror(err)); +} + +void nl_cli_exp_parse_l4protonum(struct nfnl_exp *exp, int tuple, char *arg) +{ + int l4protonum; + + if ((l4protonum = nl_str2ip_proto(arg)) < 0) + nl_cli_fatal(l4protonum, + "Unable to nl_cli_exp_parse protocol \"%s\": %s", + arg, nl_geterror(l4protonum)); + + nfnl_exp_set_l4protonum(exp, tuple, l4protonum); +} + +void nl_cli_exp_parse_src_port(struct nfnl_exp *exp, int tuple, char *arg) +{ + uint32_t sport = nl_cli_parse_u32(arg); + uint16_t dport = nfnl_exp_get_dst_port(exp, tuple); + nfnl_exp_set_ports(exp, tuple, sport, dport); +} + +void nl_cli_exp_parse_dst_port(struct nfnl_exp *exp, int tuple, char *arg) +{ + uint32_t dport = nl_cli_parse_u32(arg); + uint16_t sport = nfnl_exp_get_src_port(exp, tuple); + nfnl_exp_set_ports(exp, tuple, sport, dport); +} + +void nl_cli_exp_parse_icmp_id(struct nfnl_exp *exp, int tuple, char *arg) +{ + uint32_t id = nl_cli_parse_u32(arg); + uint8_t type = nfnl_exp_get_icmp_type(exp, tuple); + uint8_t code = nfnl_exp_get_icmp_code(exp, tuple); + nfnl_exp_set_icmp(exp, tuple, id, type, code); +} + +void nl_cli_exp_parse_icmp_type(struct nfnl_exp *exp, int tuple, char *arg) +{ + uint32_t type = nl_cli_parse_u32(arg); + uint16_t id = nfnl_exp_get_icmp_id(exp, tuple); + uint8_t code = nfnl_exp_get_icmp_code(exp, tuple); + nfnl_exp_set_icmp(exp, tuple, id, type, code); +} + +void nl_cli_exp_parse_icmp_code(struct nfnl_exp *exp, int tuple, char *arg) +{ + uint32_t code = nl_cli_parse_u32(arg); + uint16_t id = nfnl_exp_get_icmp_id(exp, tuple); + uint8_t type = nfnl_exp_get_icmp_type(exp, tuple); + nfnl_exp_set_icmp(exp, tuple, id, type, code); +} + +/** @} */ diff --git a/src/lib/link.c b/src/lib/link.c index db90558..5bce824 100644 --- a/src/lib/link.c +++ b/src/lib/link.c @@ -86,8 +86,6 @@ void nl_cli_link_parse_txqlen(struct rtnl_link *link, char *arg) void nl_cli_link_parse_weight(struct rtnl_link *link, char *arg) { - uint32_t weight = nl_cli_parse_u32(arg); - rtnl_link_set_weight(link, weight); } void nl_cli_link_parse_ifalias(struct rtnl_link *link, char *arg) diff --git a/src/lib/route.c b/src/lib/route.c index 05cb2ad..cd3e897 100644 --- a/src/lib/route.c +++ b/src/lib/route.c @@ -198,14 +198,18 @@ void nl_cli_route_parse_table(struct rtnl_route *route, char *arg) { unsigned long lval; char *endptr; + int table; lval = strtoul(arg, &endptr, 0); if (endptr == arg) { - if ((lval = rtnl_route_str2table(arg)) < 0) + if ((table = rtnl_route_str2table(arg)) < 0) nl_cli_fatal(EINVAL, "Unknown table name \"%s\"", arg); } + else { + table = lval; + } - rtnl_route_set_table(route, lval); + rtnl_route_set_table(route, table); } void nl_cli_route_parse_prio(struct rtnl_route *route, char *arg) @@ -233,16 +237,20 @@ void nl_cli_route_parse_protocol(struct rtnl_route *route, char *arg) { unsigned long lval; char *endptr; + int proto; lval = strtoul(arg, &endptr, 0); if (endptr == arg) { - if ((lval = rtnl_route_str2proto(arg)) < 0) + if ((proto = rtnl_route_str2proto(arg)) < 0) nl_cli_fatal(EINVAL, "Unknown routing protocol name \"%s\"", arg); } + else { + proto = lval; + } - rtnl_route_set_protocol(route, lval); + rtnl_route_set_protocol(route, proto); } void nl_cli_route_parse_type(struct rtnl_route *route, char *arg) diff --git a/src/lib/tc.c b/src/lib/tc.c index 4cdd081..dde729f 100644 --- a/src/lib/tc.c +++ b/src/lib/tc.c @@ -11,7 +11,7 @@ #include #include -#include +#include /** * @ingroup cli @@ -94,7 +94,7 @@ void nl_cli_tc_parse_linktype(struct rtnl_tc *tc, char *arg) static NL_LIST_HEAD(tc_modules); -struct nl_cli_tc_module *__nl_cli_tc_lookup(struct rtnl_tc_ops *ops) +static struct nl_cli_tc_module *__nl_cli_tc_lookup(struct rtnl_tc_ops *ops) { struct nl_cli_tc_module *tm; diff --git a/src/lib/utils.c b/src/lib/utils.c index 78ad260..e5eacde 100644 --- a/src/lib/utils.c +++ b/src/lib/utils.c @@ -70,6 +70,7 @@ void nl_cli_print_version(void) void nl_cli_fatal(int err, const char *fmt, ...) { va_list ap; + char buf[256]; fprintf(stderr, "Error: "); @@ -79,7 +80,7 @@ void nl_cli_fatal(int err, const char *fmt, ...) va_end(ap); fprintf(stderr, "\n"); } else - fprintf(stderr, "%s\n", strerror(err)); + fprintf(stderr, "%s\n", strerror_r(err, buf, sizeof(buf))); exit(abs(err)); } diff --git a/src/nf-exp-add.c b/src/nf-exp-add.c new file mode 100644 index 0000000..4b7f9d9 --- /dev/null +++ b/src/nf-exp-add.c @@ -0,0 +1,187 @@ +/* + * src/nf-exp-add.c Create an expectation + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2009 Thomas Graf + * Copyright (c) 2007 Philip Craig + * Copyright (c) 2007 Secure Computing Corporation + * Copyright (c) 2012 Rich Fought + * + */ + +#include +#include + +static int quiet = 0; + +static void print_usage(void) +{ + printf( + "Usage: nf-exp-list [OPTION]... [CONNTRACK ENTRY]\n" + "\n" + "Options\n" + " --replace Replace the address if it exists.\n" + " -q, --quiet Do not print informal notifications.\n" + " -h, --help Show this help\n" + " -v, --version Show versioning information\n" + "\n" + "Expectation Selection\n" + " -i, --id=NUM Identifier\n" + " --expect-proto=PROTOCOL Expectation protocol\n" + " --expect-src=ADDR Expectation source address\n" + " --expect-sport=PORT Expectation source port\n" + " --expect-dst=ADDR Expectation destination address\n" + " --expect-dport=PORT Expectation destination port\n" + " --master-proto=PROTOCOL Master conntrack protocol\n" + " --master-src=ADDR Master conntrack source address\n" + " --master-sport=PORT Master conntrack source port\n" + " --master-dst=ADDR Master conntrack destination address\n" + " --master-dport=PORT Master conntrack destination port\n" + " --mask-proto=PROTOCOL Mask protocol\n" + " --mask-src=ADDR Mask source address\n" + " --mask-sport=PORT Mask source port\n" + " --mask-dst=ADDR Mask destination address\n" + " --mask-dport=PORT Mask destination port\n" + " -F, --family=FAMILY Address family\n" + " --timeout=NUM Timeout value\n" + " --helper=STRING Helper Name\n" + " --flags Flags (Kernel 2.6.37)\n" + ); + exit(0); +} + +int main(int argc, char *argv[]) +{ + struct nl_sock *sock; + struct nfnl_exp *exp; + struct nl_dump_params params = { + .dp_type = NL_DUMP_LINE, + .dp_fd = stdout, + }; + int err, nlflags = NLM_F_CREATE; + + exp = nl_cli_exp_alloc(); + + for (;;) { + int c, optidx = 0; + enum { + ARG_MARK = 270, + ARG_TCP_STATE = 271, + ARG_EXPECT_PROTO, + ARG_EXPECT_SRC, + ARG_EXPECT_SPORT, + ARG_EXPECT_DST, + ARG_EXPECT_DPORT, + ARG_MASTER_PROTO, + ARG_MASTER_SRC, + ARG_MASTER_SPORT, + ARG_MASTER_DST, + ARG_MASTER_DPORT, + ARG_MASK_PROTO, + ARG_MASK_SRC, + ARG_MASK_SPORT, + ARG_MASK_DST, + ARG_MASK_DPORT, + ARG_NAT_PROTO, + ARG_NAT_SRC, + ARG_NAT_SPORT, + ARG_NAT_DST, + ARG_NAT_DPORT, + ARG_NAT_DIR, + ARG_TIMEOUT, + ARG_HELPER_NAME, + ARG_REPLACE, + ARG_FLAGS, + }; + static struct option long_opts[] = { + { "replace", 1, 0, ARG_REPLACE }, + { "quiet", 0, 0, 'q' }, + { "help", 0, 0, 'h' }, + { "version", 0, 0, 'v' }, + { "id", 1, 0, 'i' }, + { "expect-proto", 1, 0, ARG_EXPECT_PROTO }, + { "expect-src", 1, 0, ARG_EXPECT_SRC }, + { "expect-sport", 1, 0, ARG_EXPECT_SPORT }, + { "expect-dst", 1, 0, ARG_EXPECT_DST }, + { "expect-dport", 1, 0, ARG_EXPECT_DPORT }, + { "master-proto", 1, 0, ARG_MASTER_PROTO }, + { "master-src", 1, 0, ARG_MASTER_SRC }, + { "master-sport", 1, 0, ARG_MASTER_SPORT }, + { "master-dst", 1, 0, ARG_MASTER_DST }, + { "master-dport", 1, 0, ARG_MASTER_DPORT }, + { "mask-proto", 1, 0, ARG_MASK_PROTO }, + { "mask-src", 1, 0, ARG_MASK_SRC }, + { "mask-sport", 1, 0, ARG_MASK_SPORT }, + { "mask-dst", 1, 0, ARG_MASK_DST }, + { "mask-dport", 1, 0, ARG_MASK_DPORT }, + { "nat-proto", 1, 0, ARG_NAT_PROTO }, + { "nat-src", 1, 0, ARG_NAT_SRC }, + { "nat-sport", 1, 0, ARG_NAT_SPORT }, + { "nat-dst", 1, 0, ARG_NAT_DST }, + { "nat-dport", 1, 0, ARG_NAT_DPORT }, + { "nat-dir", 1, 0, ARG_NAT_DIR }, + { "family", 1, 0, 'F' }, + { "timeout", 1, 0, ARG_TIMEOUT }, + { "helper", 1, 0, ARG_HELPER_NAME }, + { "flags", 1, 0, ARG_FLAGS}, + { 0, 0, 0, 0 } + }; + + c = getopt_long(argc, argv, "46f:hvi:p:F:", long_opts, &optidx); + if (c == -1) + break; + + switch (c) { + case '?': exit(NLE_INVAL); + case ARG_REPLACE: nlflags |= NLM_F_REPLACE; break; + case 'q': quiet = 1; break; + case '4': nfnl_exp_set_family(exp, AF_INET); break; + case '6': nfnl_exp_set_family(exp, AF_INET6); break; + case 'h': print_usage(); break; + case 'v': nl_cli_print_version(); break; + case 'i': nl_cli_exp_parse_id(exp, optarg); break; + case ARG_EXPECT_PROTO: nl_cli_exp_parse_l4protonum(exp, NFNL_EXP_TUPLE_EXPECT, optarg); break; + case ARG_EXPECT_SRC: nl_cli_exp_parse_src(exp, NFNL_EXP_TUPLE_EXPECT, optarg); break; + case ARG_EXPECT_SPORT: nl_cli_exp_parse_src_port(exp, NFNL_EXP_TUPLE_EXPECT, optarg); break; + case ARG_EXPECT_DST: nl_cli_exp_parse_dst(exp, NFNL_EXP_TUPLE_EXPECT, optarg); break; + case ARG_EXPECT_DPORT: nl_cli_exp_parse_dst_port(exp, NFNL_EXP_TUPLE_EXPECT, optarg); break; + case ARG_MASTER_PROTO: nl_cli_exp_parse_l4protonum(exp, NFNL_EXP_TUPLE_MASTER, optarg); break; + case ARG_MASTER_SRC: nl_cli_exp_parse_src(exp, NFNL_EXP_TUPLE_MASTER, optarg); break; + case ARG_MASTER_SPORT: nl_cli_exp_parse_src_port(exp, NFNL_EXP_TUPLE_MASTER, optarg); break; + case ARG_MASTER_DST: nl_cli_exp_parse_dst(exp, NFNL_EXP_TUPLE_MASTER, optarg); break; + case ARG_MASTER_DPORT: nl_cli_exp_parse_dst_port(exp, NFNL_EXP_TUPLE_MASTER, optarg); break; + case ARG_MASK_PROTO: nl_cli_exp_parse_l4protonum(exp, NFNL_EXP_TUPLE_MASK, optarg); break; + case ARG_MASK_SRC: nl_cli_exp_parse_src(exp, NFNL_EXP_TUPLE_MASK, optarg); break; + case ARG_MASK_SPORT: nl_cli_exp_parse_src_port(exp, NFNL_EXP_TUPLE_MASK, optarg); break; + case ARG_MASK_DST: nl_cli_exp_parse_dst(exp, NFNL_EXP_TUPLE_MASK, optarg); break; + case ARG_MASK_DPORT: nl_cli_exp_parse_dst_port(exp, NFNL_EXP_TUPLE_MASK, optarg); break; + case ARG_NAT_PROTO: nl_cli_exp_parse_l4protonum(exp, NFNL_EXP_TUPLE_NAT, optarg); break; + case ARG_NAT_SRC: nl_cli_exp_parse_src(exp, NFNL_EXP_TUPLE_NAT, optarg); break; + case ARG_NAT_SPORT: nl_cli_exp_parse_src_port(exp, NFNL_EXP_TUPLE_NAT, optarg); break; + case ARG_NAT_DST: nl_cli_exp_parse_dst(exp, NFNL_EXP_TUPLE_NAT, optarg); break; + case ARG_NAT_DPORT: nl_cli_exp_parse_dst_port(exp, NFNL_EXP_TUPLE_NAT, optarg); break; + case ARG_NAT_DIR: nl_cli_exp_parse_nat_dir(exp, optarg); break; + case 'F': nl_cli_exp_parse_family(exp, optarg); break; + case ARG_TIMEOUT: nl_cli_exp_parse_timeout(exp, optarg); break; + case ARG_HELPER_NAME: nl_cli_exp_parse_helper_name(exp, optarg); break; + case ARG_FLAGS: nl_cli_exp_parse_flags(exp, optarg); break; + } + } + + sock = nl_cli_alloc_socket(); + nl_cli_connect(sock, NETLINK_NETFILTER); + + if ((err = nfnl_exp_add(sock, exp, nlflags)) < 0) + nl_cli_fatal(err, "Unable to add expectation: %s", nl_geterror(err)); + + if (!quiet) { + printf("Added "); + nl_object_dump(OBJ_CAST(exp), ¶ms); + } + + return 0; +} diff --git a/src/nf-exp-delete.c b/src/nf-exp-delete.c new file mode 100644 index 0000000..2ec45ae --- /dev/null +++ b/src/nf-exp-delete.c @@ -0,0 +1,165 @@ +/* + * src/nf-exp-delete.c Delete an expectation + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2009 Thomas Graf + * Copyright (c) 2007 Philip Craig + * Copyright (c) 2007 Secure Computing Corporation + * Copyright (c) 2012 Rich Fought + */ + +#include +#include + +static int quiet = 0; + +static void print_usage(void) +{ + printf( + "Usage: nf-exp-list [OPTION]... [CONNTRACK ENTRY]\n" + "\n" + "Options\n" + " --replace Replace the address if it exists.\n" + " -q, --quiet Do not print informal notifications.\n" + " -h, --help Show this help\n" + " -v, --version Show versioning information\n" + "\n" + "Expectation Selection\n" + " -i, --id=NUM Identifier\n" + " --expect-proto=PROTOCOL Expectation protocol\n" + " --expect-src=ADDR Expectation source address\n" + " --expect-sport=PORT Expectation source port\n" + " --expect-dst=ADDR Expectation destination address\n" + " --expect-dport=PORT Expectation destination port\n" + " --master-proto=PROTOCOL Master conntrack protocol\n" + " --master-src=ADDR Master conntrack source address\n" + " --master-sport=PORT Master conntrack source port\n" + " --master-dst=ADDR Master conntrack destination address\n" + " --master-dport=PORT Master conntrack destination port\n" + " --mask-proto=PROTOCOL Mask protocol\n" + " --mask-src=ADDR Mask source address\n" + " --mask-sport=PORT Mask source port\n" + " --mask-dst=ADDR Mask destination address\n" + " --mask-dport=PORT Mask destination port\n" + " -F, --family=FAMILY Address family\n" + " --timeout=NUM Timeout value\n" + " --helper=STRING Helper Name\n" + " --flags Flags\n" + ); + exit(0); +} + +int main(int argc, char *argv[]) +{ + struct nl_sock *sock; + struct nfnl_exp *exp; + struct nl_dump_params params = { + .dp_type = NL_DUMP_LINE, + .dp_fd = stdout, + }; + int err, nlflags = 0; + + exp = nl_cli_exp_alloc(); + + for (;;) { + int c, optidx = 0; + enum { + ARG_MARK = 270, + ARG_TCP_STATE = 271, + ARG_EXPECT_PROTO, + ARG_EXPECT_SRC, + ARG_EXPECT_SPORT, + ARG_EXPECT_DST, + ARG_EXPECT_DPORT, + ARG_MASTER_PROTO, + ARG_MASTER_SRC, + ARG_MASTER_SPORT, + ARG_MASTER_DST, + ARG_MASTER_DPORT, + ARG_MASK_PROTO, + ARG_MASK_SRC, + ARG_MASK_SPORT, + ARG_MASK_DST, + ARG_MASK_DPORT, + ARG_TIMEOUT, + ARG_HELPER_NAME, + ARG_FLAGS, + }; + static struct option long_opts[] = { + { "quiet", 0, 0, 'q' }, + { "help", 0, 0, 'h' }, + { "version", 0, 0, 'v' }, + { "id", 1, 0, 'i' }, + { "expect-proto", 1, 0, ARG_EXPECT_PROTO }, + { "expect-src", 1, 0, ARG_EXPECT_SRC }, + { "expect-sport", 1, 0, ARG_EXPECT_SPORT }, + { "expect-dst", 1, 0, ARG_EXPECT_DST }, + { "expect-dport", 1, 0, ARG_EXPECT_DPORT }, + { "master-proto", 1, 0, ARG_MASTER_PROTO }, + { "master-src", 1, 0, ARG_MASTER_SRC }, + { "master-sport", 1, 0, ARG_MASTER_SPORT }, + { "master-dst", 1, 0, ARG_MASTER_DST }, + { "master-dport", 1, 0, ARG_MASTER_DPORT }, + { "mask-proto", 1, 0, ARG_MASK_PROTO }, + { "mask-src", 1, 0, ARG_MASK_SRC }, + { "mask-sport", 1, 0, ARG_MASK_SPORT }, + { "mask-dst", 1, 0, ARG_MASK_DST }, + { "mask-dport", 1, 0, ARG_MASK_DPORT }, + { "family", 1, 0, 'F' }, + { "timeout", 1, 0, ARG_TIMEOUT }, + { "helper", 1, 0, ARG_HELPER_NAME }, + { "flags", 1, 0, ARG_FLAGS}, + { 0, 0, 0, 0 } + }; + + c = getopt_long(argc, argv, "46f:hvi:p:F:", long_opts, &optidx); + if (c == -1) + break; + + switch (c) { + case '?': exit(NLE_INVAL); + case 'q': quiet = 1; break; + case '4': nfnl_exp_set_family(exp, AF_INET); break; + case '6': nfnl_exp_set_family(exp, AF_INET6); break; + case 'h': print_usage(); break; + case 'v': nl_cli_print_version(); break; + case 'i': nl_cli_exp_parse_id(exp, optarg); break; + case ARG_EXPECT_PROTO: nl_cli_exp_parse_l4protonum(exp, NFNL_EXP_TUPLE_EXPECT, optarg); break; + case ARG_EXPECT_SRC: nl_cli_exp_parse_src(exp, NFNL_EXP_TUPLE_EXPECT, optarg); break; + case ARG_EXPECT_SPORT: nl_cli_exp_parse_src_port(exp, NFNL_EXP_TUPLE_EXPECT, optarg); break; + case ARG_EXPECT_DST: nl_cli_exp_parse_dst(exp, NFNL_EXP_TUPLE_EXPECT, optarg); break; + case ARG_EXPECT_DPORT: nl_cli_exp_parse_dst_port(exp, NFNL_EXP_TUPLE_EXPECT, optarg); break; + case ARG_MASTER_PROTO: nl_cli_exp_parse_l4protonum(exp, NFNL_EXP_TUPLE_MASTER, optarg); break; + case ARG_MASTER_SRC: nl_cli_exp_parse_src(exp, NFNL_EXP_TUPLE_MASTER, optarg); break; + case ARG_MASTER_SPORT: nl_cli_exp_parse_src_port(exp, NFNL_EXP_TUPLE_MASTER, optarg); break; + case ARG_MASTER_DST: nl_cli_exp_parse_dst(exp, NFNL_EXP_TUPLE_MASTER, optarg); break; + case ARG_MASTER_DPORT: nl_cli_exp_parse_dst_port(exp, NFNL_EXP_TUPLE_MASTER, optarg); break; + case ARG_MASK_PROTO: nl_cli_exp_parse_l4protonum(exp, NFNL_EXP_TUPLE_MASK, optarg); break; + case ARG_MASK_SRC: nl_cli_exp_parse_src(exp, NFNL_EXP_TUPLE_MASK, optarg); break; + case ARG_MASK_SPORT: nl_cli_exp_parse_src_port(exp, NFNL_EXP_TUPLE_MASK, optarg); break; + case ARG_MASK_DST: nl_cli_exp_parse_dst(exp, NFNL_EXP_TUPLE_MASK, optarg); break; + case ARG_MASK_DPORT: nl_cli_exp_parse_dst_port(exp, NFNL_EXP_TUPLE_MASK, optarg); break; + case 'F': nl_cli_exp_parse_family(exp, optarg); break; + case ARG_TIMEOUT: nl_cli_exp_parse_timeout(exp, optarg); break; + case ARG_HELPER_NAME: nl_cli_exp_parse_helper_name(exp, optarg); break; + case ARG_FLAGS: nl_cli_exp_parse_flags(exp, optarg); break; + } + } + + sock = nl_cli_alloc_socket(); + nl_cli_connect(sock, NETLINK_NETFILTER); + + if ((err = nfnl_exp_del(sock, exp, nlflags)) < 0) + nl_cli_fatal(err, "Unable to delete expectation: %s", nl_geterror(err)); + + if (!quiet) { + printf("Deleted "); + nl_object_dump(OBJ_CAST(exp), ¶ms); + } + + return 0; +} diff --git a/src/nf-exp-list.c b/src/nf-exp-list.c new file mode 100644 index 0000000..1c6ec69 --- /dev/null +++ b/src/nf-exp-list.c @@ -0,0 +1,137 @@ +/* + * src/nf-exp-list.c List Expectation Entries + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2009 Thomas Graf + * Copyright (c) 2007 Philip Craig + * Copyright (c) 2007 Secure Computing Corporation + * Copyright (c) 2012 Rich Fought + */ + +#include +#include + +static void print_usage(void) +{ + printf( + "Usage: nf-exp-list [OPTION]... [EXPECTATION ENTRY]\n" + "\n" + "Options\n" + " -f, --format=TYPE Output format { brief | details }\n" + " -h, --help Show this help\n" + " -v, --version Show versioning information\n" + "\n" + "Expectation Selection\n" + " -i, --id=NUM Identifier\n" + " --expect-proto=PROTOCOL Expectation protocol\n" + " --expect-src=ADDR Expectation source address\n" + " --expect-sport=PORT Expectation source port\n" + " --expect-dst=ADDR Expectation destination address\n" + " --expect-dport=PORT Expectation destination port\n" + " --master-proto=PROTOCOL Master conntrack protocol\n" + " --master-src=ADDR Master conntrack source address\n" + " --master-sport=PORT Master conntrack source port\n" + " --master-dst=ADDR Master conntrack destination address\n" + " --master-dport=PORT Master conntrack destination port\n" + " -F, --family=FAMILY Address family\n" + " --timeout=NUM Timeout value\n" + " --helper=STRING Helper Name\n" + //" --flags Flags\n" + ); + exit(0); +} + +int main(int argc, char *argv[]) +{ + struct nl_sock *sock; + struct nl_cache *exp_cache; + struct nfnl_exp *exp; + struct nl_dump_params params = { + .dp_type = NL_DUMP_LINE, + .dp_fd = stdout, + }; + + exp = nl_cli_exp_alloc(); + + for (;;) { + int c, optidx = 0; + enum { + ARG_MARK = 270, + ARG_TCP_STATE = 271, + ARG_EXPECT_PROTO, + ARG_EXPECT_SRC, + ARG_EXPECT_SPORT, + ARG_EXPECT_DST, + ARG_EXPECT_DPORT, + ARG_MASTER_PROTO, + ARG_MASTER_SRC, + ARG_MASTER_SPORT, + ARG_MASTER_DST, + ARG_MASTER_DPORT, + ARG_TIMEOUT, + ARG_HELPER_NAME, + ARG_FLAGS, + }; + static struct option long_opts[] = { + { "format", 1, 0, 'f' }, + { "help", 0, 0, 'h' }, + { "version", 0, 0, 'v' }, + { "id", 1, 0, 'i' }, + { "expect-proto", 1, 0, ARG_EXPECT_PROTO }, + { "expect-src", 1, 0, ARG_EXPECT_SRC }, + { "expect-sport", 1, 0, ARG_EXPECT_SPORT }, + { "expect-dst", 1, 0, ARG_EXPECT_DST }, + { "expect-dport", 1, 0, ARG_EXPECT_DPORT }, + { "master-proto", 1, 0, ARG_MASTER_PROTO }, + { "master-src", 1, 0, ARG_MASTER_SRC }, + { "master-sport", 1, 0, ARG_MASTER_SPORT }, + { "master-dst", 1, 0, ARG_MASTER_DST }, + { "master-dport", 1, 0, ARG_MASTER_DPORT }, + { "family", 1, 0, 'F' }, + { "timeout", 1, 0, ARG_TIMEOUT }, + { "helper", 1, 0, ARG_HELPER_NAME }, + { "flags", 1, 0, ARG_FLAGS}, + { 0, 0, 0, 0 } + }; + + c = getopt_long(argc, argv, "46f:hvi:p:F:", long_opts, &optidx); + if (c == -1) + break; + + switch (c) { + case '?': exit(NLE_INVAL); + case '4': nfnl_exp_set_family(exp, AF_INET); break; + case '6': nfnl_exp_set_family(exp, AF_INET6); break; + case 'f': params.dp_type = nl_cli_parse_dumptype(optarg); break; + case 'h': print_usage(); break; + case 'v': nl_cli_print_version(); break; + case 'i': nl_cli_exp_parse_id(exp, optarg); break; + case ARG_EXPECT_PROTO: nl_cli_exp_parse_l4protonum(exp, NFNL_EXP_TUPLE_EXPECT, optarg); break; + case ARG_EXPECT_SRC: nl_cli_exp_parse_src(exp, NFNL_EXP_TUPLE_EXPECT, optarg); break; + case ARG_EXPECT_SPORT: nl_cli_exp_parse_src_port(exp, NFNL_EXP_TUPLE_EXPECT, optarg); break; + case ARG_EXPECT_DST: nl_cli_exp_parse_dst(exp, NFNL_EXP_TUPLE_EXPECT, optarg); break; + case ARG_EXPECT_DPORT: nl_cli_exp_parse_dst_port(exp, NFNL_EXP_TUPLE_EXPECT, optarg); break; + case ARG_MASTER_PROTO: nl_cli_exp_parse_l4protonum(exp, NFNL_EXP_TUPLE_MASTER, optarg); break; + case ARG_MASTER_SRC: nl_cli_exp_parse_src(exp, NFNL_EXP_TUPLE_MASTER, optarg); break; + case ARG_MASTER_SPORT: nl_cli_exp_parse_src_port(exp, NFNL_EXP_TUPLE_MASTER, optarg); break; + case ARG_MASTER_DST: nl_cli_exp_parse_dst(exp, NFNL_EXP_TUPLE_MASTER, optarg); break; + case ARG_MASTER_DPORT: nl_cli_exp_parse_dst_port(exp, NFNL_EXP_TUPLE_MASTER, optarg); break; + case 'F': nl_cli_exp_parse_family(exp, optarg); break; + case ARG_TIMEOUT: nl_cli_exp_parse_timeout(exp, optarg); break; + case ARG_HELPER_NAME: nl_cli_exp_parse_helper_name(exp, optarg); break; + case ARG_FLAGS: nl_cli_exp_parse_flags(exp, optarg); break; + } + } + + sock = nl_cli_alloc_socket(); + nl_cli_connect(sock, NETLINK_NETFILTER); + exp_cache = nl_cli_exp_alloc_cache(sock); + + nl_cache_dump_filter(exp_cache, ¶ms, OBJ_CAST(exp)); + + return 0; +} diff --git a/src/nf-log.c b/src/nf-log.c index 26bae6d..913ba16 100644 --- a/src/nf-log.c +++ b/src/nf-log.c @@ -96,7 +96,7 @@ int main(int argc, char *argv[]) copy_range = 0xFFFF; if (argc > 4) - copy_mode = atoi(argv[4]); + copy_range = atoi(argv[4]); nfnl_log_set_copy_range(log, copy_range); if ((err = nfnl_log_create(nf_sock, log)) < 0) diff --git a/src/nf-queue.c b/src/nf-queue.c index bd10adf..922d9c8 100644 --- a/src/nf-queue.c +++ b/src/nf-queue.c @@ -13,6 +13,7 @@ #include #include +#include #include #include #include diff --git a/src/nl-addr-list.c b/src/nl-addr-list.c index 9b045a5..20995a8 100644 --- a/src/nl-addr-list.c +++ b/src/nl-addr-list.c @@ -41,7 +41,7 @@ static void print_usage(void) static char *prefix; -void print_prefix(struct nl_dump_params *p, int line) +static void print_prefix(struct nl_dump_params *p, int line) { if (prefix) nl_dump(p, "%s", prefix); @@ -65,7 +65,7 @@ static void env_dump(struct nl_object *obj, void *arg) nl_addr2str(rtnl_addr_get_local(addr), buf, sizeof(buf))); nl_dump_line(p, "%s_IFINDEX=%u\n", pfx, rtnl_addr_get_ifindex(addr)); - link_cache = nl_cache_mngt_require("route/link"); + link_cache = nl_cache_mngt_require_safe("route/link"); if (link_cache) nl_dump_line(p, "%s_IFNAME=%s\n", pfx, rtnl_link_i2name(link_cache, @@ -94,6 +94,9 @@ static void env_dump(struct nl_object *obj, void *arg) nl_dump_line(p, "%s_CACHEINFO_VALID=%u\n", pfx, rtnl_addr_get_valid_lifetime(addr)); + if (link_cache) + nl_cache_put(link_cache); + #if 0 if (addr->ce_mask & ADDR_ATTR_CACHEINFO) { struct rtnl_addr_cacheinfo *ci = &addr->a_cacheinfo; diff --git a/src/nl-class-add.c b/src/nl-class-add.c index 9698f0d..b9a17dc 100644 --- a/src/nl-class-add.c +++ b/src/nl-class-add.c @@ -15,6 +15,8 @@ #include #include +#include + static int quiet = 0; static void print_usage(void) diff --git a/src/nl-class-delete.c b/src/nl-class-delete.c index 3df5a9a..37657a4 100644 --- a/src/nl-class-delete.c +++ b/src/nl-class-delete.c @@ -14,7 +14,7 @@ #include static int quiet = 0, default_yes = 0, deleted = 0, interactive = 0; -struct nl_sock *sock; +static struct nl_sock *sock; static void print_usage(void) { diff --git a/src/nl-class-list.c b/src/nl-class-list.c index 44ca49b..c2423fb 100644 --- a/src/nl-class-list.c +++ b/src/nl-class-list.c @@ -14,9 +14,9 @@ #include #include -struct nl_sock *sock; +static struct nl_sock *sock; -struct nl_dump_params params = { +static struct nl_dump_params params = { .dp_type = NL_DUMP_LINE, }; diff --git a/src/nl-cls-add.c b/src/nl-cls-add.c index 054bab3..6acb320 100644 --- a/src/nl-cls-add.c +++ b/src/nl-cls-add.c @@ -13,6 +13,8 @@ #include #include +#include + static int quiet = 0; static void print_usage(void) diff --git a/src/nl-cls-delete.c b/src/nl-cls-delete.c index 7d998fd..2b3db1f 100644 --- a/src/nl-cls-delete.c +++ b/src/nl-cls-delete.c @@ -14,7 +14,7 @@ #include static int quiet = 0, default_yes = 0, deleted = 0, interactive = 0; -struct nl_sock *sock; +static struct nl_sock *sock; static void print_usage(void) { diff --git a/src/nl-cls-list.c b/src/nl-cls-list.c index fcb5dcb..5072585 100644 --- a/src/nl-cls-list.c +++ b/src/nl-cls-list.c @@ -14,9 +14,9 @@ #include #include -struct nl_sock *sock; +static struct nl_sock *sock; -struct nl_dump_params params = { +static struct nl_dump_params params = { .dp_type = NL_DUMP_LINE, }; diff --git a/src/nl-link-enslave.c b/src/nl-link-enslave.c new file mode 100644 index 0000000..2b5d47d --- /dev/null +++ b/src/nl-link-enslave.c @@ -0,0 +1,50 @@ +/* + * src/nl-link-enslave.c Enslave a link + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2011 Thomas Graf + */ + +#include +#include +#include + +int main(int argc, char *argv[]) +{ + struct nl_sock *sock; + struct nl_cache *link_cache; + struct rtnl_link *master, *slave; + int err; + + if (argc < 3) { + fprintf(stderr, "Usage: nl-link-enslave master slave\n"); + return 1; + } + + sock = nl_cli_alloc_socket(); + nl_cli_connect(sock, NETLINK_ROUTE); + link_cache = nl_cli_link_alloc_cache(sock); + + if (!(master = rtnl_link_get_by_name(link_cache, argv[1]))) { + fprintf(stderr, "Unknown link: %s\n", argv[1]); + return 1; + } + + if (!(slave = rtnl_link_get_by_name(link_cache, argv[2]))) { + fprintf(stderr, "Unknown link: %s\n", argv[2]); + return 1; + } + + if ((err = rtnl_link_bond_enslave(sock, master, slave)) < 0) { + fprintf(stderr, "Unable to enslave %s to %s: %s\n", + argv[2], argv[1], nl_geterror(err)); + return 1; + } + + return 0; +} + diff --git a/src/nl-link-name2ifindex.c b/src/nl-link-name2ifindex.c index b04af04..b8ae4bc 100644 --- a/src/nl-link-name2ifindex.c +++ b/src/nl-link-name2ifindex.c @@ -14,7 +14,7 @@ static void print_usage(void) { - printf("Usage: nl-link-ifindex2name \n"); + printf("Usage: nl-link-name2ifindex \n"); exit(0); } diff --git a/src/nl-link-release.c b/src/nl-link-release.c new file mode 100644 index 0000000..4c9f15a --- /dev/null +++ b/src/nl-link-release.c @@ -0,0 +1,45 @@ +/* + * src/nl-link-release.c release a link + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2011 Thomas Graf + */ + +#include +#include +#include + +int main(int argc, char *argv[]) +{ + struct nl_sock *sock; + struct nl_cache *link_cache; + struct rtnl_link *slave; + int err; + + if (argc < 2) { + fprintf(stderr, "Usage: nl-link-release slave\n"); + return 1; + } + + sock = nl_cli_alloc_socket(); + nl_cli_connect(sock, NETLINK_ROUTE); + link_cache = nl_cli_link_alloc_cache(sock); + + if (!(slave = rtnl_link_get_by_name(link_cache, argv[1]))) { + fprintf(stderr, "Unknown link: %s\n", argv[1]); + return 1; + } + + if ((err = rtnl_link_bond_release(sock, slave)) < 0) { + fprintf(stderr, "Unable to release slave %s: %s\n", + argv[1], nl_geterror(err)); + return 1; + } + + return 0; +} + diff --git a/src/nl-list-caches.c b/src/nl-list-caches.c index 3c35dd5..853d8a4 100644 --- a/src/nl-list-caches.c +++ b/src/nl-list-caches.c @@ -9,7 +9,7 @@ * Copyright (c) 2003-2009 Thomas Graf */ -#include +#include #include static void print_usage(void) @@ -81,9 +81,9 @@ static void print(struct nl_cache_ops *ops, void *arg) printf(" genl:\n" \ " name: %s\n" \ - " family: %d\n" \ + " user-hdr: %d\n" \ " id: %d\n", - genl_ops->o_name, genl_ops->o_family, genl_ops->o_id); + genl_ops->o_name, genl_ops->o_hdrsize, genl_ops->o_id); if (genl_ops->o_ncmds) { int i; diff --git a/src/nl-list-sockets.c b/src/nl-list-sockets.c index 868006e..c2fa1cd 100644 --- a/src/nl-list-sockets.c +++ b/src/nl-list-sockets.c @@ -30,7 +30,7 @@ int main(int argc, char *argv[]) while (fgets(buf, sizeof(buf), fd)) { unsigned long sk, cb; int ret, proto, pid, rmem, wmem, refcnt; - uint32_t groups; + unsigned int groups; ret = sscanf(buf, "%lx %d %d %08x %d %d %lx %d\n", &sk, &proto, &pid, &groups, &rmem, &wmem, diff --git a/src/nl-neigh-delete.c b/src/nl-neigh-delete.c index 887bd84..c418608 100644 --- a/src/nl-neigh-delete.c +++ b/src/nl-neigh-delete.c @@ -14,7 +14,7 @@ #include static int quiet = 0, default_yes = 0, deleted = 0, interactive = 0; -struct nl_sock *sock; +static struct nl_sock *sock; static void print_usage(void) { diff --git a/src/nl-qdisc-add.c b/src/nl-qdisc-add.c index dd2ed2b..c2a7c9f 100644 --- a/src/nl-qdisc-add.c +++ b/src/nl-qdisc-add.c @@ -14,6 +14,8 @@ #include #include +#include + static int quiet = 0; static void print_usage(void) diff --git a/src/nl-qdisc-delete.c b/src/nl-qdisc-delete.c index 9c7ec14..2f945bb 100644 --- a/src/nl-qdisc-delete.c +++ b/src/nl-qdisc-delete.c @@ -15,7 +15,7 @@ #include static int quiet = 0, default_yes = 0, deleted = 0, interactive = 0; -struct nl_sock *sock; +static struct nl_sock *sock; static void print_usage(void) { diff --git a/src/nl-route-add.c b/src/nl-route-add.c index 2f187df..d4aa767 100644 --- a/src/nl-route-add.c +++ b/src/nl-route-add.c @@ -120,7 +120,7 @@ int main(int argc, char *argv[]) } } - if ((err = rtnl_route_add(sock, route, 0)) < 0) + if ((err = rtnl_route_add(sock, route, NLM_F_EXCL)) < 0) nl_cli_fatal(err, "Unable to add route: %s", nl_geterror(err)); if (!quiet) { diff --git a/src/nl-tctree-list.c b/src/nl-tctree-list.c index 4cd5035..d90cb28 100644 --- a/src/nl-tctree-list.c +++ b/src/nl-tctree-list.c @@ -12,6 +12,7 @@ #include #include #include +#include #include static struct nl_sock *sock; @@ -22,6 +23,7 @@ static struct nl_dump_params params = { static int ifindex; static void print_qdisc(struct nl_object *, void *); +static void print_tc_childs(struct rtnl_tc *, void *); static void print_usage(void) { @@ -50,7 +52,7 @@ static void print_class(struct nl_object *obj, void *arg) if (leaf) print_qdisc((struct nl_object *) leaf, arg + 2); - rtnl_class_foreach_child(class, class_cache, &print_class, arg + 2); + print_tc_childs(TC_CAST(class), arg + 2); if (rtnl_cls_alloc_cache(sock, ifindex, parent, &cls_cache) < 0) return; @@ -60,6 +62,20 @@ static void print_class(struct nl_object *obj, void *arg) nl_cache_free(cls_cache); } +static void print_tc_childs(struct rtnl_tc *tc, void *arg) +{ + struct rtnl_class *filter; + + filter = nl_cli_class_alloc(); + + rtnl_tc_set_parent(TC_CAST(filter), rtnl_tc_get_handle(tc)); + rtnl_tc_set_ifindex(TC_CAST(filter), rtnl_tc_get_ifindex(tc)); + + nl_cache_foreach_filter(class_cache, OBJ_CAST(filter), &print_class, arg); + + rtnl_class_put(filter); +} + static void print_qdisc(struct nl_object *obj, void *arg) { struct rtnl_qdisc *qdisc = (struct rtnl_qdisc *) obj; @@ -69,7 +85,7 @@ static void print_qdisc(struct nl_object *obj, void *arg) params.dp_prefix = (int)(long) arg; nl_object_dump(obj, ¶ms); - rtnl_qdisc_foreach_child(qdisc, class_cache, &print_class, arg + 2); + print_tc_childs(TC_CAST(qdisc), arg + 2); if (rtnl_cls_alloc_cache(sock, ifindex, parent, &cls_cache) < 0) return; diff --git a/tests/.gitignore b/tests/.gitignore index 19184ee..151e9b5 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -1,4 +1,9 @@ test-cache-mngr +test-complex-HTB-with-hash-filters +test-create-bond +test-create-vlan +test-delete-link test-genl test-nf-cache-mngr test-socket-creation +check-all diff --git a/tests/Makefile b/tests/Makefile deleted file mode 100644 index 8494eea..0000000 --- a/tests/Makefile +++ /dev/null @@ -1,37 +0,0 @@ -# -# src/Makefile -# -# 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 version 2.1 -# of the License. -# -# Copyright (c) 2003-2006 Thomas Graf -# - -ifeq ($(shell [ ! -r ../Makefile.opts ] && echo 1),) - include ../Makefile.opts -endif - -LDFLAGS += -L../lib -lnl ../src/utils.o -CIN := $(wildcard test-*.c) -TOOLS := $(CIN:%.c=%) - -all: $(TOOLS) - -$(TOOLS): ../src/utils.o - -test-%: test-%.c - @echo " LD $@"; \ - $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) -lnl-genl -lnl-route - -clean: - @echo " CLEAN src"; \ - rm -f $(TOOLS) - -distclean: clean - -install: - @true - -include ../Makefile.rules diff --git a/tests/Makefile.am b/tests/Makefile.am new file mode 100644 index 0000000..6a5606a --- /dev/null +++ b/tests/Makefile.am @@ -0,0 +1,47 @@ +# -*- Makefile -*- + +AM_CPPFLAGS = -Wall -I${top_srcdir}/include -I${top_builddir}/include -D_GNU_SOURCE -DSYSCONFDIR=\"$(sysconfdir)/libnl\" + +LDADD = \ + ${top_builddir}/lib/libnl-3.la \ + ${top_builddir}/lib/libnl-nf-3.la \ + ${top_builddir}/lib/libnl-genl-3.la \ + ${top_builddir}/lib/libnl-route-3.la \ + @CHECK_LIBS@ + +CFLAGS += @CHECK_CFLAGS@ + +UNIT_TESTS = check-all + +check_PROGRAMS = \ + test-create-bond \ + test-create-vlan \ + test-delete-link \ + test-socket-creation \ + test-complex-HTB-with-hash-filters \ + ${UNIT_TESTS} + +TESTS = \ + ${UNIT_TESTS} + +if ENABLE_CLI +LDADD += ${top_builddir}/src/lib/libnl-cli-3.la +check_PROGRAMS += \ + test-cache-mngr \ + test-genl \ + test-nf-cache-mngr +endif + +test_cache_mngr_SOURCES = test-cache-mngr.c +test_create_bond_SOURCES = test-create-bond.c +test_create_vlan_SOURCES = test-create-vlan.c +test_delete_link_SOURCES = test-delete-link.c +test_genl_SOURCES = test-genl.c +test_nf_cache_mngr_SOURCES = test-nf-cache-mngr.c +test_socket_creation_SOURCES = test-socket-creation.c +test_complex_HTB_with_hash_filters_SOURCES = test-complex-HTB-with-hash-filters.c + +# Unit tests +check_all_SOURCES = \ + check-all.c \ + check-addr.c diff --git a/tests/check-addr.c b/tests/check-addr.c new file mode 100644 index 0000000..39f3ede --- /dev/null +++ b/tests/check-addr.c @@ -0,0 +1,212 @@ +/* + * tests/check-addr.c nl_addr unit tests + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2013 Thomas Graf + */ + +#include +#include + +START_TEST(addr_alloc) +{ + struct nl_addr *addr; + + addr = nl_addr_alloc(16); + fail_if(addr == NULL, + "Allocation should not return NULL"); + + fail_if(nl_addr_iszero(addr) == 0, + "New empty address should be all zeros"); + + fail_if(nl_addr_get_family(addr) != AF_UNSPEC, + "New empty address should have family AF_UNSPEC"); + + fail_if(nl_addr_get_prefixlen(addr) != 0, + "New empty address should have prefix length 0"); + + fail_if(nl_addr_shared(addr), + "New empty address should not be shared"); + + fail_if(nl_addr_get(addr) != addr, + "nl_addr_get() should return pointer to address"); + + fail_if(nl_addr_shared(addr) == 0, + "Address should be shared after call to nl_addr_get()"); + + nl_addr_put(addr); + + fail_if(nl_addr_shared(addr), + "Address should not be shared after call to nl_addr_put()"); + + fail_if(nl_addr_fill_sockaddr(addr, NULL, 0) == 0, + "Socket address filling should fail for empty address"); + + nl_addr_put(addr); +} +END_TEST + +START_TEST(addr_binary_addr) +{ + struct nl_addr *addr, *addr2; + char baddr[4] = { 0x1, 0x2, 0x3, 0x4 }; + char baddr2[6] = { 0x1, 0x2, 0x3, 0x4, 0x5, 0x6 }; + + addr = nl_addr_alloc(4); + fail_if(addr == NULL, + "Allocation should not return NULL"); + + fail_if(nl_addr_set_binary_addr(addr, baddr, 4) < 0, + "Valid binary address should be settable"); + + fail_if(nl_addr_get_prefixlen(addr) != 0, + "Prefix length should be unchanged after nl_addr_set_binary_addr()"); + + fail_if(nl_addr_get_len(addr) != 4, + "Address length should be 4"); + + fail_if(nl_addr_set_binary_addr(addr, baddr2, 6) == 0, + "Should not be able to set binary address exceeding maximum length"); + + fail_if(nl_addr_get_len(addr) != 4, + "Address length should still be 4"); + + fail_if(nl_addr_guess_family(addr) != AF_INET, + "Binary address of length 4 should be guessed as AF_INET"); + + fail_if(memcmp(baddr, nl_addr_get_binary_addr(addr), 4) != 0, + "Binary address mismatches"); + + addr2 = nl_addr_build(AF_UNSPEC, baddr, 4); + fail_if(addr2 == NULL, + "Building of address should not fail"); + + nl_addr_set_prefixlen(addr, 32); + fail_if(nl_addr_get_prefixlen(addr) != 32, + "Prefix length should be successful changed after nl_addr_set_prefixlen()"); + + fail_if(nl_addr_cmp(addr, addr2), + "Addresses built from same binary address should match"); + + nl_addr_put(addr); + nl_addr_put(addr2); +} +END_TEST + +START_TEST(addr_parse4) +{ + struct nl_addr *addr4, *clone; + struct sockaddr_in sin; + socklen_t len = sizeof(sin); + char *addr_str = "10.0.0.1/16"; + char buf[128]; + + fail_if(nl_addr_parse(addr_str, AF_INET6, &addr4) == 0, + "Should not be able to parse IPv4 address in IPv6 mode"); + + fail_if(nl_addr_parse(addr_str, AF_UNSPEC, &addr4) != 0, + "Should be able to parse \"%s\"", addr_str); + + fail_if(nl_addr_get_family(addr4) != AF_INET, + "Address family should be AF_INET"); + + fail_if(nl_addr_get_prefixlen(addr4) != 16, + "Prefix length should be 16"); + + fail_if(nl_addr_iszero(addr4), + "Address should not be all zeroes"); + + clone = nl_addr_clone(addr4); + fail_if(clone == NULL, + "Cloned address should not be NULL"); + + fail_if(nl_addr_cmp(addr4, clone) != 0, + "Cloned address should not mismatch original"); + + fail_if(nl_addr_fill_sockaddr(addr4, (struct sockaddr *) &sin, &len) != 0, + "Should be able to fill socketaddr"); + + fail_if(strcmp(nl_addr2str(addr4, buf, sizeof(buf)), addr_str), + "Address translated back to string does not match original"); + + nl_addr_put(addr4); + nl_addr_put(clone); +} +END_TEST + +START_TEST(addr_parse6) +{ + struct nl_addr *addr6, *clone; + struct sockaddr_in6 sin; + socklen_t len = sizeof(sin); + char *addr_str = "2001:1:2::3/64"; + char buf[128]; + + fail_if(nl_addr_parse(addr_str, AF_INET, &addr6) == 0, + "Should not be able to parse IPv6 address in IPv4 mode"); + + fail_if(nl_addr_parse(addr_str, AF_UNSPEC, &addr6) != 0, + "Should be able to parse \"%s\"", addr_str); + + fail_if(nl_addr_get_family(addr6) != AF_INET6, + "Address family should be AF_INET6"); + + fail_if(nl_addr_get_prefixlen(addr6) != 64, + "Prefix length should be 64"); + + fail_if(nl_addr_iszero(addr6), + "Address should not be all zeroes"); + + clone = nl_addr_clone(addr6); + fail_if(clone == NULL, + "Cloned address should not be NULL"); + + fail_if(nl_addr_cmp(addr6, clone) != 0, + "Cloned address should not mismatch original"); + + fail_if(nl_addr_fill_sockaddr(addr6, (struct sockaddr *) &sin, &len) != 0, + "Should be able to fill socketaddr"); + + fail_if(strcmp(nl_addr2str(addr6, buf, sizeof(buf)), addr_str), + "Address translated back to string does not match original"); + + nl_addr_put(addr6); + nl_addr_put(clone); +} +END_TEST + +START_TEST(addr_info) +{ + struct nl_addr *addr; + char *addr_str = "127.0.0.1"; + struct addrinfo *result; + + fail_if(nl_addr_parse(addr_str, AF_UNSPEC, &addr) != 0, + "Parsing of valid address should not fail"); + + fail_if(nl_addr_info(addr, &result) != 0, + "getaddrinfo() on loopback address should work"); + + freeaddrinfo(result); + nl_addr_put(addr); +} +END_TEST + +Suite *make_nl_addr_suite(void) +{ + Suite *suite = suite_create("Abstract addresses"); + + TCase *tc_addr = tcase_create("Core"); + tcase_add_test(tc_addr, addr_alloc); + tcase_add_test(tc_addr, addr_binary_addr); + tcase_add_test(tc_addr, addr_parse4); + tcase_add_test(tc_addr, addr_parse6); + tcase_add_test(tc_addr, addr_info); + suite_add_tcase(suite, tc_addr); + + return suite; +} diff --git a/tests/check-all.c b/tests/check-all.c new file mode 100644 index 0000000..e801003 --- /dev/null +++ b/tests/check-all.c @@ -0,0 +1,42 @@ +/* + * tests/check-all.c overall unit test program + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2013 Thomas Graf + */ + +#include + +extern Suite *make_nl_addr_suite(void); + +static Suite *main_suite(void) +{ + Suite *suite = suite_create("main"); + + return suite; +} + +int main(int argc, char *argv[]) +{ + SRunner *runner; + int nfailed; + + runner = srunner_create(main_suite()); + + /* Add testsuites below */ + + srunner_add_suite(runner, make_nl_addr_suite()); + + /* Do not add testsuites below this line */ + + srunner_run_all(runner, CK_ENV); + + nfailed = srunner_ntests_failed(runner); + srunner_free(runner); + + return nfailed != 0; +} diff --git a/tests/test-cache-mngr.c b/tests/test-cache-mngr.c index 777bce8..8999e58 100644 --- a/tests/test-cache-mngr.c +++ b/tests/test-cache-mngr.c @@ -1,16 +1,20 @@ -#include "../src/utils.h" +#include +#include +#include #include +#include + static int quit = 0; -static void change_cb(struct nl_cache *cache, struct nl_object *obj, - int action) -{ - struct nl_dump_params dp = { - .dp_type = NL_DUMP_LINE, - .dp_fd = stdout, - }; +static struct nl_dump_params dp = { + .dp_type = NL_DUMP_LINE, +}; + +static void change_cb(struct nl_cache *cache, struct nl_object *obj, + int action, void *data) +{ if (action == NL_ACT_NEW) printf("NEW "); else if (action == NL_ACT_DEL) @@ -29,43 +33,34 @@ static void sigint(int arg) int main(int argc, char *argv[]) { struct nl_cache_mngr *mngr; - struct nl_cache *lc, *nc, *ac, *rc; - struct nl_sock *sock; - int err; + struct nl_cache *cache; + int err, i; + + dp.dp_fd = stdout; signal(SIGINT, sigint); - sock = nlt_alloc_socket(); - err = nl_cache_mngr_alloc(sock, NETLINK_ROUTE, NL_AUTO_PROVIDE, &mngr); + err = nl_cache_mngr_alloc(NULL, NETLINK_ROUTE, NL_AUTO_PROVIDE, &mngr); if (err < 0) - fatal(err, "Unable to allocate cache manager: %s", - nl_geterror(err)); + nl_cli_fatal(err, "Unable to allocate cache manager: %s", + nl_geterror(err)); - if ((err = nl_cache_mngr_add(mngr, "route/link", &change_cb, &lc)) < 0) - fatal(err, "Unable to add cache route/link: %s", - nl_geterror(err)); - - if ((err = nl_cache_mngr_add(mngr, "route/neigh", &change_cb, &nc)) < 0) - fatal(err, "Unable to add cache route/neigh: %s", - nl_geterror(err)); - - if ((err = nl_cache_mngr_add(mngr, "route/addr", &change_cb, &ac)) < 0) - fatal(err, "Unable to add cache route/addr: %s", - nl_geterror(err)); - - if ((err = nl_cache_mngr_add(mngr, "route/route", &change_cb, &rc)) < 0) - fatal(err, "Unable to add cache route/route: %s", - nl_geterror(err)); + for (i = 1; i < argc; i++) { + err = nl_cache_mngr_add(mngr, argv[i], &change_cb, NULL, &cache); + if (err < 0) + nl_cli_fatal(err, "Unable to add cache %s: %s", + argv[i], nl_geterror(err)); + } while (!quit) { - int err = nl_cache_mngr_poll(mngr, 5000); + int err = nl_cache_mngr_poll(mngr, 1000); if (err < 0 && err != -NLE_INTR) - fatal(err, "Polling failed: %s", nl_geterror(err)); + nl_cli_fatal(err, "Polling failed: %s", nl_geterror(err)); + nl_cache_mngr_info(mngr, &dp); } nl_cache_mngr_free(mngr); - nl_socket_free(sock); return 0; } diff --git a/tests/test-complex-HTB-with-hash-filters.c b/tests/test-complex-HTB-with-hash-filters.c new file mode 100644 index 0000000..48cf5e3 --- /dev/null +++ b/tests/test-complex-HTB-with-hash-filters.c @@ -0,0 +1,761 @@ +/* + * test/test-complex-HTB-with-hash-filters.c Add HTB qdisc, HTB classes and creates some hash filters + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2011 Adrian Ban + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +//#include "include/rtnl_u32.h" + +#include +#include +//#include "include/rtnl_u32_addon.h" + +#define TC_HANDLE(maj, min) (TC_H_MAJ((maj) << 16) | TC_H_MIN(min)) + +/* some functions are copied from iproute-tc tool */ +int get_u32(__u32 *val, const char *arg, int base) +{ + unsigned long res; + char *ptr; + + if (!arg || !*arg) + return -1; + res = strtoul(arg, &ptr, base); + if (!ptr || ptr == arg || *ptr || res > 0xFFFFFFFFUL) + return -1; + *val = res; + return 0; +} + +int get_u32_handle(__u32 *handle, const char *str) +{ + __u32 htid=0, hash=0, nodeid=0; + char *tmp = strchr(str, ':'); + + if (tmp == NULL) { + if (memcmp("0x", str, 2) == 0) + return get_u32(handle, str, 16); + return -1; + } + htid = strtoul(str, &tmp, 16); + if (tmp == str && *str != ':' && *str != 0) + return -1; + if (htid>=0x1000) + return -1; + if (*tmp) { + str = tmp+1; + hash = strtoul(str, &tmp, 16); + if (tmp == str && *str != ':' && *str != 0) + return -1; + if (hash>=0x100) + return -1; + if (*tmp) { + str = tmp+1; + nodeid = strtoul(str, &tmp, 16); + if (tmp == str && *str != 0) + return -1; + if (nodeid>=0x1000) + return -1; + } + } + *handle = (htid<<20)|(hash<<12)|nodeid; + return 0; +} + +uint32_t get_u32_parse_handle(const char *cHandle) +{ + uint32_t handle=0; + + if(get_u32_handle(&handle, cHandle)) { + printf ("Illegal \"ht\"\n"); + return -1; + } + + if (handle && TC_U32_NODE(handle)) { + printf("\"link\" must be a hash table.\n"); + return -1; + } + return handle; +} + +int get_tc_classid(__u32 *h, const char *str) +{ + __u32 maj, min; + char *p; + + maj = TC_H_ROOT; + if (strcmp(str, "root") == 0) + goto ok; + maj = TC_H_UNSPEC; + if (strcmp(str, "none") == 0) + goto ok; + maj = strtoul(str, &p, 16); + if (p == str) { + maj = 0; + if (*p != ':') + return -1; + } + if (*p == ':') { + if (maj >= (1<<16)) + return -1; + maj <<= 16; + str = p+1; + min = strtoul(str, &p, 16); + if (*p != 0) + return -1; + if (min >= (1<<16)) + return -1; + maj |= min; + } else if (*p != 0) + return -1; + +ok: + *h = maj; + return 0; +} + +/* + * Function that adds a new filter and attach it to a hash table + * + */ +int u32_add_filter_on_ht(struct nl_sock *sock, struct rtnl_link *rtnlLink, uint32_t prio, + uint32_t keyval, uint32_t keymask, int keyoff, int keyoffmask, + uint32_t htid, uint32_t classid +) +{ + struct rtnl_cls *cls; + int err; + + //printf("Key Val : 0x%x\n", keyval); + //printf("Key Mask : 0x%x\n", keymask); + + cls=rtnl_cls_alloc(); + if (!(cls)) { + printf("Can not allocate classifier\n"); + nl_socket_free(sock); + exit(1); + } + + rtnl_tc_set_link(TC_CAST(cls), rtnlLink); + + if ((err = rtnl_tc_set_kind(TC_CAST(cls), "u32"))) { + printf("Can not set classifier as u32\n"); + return 1; + } + + rtnl_cls_set_prio(cls, prio); + rtnl_cls_set_protocol(cls, ETH_P_IP); + + rtnl_tc_set_parent(TC_CAST(cls), TC_HANDLE(1, 0)); + + rtnl_u32_set_hashtable(cls, htid); + + rtnl_u32_add_key_uint32(cls, keyval, keymask, keyoff, keyoffmask); /* 10.0.0.0/8 */ + + rtnl_u32_set_classid(cls, classid); + + rtnl_u32_set_cls_terminal(cls); + + if ((err = rtnl_cls_add(sock, cls, NLM_F_CREATE))) { + printf("Can not add classifier: %s\n", nl_geterror(err)); + return -1; + } + rtnl_cls_put(cls); + return 0; + +} + +/* + * Function that adds a new filter and attach it to a hash table + * and set next hash table link with hash mask + * + */ +int u32_add_filter_on_ht_with_hashmask(struct nl_sock *sock, struct rtnl_link *rtnlLink, uint32_t prio, + uint32_t keyval, uint32_t keymask, int keyoff, int keyoffmask, + uint32_t htid, uint32_t htlink, uint32_t hmask, uint32_t hoffset +) +{ + struct rtnl_cls *cls; + int err; + + //printf("Key Val : 0x%x\n", keyval); + //printf("Key Mask : 0x%x\n", keymask); + + cls=rtnl_cls_alloc(); + if (!(cls)) { + printf("Can not allocate classifier\n"); + nl_socket_free(sock); + exit(1); + } + + rtnl_tc_set_link(TC_CAST(cls), rtnlLink); + + if ((err = rtnl_tc_set_kind(TC_CAST(cls), "u32"))) { + printf("Can not set classifier as u32\n"); + return 1; + } + + rtnl_cls_set_prio(cls, prio); + rtnl_cls_set_protocol(cls, ETH_P_IP); + + rtnl_tc_set_parent(TC_CAST(cls), TC_HANDLE(1, 0)); + + if (htid) + rtnl_u32_set_hashtable(cls, htid); + + rtnl_u32_add_key_uint32(cls, keyval, keymask, keyoff, keyoffmask); + + rtnl_u32_set_hashmask(cls, hmask, hoffset); + + rtnl_u32_set_link(cls, htlink); + + + if ((err = rtnl_cls_add(sock, cls, NLM_F_CREATE))) { + printf("Can not add classifier: %s\n", nl_geterror(err)); + return -1; + } + rtnl_cls_put(cls); + return 0; +} + +/* + * function that creates a new hash table + */ +int u32_add_ht(struct nl_sock *sock, struct rtnl_link *rtnlLink, uint32_t prio, uint32_t htid, uint32_t divisor) +{ + + int err; + struct rtnl_cls *cls; + + cls=rtnl_cls_alloc(); + if (!(cls)) { + printf("Can not allocate classifier\n"); + nl_socket_free(sock); + exit(1); + } + + rtnl_tc_set_link(TC_CAST(cls), rtnlLink); + + if ((err = rtnl_tc_set_kind(TC_CAST(cls), "u32"))) { + printf("Can not set classifier as u32\n"); + return 1; + } + + rtnl_cls_set_prio(cls, prio); + rtnl_cls_set_protocol(cls, ETH_P_IP); + rtnl_tc_set_parent(TC_CAST(cls), TC_HANDLE(1, 0)); + + rtnl_u32_set_handle(cls, htid, 0x0, 0x0); + //printf("htid: 0x%X\n", htid); + rtnl_u32_set_divisor(cls, divisor); + + if ((err = rtnl_cls_add(sock, cls, NLM_F_CREATE))) { + printf("Can not add classifier: %s\n", nl_geterror(err)); + return -1; + } + rtnl_cls_put(cls); + return 0; +} + +/* + * function that adds a new HTB qdisc and set the default class for unclassified traffic + */ +int qdisc_add_HTB(struct nl_sock *sock, struct rtnl_link *rtnlLink, uint32_t defaultClass) +{ + + struct rtnl_qdisc *qdisc; + int err; + + /* Allocation of a qdisc object */ + if (!(qdisc = rtnl_qdisc_alloc())) { + printf("Can not allocate Qdisc\n"); + return -1; + } + + //rtnl_tc_set_ifindex(TC_CAST(qdisc), master_index); + rtnl_tc_set_link(TC_CAST(qdisc), rtnlLink); + rtnl_tc_set_parent(TC_CAST(qdisc), TC_H_ROOT); + + //delete the qdisc + //printf("Delete current qdisc\n"); + rtnl_qdisc_delete(sock, qdisc); + //rtnl_qdisc_put(qdisc); + + //add a HTB qdisc + //printf("Add a new HTB qdisc\n"); + rtnl_tc_set_handle(TC_CAST(qdisc), TC_HANDLE(1,0)); + + if ((err = rtnl_tc_set_kind(TC_CAST(qdisc), "htb"))) { + printf("Can not allocate HTB\n"); + return -1; + } + + /* Set default class for unclassified traffic */ + //printf("Set default class for unclassified traffic\n"); + rtnl_htb_set_defcls(qdisc, TC_HANDLE(1, defaultClass)); + rtnl_htb_set_rate2quantum(qdisc, 1); + + /* Submit request to kernel and wait for response */ + if ((err = rtnl_qdisc_add(sock, qdisc, NLM_F_CREATE))) { + printf("Can not allocate HTB Qdisc\n"); + return -1; + } + + /* Return the qdisc object to free memory resources */ + rtnl_qdisc_put(qdisc); + + return 0; +} + +/* + * function that adds a new HTB class and set its parameters + */ +int class_add_HTB(struct nl_sock *sock, struct rtnl_link *rtnlLink, + uint32_t parentMaj, uint32_t parentMin, + uint32_t childMaj, uint32_t childMin, + uint64_t rate, uint64_t ceil, + uint32_t burst, uint32_t cburst, + uint32_t prio +) +{ + int err; + struct rtnl_class *class; + //struct rtnl_class *class = (struct rtnl_class *) tc; + + //create a HTB class + //class = (struct rtnl_class *)rtnl_class_alloc(); + if (!(class = rtnl_class_alloc())) { + printf("Can not allocate class object\n"); + return 1; + } + // + rtnl_tc_set_link(TC_CAST(class), rtnlLink); + //add a HTB qdisc + //printf("Add a new HTB class with 0x%X:0x%X on parent 0x%X:0x%X\n", childMaj, childMin, parentMaj, parentMin); + rtnl_tc_set_parent(TC_CAST(class), TC_HANDLE(parentMaj, parentMin)); + rtnl_tc_set_handle(TC_CAST(class), TC_HANDLE(childMaj, childMin)); + + if ((err = rtnl_tc_set_kind(TC_CAST(class), "htb"))) { + printf("Can not set HTB to class\n"); + return 1; + } + + //printf("set HTB class prio to %u\n", prio); + rtnl_htb_set_prio((struct rtnl_class *)class, prio); + + if (rate) { + //rate=rate/8; + rtnl_htb_set_rate(class, rate); + } + if (ceil) { + //ceil=ceil/8; + rtnl_htb_set_ceil(class, ceil); + } + + if (burst) { + //printf ("Class HTB: set rate burst: %u\n", burst); + rtnl_htb_set_rbuffer(class, burst); + } + if (cburst) { + //printf ("Class HTB: set rate cburst: %u\n", cburst); + rtnl_htb_set_cbuffer(class, cburst); + } + /* Submit request to kernel and wait for response */ + if ((err = rtnl_class_add(sock, class, NLM_F_CREATE))) { + printf("Can not allocate HTB Qdisc\n"); + return 1; + } + rtnl_class_put(class); + return 0; +} + +/* + * function that adds a HTB root class and set its parameters + */ +int class_add_HTB_root(struct nl_sock *sock, struct rtnl_link *rtnlLink, + uint64_t rate, uint64_t ceil, + uint32_t burst, uint32_t cburst +) +{ + int err; + struct rtnl_class *class; + + //create a HTB class + class = (struct rtnl_class *)rtnl_class_alloc(); + //class = rtnl_class_alloc(); + if (!class) { + printf("Can not allocate class object\n"); + return 1; + } + // + rtnl_tc_set_link(TC_CAST(class), rtnlLink); + rtnl_tc_set_parent(TC_CAST(class), TC_H_ROOT); + //add a HTB class + //printf("Add a new HTB ROOT class\n"); + rtnl_tc_set_handle(TC_CAST(class), 1); + + if ((err = rtnl_tc_set_kind(TC_CAST(class), "htb"))) { + printf("Can not set HTB to class\n"); + return 1; + } + + if (rate) { + //rate=rate/8; + rtnl_htb_set_rate(class, rate); + } + if (ceil) { + //ceil=ceil/8; + rtnl_htb_set_ceil(class, ceil); + } + + if (burst) { + rtnl_htb_set_rbuffer(class, burst); + } + if (cburst) { + rtnl_htb_set_cbuffer(class, cburst); + } + + /* Submit request to kernel and wait for response */ + if ((err = rtnl_class_add(sock, class, NLM_F_CREATE))) { + printf("Can not allocate HTB Qdisc\n"); + return 1; + } + rtnl_class_put(class); + return 0; +} + +/* + * function that adds a new SFQ qdisc as a leaf for a HTB class + */ +int qdisc_add_SFQ_leaf(struct nl_sock *sock, struct rtnl_link *rtnlLink, + uint32_t parentMaj, uint32_t parentMin, + int quantum, int limit, int perturb +) +{ + int err; + struct rtnl_qdisc *qdisc; + + if (!(qdisc = rtnl_qdisc_alloc())) { + printf("Can not allocate qdisc object\n"); + return 1; + } + rtnl_tc_set_link(TC_CAST(qdisc), rtnlLink); + rtnl_tc_set_parent(TC_CAST(qdisc), TC_HANDLE(parentMaj, parentMin)); + + rtnl_tc_set_handle(TC_CAST(qdisc), TC_HANDLE(parentMin,0)); + + if ((err = rtnl_tc_set_kind(TC_CAST(qdisc), "sfq"))) { + printf("Can not set SQF class\n"); + return 1; + } + + if(quantum) { + rtnl_sfq_set_quantum(qdisc, quantum); + } else { + rtnl_sfq_set_quantum(qdisc, 16000); // tc default value + } + if(limit) { + rtnl_sfq_set_limit(qdisc, limit); // default is 127 + } + if(perturb) { + rtnl_sfq_set_perturb(qdisc, perturb); // default never perturb the hash + } + + /* Submit request to kernel and wait for response */ + if ((err = rtnl_qdisc_add(sock, qdisc, NLM_F_CREATE))) { + printf("Can not allocate SFQ qdisc\n"); + return -1; + } + + /* Return the qdisc object to free memory resources */ + rtnl_qdisc_put(qdisc); + return 0; +} + + + + +int main() { + + struct nl_sock *sock; + struct rtnl_link *link; + + //struct rtnl_qdisc *qdisc; + //struct rtnl_class *class; + //struct rtnl_cls *cls; + + uint32_t ht, htlink, htid, direction, classid; + //uint32_t hash, hashmask, nodeid, divisor, handle; + //struct rtnl_u32 *f_u32; + char chashlink[16]=""; + + //uint64_t drops, qlen; + + //int master_index; + int err; + + //uint64_t rate=0, ceil=0; + + struct nl_cache *link_cache; + + if (!(sock = nl_socket_alloc())) { + printf("Unable to allocate netlink socket\n"); + exit(1); + } + + if ((err = nl_connect(sock, NETLINK_ROUTE)) < 0 ) { + printf("Nu s-a putut conecta la NETLINK!\n"); + nl_socket_free(sock); + exit(1); + } + + + if ((err = rtnl_link_alloc_cache(sock, AF_UNSPEC, &link_cache)) < 0) { + printf("Unable to allocate link cache: %s\n", + nl_geterror(err)); + nl_socket_free(sock); + exit(1); + } + + /* lookup interface index of eth0 */ + if (!(link = rtnl_link_get_by_name(link_cache, "imq0"))) { + /* error */ + printf("Interface not found\n"); + nl_socket_free(sock); + exit(1); + } + + err=qdisc_add_HTB(sock, link, 0xffff); + //drops = rtnl_tc_get_stat(TC_CAST(qdisc), RTNL_TC_DROPS); + + //printf("Add ROOT HTB class\n"); + err=class_add_HTB_root(sock, link, 12500000, 12500000, 25000, 25000); + err=class_add_HTB(sock, link, 1, 0, 1, 0xffff, 1250000, 12500000, 25000, 25000, 5); + err=qdisc_add_SFQ_leaf(sock, link, 1, 0xffff, 16000, 0, 10); + err=class_add_HTB(sock, link, 1, 1, 1, 0x5, 2000000, 2000000, 25000, 25000, 5); + err=qdisc_add_SFQ_leaf(sock, link, 1, 0x5, 16000, 0, 10); + err=class_add_HTB(sock, link, 1, 1, 1, 0x6, 1000000, 1000000, 25000, 25000, 5); + err=qdisc_add_SFQ_leaf(sock, link, 1, 0x6, 16000, 0, 10); + //err=class_add_HTB(sock, link, 1, 0, 1, 0x7, 1024000, 100000000, 5); + //err=class_add_HTB(sock, link, 1, 0, 1, 0x8, 2048000, 100000000, 5); + //err=class_add_HTB(sock, link, 1, 0, 1, 0x9, 4096000, 100000000, 5); + //err=class_add_HTB(sock, link, 1, 0, 1, 0xa, 8192000, 100000000, 5); + + //printf("Add main hash table\n"); + + /* create u32 first hash filter table + * + */ + /* formula calcul handle: + * uint32_t handle = (htid << 20) | (hash << 12) | nodeid; + */ + + /* + * Upper limit of number of hash tables: 4096 (0xFFF) + * Number of hashes in a table: 256 values (0xFF) + * + */ + + /* using 256 values for hash table + * each entry in hash table match a byte from IP address specified later by a hash key + */ + + uint32_t i; + for (i = 1; i <= 0xf; i++) + u32_add_ht(sock, link, 1, i, 256); + + /* + * attach a u32 filter to the first hash + * that redirects all traffic and make a hash key + * from the fist byte of the IP address + * + */ + + //divisor=0x0; // unused here + //handle = 0x0; // unused here + //hash = 0x0; // unused here + //htid = 0x0; // unused here + //nodeid = 0x0; // unused here + + // direction = 12 -> source IP + // direction = 16 -> destination IP + direction = 16; + + /* + * which hash table will use + * in our case is hash table no 1 defined previous + * + * There are 2 posibilities to set the the hash table: + * 1. Using function get_u32_handle and sent a string in + * format 10: where 10 is number of the hash table + * 2. Create your own value in format: 0xa00000 + * + */ + strcpy(chashlink, "1:"); + //printf("Hash Link: %s\n", chashlink); + //chashlink=malloc(sizeof(char) * + htlink = 0x0; // is used by get_u32_handle to return the correct value of hash table (link) + + if(get_u32_handle(&htlink, chashlink)) { + printf ("Illegal \"link\""); + nl_socket_free(sock); + exit(1); + } + //printf ("hash link : 0x%X\n", htlink); + //printf ("hash link test : %u\n", (htlink && TC_U32_NODE(htlink))); + + if (htlink && TC_U32_NODE(htlink)) { + printf("\"link\" must be a hash table.\n"); + nl_socket_free(sock); + exit(1); + } + /* the hash mask will hit the hash table (link) no 1: in our case + */ + + /* set the hash key mask */ + //hashmask = 0xFF000000UL; // the mask that is used to match the hash in specific table, in our case for example 1:a with mean the first byte which is 10 in hash table 1 + + /* Here we add a hash filter which match the first byte (see the hashmask value) + * of the source IP (offset 12 in the packet header) + * You can use also offset 16 to match the destination IP + */ + + /* + * Also we need a filter to match our rule + * This mean that we will put a 0.0.0.0/0 filter in our first rule + * that match the offset 12 (source IP) + * Also you can put offset 16 to match the destination IP + */ + + u32_add_filter_on_ht_with_hashmask(sock, link, 1, + 0x0, 0x0, direction, 0, + 0, htlink, 0xff000000, direction); + + /* + * For each first byte that we need to match we will create a new hash table + * For example: you have those clases: 10.0.0.0/24 and 172.16.0.0/23 + * For byte 10 and byte 172 will create a separate hash table that will match the second + * byte from each class. + * + */ + + + // Create a new hash table with prio 1, id 2 and 256 entries +// u32_CreateNewHashTable(sock, link, 1, 2, 256); + // Create a new hash table with prio 1, id 3 and 256 entries +// u32_CreateNewHashTable(sock, link, 1, 3, 256); +// u32_CreateNewHashTable(sock, link, 1, 4, 256); +// u32_CreateNewHashTable(sock, link, 1, 5, 256); + + /* + * Now we will create other filter under (ATENTION) our first hash table (link) 1: + * Previous rule redirects the trafic according the hash mask to hash table (link) no 1: + * Here we will match the hash tables from 1:0 to 1:ff. Under each hash table we will attach + * other rules that matches next byte from IP source/destination IP and we will repeat the + * previous steps. + * + */ + + + // /8 check + + // 10.0.0.0/8 + ht=get_u32_parse_handle("1:a:"); + htid = (ht&0xFFFFF000); + htlink=get_u32_parse_handle("2:"); + + u32_add_filter_on_ht_with_hashmask(sock, link, 1, + 0x0a000000, 0xff000000, direction, 0, + htid, htlink, 0x00ff0000, direction); + + // 172.0.0.0/8 + ht=get_u32_parse_handle("1:ac:"); + htid = (ht&0xFFFFF000); + htlink=get_u32_parse_handle("3:"); + + u32_add_filter_on_ht_with_hashmask(sock, link, 1, + 0xac000000, 0xff000000, direction, 0, + htid, htlink, 0x00ff0000, direction); + + + // /16 check + // 10.0.0.0/16 + ht=get_u32_parse_handle("2:0:"); + htid = (ht&0xFFFFF000); + htlink=get_u32_parse_handle("4:"); + + u32_add_filter_on_ht_with_hashmask(sock, link, 1, + 0x0a000000, 0xffff0000, direction, 0, + htid, htlink, 0x0000ff00, direction); + + // 172.17.0.0/16 + ht=get_u32_parse_handle("3:11:"); + htid = (ht&0xFFFFF000); + htlink=get_u32_parse_handle("5:"); + + u32_add_filter_on_ht_with_hashmask(sock, link, 1, + 0xac110000, 0xffff0000, direction, 0, + htid, htlink, 0x0000ff00, direction); + + // /24 check + // 10.0.9.0/24 + ht=get_u32_parse_handle("4:9:"); + htid = (ht&0xFFFFF000); + htlink=get_u32_parse_handle("6:"); + + u32_add_filter_on_ht_with_hashmask(sock, link, 1, + 0x0a000900, 0xffffff00, direction, 0, + htid, htlink, 0x000000ff, direction); + + // 172.17.2.0/16 + ht=get_u32_parse_handle("5:2:"); + htid = (ht&0xFFFFF000); + htlink=get_u32_parse_handle("7:"); + + u32_add_filter_on_ht_with_hashmask(sock, link, 1, + 0xac110200, 0xffffff00, direction, 0, + htid, htlink, 0x000000ff, direction); + + + // final filters + // 10.0.9.20 + ht=get_u32_parse_handle("6:14:"); + htid = (ht&0xFFFFF000); + + err = get_tc_classid(&classid, "1:5"); + + u32_add_filter_on_ht(sock, link, 1, + 0x0a000914, 0xffffffff, direction, 0, + htid, classid); + + // 172.17.2.120 + ht=get_u32_parse_handle("7:78:"); + htid = (ht&0xFFFFF000); + + err = get_tc_classid(&classid, "1:6"); + + u32_add_filter_on_ht(sock, link, 1, + 0xac110278, 0xffffffff, direction, 0, + htid, classid); + + + + nl_socket_free(sock); + return 0; +} diff --git a/tests/test-create-bond.c b/tests/test-create-bond.c new file mode 100644 index 0000000..11bc5b0 --- /dev/null +++ b/tests/test-create-bond.c @@ -0,0 +1,29 @@ +#include +#include +#include + +int main(int argc, char *argv[]) +{ + struct rtnl_link *link; + struct nl_sock *sk; + int err; + + sk = nl_socket_alloc(); + if ((err = nl_connect(sk, NETLINK_ROUTE)) < 0) { + nl_perror(err, "Unable to connect socket"); + return err; + } + + link = rtnl_link_bond_alloc(); + rtnl_link_set_name(link, "my_bond"); + + if ((err = rtnl_link_add(sk, link, NLM_F_CREATE)) < 0) { + nl_perror(err, "Unable to add link"); + return err; + } + + rtnl_link_put(link); + nl_close(sk); + + return 0; +} diff --git a/tests/test-create-vlan.c b/tests/test-create-vlan.c new file mode 100644 index 0000000..64e478f --- /dev/null +++ b/tests/test-create-vlan.c @@ -0,0 +1,43 @@ +#include +#include +#include + +int main(int argc, char *argv[]) +{ + struct rtnl_link *link; + struct nl_cache *link_cache; + struct nl_sock *sk; + int err, master_index; + + sk = nl_socket_alloc(); + if ((err = nl_connect(sk, NETLINK_ROUTE)) < 0) { + nl_perror(err, "Unable to connect socket"); + return err; + } + + if ((err = rtnl_link_alloc_cache(sk, AF_UNSPEC, &link_cache)) < 0) { + nl_perror(err, "Unable to allocate cache"); + return err; + } + + if (!(master_index = rtnl_link_name2i(link_cache, "eth0"))) { + fprintf(stderr, "Unable to lookup eth0"); + return -1; + } + + link = rtnl_link_vlan_alloc(); + + rtnl_link_set_link(link, master_index); + + rtnl_link_vlan_set_id(link, 10); + + if ((err = rtnl_link_add(sk, link, NLM_F_CREATE)) < 0) { + nl_perror(err, "Unable to add link"); + return err; + } + + rtnl_link_put(link); + nl_close(sk); + + return 0; +} diff --git a/tests/test-delete-link.c b/tests/test-delete-link.c new file mode 100644 index 0000000..9cf1034 --- /dev/null +++ b/tests/test-delete-link.c @@ -0,0 +1,28 @@ +#include +#include + +int main(int argc, char *argv[]) +{ + struct rtnl_link *link; + struct nl_sock *sk; + int err; + + sk = nl_socket_alloc(); + if ((err = nl_connect(sk, NETLINK_ROUTE)) < 0) { + nl_perror(err, "Unable to connect socket"); + return err; + } + + link = rtnl_link_alloc(); + rtnl_link_set_name(link, "my_bond"); + + if ((err = rtnl_link_delete(sk, link)) < 0) { + nl_perror(err, "Unable to delete link"); + return err; + } + + rtnl_link_put(link); + nl_close(sk); + + return 0; +} diff --git a/tests/test-genl.c b/tests/test-genl.c index 8bf60c5..74aea10 100644 --- a/tests/test-genl.c +++ b/tests/test-genl.c @@ -1,4 +1,72 @@ -#include "../src/utils.h" +#include +#include + +static struct nla_policy attr_policy[TASKSTATS_TYPE_MAX+1] = { + [TASKSTATS_TYPE_PID] = { .type = NLA_U32 }, + [TASKSTATS_TYPE_TGID] = { .type = NLA_U32 }, + [TASKSTATS_TYPE_STATS] = { .minlen = sizeof(struct taskstats) }, + [TASKSTATS_TYPE_AGGR_PID] = { .type = NLA_NESTED }, + [TASKSTATS_TYPE_AGGR_TGID] = { .type = NLA_NESTED }, +}; + + +static int parse_cmd_new(struct nl_cache_ops *unused, struct genl_cmd *cmd, + struct genl_info *info, void *arg) +{ + struct nlattr *attrs[TASKSTATS_TYPE_MAX+1]; + struct nlattr *nested; + int err; + + if (info->attrs[TASKSTATS_TYPE_AGGR_PID]) + nested = info->attrs[TASKSTATS_TYPE_AGGR_PID]; + else if (info->attrs[TASKSTATS_TYPE_AGGR_TGID]) + nested = info->attrs[TASKSTATS_TYPE_AGGR_TGID]; + else { + fprintf(stderr, "Invalid taskstats message: Unable to find " + "nested attribute/\n"); + return NL_SKIP; + } + + err = nla_parse_nested(attrs, TASKSTATS_TYPE_MAX, nested, attr_policy); + if (err < 0) { + nl_perror(err, "Error while parsing generic netlink message"); + return err; + } + + + if (attrs[TASKSTATS_TYPE_STATS]) { + struct taskstats *stats = nla_data(attrs[TASKSTATS_TYPE_STATS]); + + printf("%s pid %u uid %u gid %u parent %u\n", + stats->ac_comm, stats->ac_pid, stats->ac_uid, + stats->ac_gid, stats->ac_ppid); + } + + return 0; +} + +static int parse_cb(struct nl_msg *msg, void *arg) +{ + return genl_handle_msg(msg, NULL); +} + +static struct genl_cmd cmds[] = { + { + .c_id = TASKSTATS_CMD_NEW, + .c_name = "taskstats_new()", + .c_maxattr = TASKSTATS_TYPE_MAX, + .c_attr_policy = attr_policy, + .c_msg_parser = &parse_cmd_new, + }, +}; + +#define ARRAY_SIZE(X) (sizeof(X) / sizeof((X)[0])) + +static struct genl_ops ops = { + .o_name = TASKSTATS_GENL_NAME, + .o_cmds = cmds, + .o_ncmds = ARRAY_SIZE(cmds), +}; int main(int argc, char *argv[]) { @@ -7,28 +75,42 @@ int main(int argc, char *argv[]) void *hdr; int err; - sock = nlt_alloc_socket(); - nlt_connect(sock, NETLINK_GENERIC); + sock = nl_cli_alloc_socket(); + nl_cli_connect(sock, NETLINK_GENERIC); + + if ((err = genl_register_family(&ops)) < 0) + nl_cli_fatal(err, "Unable to register Generic Netlink family"); + + if ((err = genl_ops_resolve(sock, &ops)) < 0) + nl_cli_fatal(err, "Unable to resolve family name"); + + if (genl_ctrl_resolve(sock, "nlctrl") != GENL_ID_CTRL) + nl_cli_fatal(NLE_INVAL, "Resolving of \"nlctrl\" failed"); msg = nlmsg_alloc(); if (msg == NULL) - fatal(NLE_NOMEM, "Unable to allocate netlink message"); + nl_cli_fatal(NLE_NOMEM, "Unable to allocate netlink message"); - hdr = genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, GENL_ID_CTRL, - 0, 0, CTRL_CMD_GETFAMILY, 1); + hdr = genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, ops.o_id, + 0, 0, TASKSTATS_CMD_GET, TASKSTATS_GENL_VERSION); if (hdr == NULL) - fatal(ENOMEM, "Unable to write genl header"); + nl_cli_fatal(ENOMEM, "Unable to write genl header"); - if ((err = nla_put_u32(msg, CTRL_ATTR_FAMILY_ID, GENL_ID_CTRL)) < 0) - fatal(err, "Unable to add attribute: %s", nl_geterror(err)); + if ((err = nla_put_u32(msg, TASKSTATS_CMD_ATTR_PID, 1)) < 0) + nl_cli_fatal(err, "Unable to add attribute: %s", nl_geterror(err)); if ((err = nl_send_auto_complete(sock, msg)) < 0) - fatal(err, "Unable to send message: %s", nl_geterror(err)); - - if ((err = nl_recvmsgs_default(sock)) < 0) - fatal(err, "Unable to receive message: %s", nl_geterror(err)); + nl_cli_fatal(err, "Unable to send message: %s", nl_geterror(err)); nlmsg_free(msg); + + if ((err = nl_socket_modify_cb(sock, NL_CB_VALID, NL_CB_CUSTOM, + parse_cb, NULL)) < 0) + nl_cli_fatal(err, "Unable to modify valid message callback"); + + if ((err = nl_recvmsgs_default(sock)) < 0) + nl_cli_fatal(err, "Unable to receive message: %s", nl_geterror(err)); + nl_close(sock); nl_socket_free(sock); diff --git a/tests/test-nf-cache-mngr.c b/tests/test-nf-cache-mngr.c index 05485bf..b4f3022 100644 --- a/tests/test-nf-cache-mngr.c +++ b/tests/test-nf-cache-mngr.c @@ -1,13 +1,13 @@ -#include "../src/utils.h" +#include static void change_cb(struct nl_cache *cache, struct nl_object *obj, - int action) + int action, void *data) { struct nfnl_ct *ct = (struct nfnl_ct *) obj; static struct nl_addr *hack = NULL; if (!hack) - hack = nl_addr_parse("194.88.212.233", AF_INET); + nl_addr_parse("194.88.212.233", AF_INET, &hack); if (!nl_addr_cmp(hack, nfnl_ct_get_src(ct, 1)) || !nl_addr_cmp(hack, nfnl_ct_get_dst(ct, 1))) { @@ -26,25 +26,26 @@ int main(int argc, char *argv[]) struct nl_cache_mngr *mngr; struct nl_sock *sock; struct nl_cache *ct; + int err; - sock = nlt_socket_alloc(); + sock = nl_cli_alloc_socket(); - mngr = nl_cache_mngr_alloc(sock, NETLINK_NETFILTER, NL_AUTO_PROVIDE); - if (!mngr) { - nl_perror("nl_cache_mngr_alloc"); + err = nl_cache_mngr_alloc(sock, NETLINK_NETFILTER, NL_AUTO_PROVIDE, &mngr); + if (err < 0) { + nl_perror(err, "nl_cache_mngr_alloc"); return -1; } - ct = nl_cache_mngr_add(mngr, "netfilter/ct", &change_cb); - if (ct == NULL) { - nl_perror("nl_cache_mngr_add(netfilter/ct)"); + err = nl_cache_mngr_add(mngr, "netfilter/ct", &change_cb, NULL, &ct); + if (err < 0) { + nl_perror(err, "nl_cache_mngr_add(netfilter/ct)"); return -1; } for (;;) { int err = nl_cache_mngr_poll(mngr, 5000); if (err < 0) { - nl_perror("nl_cache_mngr_poll()"); + nl_perror(err, "nl_cache_mngr_poll()"); return -1; } diff --git a/tests/test-socket-creation.c b/tests/test-socket-creation.c index a170ccd..83f3ad4 100644 --- a/tests/test-socket-creation.c +++ b/tests/test-socket-creation.c @@ -1,23 +1,24 @@ -#include "../src/utils.h" +#include +#include int main(int argc, char *argv[]) { struct nl_sock *h[1025]; int i; - h[0] = nl_handle_alloc(); + h[0] = nl_socket_alloc(); printf("Created handle with port 0x%x\n", nl_socket_get_local_port(h[0])); - nl_handle_destroy(h[0]); - h[0] = nl_handle_alloc(); + nl_socket_free(h[0]); + h[0] = nl_socket_alloc(); printf("Created handle with port 0x%x\n", nl_socket_get_local_port(h[0])); - nl_handle_destroy(h[0]); + nl_socket_free(h[0]); for (i = 0; i < 1025; i++) { - h[i] = nl_handle_alloc(); + h[i] = nl_socket_alloc(); if (h[i] == NULL) - nl_perror("Unable to allocate socket"); + nl_perror(ENOMEM, "Unable to allocate socket"); else printf("Created handle with port 0x%x\n", nl_socket_get_local_port(h[i]));