]> granicus.if.org Git - libnl/commitdiff
python interface to netlink protocols
authorThomas Graf <tgraf@suug.ch>
Tue, 10 May 2011 10:17:33 +0000 (12:17 +0200)
committerThomas Graf <tgraf@suug.ch>
Tue, 10 May 2011 10:17:33 +0000 (12:17 +0200)
currently includes experimental support for links, addresses
and some traffic control

25 files changed:
configure.in
m4/ax_pkg_swig.m4 [new file with mode: 0644]
m4/ax_python_devel.m4 [new file with mode: 0644]
m4/ax_swig_python.m4 [new file with mode: 0644]
python/doc/conf.py [new file with mode: 0644]
python/doc/core.rst [new file with mode: 0644]
python/doc/index.rst [new file with mode: 0644]
python/doc/route.rst [new file with mode: 0644]
python/doc/route_addr.rst [new file with mode: 0644]
python/netlink/__init__.py [new file with mode: 0644]
python/netlink/capi.i [new file with mode: 0644]
python/netlink/core.py [new file with mode: 0644]
python/netlink/fixes.h [new file with mode: 0644]
python/netlink/route/__init__.py [new file with mode: 0644]
python/netlink/route/address.py [new file with mode: 0644]
python/netlink/route/capi.i [new file with mode: 0644]
python/netlink/route/link.py [new file with mode: 0644]
python/netlink/route/links/__init__.py [new file with mode: 0644]
python/netlink/route/links/dummy.py [new file with mode: 0644]
python/netlink/route/links/inet.py [new file with mode: 0644]
python/netlink/route/links/vlan.py [new file with mode: 0644]
python/netlink/route/qdisc.py [new file with mode: 0644]
python/netlink/route/tc.py [new file with mode: 0644]
python/netlink/util.py [new file with mode: 0644]
python/setup.py [new file with mode: 0644]

index 43150a724ca28f7aab572fc72fdeef19a2a776c4..00ad3ee145ced82ff85f20f1b751f97fc5e7490d 100644 (file)
@@ -27,6 +27,11 @@ AM_PROG_LIBTOOL
 AM_PROG_LEX
 AC_PROG_YACC
 
+AM_PATH_PYTHON(2.6)
+AX_PKG_SWIG
+AX_PYTHON_DEVEL
+AX_SWIG_PYTHON
+
 AC_C_CONST
 AC_C_INLINE
 
diff --git a/m4/ax_pkg_swig.m4 b/m4/ax_pkg_swig.m4
new file mode 100644 (file)
index 0000000..e112f3d
--- /dev/null
@@ -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 <sebastian-huber@web.de>
+#   Copyright (c) 2008 Alan W. Irwin <irwin@beluga.phys.uvic.ca>
+#   Copyright (c) 2008 Rafael Laboissiere <rafael@laboissiere.net>
+#   Copyright (c) 2008 Andrew Collier <colliera@ukzn.ac.za>
+#   Copyright (c) 2011 Murray Cumming <murrayc@openismus.com>
+#
+#   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 <http://www.gnu.org/licenses/>.
+#
+#   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_devel.m4 b/m4/ax_python_devel.m4
new file mode 100644 (file)
index 0000000..a62b860
--- /dev/null
@@ -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 <sebastian-huber@web.de>
+#   Copyright (c) 2009 Alan W. Irwin <irwin@beluga.phys.uvic.ca>
+#   Copyright (c) 2009 Rafael Laboissiere <rafael@laboissiere.net>
+#   Copyright (c) 2009 Andrew Collier <colliera@ukzn.ac.za>
+#   Copyright (c) 2009 Matteo Settenvini <matteo@member.fsf.org>
+#   Copyright (c) 2009 Horst Knorr <hk_classes@knoda.org>
+#
+#   This program is free software: you can redistribute it and/or modify it
+#   under the terms of the GNU General Public License as published by the
+#   Free Software Foundation, either version 3 of the License, or (at your
+#   option) any later version.
+#
+#   This program is distributed in the hope that it will be useful, but
+#   WITHOUT ANY WARRANTY; without even the implied warranty of
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+#   Public License for more details.
+#
+#   You should have received a copy of the GNU General Public License along
+#   with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+#   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<<EOD | $PYTHON -
+
+# join all versioning strings, on some systems
+# major/minor numbers could be in different list elements
+from distutils.sysconfig import *
+ret = ''
+for e in get_config_vars ('VERSION'):
+       if (e != None):
+               ret += e
+print (ret)
+EOD`
+
+               if test -z "$ac_python_version"; then
+                       if test -n "$PYTHON_VERSION"; then
+                               ac_python_version=$PYTHON_VERSION
+                       else
+                               ac_python_version=`$PYTHON -c "import sys; \
+                                       print (sys.version[[:3]])"`
+                       fi
+               fi
+
+               # Make the versioning information available to the compiler
+               AC_DEFINE_UNQUOTED([HAVE_PYTHON], ["$ac_python_version"],
+                                   [If available, contains the Python version number currently in use.])
+
+               # First, the library directory:
+               ac_python_libdir=`cat<<EOD | $PYTHON -
+
+# There should be only one
+import distutils.sysconfig
+for e in distutils.sysconfig.get_config_vars ('LIBDIR'):
+       if e != None:
+               print (e)
+               break
+EOD`
+
+               # Before checking for libpythonX.Y, we need to know
+               # the extension the OS we're on uses for libraries
+               # (we take the first one, if there's more than one fix me!):
+               ac_python_soext=`$PYTHON -c \
+                 "import distutils.sysconfig; \
+                 print (distutils.sysconfig.get_config_vars('SO')[[0]])"`
+
+               # Now, for the library:
+               ac_python_soname=`$PYTHON -c \
+                 "import distutils.sysconfig; \
+                 print (distutils.sysconfig.get_config_vars('LDLIBRARY')[[0]])"`
+
+               # Strip away extension from the end to canonicalize its name:
+               ac_python_library=`echo "$ac_python_soname" | sed "s/${ac_python_soext}$//"`
+
+               # This small piece shamelessly adapted from PostgreSQL python macro;
+               # credits goes to momjian, I think. I'd like to put the right name
+               # in the credits, if someone can point me in the right direction... ?
+               #
+               if test -n "$ac_python_libdir" -a -n "$ac_python_library" \
+                       -a x"$ac_python_library" != x"$ac_python_soname"
+               then
+                       # use the official shared library
+                       ac_python_library=`echo "$ac_python_library" | sed "s/^lib//"`
+                       PYTHON_LDFLAGS="-L$ac_python_libdir -l$ac_python_library"
+               else
+                       # old way: use libpython from python_configdir
+                       ac_python_libdir=`$PYTHON -c \
+                         "from distutils.sysconfig import get_python_lib as f; \
+                         import os; \
+                         print (os.path.join(f(plat_specific=1, standard_lib=1), 'config'));"`
+                       PYTHON_LDFLAGS="-L$ac_python_libdir -lpython$ac_python_version"
+               fi
+
+               if test -z "PYTHON_LDFLAGS"; then
+                       AC_MSG_ERROR([
+  Cannot determine location of your Python DSO. Please check it was installed with
+  dynamic libraries enabled, or try setting PYTHON_LDFLAGS by hand.
+                       ])
+               fi
+       fi
+       AC_MSG_RESULT([$PYTHON_LDFLAGS])
+       AC_SUBST([PYTHON_LDFLAGS])
+
+       #
+       # Check for site packages
+       #
+       AC_MSG_CHECKING([for Python site-packages path])
+       if test -z "$PYTHON_SITE_PKG"; then
+               PYTHON_SITE_PKG=`$PYTHON -c "import distutils.sysconfig; \
+                       print (distutils.sysconfig.get_python_lib(0,0));"`
+       fi
+       AC_MSG_RESULT([$PYTHON_SITE_PKG])
+       AC_SUBST([PYTHON_SITE_PKG])
+
+       #
+       # libraries which must be linked in when embedding
+       #
+       AC_MSG_CHECKING(python extra libraries)
+       if test -z "$PYTHON_EXTRA_LIBS"; then
+          PYTHON_EXTRA_LIBS=`$PYTHON -c "import distutils.sysconfig; \
+                conf = distutils.sysconfig.get_config_var; \
+                print (conf('LOCALMODLIBS') + ' ' + conf('LIBS'))"`
+       fi
+       AC_MSG_RESULT([$PYTHON_EXTRA_LIBS])
+       AC_SUBST(PYTHON_EXTRA_LIBS)
+
+       #
+       # linking flags needed when embedding
+       #
+       AC_MSG_CHECKING(python extra linking flags)
+       if test -z "$PYTHON_EXTRA_LDFLAGS"; then
+               PYTHON_EXTRA_LDFLAGS=`$PYTHON -c "import distutils.sysconfig; \
+                       conf = distutils.sysconfig.get_config_var; \
+                       print (conf('LINKFORSHARED'))"`
+       fi
+       AC_MSG_RESULT([$PYTHON_EXTRA_LDFLAGS])
+       AC_SUBST(PYTHON_EXTRA_LDFLAGS)
+
+       #
+       # final check to see if everything compiles alright
+       #
+       AC_MSG_CHECKING([consistency of all components of python development environment])
+       # save current global flags
+       ac_save_LIBS="$LIBS"
+       ac_save_CPPFLAGS="$CPPFLAGS"
+       LIBS="$ac_save_LIBS $PYTHON_LDFLAGS $PYTHON_EXTRA_LDFLAGS $PYTHON_EXTRA_LIBS"
+       CPPFLAGS="$ac_save_CPPFLAGS $PYTHON_CPPFLAGS"
+       AC_LANG_PUSH([C])
+       AC_LINK_IFELSE([
+               AC_LANG_PROGRAM([[#include <Python.h>]],
+                               [[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 (file)
index 0000000..8fd3df5
--- /dev/null
@@ -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 <sebastian-huber@web.de>
+#   Copyright (c) 2008 Alan W. Irwin <irwin@beluga.phys.uvic.ca>
+#   Copyright (c) 2008 Rafael Laboissiere <rafael@laboissiere.net>
+#   Copyright (c) 2008 Andrew Collier <colliera@ukzn.ac.za>
+#
+#   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 <http://www.gnu.org/licenses/>.
+#
+#   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/python/doc/conf.py b/python/doc/conf.py
new file mode 100644 (file)
index 0000000..74041f0
--- /dev/null
@@ -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 <tgraf@suug.ch>'
+
+# 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
+# "<project> v<release> 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 <link> 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 <tgraf@suug.ch>'], 1)
+]
diff --git a/python/doc/core.rst b/python/doc/core.rst
new file mode 100644 (file)
index 0000000..012e14c
--- /dev/null
@@ -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 (file)
index 0000000..8de8ae3
--- /dev/null
@@ -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 (file)
index 0000000..0b8f3f9
--- /dev/null
@@ -0,0 +1,3 @@
+**********************
+Routing
+**********************
diff --git a/python/doc/route_addr.rst b/python/doc/route_addr.rst
new file mode 100644 (file)
index 0000000..2cfe139
--- /dev/null
@@ -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/__init__.py b/python/netlink/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/python/netlink/capi.i b/python/netlink/capi.i
new file mode 100644 (file)
index 0000000..d80d87b
--- /dev/null
@@ -0,0 +1,457 @@
+%module capi
+%{
+#include <netlink/netlink.h>
+#include <netlink/types.h>
+#include <netlink/socket.h>
+#include <netlink/msg.h>
+#include <netlink/object.h>
+#include <netlink/cache.h>
+%}
+
+%include <stdint.i>
+%include <cstring.i>
+
+%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);
+        }
+%};
+
+/* <netlink/types.h> */
+
+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;
+};
+
+/* <netlink/errno.h> */
+extern const char *nl_geterror(int);
+
+/* <netlink/utils.h> */
+
+extern double nl_cancel_down_bytes(unsigned long long, char **);
+extern double nl_cancel_down_bits(unsigned long long, char **);
+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 *, ...);
+
+/* <netlink/netlink.h> */
+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 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/socket.h> */
+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);
+
+/* <netlink/msg.h> */
+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 *);
+
+/* <netlink/cache.h> */
+
+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 *);
+
+/* <netlink/addr.h> */
+%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 (file)
index 0000000..f97528d
--- /dev/null
@@ -0,0 +1,737 @@
+#
+# Netlink interface based on libnl
+#
+# Copyright (c) 2011 Thomas Graf <tgraf@suug.ch>
+#
+
+"""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.
+"""
+
+import capi
+import sys
+import socket
+import struct
+
+__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: " + self._msg
+
+class ImmutableError(NetlinkError):
+       def __init__(self, msg):
+               self._msg = msg
+
+       def __str__(self):
+               return "Immutable attribute: " + 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, socket):
+               socket.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<" + str(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(type=NL_DUMP_LINE)
+
+###########################################################################
+# Cacheable Object (Base Class)
+class Object(object):
+       """Cacheable object (base class)"""
+
+       def __init__(self, obj_name, name, obj=None):
+               self._obj_name = obj_name
+               self._name = name
+
+               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 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)
+
+       #####################################################################
+       # mark
+       @property
+        def mark(self):
+               if capi.nl_object_is_marked(self.obj):
+                        return True
+                else:
+                        return False
+
+       @mark.setter
+        def mark(self, value):
+               if value:
+                        capi.nl_object_mark(self._nl_object)
+                else:
+                        capi.nl_object_unmark(self._nl_object)
+
+       #####################################################################
+       # shared
+       @property
+        def shared(self):
+               return capi.nl_object_shared(self._nl_object) != 0
+
+       #####################################################################
+       # attrs
+       @property
+       def attrs(self):
+               attr_list = capi.nl_object_attr_list(self._nl_object, 1024)
+               return re.split('\W+', attr_list[0])
+
+       #####################################################################
+       # refcnt
+       @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)
+
+       def apply(self, attr, val):
+                try:
+                        d = attrs[self._name + "." + attr]
+                except KeyError:
+                        raise KeyError("Unknown " + self._name +
+                                           " attribute: " + attr)
+
+                if 'immutable' in d:
+                        raise ImmutableError(attr)
+
+                if not self._hasattr(attr):
+                       raise KeyError("Invalid " + self._name +
+                                          " attribute: " + attr)
+                self._setattr(attr, val)
+
+class ObjIterator(object):
+       def __init__(self, cache, obj):
+                self._cache = cache
+
+                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)
+
+###########################################################################
+# Cache
+class Cache(object):
+       """Collection of netlink objects"""
+       def __init__(self):
+               raise NotImplementedError()
+
+       def __del(self):
+               capi.nl_cache_free(self._c_cache)
+
+       def __len__(self):
+               return capi.nl_cache_nitems(self._c_cache)
+
+       def __iter__(self):
+               obj = capi.nl_cache_get_first(self._c_cache)
+               return ObjIterator(self, obj)
+
+       def __reversed__(self):
+               obj = capi.nl_cache_get_last(self._c_cache)
+               return ReverseObjIterator(self, obj)
+
+       def __contains__(self, item):
+               obj = capi.nl_cache_search(self._c_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
+       def _alloc_cache_name(self, name):
+               return capi.alloc_cache_name(name)
+
+       # implemented by sub classes, must return new instasnce of cacheable
+       # object
+       def _new_object(self, 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._c_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._c_cache, params._dp, filter)
+
+       def clear(self):
+               """Remove all cache entries"""
+               capi.nl_cache_clear(self._c_cache)
+
+       # Called by sub classes to set first cache argument
+       def _set_arg1(self, arg):
+               self.arg1 = arg
+                capi.nl_cache_set_arg1(self._c_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._c_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._c_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._c_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._c_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._c_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 = 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)
+
+###########################################################################
+# Address Family
+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 __len__(self):
+               return len(str(self))
+
+       def __int__(self):
+               return self._family
+
+       def __repr__(self):
+               return 'AddressFamily(\'' + str(self) + '\')'
+
+
+###########################################################################
+# Abstract Address
+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))
+
+
+# global dictionay for all object attributes
+#
+# attrs[type][keyword] : value
+#
+# keyword:
+#   type = { int | str }
+#   immutable = { True | False }
+#   fmt = func (formatting function)
+#
+attrs = {}
+
+def attr(name, **kwds):
+       attrs[name] = {}
+        for k in kwds:
+                attrs[name][k] = kwds[k]
+
+def nlattr(name, **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.
+
+       @netlink.nlattr('my_type.my_attr', type=int)
+       @property
+        def my_attr(self):
+               return self._my_attr
+
+        """
+       attrs[name] = {}
+        for k in kwds:
+                attrs[name][k] = kwds[k]
+
+        def wrap_fn(func):
+               return func
+
+        return wrap_fn
+
diff --git a/python/netlink/fixes.h b/python/netlink/fixes.h
new file mode 100644 (file)
index 0000000..9a6118b
--- /dev/null
@@ -0,0 +1 @@
+#include <stdint.h>
diff --git a/python/netlink/route/__init__.py b/python/netlink/route/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/python/netlink/route/address.py b/python/netlink/route/address.py
new file mode 100644 (file)
index 0000000..c0ce54f
--- /dev/null
@@ -0,0 +1,398 @@
+#
+# Copyright (c) 2011 Thomas Graf <tgraf@suug.ch>
+#
+
+"""Module providing access to network addresses
+"""
+
+__version__ = "1.0"
+__all__ = [
+       'AddressCache',
+       'Address']
+
+import datetime
+import netlink.core as netlink
+import netlink.capi as core_capi
+import netlink.route.capi as capi
+import netlink.route.link as Link
+import netlink.util as util
+
+###########################################################################
+# Address Cache
+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._c_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._c_cache, ifindex,
+                                         local._nl_addr)
+               if addr is None:
+                       raise KeyError()
+
+                return Address._from_capi(addr)
+
+       def _new_object(self, obj):
+               return Address(obj)
+
+       def _new_cache(self, cache):
+               return AddressCache(cache=cache)
+
+###########################################################################
+# Address Object
+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))
+
+       def _obj2type(self, 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
+
+       def _new_instance(self, obj):
+               return Address(obj)
+
+       #####################################################################
+       # ifindex
+        @netlink.nlattr('address.ifindex', type=int, immutable=True,
+                       fmt=util.num)
+        @property
+        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
+
+       #####################################################################
+       # link
+        @netlink.nlattr('address.link', type=str, fmt=util.string)
+        @property
+        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)
+
+       #####################################################################
+       # label
+        @netlink.nlattr('address.label', type=str, fmt=util.string)
+       @property
+        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)
+
+       #####################################################################
+       # flags
+        @netlink.nlattr('address.flags', type=str, fmt=util.string)
+       @property
+        def flags(self):
+               """Flags"""
+               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[0] == '-':
+                        i = capi.rtnl_addr_str2flags(flag[1:])
+                        capi.rtnl_addr_unset_flags(self._rtnl_addr, i)
+                else:
+                        i = capi.rtnl_addr_str2flags(flag[1:])
+                        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)
+
+       #####################################################################
+       # family
+        @netlink.nlattr('address.family', type=int, immutable=True,
+                       fmt=util.num)
+       @property
+        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, AddressFamily):
+                        value = AddressFamily(value)
+
+               capi.rtnl_addr_set_family(self._rtnl_addr, int(value))
+
+       #####################################################################
+       # scope
+        @netlink.nlattr('address.scope', type=int, fmt=util.num)
+       @property
+        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)
+
+       #####################################################################
+       # local address
+        @netlink.nlattr('address.local', type=str, immutable=True,
+                       fmt=util.addr)
+       @property
+        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)
+
+       #####################################################################
+       # Peer address
+        @netlink.nlattr('address.peer', type=str, fmt=util.addr)
+       @property
+        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)
+
+       #####################################################################
+       # Broadcast address
+        @netlink.nlattr('address.broadcast', type=str, fmt=util.addr)
+       @property
+        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)
+
+       #####################################################################
+       # Multicast address
+        @netlink.nlattr('address.multicast', type=str, fmt=util.addr)
+       @property
+        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)
+
+       #####################################################################
+       # Anycast address
+        @netlink.nlattr('address.anycast', type=str, fmt=util.addr)
+       @property
+        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)
+
+       #####################################################################
+       # Valid lifetime
+        @netlink.nlattr('address.valid_lifetime', type=int, immutable=True,
+                       fmt=util.num)
+       @property
+        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))
+
+       #####################################################################
+       # Preferred lifetime
+        @netlink.nlattr('address.preferred_lifetime', type=int,
+                       immutable=True, fmt=util.num)
+       @property
+        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))
+
+       #####################################################################
+       # Creation Time
+        @netlink.nlattr('address.create_time', type=int, immutable=True,
+                       fmt=util.num)
+       @property
+        def create_time(self):
+               """Creation time"""
+               hsec = capi.rtnl_addr_get_create_time(self._rtnl_addr)
+               return datetime.timedelta(milliseconds=10*hsec)
+
+       #####################################################################
+       # Last Update
+        @netlink.nlattr('address.last_update', type=int, immutable=True,
+                       fmt=util.num)
+       @property
+        def last_update(self):
+               """Last update"""
+               hsec = capi.rtnl_addr_get_last_update_time(self._rtnl_addr)
+               return datetime.timedelta(milliseconds=10*hsec)
+
+       #####################################################################
+       # add()
+       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)
+
+       #####################################################################
+       # delete()
+       def delete(self, socket):
+               """Attempt to delete this link in the kernel"""
+               ret = capi.rtnl_addr_delete(socket._sock, self._addr)
+                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)
+
+       ###################################################################
+       #
+       # format(details=False, stats=False)
+       #
+       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 (file)
index 0000000..abe3cc9
--- /dev/null
@@ -0,0 +1,338 @@
+%module capi
+%{
+#include <netlink/route/rtnl.h>
+#include <netlink/route/link.h>
+#include <netlink/route/link/vlan.h>
+#include <netlink/route/link/inet.h>
+
+#include <netlink/route/tc.h>
+#include <netlink/route/qdisc.h>
+
+#include <netlink/route/addr.h>
+%}
+
+%include <stdint.i>
+%include <cstring.i>
+
+%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 *);
+
+/* <netlink/route/rtnl.h> */
+
+%cstring_output_maxsize(char *buf, size_t len)
+extern char *          rtnl_scope2str(int, char *buf, size_t len);
+extern int             rtnl_str2scope(const char *);
+
+/* <netlink/route/link.h> */
+
+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_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 *);
+
+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_info_type(struct rtnl_link *, const char *);
+extern char *rtnl_link_get_info_type(struct rtnl_link *);
+
+/* <netlink/route/link/vlan.h> */
+
+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 *);
+
+/* <netlink/route/link/inet.h> */
+%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);
+
+/* <netlink/route/tc.h> */
+
+%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);
+
+/* <netlink/route/qdisc.h> */
+
+%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 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 *);
+
+/* <netlink/route/addr.h> */
+
+%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 (file)
index 0000000..b8e19fa
--- /dev/null
@@ -0,0 +1,596 @@
+#
+# Copyright (c) 2011 Thomas Graf <tgraf@suug.ch>
+#
+
+"""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)
+
+"""
+
+__version__ = "0.1"
+__all__ = [
+       'LinkCache',
+       'Link',
+       'get_from_kernel']
+
+import socket
+import sys
+import netlink.core as netlink
+import netlink.capi as core_capi
+import netlink.route.capi as capi
+import netlink.route.links.inet as inet
+import netlink.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
+
+###########################################################################
+# Link Cache
+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._protocol = netlink.NETLINK_ROUTE
+                self._c_cache = cache
+               self._set_arg1(family)
+
+       def __getitem__(self, key):
+               if type(key) is int:
+                        link = capi.rtnl_link_get(self._c_cache, key)
+                elif type(key) is str:
+                        link = capi.rtnl_link_get_by_name(self._c_cache, key)
+
+               if link is None:
+                        raise KeyError()
+                else:
+                        return Link.from_capi(link)
+
+       def _new_object(self, obj):
+               return Link(obj)
+
+       def _new_cache(self, cache):
+               return LinkCache(family=self.arg1, cache=cache)
+
+###########################################################################
+# Link Object
+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)
+
+               self._type = None
+
+               if self.type:
+                       self._type_lookup(self.type)
+
+               self.inet = inet.InetLink(self)
+               self.af = {'inet' : self.inet }
+
+       @classmethod
+        def from_capi(cls, obj):
+               return cls(capi.link2obj(obj))
+
+       def _obj2type(self, obj):
+               return capi.obj2link(obj)
+
+       def __cmp__(self, other):
+               return self.ifindex - other.ifindex
+
+       def _new_instance(self, obj):
+                if not obj:
+                        raise ValueError()
+
+                return Link(obj)
+
+       def _type_lookup(self, name):
+               rname = 'netlink.route.links.' + name
+               tmp = __import__(rname)
+                mod = sys.modules[rname]
+
+                # this will create a type instance and assign it to
+               # link.<type>, e.g. link.vlan.id
+                self._type = mod.assign_type(self)
+
+       #####################################################################
+       # ifindex
+        @netlink.nlattr('link.ifindex', type=int, immutable=True, fmt=util.num)
+        @property
+        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))
+
+       #####################################################################
+       # name
+        @netlink.nlattr('link.name', type=str, fmt=util.bold)
+       @property
+        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)
+
+       #####################################################################
+       # flags
+        @netlink.nlattr('link.flags', type=str, fmt=util.string)
+       @property
+        def flags(self):
+               """Flags"""
+               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[0] == '-':
+                        i = capi.rtnl_link_str2flags(flag[1:])
+                        capi.rtnl_link_unset_flags(self._rtnl_link, i)
+                else:
+                        i = capi.rtnl_link_str2flags(flag[1:])
+                        capi.rtnl_link_set_flags(self._rtnl_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)
+
+       #####################################################################
+       # mtu
+        @netlink.nlattr('link.mtu', type=int, fmt=util.num)
+       @property
+        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))
+
+       #####################################################################
+       # family
+        @netlink.nlattr('link.family', type=int, immutable=True, fmt=util.num)
+       @property
+        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)
+
+       #####################################################################
+       # address
+        @netlink.nlattr('link.address', type=str, fmt=util.addr)
+       @property
+        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)
+
+       #####################################################################
+       # broadcast
+        @netlink.nlattr('link.broadcast', type=str, fmt=util.addr)
+       @property
+        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)
+
+       #####################################################################
+       # qdisc
+        @netlink.nlattr('link.qdisc', type=str, immutable=True, fmt=util.string)
+       @property
+        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)
+
+       #####################################################################
+       # txqlen
+        @netlink.nlattr('link.txqlen', type=int, fmt=util.num)
+        @property
+        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))
+
+       #####################################################################
+       # weight
+        @netlink.nlattr('link.weight', type=str, fmt=util.string)
+        @property
+        def weight(self):
+               """Weight"""
+               v = capi.rtnl_link_get_weight(self._rtnl_link)
+               if v == 4294967295:
+                       return 'max'
+               else:
+                       return str(v)
+
+       @weight.setter
+        def weight(self, value):
+               if value == 'max':
+                       v = 4294967295
+               else:
+                       v = int(value)
+               capi.rtnl_link_set_weight(self._rtnl_link, v)
+
+       #####################################################################
+       # arptype
+        @netlink.nlattr('link.arptype', type=str, immutable=True, fmt=util.string)
+        @property
+        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)
+
+       #####################################################################
+       # operstate
+        @netlink.nlattr('link.operstate', type=str, immutable=True,
+                       fmt=util.string, title='state')
+       @property
+        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(flag)
+                capi.rtnl_link_set_operstate(self._rtnl_link, i)
+
+       #####################################################################
+       # mode
+        @netlink.nlattr('link.mode', type=str, immutable=True, fmt=util.string)
+       @property
+        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(flag)
+                capi.rtnl_link_set_linkmode(self._rtnl_link, i)
+
+       #####################################################################
+       # alias
+        @netlink.nlattr('link.alias', type=str, fmt=util.string)
+       @property
+        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)
+
+       #####################################################################
+       # type
+        @netlink.nlattr('link.type', type=str, fmt=util.string)
+       @property
+        def type(self):
+               """Link type"""
+               return capi.rtnl_link_get_info_type(self._rtnl_link)
+
+       @type.setter
+        def type(self, value):
+                if capi.rtnl_link_set_info_type(self._rtnl_link, value) < 0:
+                        raise NameError("unknown info type")
+
+                self._type_lookup(value)
+
+       #####################################################################
+       # get_stat()
+       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)
+
+       #####################################################################
+       # add()
+       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_link_add(socket._sock, self._rtnl_link, flags)
+               if ret < 0:
+                       raise netlink.KernelError(ret)
+
+       #####################################################################
+       # change()
+       def change(self, socket=None, flags=0):
+               """Commit changes made to the link object"""
+               if not socket:
+                        socket = netlink.lookup_socket(netlink.NETLINK_ROUTE)
+
+               if not self._orig:
+                       raise NetlinkError("Original link not available")
+               ret = capi.rtnl_link_change(socket._sock, self._orig, self._rtnl_link, flags)
+                if ret < 0:
+                        raise netlink.KernelError(ret)
+
+       #####################################################################
+       # delete()
+       def delete(self, socket=None):
+               """Attempt to delete this link in the kernel"""
+               if not socket:
+                        socket = netlink.lookup_socket(netlink.NETLINK_ROUTE)
+
+               ret = capi.rtnl_link_delete(socket._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):
+               buf = ''
+               if self.type:
+                       if self._type and hasattr(self._type, 'brief'):
+                               buf += self._type.brief()
+                       else:
+                               buf += util.yellow(self.type)
+
+                return buf + 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
+
+       ###################################################################
+       #
+       # format(details=False, stats=False)
+       #
+       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:27} {1:>16} {2:>16}\n'.format(*row)
+
+                        buf += self._foreach_af('stats')
+
+               return buf
+
+def get(name, socket=None):
+       """Lookup Link object directly from kernel"""
+       if not name:
+               raise ValueError()
+
+        if not socket:
+                socket = netlink.lookup_socket(netlink.NETLINK_ROUTE)
+
+        link = capi.get_from_kernel(socket._sock, 0, name)
+        if not link:
+                return None
+
+       return Link.from_capi(link)
+
+link_cache = LinkCache()
+
+def resolve(name):
+        socket = netlink.lookup_socket(netlink.NETLINK_ROUTE)
+        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 (file)
index 0000000..e69de29
diff --git a/python/netlink/route/links/dummy.py b/python/netlink/route/links/dummy.py
new file mode 100644 (file)
index 0000000..495be94
--- /dev/null
@@ -0,0 +1,21 @@
+#
+# Copyright (c) 2011 Thomas Graf <tgraf@suug.ch>
+#
+
+"""Dummy
+
+"""
+
+__version__ = "1.0"
+__all__ = ['assign_type']
+
+import netlink.core as netlink
+import netlink.route.capi as capi
+
+class DummyLink(object):
+       def __init__(self, link):
+               self._rtnl_link = link
+       
+def assign_type(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 (file)
index 0000000..63c234f
--- /dev/null
@@ -0,0 +1,153 @@
+#
+# Copyright (c) 2011 Thomas Graf <tgraf@suug.ch>
+#
+
+"""IPv4
+
+"""
+
+__all__ = ['']
+
+import netlink.core as netlink
+import netlink.route.capi as capi
+import netlink.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 = '\n' + fmt.nl('\t%s\n\t' % 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))
+
+        @netlink.nlattr('link.inet.forwarding', type=bool, fmt=util.bool)
+       @property
+       def forwarding(self):
+               return bool(self.get_conf(DEVCONF_FORWARDING))
+       
+       @forwarding.setter
+       def forwarding(self, value):
+               self.set_conf(DEVCONF_FORWARDING, int(value))
+
+        @netlink.nlattr('link.inet.mc_forwarding', type=bool, fmt=util.bool)
+       @property
+       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))
+
+        @netlink.nlattr('link.inet.proxy_arp', type=bool, fmt=util.bool)
+       @property
+       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))
+
+        @netlink.nlattr('link.inet.accept_redirects', type=bool, fmt=util.bool)
+       @property
+       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))
+
+        @netlink.nlattr('link.inet.secure_redirects', type=bool, fmt=util.bool)
+       @property
+       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))
+
+        @netlink.nlattr('link.inet.send_redirects', type=bool, fmt=util.bool)
+       @property
+       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))
+
+        @netlink.nlattr('link.inet.shared_media', type=bool, fmt=util.bool)
+       @property
+       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 (file)
index 0000000..1d4e03d
--- /dev/null
@@ -0,0 +1,62 @@
+#
+# Copyright (c) 2011 Thomas Graf <tgraf@suug.ch>
+#
+
+"""VLAN network link
+
+"""
+
+import netlink.core as netlink
+import netlink.route.capi as capi
+
+class VLANLink(object):
+       def __init__(self, link):
+               self._link = link
+
+       ###################################################################
+       # id
+        @netlink.nlattr('link.vlan.id', type=int)
+       @property
+        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))
+
+       ###################################################################
+       # flags
+        @netlink.nlattr('link.vlan.flags', type=str)
+       @property
+        def flags(self):
+               """vlan flags"""
+               flags = capi.rtnl_link_vlan_get_flags(self._link)
+                return capi.rtnl_link_vlan_flags2str(flags, 256)[0].split(',')
+
+       def _set_flag(self, flag):
+                i = capi.rtnl_link_vlan_str2flags(flag[1:])
+                if flag[0] == '-':
+                        capi.rtnl_link_vlan_unset_flags(self._link, i)
+                else:
+                        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 ' + self.id
+
+def assign_type(link):
+       link.vlan = VLANLink(link._link)
+       return link.vlan
diff --git a/python/netlink/route/qdisc.py b/python/netlink/route/qdisc.py
new file mode 100644 (file)
index 0000000..c8a4c6f
--- /dev/null
@@ -0,0 +1,185 @@
+#
+# Copyright (c) 2011 Thomas Graf <tgraf@suug.ch>
+#
+
+__all__ = [
+       'QdiscCache',
+       'Qdisc']
+
+import netlink.core as netlink
+import netlink.capi as core_capi
+import netlink.route.capi as capi
+import netlink.util as util
+import netlink.route.tc as tc
+
+###########################################################################
+# Link Cache
+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._c_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))
+
+       def _new_object(self, obj):
+               return Qdisc(obj)
+
+       def _new_cache(self, cache):
+               return QdiscCache(cache=cache)
+
+###########################################################################
+# Qdisc Object
+class Qdisc(tc.Tc):
+       """Network link"""
+
+       def __init__(self, obj=None):
+               self._name = "qdisc"
+               self._abbr = "qdisc"
+
+               netlink.Object.__init__(self, "route/qdisc", "qdisc", obj)
+                self._tc = capi.obj2tc(self._nl_object)
+               self._rtnl_qdisc = self._obj2type(self._nl_object)
+
+               netlink.attr('qdisc.handle', fmt=util.handle)
+               netlink.attr('qdisc.parent', fmt=util.handle)
+               netlink.attr('qdisc.kind', fmt=util.bold)
+
+       def _obj2type(self, obj):
+               return capi.obj2qdisc(obj)
+
+       def __cmp__(self, other):
+               return self.handle - other.handle
+
+       def _new_instance(self, obj):
+               if not obj:
+                        raise ValueError()
+
+                return Qdisc(obj)
+
+#      #####################################################################
+#      # add()
+#      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)
+#
+#      #####################################################################
+#      # change()
+#      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)
+#
+#      #####################################################################
+#      # delete()
+#      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)
+
+        @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)
+
+        @property
+        def _parent(self):
+               return util.kw('parent') + ' ' + str(self.parent)
+
+       ###################################################################
+       #
+       # format(details=False, stats=False)
+       #
+       def format(self, details=False, stats=False):
+               """Return qdisc as formatted text"""
+               fmt = util.MyFormatter(self)
+
+               buf = fmt.format('qdisc {kind} {handle} {_dev} {_parent}')
+
+               if details:
+                       fmt = util.MyFormatter(self)
+                       buf += fmt.format('\n'\
+                          '\t{mtu} {mpu} {overhead}\n')
+                       
+#              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
diff --git a/python/netlink/route/tc.py b/python/netlink/route/tc.py
new file mode 100644 (file)
index 0000000..ebe25e1
--- /dev/null
@@ -0,0 +1,357 @@
+
+
+#
+# Copyright (c) 2011 Thomas Graf <tgraf@suug.ch>
+#
+
+__all__ = [
+       'TcCache',
+       'Tc',
+       'QdiscCache',
+       'Qdisc']
+
+import socket
+import sys
+import netlink.core as netlink
+import netlink.capi as core_capi
+import netlink.route.capi as capi
+import netlink.util as util
+
+from netlink.route.link import 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
+
+class Handle(object):
+       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 __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
+
+###########################################################################
+# TC Cache
+class TcCache(netlink.Cache):
+       """Cache of traffic control object"""
+
+       def __getitem__(self, key):
+               raise NotImplementedError()
+
+###########################################################################
+# Tc Object
+class Tc(netlink.Object):
+       def __cmp__(self, other):
+               return self.ifindex - other.ifindex
+
+       def isroot(self):
+               return self.parent.isroot()
+
+       #####################################################################
+       # ifindex
+        @property
+        def ifindex(self):
+                """interface index"""
+                return capi.rtnl_tc_get_ifindex(self._tc)
+
+       @ifindex.setter
+        def ifindex(self, value):
+                capi.rtnl_tc_set_ifindex(self._tc, int(value))
+
+       #####################################################################
+       # link
+        @property
+        def link(self):
+               link = capi.rtnl_tc_get_link(self._tc)
+                if not link:
+                        return None
+                else:
+                        return Link._from_capi(link)
+
+        @link.setter
+        def link(self, value):
+               capi.rtnl_tc_set_link(self._tc, value._link)
+
+       #####################################################################
+       # mtu
+        @property
+        def mtu(self):
+                return capi.rtnl_tc_get_mtu(self._tc)
+
+       @mtu.setter
+        def mtu(self, value):
+                capi.rtnl_tc_set_mtu(self._tc, int(value))
+
+       #####################################################################
+       # mpu
+        @property
+        def mpu(self):
+                return capi.rtnl_tc_get_mpu(self._tc)
+
+       @mpu.setter
+        def mpu(self, value):
+                capi.rtnl_tc_set_mpu(self._tc, int(value))
+
+       #####################################################################
+       # overhead
+        @property
+        def overhead(self):
+                return capi.rtnl_tc_get_overhead(self._tc)
+
+       @overhead.setter
+        def overhead(self, value):
+                capi.rtnl_tc_set_overhead(self._tc, int(value))
+
+       #####################################################################
+       # linktype
+        @property
+        def linktype(self):
+                return capi.rtnl_tc_get_linktype(self._tc)
+
+       @linktype.setter
+        def linktype(self, value):
+                capi.rtnl_tc_set_linktype(self._tc, int(value))
+
+       #####################################################################
+       # handle
+        @property
+        def handle(self):
+                return Handle(capi.rtnl_tc_get_handle(self._tc))
+
+       @handle.setter
+        def handle(self, value):
+                capi.rtnl_tc_set_handle(self._tc, int(value))
+
+       #####################################################################
+       # parent
+        @property
+        def parent(self):
+                return Handle(capi.rtnl_tc_get_parent(self._tc))
+
+       @parent.setter
+        def parent(self, value):
+                capi.rtnl_tc_set_parent(self._tc, int(value))
+
+       #####################################################################
+       # kind
+        @property
+        def kind(self):
+                return capi.rtnl_tc_get_kind(self._tc)
+
+       @kind.setter
+        def kind(self, value):
+                capi.rtnl_tc_set_kind(self._tc, value)
+
+       def get_stat(self, id):
+               return capi.rtnl_tc_get_stat(self._tc, id)
+
+class TcTree(object):
+       def __init__(self, link, sock):
+               self._qdisc_cache = QdiscCache().refill(sock)
+
+       def __getitem__(self, key):
+               pass
+#              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))
+
+       
+
+
+###########################################################################
+# Link Cache
+class QdiscCache(netlink.Cache):
+       """Cache of qdiscs"""
+
+       def __init__(self, cache=None):
+               if not cache:
+                        cache = self._alloc_cache_name("route/qdisc")
+
+                self._c_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))
+
+       def _new_object(self, obj):
+               return Qdisc(obj)
+
+       def _new_cache(self, cache):
+               return QdiscCache(cache=cache)
+
+###########################################################################
+# Qdisc Object
+class Qdisc(Tc):
+       """Network link"""
+
+       def __init__(self, obj=None):
+               self._name = "qdisc"
+               self._abbr = "qdisc"
+
+               if not obj:
+                       self._qdisc = capi.rtnl_qdisc_alloc()
+               else:
+                       self._qdisc = capi.obj2qdisc(obj)
+
+               self._obj = capi.qdisc2obj(self._qdisc)
+               self._orig = capi.obj2qdisc(core_capi.nl_object_clone(self._obj))
+
+               Tc.__init__(self)
+
+               netlink.attr('qdisc.handle', fmt=util.handle)
+               netlink.attr('qdisc.parent', fmt=util.handle)
+               netlink.attr('qdisc.kind', fmt=util.bold)
+
+       def __cmp__(self, other):
+               return self.handle - other.handle
+
+       def _new_instance(self, obj):
+               if not obj: raise ValueError()
+                return Qdisc(obj)
+
+#      #####################################################################
+#      # add()
+#      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)
+#
+#      #####################################################################
+#      # change()
+#      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)
+#
+#      #####################################################################
+#      # delete()
+#      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)
+
+        @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)
+
+        @property
+        def _parent(self):
+               return util.kw('parent') + ' ' + str(self.parent)
+
+       ###################################################################
+       #
+       # format(details=False, stats=False)
+       #
+       def format(self, details=False, stats=False):
+               """Return qdisc as formatted text"""
+               fmt = util.BriefFormatter(self)
+
+               buf = fmt.format('qdisc {kind} {handle} {_dev} {_parent}')
+
+               if details:
+                       fmt = util.DetailFormatter(self)
+                       buf += fmt.format('\n'\
+                          '\t{mtu} {mpu} {overhead}\n')
+                       
+#              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
diff --git a/python/netlink/util.py b/python/netlink/util.py
new file mode 100644 (file)
index 0000000..d3d0167
--- /dev/null
@@ -0,0 +1,146 @@
+#
+# Utilities 
+#
+# Copyright (c) 2011 Thomas Graf <tgraf@suug.ch>
+#
+
+"""utility module for netlink
+
+"""
+
+import netlink.core as netlink
+from string import Formatter
+
+__version__ = "1.0"
+
+def _color(t, c):
+       return chr(0x1b)+"["+str(c)+"m"+str(t)+chr(0x1b)+"[0m"
+
+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 mangenta(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 bool(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, key)
+                title = None
+
+                if type(value) == 'instancemethod':
+                        value = value()
+
+               try:
+                        d = netlink.attrs[self._obj._name + '.' + key]
+
+                        if 'fmt' in d:
+                                value = d['fmt'](value)
+
+                        if 'title' in d:
+                                title = d['title']
+                except KeyError:
+                       pass
+
+                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 = 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):
+               return '\n' + self._indent
+
+       def nl(self, format_string=''):
+               return '\n' + self._indent + self.format(format_string)
diff --git a/python/setup.py b/python/setup.py
new file mode 100644 (file)
index 0000000..9ea7c15
--- /dev/null
@@ -0,0 +1,29 @@
+#!/usr/bin/env python
+
+from distutils.core import setup, Extension
+
+opts = ['-O', '-nodefaultctor']
+include = ['../include']
+
+netlink_capi = Extension('netlink/_capi',
+                         sources = ['netlink/capi.i'],
+                        include_dirs = include,
+                        swig_opts = opts,
+                        libraries = ['nl'],
+                       )
+
+route_capi = Extension('netlink/route/_capi',
+                         sources = ['netlink/route/capi.i'],
+                        include_dirs = include,
+                        swig_opts = opts,
+                        libraries = ['nl', 'nl-route'],
+                       )
+
+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'],
+     )